Java 创建对象的五种方式

对初学Java的人来说,这个问题可能有点奇怪,创建对象不就是通过 new 关键字,还有其它方式吗?

事实上,Java中有五种方法可以用来创建对象,每种方法都有其用途:

  1. new 关键字,会调用构造方法
  2. clone() 方法,不会调用构造方法
  3. 反序列化方式,不会调用构造方法
  4. Class 对象的 newInstance() 方法,会调用构造方法
  5. 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。

猜你喜欢

转载自blog.csdn.net/weixin_43320847/article/details/83189557