类的初始化过程、super关键字与函数绑定

一:类的初始化过程:

1.先加载类级别的成员(静态成员变量的方法 以及 静态初始化块,这两个打印的顺序依据出现的先后顺序)
2.再加载对象级别的成员(实例初始化块、普通成员变量的方法,打印的顺序依据出现的先后顺序)
3.构造函数

具体例子:
public class ObjectInitTest {
    public ObjectInitTest() {
        System.out.println("这是构造函数alta+Insert快捷键:ObjectInitTest()");
    }
    //静态初始化块
    static{
        b=10;
        System.out.println("静态初始化块");
    }
    //实例初始化块  对实例成员变量(依赖于对象)
    {
        System.out.println("instance init block");
    }
    private int a=method1();//普通的成员变量,对象级别的
    private static int b=method2();//静态的成员是共享的,类级别的
    private int method1() {
        System.out.println("对象级别的method 1");
        return 1;
    }
    private static int method2() {
        System.out.println("静态的,类级别的method 2");
        return 2;
    }

    public static void main(String[] args) {
        /**
         * new ObjectInitTest()
         * 对象的产生过程
         * 1. JVM会在ObjectInitTest.class文件
         * 2. 先加载类级别的成员(静态成员变量的方法 以及 静态初始化块,这两个打印的顺序依据出现的先后顺序)
         * 3. 再加载对象级别的成员(实例初始化块、普通成员变量的方法,打印的顺序依据出现的先后顺序)
         * 4.构造函数
         */
        ObjectInitTest o=new ObjectInitTest();
    }
}

 继承类同理:  静态>普通        基类>继承类

先打印基类的静态方法以及静态初始化块(根据出现的先后顺序),再打印子类的静态方法及静态初始化块(先后顺序)。

再打印基类普通的方法以及普通实例化块(先后),最后打印基类的构造函数;再打印子类的普通方法及普通实例化块(先后),最后打印子类的构造函数

public class ObjectInitTest2 extends ObjectInitTest {
        private  static int c=method3();
        public static int method3(){
            System.out.println("这是继承类的静态方法");
            return 3;
        }
        static{
            System.out.println("继承类的静态初始化块");
        }
        private int d=method4();
        public int method4(){
            System.out.println("这是继承类的普通的成员方法");
            return 4;
        }
    {
        System.out.println("继承类的普通实例化块");
    }
        public ObjectInitTest2(){
            System.out.println("继承类的构造方法");
        }

    public static void main(String[] args) {
        ObjectInitTest2 o2=new ObjectInitTest2();
    }
}

 二:super关键字  

this关键字的两个用法:
* 1.在构造函数中调用如 this(10),调用带int参数的其它的构造函数
* 2.在成员方法里面访问其它成员,前面可以添加this.
*
* super关键字的三个用法:
* 1.在派生类的构造函数中,调用super(10),调用基类的带int参数的构造函数
* 2.super.func() 在派生类中,调用从基类继承来的func方法
* 3.super.a 在派生类中,调用从基类继承来的名字叫a的成员变量
*
* this和super关键字都和对象有关,所以在static方法中不能使用这两个关键字

 实例:

/**
 * 描述:
 * OOP(面向对象编程)中两个类常有的关系有两种:
 * 组合关系:满足a part of... 一部分   一个类定义的对象称为另一个类的成员变量   has a..的关系
 * A类是B类的一部分,A和B就设计成组合关系,设计如下
 * class A{}
 * class B{
 *     A a;  // B组合了A对象   A是B的成员变量
 *     public B{
   *         this.a = new A():
 *     }
 * }
 *
 * 继承关系:满足  a kind of... 一种的关系    用extends继承     is a..的关系
 * A是一个已经定义的类,B是A的一种,那么设计成继承关系,如下:
 * class A{}
 * class B extends A {
 *
 * }
 *
 * OOP语言的四大特征是什么?
 * 抽象   封装    继承    多态
 *         三大特征是什么?
 *       封装    继承    多态
 * 封装/隐藏 : 通过类的访问限定符实现的   private    public
 *
 *
 * 继承的意义之一:代码的复用
 *
 * 两个类什么时候设计成继承?
 * 派生类对象的初始化过程?
 * 基类的成员在派生类中如何访问?
 * 在派生类中如何调用基类的成员?
 *
 * this关键字的两个用法:
 * 1.在构造函数中调用如 this(10),调用带int参数的其它的构造函数
 * 2.在成员方法里面访问其它成员,前面可以添加this.
 *
 * super关键字的三个用法:
 * 1.在派生类的构造函数中,调用super(10),调用基类的带int参数的构造函数
 * 2.super.func() 在派生类中,调用从基类继承来的func方法
 * 3.super.a 在派生类中,调用从基类继承来的名字叫a的成员变量
 *
 * this和super关键字都和对象有关,所以在static方法中不能使用这两个关键字
 *
 * private和protected的区别?
 * 1.它们两个修饰的成员,在类的外部都不能访问
 * 2.基类的private成员,在派生类中无法访问;基类的protected成员,在派生类中可以访问
 * @Author hui
 * @Date 2019/10/15
 */
public class DeriveTest {
    public static void main(String[] args) {
       // A a=new A();
        B b=new B(6);
        b.show();
    }
}
class A{
    protected int a;
    static {
        System.out.println("A的静态块");
    }
    {
        System.out.println("A的实例化块");
    }
    public A(){
        System.out.println("A的无参数构造函数");
        this.a=0;
    }
    public A(int a){
        System.out.println("A的int参数的构造方法");
        this.a=a;
    }
    public void show(){
        System.out.println("A.show is a:"+a);
    }
}
/**
 * 派生类B有两部分
 * 1.从基类继承来的成员
 * 2.自己定义的成员
 *
 * super()和this()都必须写在第一行
 */
class B extends  A{
  private int a;
  private int b;
  public B(int data){
      super(4);//将4传给基类的a
      this.a=data;//给B的a进行初始化赋
      System.out.println("B的带参数的构造函数int(data)");
  }
  static {
      System.out.println("派生类B的静态块");
  }
    {
        System.out.println("B的实例化方法");
    }
    //重写父类的方法,打印基类与自己的a变量
    public void show(){
        System.out.println("A.show a is:"+super.a);
        System.out.println("B.show is a:"+a);
    }
    //调用基类的方法super关键字
    void testShow2(){
      super.show();
    }

}

 

 

三:函数的动态绑定

实例:

/**
 * 函数的静态绑定和动态绑定是什么?
 * 绑定(函数调用)
 * invokestatic指令就是在以静态绑定的方法,调用函数
 * invokevirtual指令就是在以动态绑定的方法,调用函数
 *
 * static方法都是静态绑定调用
 * 实例方法都是动态绑定调用
 *
 * 静态绑定,指的是编译时期的绑定,编译阶段,这个方法的调用就是确定好的,永不不会再改变
 *
 * 动态绑定,指的是运行时期的绑定,就是在编译阶段,此处调用哪个函数,是不确定的,
 *
 * final的应用场景有三个:
 * 1.final int data = 10; 可以用来定义常量
 * 2.final可以修饰类称作密封类,不能再被继承
 * 3.final可以修饰类的实例方法,称作密封方法,
 *        表示该方法不能再派生类中重写(覆盖)
 */

class Base{
    protected int a;
    private String str;
    private Integer data;

    public Base(int val){   //构造函数
        System.out.println("Base(val)");
        this.a = val;
    }

    public static void show(){   //静态方法
        System.out.println("static Base.show");
    }


    public void func(){   //实例化方法
        System.out.println("instance Base.func");
    }

    public void func(int data){  //方法的重载,在同一个类里进行
        System.out.println("instance Base.func data");
    }
}
    /**
        * 继承结构中,基类和派生类的方法通常有两种关系:重载和重写
        * 重载:在一个类作用域中,函数名相同,参数列表不同
        * 重写:在基类和派生类中,出现返回值相同,函数名相同,参数列表也相同的实例方法
        * 重写指的是派生类方法表中,派生类提供的重写方法,把基类相应的方法的地址给重写了(覆盖了)
        */
class E extends Base{
        //E的构造函数调用父类的构造函数
    public E(int val) {
        super(val);
        System.out.println("派生类E的构造函数");

    }
    //派生类重写基类的方法
    public static void show(){
        System.out.println("static E.show");
    }

    // 构成重写(覆盖)的关系
    public void func(){
        System.out.println("instance E.func");
    }
}


/*
 *基类引用,可以引用派生类对象
 *派生类引用,不可以引用基类对象
 *
 *把基类和派生类的继承结构,也经常称作从上到下的继承结构,
 *继承结构中的类型,只支持从下到上的转换,不支持从上到下的转换
 */
public class FunctionBond {
    public static void main(String[] args) {
        E e = new E(20); // invokespecial 构造函数,调用派生类E的构造函数
        E.show(); // invokestatic 静态绑定
        e.func(); // invokevirtual 实例方法

        Base b = new E(20);  //动态绑定,调用派生类E的构造函数
        Base.show(); // Base.show
        b.func(); // 调用派生类的方法



    }
}

反汇编指令:javap -c 类名.class

 

实际的调用分析:

 

 注:基类可以引用派生类对象,派生类不可以引用基类对象

(举例:你此时需要一个人,来一个教授可以满足;但是当你需要一个教授,随便来一个人则不能满足需求)


原文地址:https://www.cnblogs.com/laurarararararara/p/11676773.html