前置

JVM[1]-对象创建-基础


Java 对象创建时机

由执行类实例创建表达式而引起的对象创建 (new 关键字)

通过 new 关键词 调用对象类的构造函数

1
Info info = new Info();

反射机制

  • 使用 Class 类的 newInstance 方法

通过 Java 的反射机制使用 Class 类的 newInstance 方法来创建对象,事实上这个 newInstance 方法调用无参的构造器创建对象

1
2
3
4
5
// 根据类的限定名
Info info = (Info)Class.forName("com.xxx.Info").newInstance();

// 或者
Info info2 = Info.class.newInstance();
  • 使用 Constructor 类的 newInstance 方法

java.lang.relect.Constructor 类里也有一个 newInstance 方法可以创建对象,该方法和 Class 类中的 newInstance 方法很像,但是相比之下 Constructor 类的 newInstance 方法更加强大,我们可以通过这个 newInstance 方法调用有参数的和私有的构造函数

1
2
3
// 实例一个 参数为integer的有参构造函数
Constructor<Info> constructor = Info.class.getConstructor(Integer.class);
Info info3 = constructor.newInstance(123);

事实上 Class 的 newInstance 方法内部调用的也是 Constructor 的 newInstance 方法

使用 Clone 方法创建对象

要想使用 clone 方法,必须先实现 Cloneable 接口并实现其定义的 clone 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Student implements Cloneable{
private int id;
public Student(Integer id) {
this.id = id;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) throws Exception {
// 先通过其他方法创建一个对象实例
Student stu3 = constructor.newInstance(123);
// 根据已有的对象实例 clone
Student stu4 = (Student) stu3.clone();
}
}

使用(反)序列化机制创建对象

反序列化一个对象,需要类实现 Serializable 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Student implements Serializable{
private int id;
public Student(Integer id) {
this.id = id;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) throws Exception {
Constructor<Student> constructor = Student.class.getConstructor(Integer.class);
Student stu3 = constructor.newInstance(123);

// 写对象
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("student.bin"));
output.writeObject(stu3);
output.close();

// 读对象
ObjectInputStream input = new ObjectInputStream(new FileInputStream("student.bin"));
Student stu5 = (Student) input.readObject();
System.out.println(stu5);
}
}

除了使用 java 自带的 Serializable 序列化方式以外,还可以通过 json 的方式


Java 对象的创建过程

当对象被创建时,虚拟机会为其分配内存来存放对象自己的实例变量及其从父类继承过来的实例变量(即使这些从超类继承过来的实例变量有可能被隐藏也会被分配空间) 在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值(零值)。
在内存分配完成之后,Java 虚拟机就会开始对新创建的对象按照程序设计者的意志进行初始化。在 Java 对象初始化过程中,主要涉及三种执行对象初始化的结构,分别是 实例变量初始化、实例代码块初始化 以及 构造函数初始化。

实例变量初始化与实例代码块初始化

在定义(声明)实例变量的同时,还可以直接对实例变量进行赋值或者使用实例代码块对其进行赋值。
如果以这两种方式为实例变量进行初始化,那么它们将 在构造函数执行之前完成 这些初始化操作。

实际上,如果我们对实例变量直接赋值或者使用实例代码块赋值,那么编译器会将其中的代码放到类的构造函数中去,并且这些代码会被放在对超类构造函数的调用语句之后,构造函数本身的代码之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class InstanceVariableInitializer {

private int i = 1;
private int j = i + 1;

public InstanceVariableInitializer(int var){
System.out.println(i);
System.out.println(j);
this.i = var;
System.out.println(i);
System.out.println(j);
}

// 实例代码块
{
j += 3;

}

public static void main(String[] args) {
new InstanceVariableInitializer(8);
}
// 输出: 1 5 8 5
}

上边的代码相当于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class InstanceVariableInitializer {
// 初始化0值
private int i;
private int j;

public InstanceVariableInitializer(int var){
// 直接赋值
this.i = 1;
this.j = i + 1;
// 执行 实例代码块
this.j += 3;
// 构造函数
System.out.println(i);
System.out.println(j);
this.i = var;
System.out.println(i);
System.out.println(j);
}

public static void main(String[] args) {
new InstanceVariableInitializer(8);
}
// 输出: 1 5 8 5
}

需要注意的是,Java 是按照编程顺序来执行实例变量初始化器和实例初始化器中的代码的,并且不允许顺序靠前的实例代码块初始化在其后面定义的实例变量

构造函数初始化

Java 中的对象都至少会有一个构造函数,如果我们没有显式定义构造函数,那么它将会有一个默认无参的构造函数。在编译生成的字节码中,这些构造函数会被命名成()方法,参数列表与 Java 语言书写的构造函数的参数列表相同。

Java要求 在实例化类之前,必须先实例化其超类,以保证所创建实例的完整性。 这一点是在构造函数中保证的:
Java强制要求Object对象(Object是Java的顶层对象,没有超类)之外的所有对象构造函数的第一条语句必须是超类构造函数的调用语句或者是类中定义的其他的构造函数,如果我们既没有调用其他的构造函数,也没有显式调用超类的构造函数,那么编译器会为我们自动生成一个对超类构造函数的调用。


总结

  • 实例化一个类的对象的过程是一个典型的递归过程。初始化一个对象先初始化其父类,直至Object对象
  • 初始化顺序遵循:先依次执行实例变量初始化和实例代码块初始化,再执行构造函数初始化。

引用:https://blog.csdn.net/justloveyou_