Java创建对象:除开new,还有其他方式吗?

1,634 阅读4分钟

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

概述

每位学习Java的童鞋,对于对象及对象的创建都非常熟悉了(甚至比对自己的「对象」还要熟)。但是除了new方式创建对象,大家还有了解对象的其他创建方式吗?今天我们就聊聊Java创建对象的所有方式。

创建对象的方式可以分为两大类:调用了构造函数和没有调用改造函数

  • 调用了构造函数

    • new方式创建对象

    • 通过反射创建对象

  • 没有调用构造函数

    • 通过clone方式创建对象

    • 反序列化创建对象

new方式创建对象

通过new方式创建对象,是我们最常见和最常用的创建对象方式。创建方式很简单,直接通过new语法创建。实际创建过程并不简单。

对象的创建流程: 1.类加载检查,先检查类是否被加载过,若没有加载,则先执行相应的类加载 2.分配内存,根据堆内存是否规整来确定分配内存方式。若规整,则采取对撞指针的方式进行内存分配(内存划分已使用和未使用两部分);如不规整,则通过空闲列表的形式分配内存(虚拟机会维护一个列表,记录哪些内存是可用的) 3.初始化零值 4.设置对象头,对象头包括两部分信息:存储对象自身的运行时数据(哈希码、GC分代年龄,锁状态标志等),另一部分是类型指针,指向它的类元数据指针,确定这个对象是哪个类的实例 5.执行init方法,成员变量实例化对应的值

package com.carrywei.bread.object;

public class Computer {
    // 类型
    private String type;

    // 颜色
    private String color;

    public Computer(String type, String color) {
        System.out.println("我是" + color + type + "电脑");
    }

    public static void main(String[] args) {
        // new方式创建对象
        Computer newComputer = new Computer("MacBook Pro", "深空灰色");
    }
}

通过反射创建对象

通过反射创建对象也是我们熟知的一种创建对象方式。

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。

代码:

// 有参构造函数,通过反射创建对象
Class cls = Class.forName("com.carrywei.bread.object.Computer");
Class[] paramTypes = { String.class, String.class };
Object[] params = { "小米笔记本", "银色"};
Constructor con = cls.getConstructor(paramTypes);
Computer reflectionComputer = (Computer) con.newInstance(params);

通过clone方式创建对象

clone方式创建对象的核心就是实现clone方法,然后调用原对象创建的clone方法创建新的对象

public class Computer implements Cloneable{


    // 其他部分省略
    // ...
    
    
    @Override
    public Computer clone(){
        try {
            Computer computer = (Computer) super.clone();
            return computer;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        Computer newComputer = new Computer("MacBook Pro", "深空灰色");

        Computer cloneComputer = newComputer.clone();
    }
}

反序列方式创建对象

Java序列化是指将Java对象转为字节序列的过程,反序列化为把字节序列恢复为Java对象的过程。

对象能被序列化和反序列化的前提:类实现Serializable接口。

反序列化创建对象编码

// 通过反序列化方式创建对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
    // 将对象序列化到内存
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    objectOutputStream.writeObject(newComputer);

    // 从内存反序列化
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
    Computer serializeComputer = (Computer) objectInputStream.readObject();
} catch (IOException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

小结

通过以上四种方式创建对象的完整代码:

package com.carrywei.bread.object;


import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * Created by  on 2021/8/8.
 * 描述:电脑
 */
public class Computer implements Cloneable, Serializable {
    // 类型
    private String type;

    // 颜色
    private String color;


    public Computer() {
        System.out.println("我是空电脑");
    }
    
    
    public Computer(String type, String color) {
        System.out.println("我是" + color + type + "电脑");
    }

    @Override
    public Computer clone(){
        try {
            Computer computer = (Computer) super.clone();
            return computer;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        Computer newComputer = new Computer("MacBook Pro", "深空灰色");
        Class cls = Class.forName("com.carrywei.bread.object.Computer");

        // 通过反射方式创建对象
        System.out.println("-----------通过反射方式创建对象---------");
        Class[] paramTypes = { String.class, String.class };
        Object[] params = { "小米笔记本", "银色"};
        Constructor con = cls.getConstructor(paramTypes);
        Computer reflectionComputer = (Computer) con.newInstance(params);


        // clone 方法创建对象
        System.out.println("-----------clone 方法创建对象---------");
        Computer cloneComputer = newComputer.clone();
        System.out.println(newComputer == cloneComputer);

        // 通过反序列化方式创建对象
        System.out.println("---------通过反序列化方式创建对象-----------");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            // 将对象序列化到内存
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(newComputer);

            // 从内存反序列化
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            Computer serializeComputer = (Computer) objectInputStream.readObject();
            System.out.println(newComputer == serializeComputer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
// 输出结果:
我是深空灰色MacBook Pro电脑
-----------通过反射方式创建对象---------
我是银色小米笔记本电脑
-----------clone 方法创建对象---------
false
---------通过反序列化方式创建对象-----------
false

从输出结果,我们看出:

  • new方式和反射创建对象,是会调用构造函数的,而clone方式和反序列化方式并不会调用构造函数

  • 通过clone和反序列化方式创建的对象跟原对象并不相同,这两种方式创建对象也是设计模式中的原型模式的实现方式。 (想了解更多关于原型模式的内容,可以阅读我之前的文章《原型模式:快速复制已有实例创建新的实例》