本固枝荣 —— Java中创建对象的方式

Java中创建对象的方式有四种,分别为:new关键字、反射机制、clone方法、反序列化操作等。作为Java中必知必懂的一个知识点,准备亲测和分析一番,将需要注意的一些细节进行总结。

目录

1、new关键字

2、反射机制

3、clone()方法

4、反序列化

拓展 —— 对象克隆中的深拷贝和浅拷贝问题


以Book实体类为例:

public class Book implements Serializable, Cloneable {
    public String bookName; //基本类型
    public PublishTime publishTime; //引用类型

    public Book() {
    }

    public Book(String bookName) {
        this.bookName = bookName;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    // 简单进行重写Object的clone()
//    @Override
//    public Object clone() throws CloneNotSupportedException {
//        return super.clone();
//    }
}

1、new关键字

用法很简单,通过new关键字调用Book的构造器(无参或有参):

// new的方式
public static void main(String[] args) {
    Book bookRedis = new Book("Redis设计与实现");
    System.out.println("创建对象方式之-----new------>" + bookRedis.getBookName());
}

# 打印结果:
创建对象方式之-----new------>Redis设计与实现

2、反射机制

Java中把动态获取类的信息以及动态调用对象方法的功能称之为反射。换言之,通过反射机制可以发现对象的类型,找到类中包含的方法和属性;可以创建对象并访问任意对象方法和属性。而反射有三种方式可以获取Class类(如下面代码标记的①②③所示),Class类的newInstance()则可以创建新的对象。

通过Class类的newInstance()创建对象:

// 反射方式Class类
public static void main(String[] args) {
    try {
//        Book bookLinux = bookRedis.getClass().newInstance(); //①
//        Book bookLinux = Book.class.newInstance(); //②
        Book bookLinux = (Book) Class.forName("com.xxwei.demo.controller.Book").newInstance(); //③
        bookLinux.setBookName("鸟哥的Linux私房菜(基础学习篇)");
        System.out.println("创建对象方式之-----反射之Class类------>" + bookLinux.getBookName());
    } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) {
        e.printStackTrace();
    }  
}

# 打印结果:
创建对象方式之-----反射之Class类------>鸟哥的Linux私房菜(基础学习篇)

也可以通过Constructor类的newInstance()创建对象:(上面Class类的newInstance()方式本质是通过Constructor类的newInstance()实现的)

// 反射方式之Constructor类
public static void main(String[] args) {
    try {
        Constructor<Book> bookConstructor = Book.class.getConstructor();
        Book bookLinux2 = bookConstructor.newInstance();
        bookLinux2.setBookName("鸟哥的Linux私房菜(服务器架设篇)");
        System.out.println("创建对象方式之-----反射之Constructor类------>" + bookLinux2.getBookName());
    } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
        e.printStackTrace();
    } 
}

# 打印结果:
创建对象方式之-----反射之Constructor类------>鸟哥的Linux私房菜(服务器架设篇)

3、clone()方法

注意:使用Object的clone()方法,Book类需要实现Cloneable接口,它是一个空接口,标识实现该接口的类的实例可被克隆。

// 克隆方式 -- #Object.clone()
public static void main(String[] args) {
    try {
        // Book类没有重写clone()
        Book bookJava = new Book("Java核心技术 卷I"); 
        Book newObj = (Book) bookJava.clone(); 
        System.out.println("创建对象方式之-----clone------>" + (bookJava.getBookName()));
        System.out.println("创建对象方式之-----clone------>" + (newObj.getBookName()));
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
}
# 打印结果:
创建对象方式之-----clone------>Java核心技术 卷I
创建对象方式之-----clone------>Java核心技术 卷I

从打印的结果来看:Book类不重写clone()方法或重写clone()方法,新对象中都复制了原型对象的基本类型变量。那么对于引用类型的变量呢?继续看后面拓展的分析。

4、反序列化

注意:Book类需要实现Serializable接口,这是一个空接口,标识实现该接口的类的实例可被序列化。除了原型对象使用static、transient修饰的变量不参与序列化外,反序列化后得到的新对象和原型对象一致。

// 反序列化方式(本质上是一种深度克隆,或深拷贝)
public static void main(String[] args) {
    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("E:\\test\\test.txt"));
         ObjectInputStream in = new ObjectInputStream(new FileInputStream("E:\\test\\test.txt"))
    ) {
        Book bookMySQL = new Book("MySQL必知必会的技巧");
        // 对象序列化
        out.writeObject(bookMySQL);
        out.flush();
        // 尝试修改序列化对象的属性(这里是不起作用的)
        bookMySQL.setBookName("鸟哥的Linux私房菜");
        // 反序列化
        Book mysqlBook = (Book) in.readObject();
        System.out.println("创建对象方式之-----反序列化------>" + mysqlBook.getBookName());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

# 打印结果:
创建对象方式之-----反序列化------>MySQL必知必会的技巧

拓展 —— 对象克隆中的深拷贝和浅拷贝问题

Java中生成对象的基本方式就这4种,其中new方式最简单而普遍使用。

实际上,对于克隆对象的做法可能会有些额外的要求,比如克隆时:只复制原型对象的本身及基本类型的成员变量(不需要引用类型的成员变量),或者需要将原型对象的所有成员变量一并复制到新对象,前者就是浅拷贝,后者为深拷贝,二者的区别就是克隆对象时是否支持对引用类型成员变量的复制

一个包含了基本类型和引用类型变量的实体类(如Book类),实现了Cloneable接口并进行简单的重写clone()方法,克隆出的新对象结果是浅拷贝类型。如果要做到深度拷贝,Book类及关联的引用类都要实现Cloneable接口并重写clone()方法,在Book类重写时,特别注意要将引用类型成员变量重新进行赋值初始化,如下:

public class Book implements Serializable, Cloneable {
    @Override
    public Object clone() throws CloneNotSupportedException {
        Book book = (Book) super.clone();
        book.publishTime = (PublishTime) publishTime.clone(); //attention!!!
        return book;
    }
}
class PublishTime implements Cloneable{
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

然而,现在有很多的工具类是支持普通对象或集合对象的克隆,没必要再重复造轮子了,推荐一款便捷好用的工具类:

  • org.apache.commons.beanUtils的工具类BeanUtils和PropertyUtils,都提供了copyProperties()方法克隆对象;
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils</artifactId>
   <version>x.x.x</version>
</dependency>

小结一下:

至此,通过亲测重新巩固了一下Java中创建对象的几种方式,还区分了对象克隆时出现的深拷贝和浅拷贝问题,最后推荐了经常使用的工具类,不积硅步无以至千里,点滴付出终将有所收获,共同进步 ~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值