继承
继承是一种机制,可以进行代码的重用,允许重用现有类创建新类。
现有类被称作超类、基类、父类。
新类被称作子类、派生类、孩子类。
子类将继承父类的所有数据和行为,还可以定义其他的数据或行为。
子类继承了父类的除构造函数以外的所有属性,与基类成员的访问修饰符无关
- 类只支持单继承,不允许多继承
- 多个类可以继承一个父类
super关键字
用于从派生类中访问基类的成员
- super(): 调用基类的构造函数
- super.data: 访问基类的数据成员
- super.fun(): 调用基类的成员方法
构造方法的继承和调用
- 构造方法不能被继承,也不能被覆盖,因为构造方法的名称与类名相同。
- 通过关键字new创建对象实例时,会根据传入的额参数构造匹配方法。在调用构造方法前,需要先调用其基类的构造方法。
- 如果使用派生类的默认构造方法构造对象实例,则会自动调用其基类的默认构造方法。如果基类没有默认的构造方法,则会导致编译错误。
- 如果使用派生类的带参数的构造方法构造对象实例,则必须在派生类的构造方法的第一条语句中,显示地使用super关键字调用其基类的构造方法,否则会产生编译错误。
class SuperClass{
public String name;
public String sex;
public SuperClass(String name,String age){
this.name = name;
this.sex = sex;
System.out.println("调用SuperClass的构造函数SuperClass()..");
}
}
//======派生类1 编译错误,SuperClass无默认构造方法========
class SuperClass1 extends SuperClass{
public int age;
}
//==========派生类1 编译错误,SuperClass无默认构造方法=========
class SuperClass2 extends SuperClass{
public int age;
public SuperClass2(){//派生类2的构造方法,默认构造方法
//自动调用基类默认构造方法,基类无默认构造方法,会导致编译错误
System.out.println("调用SuperClass2的构造函数SuperClass2()..");
}
}
//====================编译正确===========================
class SuperClass3 extends SuperClass{
public int age;
public SuperClass3(String name,String sex){
super(name,sex);//需要显示调用基类构造方法,注释则会有错误
this.age = age;
System.out.println("调用SuperClass3的构造函数SuperClass3()..");
}
}
派生类构造对象的初始化顺序
静态块初始化(一次) → 实例块初始化 → 构造方法初始化。
//父类
class Base{
private int ma;
public Base (int ma) {
System.out.println("Base.构造方法");
this.ma = ma;
}
static {
System.out.println("Base.静态块");
}
{
System.out.println("Base.实例块");
}
}
//基类
class Derieve extends Base {
private int mb;
public Derieve(int a,int b) {
super(a);//必须放在第一行,注释此语句将导致错误
/*super.ma = 10;
super.fun1();*/
//ma = a;error
this.mb = b;
System.out.println("Derieve.构造方法");
}
static {
System.out.println("Derieve.静态块");
}
{
System.out.println("Derieve.实例块");
}
}
public class Demo9 {
public static void main(String[] args) {
Derieve derieve = new Derieve(8,9);
}
}
//运行结果
Base.静态块
Derieve.静态块
Base.实例块
Base.构造方法
Derieve.实例块
Derieve.构造方法
- 基类的静态代码块
- 派生类的静态代码块
- 基类的实例代码块
- 基类的构造方法
- 派生类的示例代码块
- 派生类的构造方法
(public)基类的数据成员在派生类当中的访问权限
同包子类 | 同包非子类 | 不同包子类 | 不同包非子类 | |
---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
private | 不可以 | 不可以 | 不可以 | 不可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
默认权限 | 可以 | 可以 | 不可以 | 不可以 |
默认权限为包访问权限
多态
基本概念
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
多态----基类引用了派生类对象,并且基类和派生类有同名的覆盖方法
构造函数内能发生多态
多态的基础
基类和派生类之间的相互赋值-----向上转型(拿一个子类的对象当做父类的对象来用)
//子类的对象可以被当做父类的对象使用,子类的对象可以赋值给父类的变量
//父类的对象不能赋值给子类的变量!
base = derieve;
//derieve = base;error
//可以用造型(只有当v这个变量实际管理的是Car才行)
c = (Car) v;
方法的多态性
- 重载(overloade)
- 函数名相同,参数列表不同,和返回值无关
- 并不一定在同一个类当中,继承关系上也可以
- 重载方法在编译时绑定,又称为“静态绑定”
- 重写/覆盖(Override)
- 函数名相同,参数列表相同,函数返回值相同
- 可以遵守协变类型(就是基类1中a方法返回的是基类2的一个实例,那么在派生类1中的a方法就可以返回派生类2的一个实例)
- 重写方法在程序运行时绑定,称为“动态绑定”
- 派生类中的重写方法不能缩小基类中被重写方法的访问权限。权限顺序为public、protected、private
- 基类的abstract方法必须被派生类重写,否则派生类也必须为abstract
- 基类的final方法不能被派生类重写
动多态和静多态
当基类实例指向派生类对象的时候,拿实例去调用被重写的实例方法,计算机只有在运行的时候才知道要去调的是基类的还是派生类的方法这就是动多态,期间会发生多态绑定。
RTTI机制
方法表和类型一一对应 (方法是在编译的时候生成的)
class对象和类型一一对应
反汇编时看到的是基类的实例调用基类的方法,而运行的是派生类的方法
invokevirtual是调用实例方法;
Involespeacial是调用构造方法;
invokestatic是调用静态方法;
动多态----发生在运行时—invokevirtual
静多态—发生在编译时—invokestatic
public static void main(String[] args) {
Base base = new Derieve(1000,9999);
base.fun1();//动多态
Base.fun2();//静多态
}