一、继承
当不同类之间存在大量重复成员时,可以使用继承来进行共性抽取,实现代码复用。
继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性 的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了 由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
基础语法
表示类之间的继承关系,需要借助extends关键字。这样子类就会将父类中的成员变量或成员方法继承到子类中了。
修饰符 class 子类 extends 父类{
//...
}
子类中访问父类的成员变量
当子类和父类有同名的成员变量时,优先访问子类的。
1.子类和父类不存在同名成员变量
public class Base {
int a;
int b;
}
public class Derived extends Base{
int c;
public void method(){
a = 10; // 访问从父类中继承下来的a
b = 20; // 访问从父类中继承下来的b
c = 30; // 访问子类自己的c
}
}
2.子类和父类成员变量同名
public class Base {
int a;
int b;
int c;
}
public class Derived extends Base{
int a; // 与父类中成员a同名,且类型相同
char b; // 与父类中成员b同名,但类型不同
public void method(){
a = 100; // 访问父类继承的a,还是子类自己新增的a?
b = 101; // 访问父类继承的b,还是子类自己新增的b?
c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
// d = 103; // 编译失败,因为父类和子类都没有定义成员变量b
}
}
在子类方法中通过子类对象访问成员时:
如果访问的成员变量子类中有,优先访问自己的成员变量。
如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
子类中访问父类的成员方法
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
public class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodB(); // 访问子类自己的methodB()
methodA(); // 访问父类继承的methodA()
// methodD(); // 编译失败,在整个继承体系中没有发现方法methodD()
}
}
总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错。
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错。
如果子类和父类的方法同名,且构成重载,则按照合适的方法进行访问。
如果没有构成重载,则是构成重写,会优先访问子类的方法。
如果我们想访问父类的同名成员方法,可以使用super.同名方法,就可以访问父类的成员方法了。
二、super关键字
作用:在子类方法中访问父类的成员
super只能在非静态的方法中使用,因为
静态方法不依赖对象。super从父类中访问成员对象,与static修饰矛盾,所以不能。
还可以使用super调用父类的构造。
super和this的异同:
相同点:
1. 在构造方法中调用时,必须是构造方法的第一句,且this和super不能同时存在
2. 都只能在非静态方法中使用
3. 都是关键字
不同点:
1. this是当前对象的引用,super是子类对象从父类继承下来部分成员的引用
2. 在构造方法中一定会存在super()的调用,不管有没有显式写,都有。而this()不写就不会调用
3. this()用于调用自己类的构造方法,super()用于调用父类的构造方法,两个不能同时使用
4. 在非静态的成员方法中,this是用来访问本类的方法和属性的,super是用来访问从父类继承下来的方法和属性的。
三、子类对象的构造方法
子类对象构造时,先要帮助父类成员进行初始化。
因为子类对象成员由 继承下来的父类成员 和 子类自己新增的成员 构成,所以子类对象构造时,需要先调用父类构造方法,将从父类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。
要注意:
1. 如果父类执行默认的构造方法,那么子类构造方法的第一行默认含义super()的调用
2. 如果父类构造方法是带有参数的,此时编译器给子类不会执行默认的构造方法,这时就需要程序员自己给子类定义构造方法。
3. 在子类构造方法中,super()调用父类构造时,必须是子类构造方法的第一条语句。
4. super()只能在子类构造方法中出现一次,并且不能和this同时出现。
四、继承中,代码的执行顺序
先执行父类和子类中的静态代码块(注意,静态代码块只执行一次),再执行父类的实例和构造,再执行子类的实例和构造。
五、final关键字
final修饰变量,则表示其不能被修改了。
final修饰类,则表示这个类不能被继承(final写在类的最前面)。
final修饰方法:表示该方法不能被重写。
六、多态
多态即同一行为具有不同的表现形式或形态。
也就是对于同一个接口,使用不同的实例可能会执行不同的操作。
6.1 实现多态的条件
1. 必须在继承体系里
2. 必须有通过父类的引用来调用重写的方法
3. 子类必须要对弗雷中的方法进行重写
重写和重载的区别?
重载是。
重写是。
重写的条件:
1. 子类和父类的方法返回类型一样。但是其实被重写的方法返回值类型可以不同,但是必须要具有父子关系。(子类的返回值也可以不同,但要满足,子类的返回值和父类的返回值是父子类关系,也可以叫协变关系。)
2. 方法的方法名一样
3. 方法的参数类型、个数、顺序相同
4. static方法不能被重写
5. private修饰的方法不能被重写。
6. 子类访问修饰符权限要大于等于父类
重写的方法,记得使用@Override注解来进行合法性校验。这样我们没有完成重写时,编译器会报错。
重写是子类和父类的一种多态性表现。
重载是一个类的多态性表现。
6.2 静态绑定和动态绑定
静态绑定:也称前期绑定(早绑定),即在编译时,根据用户所传递实参类型,就确定了调用哪个方法。例如:函数重载。
动态绑定:也称为后期绑定(晚绑定)。在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用哪个类的方法。