Java对象的创建过程包括 类初始化(JVM类加载机制)和类实例化两个阶段。
一、Java对象创建时机
(1)使用new关键字创建对象
(2)反射创建对象
使用Class类的newInstance方法
Student student2 = (Student)Class.forName("Student类全限定名").newInstance();
使用Constructor类的newInstance方法
Constructor<Student> constructor = Student.class.getConstructor(Integer.class);
Student stu3 = constructor.newInstance(123);
(3)使用Clone方法创建对象(实现Cloneable接口)
无论何时我们调用一个对象的clone方法,JVM都会帮我们创建一个新的、一样的对象,特别需要说明的是,用clone方法创建对象的过程中并不会调用任何构造函数。
(4)使用(反)序列化机制创建对象(实现Serializable接口)
当我们反序列化一个对象时,JVM会给我们创建一个单独的对象,在此过程中,JVM并不会调用任何构造函数。
二. Java 对象的创建过程
当一个对象被创建时,虚拟机就会为其分配内存来存放对象自己的实例变量及其从父类继承过来的实例变量(即使这些从超类继承过来的实例变量有可能被隐藏也会被分配空间)。
在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值(零值)。
主要涉及三种执行对象初始化的结构,分别是 实例变量初始化、实例代码块初始化、构造函数初始化。
编译器构造类的构造函数<init>()(按顺序执行):
父类构造器
实例变量初始化和实例代码块初始化相关代码
本身构造函数
1、实例变量初始化和实例代码块初始化按照编程顺序来执行,不允许顺序靠前的实例代码块访问其后面定义的实例变量,但是可以赋值。
// 编译错误:代码块不可以访问其后定义的实例变量public class InstanceInitializer { { j = i; } private int i = 1; private int j; } // 没问题:代码块可以赋值其后定义的实例变量public class InstanceInitializer { { j = 1; } private int i = 1; private int j; }
2、每一个Java中的对象都至少会有一个构造函数,如果我们没有显式定义构造函数,那么它将会有一个默认无参的构造函数。
Java强制要求所有对象(Object是Java的顶层对象,没有超类)构造函数的第一条语句必须是超类构造函数的调用语句或者是类中定义的其他的构造函数(super()/this()必须在第一句,且不能同时出现)。如果我们既没有调用其他的构造函数,也没有显式调用超类的构造函数,那么编译器会为我们自动生成一个对超类构造函数的调用
3、实例化一个类的对象的过程是一个典型的递归过程。
首先实例化Object类,再依次对以下各类进行实例化,直到完成对目标类的实例化。
三、综合实例
// 父类class Foo { int i = 1; Foo() { System.out.println(i);// -----------(1) int x = getValue(); System.out.println(x);// -----------(2) } { i = 2; } protected int getValue() { return i; } }// 子类class Bar extends Foo { int j = 1; Bar() { j = 2; } { j = 3; } @Override protected int getValue() { return j; } }public class ConstructorExample { public static void main(String... args) { Bar bar = new Bar(); System.out.println(bar.getValue());// -----------(3) } }/* * Output: 2 0 2 *///Foo类构造函数的等价变换:Foo() { i = 1; i = 2; System.out.println(i); int x = getValue();// 在执行Foo的构造函数的过程中,由于Bar重载了Foo中的getValue方法,所以其调用的是Bar的getValue方法 System.out.println(x); }//Bar类构造函数的等价变换Bar() { Foo(); j = 1; j = 3; j = 2}