网站资质证书,苏州建设营销网站,抖音代运营是啥,网站的建设建议文章目录 概述创建对象的5种方式1. 通过new关键字2. 通过Class类的newInstance()方法3. 通过Constructor类的newInstance方法4. 利用Clone方法5. 反序列化 Clone方法基本类型和引用类型浅拷贝深拷贝如何实现深拷贝1. 让每个引用类型属性内部都重写clone()方法2. 利用序列化 概述… 文章目录 概述创建对象的5种方式1. 通过new关键字2. 通过Class类的newInstance()方法3. 通过Constructor类的newInstance方法4. 利用Clone方法5. 反序列化 Clone方法基本类型和引用类型浅拷贝深拷贝如何实现深拷贝1. 让每个引用类型属性内部都重写clone()方法2. 利用序列化 概述
关于Java的深拷贝和浅拷贝简单来说就是创建一个和已知对象一模一样的对象。可能日常编码过程中用得不多但了解深拷贝和浅拷贝的原理对于Java中的值传递或者引用传递将会有更深的理解。 创建对象的5种方式
1. 通过new关键字
最常用的一种方式通过new关键字调用类的有参或无参构造方法来创建对象。比如Object obj newObject()。 2. 通过Class类的newInstance()方法
这种默认是调用类的无参构造方法创建对象。比如Artisan p2 ArtisanClass. forNamecom. ys.artisan.Artisan).newInstance() 3. 通过Constructor类的newInstance方法
和第2种方法类似都是通过反射来实现的。通过java.lang. relect. Constructor类的newInstance()方法指定某个构造器来创建对象 Artisan.class.getConstructor()[0].newInstance();实际上第2种方法利用Class的newInstance()方法创建对 象其内部调用还是Constructor的newInstance()方法。 4. 利用Clone方法
Clone是Object类中的一个方法clone克隆顾名思义就是创建一个一模一样的对象出来。通过对象A. clone()方法会创建一个内容和对象A一模一样的对象B
Artisan a1 new Artisan();
Artisan a2 a1.clone();5. 反序列化
序列化是把堆内存中的Java对象数据通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点在网络上传输。
而反序列化则是把磁盘文件中的对象数据或者把网络节点上的对象数据恢复成Java对象模型的过程 Clone方法
我们这里介绍Java的深拷贝和浅拷贝其实现方式正是通过调用Object类的clone()方法来完成 IntrinsicCandidateprotected native Object clone() throws CloneNotSupportedException;这是一个用native关键字修饰的方法关于native关键字不理解也没关系只需要知道用native修饰的方法就是告诉操作系统去实现。
具体过程不需要了解只需要知道clone方法的作用就是复制对象并产生一个新的对象。那么这个新的对象和原对象是什么关系呢 基本类型和引用类型
先拉齐一个概念在Java中基本类型和引用类型的区别。
在Java中数据类型可以分为两大类基本类型和引用类型。
基本类型也称为值类型分别是字符类型char布尔类型boolean以及数值类型byte、short、int、long、float、double。引用类型则包括类、接口、数组、枚举等
Java将内存空间分为堆和栈。基本类型直接在栈中存储数值而引用类型是将引用放在栈中实际存储的值是放在堆中通过栈中的引用指向堆中存放的数据。
基本类型和引用类型在JVM存储结构如图 a和b都是基本类型其值是直接存放在栈中的而c和d是String声明的这是一个引用类型引用地址是存放在栈中然后指向堆的内存空间。d c这条语句表示将c的引用赋值给d那么c和d将指向同一块堆内存空间
浅拷贝
浅拷贝会复制对象的基本字段值但对于对象中的引用类型字段浅拷贝仅复制引用地址而不会创建新的对象实例。即拷贝后的对象与原对象共享相同的引用类型数据。
class Person implements Cloneable {String name;int age;Address address;public Person(String name, int age, Address address) {this.name name;this.age age;this.address address;}// 浅拷贝实现使用Object.clone()Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Address {String city;public Address(String city) {this.city city;}
}
使用
Person person1 new Person(Alice, 25, new Address(New York));
Person person2 (Person) person1.clone();// 修改 person2 的地址
person2.address.city Los Angeles;// person1 的 address 也会被改变因为浅拷贝复制的是引用地址
System.out.println(person1.address.city); // 输出 Los Angeles
调用对象的clone方法必须要让类实现Cloneable接口并且重写clone方法 创建一个新对象然后将当前对象的非静态字段复制到该新对象如果字段是值类型的那么对该字段执行复制如果该字段是引用类型的则复制引用但不复制引用的对象。因此原始对象及其副本引用同一个对象。 深拷贝
弄清楚了浅拷贝后深拷贝就很容易理解了。深拷贝创建一个新对象然后将当前对象的非静态字段复制到该新对象无论该字段是值类型的还是引用类型都复制独立的一份。当用户修改其中一个对象的任何内容时都不会影响另一个对象的内容
深拷贝会递归地复制对象中的所有字段包括引用类型字段所指向的对象。这样拷贝后的对象与原对象完全独立互不影响。 如何实现深拷贝
深拷贝就是要让原始对象和克隆之后的对象所具有的引用类型属性不是指向同一块堆内存
1. 让每个引用类型属性内部都重写clone()方法
既然引用类型不能实现深拷贝那么将每个引用类型都拆分为基本类型分别进行浅拷贝。比如上面的例子Person类有一个引用类型Address其实String也是引用类型但是String类型有点特殊在Address类内部也重写clone方法
class Person implements Cloneable {String name;int age;Address address;public Person(String name, int age, Address address) {this.name name;this.age age;this.address address;}// 深拷贝实现Overrideprotected Object clone() throws CloneNotSupportedException {Person cloned (Person) super.clone();cloned.address new Address(this.address.city); // 递归复制引用类型字段return cloned;}
}
使用
Person person1 new Person(Alice, 25, new Address(New York));
Person person2 (Person) person1.clone();// 修改 person2 的地址
person2.address.city Los Angeles;// person1 的 address 不会改变因为深拷贝创建了独立的引用
System.out.println(person1.address.city); // 输出 New York
这种做法有个弊端这里Person类只有一个Address引用类型而Address类没有所以这里只重写Address类的clone方法但是如果Address类也存在一个引用类型那么也要重写其clone方法这样有多少个引用类型就要重写多少次如果存在很多引用类型那么代码量显然会很大所以这种方法不太合适 2. 利用序列化
序列化是将对象写到流中便于传输而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝因为原始对象还存在JVM中所以可以利用对象的序列化产生克隆对象然后通过反序列化获取这个对象。
注意每个需要序列化的类都要实现Serializable接口如果有某个属性不需要序列化可以将其声明为transient即将其排除在克隆属性之外
因为序列化产生的是两个完全独立的对象所有无论嵌套多少个引用类型序列化都是能实现深拷贝的 首先确保要进行深拷贝的类和其引用的类都实现了Serializable接口 import java.io.*;// 需要进行深拷贝的类必须实现 Serializable 接口
class Address implements Serializable {private static final long serialVersionUID 1L;String city;public Address(String city) {this.city city;}Overridepublic String toString() {return Address{ city city \ };}
}class Person implements Serializable {private static final long serialVersionUID 1L;String name;int age;Address address;public Person(String name, int age, Address address) {this.name name;this.age age;this.address address;}Overridepublic String toString() {return Person{ name name \ , age age , address address };}// 深拷贝方法使用序列化和反序列化public Person deepCopy() {try {// 将对象写入字节流ByteArrayOutputStream bos new ByteArrayOutputStream();ObjectOutputStream oos new ObjectOutputStream(bos);oos.writeObject(this);oos.flush();// 从字节流读取对象ByteArrayInputStream bis new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois new ObjectInputStream(bis);return (Person) ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();return null;}}
}public class DeepCopyExample {public static void main(String[] args) {// 创建原始对象Person person1 new Person(Alice, 25, new Address(New York));// 进行深拷贝Person person2 person1.deepCopy();// 修改 person2 的地址if (person2 ! null) {person2.address.city Los Angeles;person2.name Bob;}// 输出原对象和拷贝对象验证深拷贝System.out.println(Original person1: person1);System.out.println(Copied person2: person2);}
}
输出结果
Original person1: Person{nameAlice, age25, addressAddress{cityNew York}}
Copied person2: Person{nameBob, age25, addressAddress{cityLos Angeles}}
deepCopy()方法通过序列化将对象写入到ByteArrayOutputStream再通过ObjectInputStream从字节流中读取对象生成新的实例。修改person2的引用类型字段如address.city不会影响person1从而验证了深拷贝的效果。