1. 继承基础
所谓就是继承,就是子类拥有父类的所有的 属性 和 方法 (包括private),同时还能定义自己独有的属性和方法。
继承使用关键字 extends 完成,其格式如下:
class Student extends Person {
// ...
}
然后,类Student就会拥有类Person的所有属性和方法,但是构造器另说,这个后面会讨论。<br /><br />虽然子类可以继承父类的 private 的结构,但是由于封装性的影响,子类并不能直接访问父类的private属性或者方法,依然需要通过public方法来调用。<br /><br />子类继承父类后,可以随意定义自身独特的属性和方法,因此子类功能是大于父类的。
2. 继承的规定
Java只支持单继承和多层继承,不允许多重继承(C++允许)
- 一个子类只能有一个父类:单继承
- 一个父类可以派生出多个子类
虽然不允许多重继承,但是这样的功能可以通过接口实现,后续会讲。
3. Object 类
所有类的爹。
- 如果我们显式的声明一个类的父类的话,则此类继承于 java.lang.Object类
- 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
- 意味着,所有的java类具有java.lang.Object类声明的功能
4. 方法重写
子类继承父类之后,可以对父类同名同参数的方法,进行覆盖操作,但是不影响父类,这就叫作重写。
class Person {
// 属性
private String name;
private int age;
// 方法
public void ShowMe(){
System.out.println("Person");
}
}
class Student extends Person{
public void ShowMe(){ // override (方法重写)
System.out.println("Student");
}
}
public class test {
public static void main(String[] args) {
Person p1 = new Person();
Student s1 = new Student();
p1.ShowMe();
s1.ShowMe();
}
}
【重写的规定】
- 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
- 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
- 特殊情况:子类无法重写父类中声明为 private 权限的方法
- 返回类型:
- 若父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
- 若父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型只能与父类相同
- 若父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A的子类
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型去
- 子类和父类中同名同参数的方法的静态性和动态性必须一致,要么都是static,要么都不是static。而且,只有非 static 的方法能被重写,static方法是一定不能被重写的。
5. toString()
可以看到,调用p1能直接输出对象,是因为 Object 类中有 toString() 方法,而 System.out.println(p1) ;相当于 System.out.println(p1.toString()) ; 这个方法的返回值为一个 String,作用就是用字符串的形式返回当前对象的一些信息。
不过一般都不用原生的 toString ,因为输出结果不是我们想要的,因此一般都需要重写 toString:
6. super 关键字
6.1 调用属性和方法
现有一个情景:当子类重写了父类的方法A后,以后默认使用已经重写的方法A,那么还能使用未被重写的原方法A吗?如果可以该怎么使用? => super
this 可以理解为当前对象的,super可以理解为父类的,可以认为 super 是 this 的爹。
举个例子,当子类又定义了和父类同名的属性或方法时,这在内存中是不冲突的,此时,如果不加任何关键字,默认为 this,即当前子类的属性或方法,如果想调用父类的属性或方法,就需要用到super
package pkg1;
class Person {
// 属性
String name;
int age;
// 方法
public void ShowMe(){
System.out.println("Person");
}
}
class Student extends Person{
int age ; // 子类的属性和父类的属性不会冲突
public Student(int d){
age = d ;
super.age = d*2 ;
}
public void ShowAge(){ //
System.out.println("Child age:"+ age);
System.out.println("Father age:"+super.age);
}
}
public class test {
public static void main(String[] args) {
Person p1 = new Person();
Student s1 = new Student(10);
s1.ShowAge();
}
}
6.2 调用构造器
- 当父类存在构造器时,子类一定要存在自定义的构造器,且必须要通过 super(…) 来调用父类的构造器,调用形式和 this(…) 一致
- 特殊情况:父类构造器无参时,子类可以不用写,这时默认使用了 super( ) ; 也即,若子类构造器首行没有显式的 super,则默认调用了父类的无参构造器
- super(…) 的使用,必须声明在子类构造器的首行
- 在定义类的构造器时,this(…) 和 super(…) 只能二选一,不能同时出现 ```java package pkg1;
class Person { // 属性 String name; int age; // 方法 // 构造器 public Person(String name){ this.name = name; } }
class Student extends Person{ int age ; // 子类的属性和父类的属性不会冲突 public Student(int d,String n){ super(n); // 调用父类的构造器 this.age = d; super.age = d*2; } public void showMe(){ System.out.println(“this.age:”+this.age); System.out.println(“super.age:”+super.age); System.out.println(“name:”+name); } }
public class test {
public static void main(String[] args) {
Student s1 = new Student(10,”zhangsan”);
s1.showMe();
}
}
```
7. 子类对象化的过程解析
- 从结果上来看(继承性)
- 子类继承父类以后,就获取了父类中声明的全部属性或方法。
- 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
- 从过程上来看
- 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接地调用其父类地构造器,进而调用父类的父类的构造器,一直往前延伸,直到调用了 java.lang.Object (万类之爹)类中无参的构造器为止。
- 正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
- 注意:虽然类一直延申到 Object 类,但是对象始终只有一个对象,就是 dog。