先了解基本数据成员在子类中的访问权限:
访问修饰符 | 同包子类 | 同包非子类 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|
public(公共的) | √ | √ | √ | √ |
protected (受保护的,主要体现在继承) | √ | √ | √ | × |
默认(就是不写访问修饰符的时候) | √ | √ | × | × |
private(私有的) | × | × | × | × |
1、继承
继承是面向对象中的一个非常重要的特性,通过继承,子类可以使用父类中的一些成员变量和方法,从而提高代码的复用性(就是说我可以有多个子类去继承这个父类,这个父类的代码就是通用的,就不必要在子类中再写一次了,子类中我只写我需要的一些特性代码就可以了),提高开发效率。被继承的类叫父类或基类,继承的类叫派生类或子类。子类通过extends继承父类。
注意:构造派生类对象先构造基类对象
继承主要有以下几大特性:
- java语言不支持多重继承,也就是说,子类最多只能继承一个父类(那怎么办呢,可以通过实现多个接口来达到多重继承的目的)。
- 子类只能继承父类非私有(不是private修饰的)的成员变量和方法,当然,构造函数函数也不能被子类继承,但是子类可以拿super()去调用父类构造。
- 当子类中定义的成员变量和父类中的成员变量同名时,子类中的成员变量会覆盖掉父类中的成员变量,而不会继承。
- 当子类中的方法与父类中的方法有相同的函数签名(相同的方法名、相同的参数列表)时,子类将会覆盖掉父类中的方法(也就是重写了父类的方法),而不会继承。
组合和继承的区别:
组合 | 组合是指在新类里面创建原有类的对象重复利用已有类的功能(就是我们最常用的用法) |
---|---|
继承 | 子类可以通过继承使用父类中的一些成员变量和方法,从而提高代码的复用性 |
顺便说一下耦合和内聚:
它们是模块独立程度的两个定标准:
- 耦合衡量不同模块彼此间互相依赖(连接)的紧密程度,耦合要低,即每个模块和其他模块之间的关系要简单;
- 内聚衡量一个模块内部各个元素彼此结合的紧密程度,内聚要高,每个模块完成相应的子功能;
关键字super:
super():调用基类的构造函数 必须放在第一行
super.X:访问基类的数据成员
super.X():调用基类的成员方法
派生类构造对象的初始化顺序:
代码执行顺序:静态代码块 实例代码块 构造方法
- 基类的静态代码块
- 派生类的静态代码块
- 基类的实例代码块
- 基类的构造函数
- 派生类的实例代码块
- 派生类的构造方法
基类和派生类之间的相互赋值:
支持向上转型 多态的基础
2、多态
多态发生的条件:基类引用,引用了派生类对象,并且,基类和派生类有同名的覆盖方法,构造函数内(基类构造函数里面调用基类实例方法)也能发生多态。
例如:Base base = new Derieve();//Base是基类,Derieve是子类
重写/覆盖:函数名相同,参数列表相同,返回值相同(可以遵守协变类型(就是基类1中x方法返回的是基类2的一个实例,那么在派生类1中的x方法就可以返回派生类2的一个实例),也就是指返回值也构成继承关系)
重载:函数名相同,参数列表不同,和返回值没有关系,并不一定是在同一个类中,在继承关系上也能构成重载
覆盖和重载的区别:
区别 | 覆盖(重写) | 重载 |
---|---|---|
方法间的关系 | 子类与父类之间,垂直关系 | 同一个类中方法之间,平行关系 |
方法间的关系 | 只是一对方法之间的关系 | 多个方法之间的关系 |
参数列表 | 参数列表必须相同 | 参数列表必须不同 |
调用时 | 根据对象的类型决定(对象对应储存空间类型) | 根据调用时的实参表与形参表来选择 |
访问权限 | 不能比基类更高 | 随便 |
返回值类型 | 必须相同 | 随便 |
静多态——静态绑定:
如果是private方法、static方法、final方法或者构造函数,那么编译器在编译时就可以准确知道要调用的是哪个方法,这种调用方式称为静态绑定;
动多态——动态绑定:
当基类实例指向派生类对象的时候,拿实例去调用被重写的实例方法,计算机只有在运行的时候才知道要去调的是基类的还是派生类的方法这就是动多态,期间会发生多态绑定。
注解:class对象不同于一般对象,它不在堆中,class对象(放的是当前对象的信息)放在方法区,class对象、方法表和类型一一对应(同一个类型只有一个class对象和方法表),方法表是在编译的时候产生的(所以静多态就是编译的时候就已经知道要去调用哪个方法了)。
代码示例:
class Base {
protected int data;
public Base (int data) {
this.x1(); //这一句可以验证构造函数内也能发生多态
this.data = data;
}
public void x1() {
System.out.println("Base.x1()");
}
public static void x2() {
System.out.println("Base.x2()");
}
}
class Derieve extends Base {
private int count;
public Derieve(int a,int b) {
super(a);//必须放在第一行
System.out.println("Derieve.init{}");
}
public void x1() {
System.out.println("Derieve.x1()");
}
public void x1(int a) {
System.out.println("Derieve.x1(int)");
}
public static void x2() {
System.out.println("Derieve.x2()");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Base base = new Derieve(1000,9999);
base.x1();//动多态 运行的时候
Base.x2();//静多态 编译的时候
}
}
运行结果:
Derieve.x1() //运行的是派生类的重写方法
Derieve.init{}
Derieve.x1() //运行的是派生类的重写方法
Base.x2()
看看这个代码的反汇编: