Java-一文读懂类的初始化和实例的初始化

类初始化和实例初始化

本文总结自哔哩哔哩视频,再次感谢老师的讲解,原视频链接:
https://www.bilibili.com/video/BV1nJ411M7ZJ?p=3&t=74

分析如下代码的输出结果

public class Son extends Father {
    private int i = test();
    private static int j = method();

    static {
        System.out.print("(6)");
    }

    Son() {
        System.out.print("(7)");
    }

    {
        System.out.print("(8)");
    }

    public int test() {
        System.out.print("(9)");
        return 1;
    }

    public static int method() {
        System.out.print("(10)");
        return 1;
    }

    public static void main(String[] args) {
        Son s1 = new Son();
        System.out.println();
        Son s2 = new Son();
    }
}

class Father {
    private int a = test();
    private static int b = method();

    static {
        System.out.print("(1)");
    }

    Father() {
        System.out.print("(2)");
    }

    {
        System.out.print("(3)");
    }

    public int test() {
        System.out.print("(4)");
        return 1;
    }

    static int method() {
        System.out.print("(5)");
        return 1;
    }
}

输出:

(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)

涉及到的知识包括:

  • 类的初始化、实例初始化
  • 方法的重写,多态

类初始化过程

  1. 一个类要创建实例需要先加载并初始化该类

    • main 方法所在的类需要先加载和初始化
  2. 一个子类初始化需要先初始化父类

  3. 一个类的初始化过程就是执行字节码中的 <clinit>() 方法

    这个方法是编译器自动生成的,由如下部分组成:

    • <clinit>() 由静态类变量显示赋值代码和静态代码块组成
    • 类变量显式赋值代码和静态块代码从上到下顺序执行
    • <clinit>() 方法只执行一次

根据上述的类初始化过程,Son 方法中包含 main() 方法,因此先尝试初始化 Son 类,但 Son 类继承自 Father 类,所以在初始化 Son 类之前初始化 Father 类。

  1. 初始化 Father 类
    • Father 类中的静态变量赋值语句 int b = method() :(5)
    • Father 类中的静态块 System.out.print("(1)"); :(1)
  2. 初始化 Son 类
    • Son 类中的静态变量赋值语句 int j = method() :(10)
    • Son 类中的静态块 System.out.print("(6)"); :(6)

到此为止,类初始化结束。即使 main() 方法中不包含任何语句,即

public void static main(String[] args){ }

也会发生上述的类初始化,结果为:

(5)(1)(10)

实例初始化过程

实例初始化就是执行 <init>() 方法

  • <init>() 方法可能重载有多个,有几个构造器就有几个 <init>() 方法

  • <init>() 方法由 非静态实例变量显式赋值代码非静态代码块、对应构造器代码组成。

  • 非静态实例变量显示赋值代码和非静态代码块按照从上到下的顺序执行,最后执行构造器代码

  • 每次 new 对象,调用对应构造器,就会执行对应的 <init> 方法

  • <init> 方法的首行是 super()super (实参列表),即对应的父类的 <init> 方法,因此子类的构造器一定会调用父类的构造器,即使没有显示地在构造器中申明 super()

方法的重写 Override

非静态方法前面其实有一个默认的对象 this,this 在构造器(或<init>)中表示正在创建的对象,如果正在实例化子类,那么 this 指的是 子类实例,所以执行的方法其实是经过子类重写的方法。

不会被重写的方法:

  • final 修饰
  • static 方法
  • private 等修饰的子类中不可见的方法

对象的多态性

  • 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的方法
  • 非静态方法默认的调用对象总是 this
  • this 在构造器(或<init>)方法中就是正在创建的对象

需要注意的是,在实例化过程中,可能会发生重写

根据上述实例初始化过程,分析 Son 类实例的初始化过程:Son 类继承自 Father 类,因此首先会调用 Son 构造器中的 super() 方法,即初始化 Father 实例

父类 Father 的实例化

  • Father 类的非静态变量的显示赋值代码 ,重载子类test() 方法 int a = test() :(9)
  • Father 类的非静态代码块 System.out.print("(3)"); :(3)
  • Father 构造器 System.out.print("(2)"); :(2)

子类 Son 的实例化

  • Son 类的非静态变量显式赋值代码 int i = test() :(9)
  • Son 类的非静态代码块 System.out.print("(8)"); :(8)
  • Son 的构造方法 System.out.print("(7)"); :(7)

到此为止,实例初始化结束,每次 new 出实例的时候,都会进行一次实例的初始化过程,因此本例中实例化过程发生两次。但是类的初始化过程只发生一次,因此第二次实例化过程不会导致类再次实例化。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值