java对象创建过程
- 先将所属类加载到内存中
- JVM 为其分配内存存储该对象自己的实例变量以及其继承超类的实例变量(被超类隐藏的实例变量也会被分配内存)
- 为实例变量分配完空间的同时,为这些实例变量赋予默认值(零值)
之后,JVM 才会根据程序员的编码进行初始化
- 执行对实例变量直接赋值和执行实例代码块。两者的执行顺序取决于代码的顺序。【实际上,我们对实例变量直接赋值和执行实例代码块,编译器会把这些代码放到类的构造函数中,并且这些代码放在调用超类构造函数之后(java规定调用 super 语句必须放在构造函数第一条),构造函数本身的代码之前】
- 执行构造函数。在编译成字节码时会将构造函数改名为 <init>() 方法。
public class Student extends Father{
private i = 1; // 对实例变量直接赋值
private j = i + 1;
public Student(int ii, int jj){
this.i = ii;
this.j = jj;
}
// 实例代码块
{
j += 3;
}
}
// 根据第3点,构造函数等价于
public Student(int ii, int jj){
// 调用超类构造函数在第一位
super();
// 执行对实例变量直接赋值和实例代码块。它们的顺序取决于代码顺序
i = 1;
j = i+1;
j += 3;
// 构造函数自身代码
this.i = ii;
this.j = jj;
}
结合继承,实例化一个对象的过程是一个典型的递归过程:
- 根据继承关系找到最终的超类 Object 类
- 再从 Object 类开始实例化,直至目标类的实例化。
关于静态成员变量:只有在该类第一次被加载的时候经过分配空间、赋予默认值,再初始化。顺序:
- 父类的静态变量和静态代码块(内部顺序与代码顺序相关)
- 子类的静态变量和静态代码块(内部顺序与代码顺序相关)
五种java对象创建的方法
- 最常规的方法:直接使用 new 创建
- 使用反射机制:Class 类的 newInstance 方法。该方法只能调用目标类的无参数构造函数
- 使用反射机制:Constructor 类的 newInstance 方法。可以调用目标类的有参数构造函数和私有构造函数。第二种方法实质上是调用了第三种方法
- 使用 clone 方法。这种方法不会调用目标类的构造函数,且需要目标类实现 Cloneable 接口
- 使用反序列化。这种方法不会调用目标类的构造函数,且需要目标类实现 Serializable 接口
从Java虚拟机层面看,除了使用new关键字创建对象的方式外,其他方式全部都是通过转变为invokevirtual指令直接创建对象的。
public class Student implements Cloneable, Serializable {
private int id;
public Student() {}
public Student(int id) {
this.id = id;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
public static void main(String[] args) throws Exception {
// 第一种方法:new
Student stu = new Student(123);
// 第二种方法:class类的newInstance方法,有两种方式
stu2 = Student.class.newInstance();
stu2 = (Student)Class.forName("Student").newInstance();
// 第三种方法:Constructor类的newInstance方法
Constructor<Student> constructor = Student.class.getConstructor(int.class); // 使用有参数的构造函数
stu3 = constructor.newInstance(123);
// 第四种方法:clone方法
Student stu4 = (Student)stu.clone();
// 第五种方法:反序列化
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("stu.bin"));
output.writeObject(stu);
output.close();
ObjectInputStream input = new ObjectInputStream(new FileInputStream("stu.bin"));
Student stu5 = (Student) input.readObject();
input.close();
}
}
参考/摘自
深入理解Java对象的创建过程:类的初始化与实例化 - 书呆子Rico 墙裂推荐这个老哥,看的很好。