对初学Java的人来说,这个问题可能有点奇怪,创建对象不就是通过 new 关键字,还有其它方式吗?
事实上,Java中有五种方法可以用来创建对象,每种方法都有其用途:
- new 关键字,会调用构造方法
- clone() 方法,不会调用构造方法
- 反序列化方式,不会调用构造方法
- Class 对象的 newInstance() 方法,会调用构造方法
- Constructor 对象的 newInstance() 方法,会调用构造方法
第四种与第五种方法也可以算作一种方法,它们都是借助反射来实现对象的创建工作。
一、new 关键字
记得刚学 Java 的时候,老师就经常问用 new 关键字创建对象的步骤。
Object o = new Object();
上面这一行的背后究竟发生了什么?
将这条语句分成两步来写:
Object o;
o = new Object();
- 第一步:声明变量o
- 第二步:通过 new 关键字实例化Object对象
- 第三步:变量o被赋值为Object对象的引用
如果从Java运行时内存分布来说的话,就是在栈中的变量引用了堆中的对象(o指局部变量而不是成员变量),如下图所示:
通过 new 创建对象时会进行内存分配、对类进行初始化,并调用类的构造方法。
二、clone()方法
clone() 方法是 Object 类中的方法,用来克隆已经存在的实例对象。使用clone() 不会调用类的构造方法,但它要求类实现 Cloneable 接口。
示例如下:
public class CloneDemo implements Cloneable{
public CloneDemo() {
System.out.println("hello");
}
public static void main(String[] args) throws CloneNotSupportedException {
CloneDemo cloneDemo1 = new CloneDemo();
CloneDemo cloneDemo2 = (CloneDemo) cloneDemo1.clone();
}
}
输出结果:
hello
可以看到,只输出了一次“hello”,clone() 方法并没有调用构造方法。
三、反序列化方式
其实说到 clone() 方法与反序列化,还涉及到深复制与浅复制的问题,这方面可以参考另一篇文章:Java对象序列化,这里不再赘述。
示例如下:
public class CloneDemo implements Serializable {
public CloneDemo() {
System.out.println("hello");
}
public static void main(String[] args) throws CloneNotSupportedException {
CloneDemo cloneDemo = new CloneDemo();
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(cloneDemo);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
hello
可以看到,反序列化时不会调用类的构造方法。
四、Class 对象与 Constructor 对象的 newInstance() 方法
示例如下:
public class ReflectDemo {
public ReflectDemo() {
System.out.println("hello");
}
public static void main(String[] args) {
try {
Class clazz = ReflectDemo.class;
ReflectDemo r1 = (ReflectDemo) clazz.newInstance();
Constructor constructor = clazz.getConstructor();
ReflectDemo r2 = (ReflectDemo) constructor.newInstance();
}
catch (IllegalAccessException | InvocationTargetException |
InstantiationException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
输出结果:
hello
hello
可以看到,使用 Class 对象与 Constructor 对象的 newInstance() 方法创建对象时,都会调用类的构造方法。
但这两者的 newInstance() 方法还是有点区别:
- Class 对象的 newInstance() 方法只能调用无参的构造函数创建对象
- Constructor 对象的 newInstance() 方法可以调用无参或有参的构造函数
实际上,Class 对象的 newInstance() 方法内部调用的就是 Constructor 的 newInstance((Object[])null) 方法。
注:自 JDK 9 开始,Class 对象的 newInstance() 已被标记为 Deprecated。