深入理解Java类的初始化和实例化

本文详细探讨了Java中类的初始化和实例化过程,包括类加载、<clinit>()方法的执行、静态和非静态变量的初始化顺序,以及<init>()方法在构造对象时的作用。在类初始化阶段,会先初始化父类,然后执行静态代码块和静态变量的赋值。在实例化过程中,<init>()方法根据构造器执行相应的代码,并遵循显示赋值和代码块的顺序。此外,文章还强调了非静态方法的多态性和不可重写的方法类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化

抛砖引玉

下面先来看一个例子吧

先来看父类 Father.java

public class Father {
    private int i = test();

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

    private static int j = method();


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

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


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

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

再看看子类 Son.java

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

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

    Son() {
//		super();//写或不写都在,在子类构造器中一定会调用父类的构造器
        System.out.print("(7)");
    }

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

    @Override
    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) {
    }
}

运行子类中的main函数,来看看结果

(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)
Process finished with exit code 0

类的初始化过程

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

  • main方法所在的类需要先加载和初始化

下面来改写Son.java中的main方法来看看:

main方法:

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

main方法中没有任何执行语句,就是一个空的main方法

来看看执行结果:

(5)(1)(10)(6)
Process finished with exit code 0

对于上面的代码,这里因为没有new任何对象,所以这里执行的都是类的初始化:

  • 父类的初始化<clinit>:

    • (1)j = method();
    • (2)父类的静态代码块
  • 子类的初始化<clinit>:

    • (1)j = method();
    • (2)子类的静态代码块

这也就对应了运行结果上面的 (5)(1)(10)(6)

  • 先初始化父类:(5)(1)
  • 初始化子类:(10)(6)

一个子类要初始化需要先初始化父类

一个类初始化就是执行<clinit>()方法

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

这里注意静态变量和静态代码块的顺序是按照代码的顺序来的,比如调换一下Father.java中二者的顺序,变成这样:

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

    private static int j = method();

上述main方法为空的情况下的执行结果如下:

(1)(5)(10)(6)
Process finished with exit code 0

可以看到之前的(5)(1)变成了(1)(5)。

类的实例化过程

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

  • <init>()方法可能重载有多个,有几个构造器就有几个<init>方法
  • <init>()方法非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成
  • 非静态实例变量显示赋值代码和非静态代码块代码从上到下顺序执行,而对应构造器的代码最后执行
  • 每次创建实例对象,调用对应构造器,执行的就是对应的<init>方法
  • <init>方法的首行是super()或super(实参列表),即对应父类的<init>方法

对于文章最开始的代码:

子类的实例化方法<init>:

  • super()(最前) (9)(3)(2)
  • i = test(); (9)
  • 子类的非静态代码块 (8)
  • 子类的无参构造(最后) (7)

注意!! 这里为什么执行父类的test()会输出(9)而不是(4)呢?

这是因为对象的多态性:

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

也就是说:

非静态方法前面其实有一个默认的对象this

this在构造器(或<init>)它表示的是正在创建的对象,因为这里是在创建Son对象,所以

test()执行的是子类重写的代码(面向对象多态)

那为什么method方法的时候输出的是父类的(5)而不是子类的(10)呢?

这是因为有三种方法不可以被重写

  1. final方法
  2. 静态方法
  3. private等子类中不可见的方法

参考资料

https://blog.csdn.net/justloveyou_/article/details/72466416

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mangoBUPT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值