Mark Xu 的博客

记录精彩的程序人生

Java 学习之类的初始化和实例化

昨天参加了来广州之后的第一场面试,在笔试中考察了很多关于类的初始化和实例化相关的问题,之前在面试题搜集阶段有总结过该问题,但自己没有亲自敲写代码,印象不够深刻,今天自己总结下。

对象的创建

昨天笔试第一题就是问是否只有 new 关键字可以创建对象,当时我只想到了还有反射,下来一查发现,除了反射外,还可以通过 clone、反序列化的方式创建对象。

反射创建对象

1、使用 Class 类的 newInstance 方法

2、使用 Constructor 类的 newInstance 方法

clone 创建对象

没有调用构造函数

public class Employee implements Cloneable {
    private String mName;
    private Date mHireDay;
    
    //...

    @Override
    protected Employee clone() throws CloneNotSupportedException {
        Employee cloned =  (Employee) super.clone();
        cloned.mHireDay = (Date) mHireDay.clone();
        return cloned;
    }
}

反序列化创建对象

没有调用构造函数

类的初始化

类的初始化指为类中各个成员变量(static 修饰的)赋初始值的过程,属于类的一个生命周期

初始化的执行时机

1、使用 new 关键字创建对象时

2、读取或设置一个类的静态属性时

3、调用一个类的静态方法时

4、对类进行反射调用时

5、虚拟机启动时,对入口类执行初始化

初始化的顺序

静态成员变量和静态代码块的执行顺序为在代码中定义的顺序

代码实例

public class ClassInitialSuper {
    private int mA = 20;

    public ClassInitialSuper() {
        System.out.println("super constructor");
        System.out.println("mA is " + mA);
    }

    static {
        ClassInitialSuper classInitialSuper = new ClassInitialSuper();
    }

    static {
        System.out.println("static");
    }

    // 此处会对入口类 ClassInitialSuper 进行初始化
    public static void main(String[] args) {

    }
}

输出结果为:

super constructor
mA is 20
static

类的实例化(对象的创建)

类的实例化指根据类创建一个实例对象的过程

成员变量/构造代码块的执行

成员变量/构造代码块 -- > 构造方法,其中成员变量/构造代码块的执行顺序为我们在代码中的定义顺序

public class ClassInitialSuper {
    public ClassInitialSuper() {
        System.out.println("super constructor");
    }
}

public class ClassInitial extends ClassInitialSuper {
    private int mA = 1;
    private int mB = 2;

    public ClassInitial(int a, int b) {
        System.out.println(mA);
        System.out.println(mB);
        mA = a;
        mB = b;
        System.out.println(mA);
        System.out.println(mB);
    }

    {
        mA += 1;
        mB += 1;
    }

    public static void main(String[] args) {
        new ClassInitial(10, 11);
    }
}

上面代码输出为:

super constructor
2
3
10
11

可以看到,成员变量、构造代码块的执行会:在类的构造方法之前,在对父类的构造方法调用之后执行。

构造方法的执行

  • 注意!子类的构造方法必须调用父类的构造方法
  • 如果子类没有显示调用父类的构造方法,系统会默认调用父类的无参构造方法(如果父类没有无参构造方法,编译报错)
  • 一个类未声明构造方法时,系统会默认生成一个无参构造方法;如果类已经声明了任何一个构造方法时,系统就不会再帮我们生成无参构造方法了
  • 类的构造方法的执行采用了递归思想,在实例化一个类之前,会首先实例化其父类,依次递归直到 Object 类。在执行每个类的实例化时都是先调用父类构造方法,再按顺序执行实例变量、构造代码块,最后执行此类的构造方法

类的初始化和实例化的关系

  • 需要将一个类进行实例化创建对象时,如果该类还未初始化,会先进行初始化
  • 类的实例化并不一定非要在类的初始化完成后才可执行,这个过程可以是交叉的

参考文档

深入理解 Java 对象的创建过程:类的初始化与实例化

留下你的脚步