继承

 一种机制,可以进行代码的重用 。允许重用现有类(基类(base class)),亦称超类(super class)、父类(parent class)创建新类(子类(sub class)),亦称派生类(derived class)、孩子类(child class)的过程。子类的对象拥有父类的全部属性与方法,称作子类对父类的继承。
在java中,一个子类只能有一个基类(单一继承),单一继承减少了代码的复杂性,使代码更可靠。不支持多重继承,即一个派生类只能继承于一个基类。

  • 派生类的声明:
class People{
   public  String name;
   public int age;
}
class Man extends People{
    public int age;
}
  • super关键字:super关键字用于从派生类访问基类的成员。
    (1)super(参数):调用基类的构造函数;调用构造函数,构造函数不会被继承,必须放在第一行;
    (2)super.date :访问基类的数据成员;
    (3)super.func() :访问基类的成员方法。
class Base{
    public  int ma;
    public Base(int ma){    //基类的构造函数/构造方法
        this.ma = ma;
    }
    public void fun1(){     //基类的实例方法
        System.out.println("Base.fun1()");
    }
    public void fun2(){
        System.out.println("Base.fun2()");
    }
}
class Derieve extends Base{ 		//派生类继承基类   调用构造函数
    private int mb;
    public Derieve(int a,int b){    //派生类的构造函数
        super(a);					//super 关键字,调用构造函数
        super.ma = 10;     			//访问基类的数据成员ma
        //mma = a;error
        super.fun1();       		//访问基类的实例方法
        this.mb = b;
        System.out.println("Derieve.init{}");
    }
}
public class Inherit {
    public static void main(String[] args) {
        Base base = new Base(100);
        Derieve derieve = new Derieve(1000,9999);
        base = derieve;//多态的基础
        //derieve = base;error
    }
}
  • 派生类构造对象的初始化过程及顺序:
class Base{
    public  int ma;
    public Base(int ma){    //基类的构造函数
        fun1();
        System.out.println("Base.init()");//构造函数初始化
        this.ma = ma;
    }
    static {
        System.out.println("Base.static{}");//基类静态块初始化
    }

    {
        System.out.println("Base.instance{}");//基类实例块初始化
    }
    public void fun1() {
        System.out.println("Base.fun1()"+ this.ma);
    }
    public static void fun2() {
        System.out.println("Base.fun2()");
    }
}
class Derieve extends Base{ //派生类继承基类   调用构造函数
    private int mb;
    public Derieve(int a,int b){    //派生类的构造函数
        super(a);//super 关键字,调用构造函数,构造函数不会被继承,必须放在第一行
        super.ma = 10;      //访问基类的数据成员ma
        //mma = a;error
        super.fun1();       //访问基类的实例方法
        this.mb = b;
        System.out.println("Derieve.init{}");//构造函数
    }
    static {
        System.out.println("Derieve.static{}");//静态块
    }

    {
        System.out.println("Derieve.instance{}");//实例块
    }
    public void fun1() { System.out.println("Derieve.fun1()");
    }
    public void fun1(int a) { System.out.println("Derieve.fun1(int)");
    }
    public static void fun2() {
        System.out.println("Derieve.fun2()");
    }
}

 基类初始化顺序:静态块实例化–> 实例块初始化–> 构造函数初始化;

  • 派生类初始化顺序:
    基类静态块实例化 ----》派生类静态块初始化 ----》
    基类实例块初始化 ----》基类构造函数初始化 ----》派生类实例块初始化 ----》派生类构造函数初始化
Base.static{}
Derieve.static{}
Base.instance{}
Base.init()
Derieve.instance{}
Derieve.init{}
  • 基类的数据成员在派生类中的访问权限:
  • 基类和派生类之间的相互赋值: 基类 = 派生类 //多态的基础
base = derieve;
  //derieve = base;error

多态

  对象的多态性是指在父类中定义的属性或方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为。
 基类引用了派生类的对象,并且,基类和派生类都有同名的覆盖方法。

  • java中提供两种多态机制: 重载和重写
  • Java的多态性体现在两个方面:
    由方法重载实现的静态多态性(编译时多态)
    方法重写实现的动态多态性(运行时多态)
    编译时多态:在编译阶段,具体调用哪个被重载的方法,编译器会根据参数的不同来静态确定调用相应的方法。
    运行时多态:由于子类继承了父类所有的属性(私有的除外),所以子类对象可以作为父类对象使用。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个对象可以通过引用子类的实例来调用子类的方法。
  1. 重载:函数名相同,参数列表不同,与函数返回值无关。(overloade)
  • 并不一定在同一个类当中,继承关系上也可构成重载。
  • 这种编译时确定的模式,又称“静态绑定”。
class Base{
	public void fun1() {
        System.out.println("Base.fun1()");
    }
    public static void fun2() {
        System.out.println("Base.fun2()");
    }
}
class Derieve extends Base{  
    public void fun1() { System.out.println("Derieve.fun1()");
    }
    public void fun1(int a) { System.out.println("Derieve.fun1(int)");
    }//重载的方法
    public static void fun2() {
        System.out.println("Derieve.fun2()");
    }
}
  1. 重写/覆盖: 函数名相同,参数列表相同,函数返回值相同。(overwrite)
  • 程序运行时,根据运行时对象的类型,调用相应类实现重写的方法。
  • 这种运行时确定的模式,称为“动态绑定”。
class A{
    public void GetVal(){
        System.out.println("Parent");
    }
}
class B extends A{
    public void GetVal(){
        System.out.println("Child");
    }
}
public class JICheng {
    public static void main(String[] args) {
        B b = new B();
        A a =(A)b;//将b强制转换为A的类型,相当于A a = new B();
        a.GetVal();
    }
}
Child

基类引用派生类对象,打印派生类方法。

  • 面试问题:构造函数内能否发生多态?
  • 可以发生多态
class Base {
    private int ma;
    public Base(int ma) {
        this.ma = ma;
    }
    public void fun1() {
        System.out.println("Base.fun1()");
    }
    public static void fun2() {
        System.out.println("Base.fun2()");
    }
}
class Derieve extends Base {
    private int mb;
    public Derieve(int a,int b) {
        super(a);   //调用基类的构造函数
        this.mb = b;
    }
    public void fun1() {
        System.out.println("Derieve.fun1()");
    }
    public static void fun2() {
        System.out.println("Derieve.fun1()");
    }
}
public class DuoTai {
    public static void main(String[] args) {
        Base base = new Base(10);
        base.fun1();
        Derieve derieve = new Derieve(1,2);
        derieve.fun1();
    }
}
Base.fun1()
Derieve.fun1()

对其进行反编译:

java派生类构造函数 java派生子类_多态

  • 基类引用派生类对象时,运行期间的多态,又叫动多态。
    注意:基类通过实例化一个派生类的对象,看到的是基类的实例调用基类的方法,而反编译实际运行的是派生类的方法,因为基类的方法地址覆盖了派生类的地址。
Base base = new Derieve();
base.fun1();//静多态      
base.fun2();//动多态 Derieve.fun1();

java派生类构造函数 java派生子类_java派生类构造函数_02


反编译:invokespecial 构造函数 invokevirtual 一般函数 invokestatic 静态方法

动多态:发生在运行时—invokevirtual

静多态:发生在编译时—invokestatic

  • 总结:

内存分配:
(1)在编译时生成方法表,一个类型只能产生一个方法表。
(2)方法表和类型一一对应,class对象与类型一一对应。

java派生类构造函数 java派生子类_覆盖_03

动多态:RunTime—Type—Information(RTTI 机制)
Over…在动多态时,通过反编译观察运行,好好理解。