java浅拷贝和深拷贝的区别???
-
假设创建一个对象A然后在将A对象赋值给B、此时你操作了B对象等同于你操作了A对象、原因就是因为他们的内存地址指向的是同一块区域、可以叫做
引用的拷贝
User user1 = new User(); User user2 = user1; // 输出结果 user1地址:demo4.User@7f31245a user2地址:demo4.User@7f31245a
-
注意点:
-
public class User implements Cloneable{ // 实现Cloneable接口 @Override // 从写Object类中的clone()方法 protected Object clone() throws CloneNotSupportedException { return super.clone(); // 主要还是调用Object类中的clone()方法 } }
-
拷贝的共同目的就是为了拷贝一个全新的对象、
浅拷贝(Shallow Copy
)
浅拷贝(
Shallow Copy
)
案例:
Hobby类
public class Hobby{
private String hobbyName;
public Hobby() {
}
public Hobby(String hobbyName) {
this.hobbyName = hobbyName;
}
public String getHobbyName() {
return hobbyName;
}
public void setHobbyName(String hobbyName) {
this.hobbyName = hobbyName;
}
public String toAddress() {
return super.toString();
}
@Override
public String toString() {
return "ID{" +
"hobbyName=" + hobbyName +
'}';
}
}
User类
public class User implements Cloneable{
private String name;
private Integer age;
private Hobby hobby;
public User() {
}
public User(String name, Integer age, Hobby id) {
this.name = name;
this.age = age;
this.hobby = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Hobby getHobby() {
return hobby;
}
public void setHobby(Hobby hobby) {
this.hobby = hobby;
}
public String toAddress() {
return super.toString();
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", Hobby=" + hobby +
'}';
}
// 从写clone方法()
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试类
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
Hobby hobby = new Hobby("唱歌");
User user1 = new User("小明",18,hobby);
User user2 = (User) user1.clone();
System.out.println("============初始内容==========");
System.out.println("user1地址:"+user1.toAddress()+"\t\tuser2地址:"+user2.toAddress());
System.out.println("user1的Hobby地址:"+user1.getHobby().toAddress()+"\t\t" +
"user2的Hobby地址:"+user2.getHobby().toAddress());
System.out.println("user1:"+user1.toString());
System.out.println("user2:"+user2.toString());
user2.setName("小红");
user2.setAge(17);
hobby.setHobbyName("跳舞");
user2.setHobby(hobby);
System.out.println("============值赋完内容==========");
System.out.println("user1的Hobby地址:"+user1.getHobby().toAddress()+"\t\t" +
"user2的Hobby地址:"+user2.getHobby().toAddress());
System.out.println("user1的Hobby地址:"+user1.toString());
System.out.println("user2的Hobby地址:"+user2.toString());
}
}
测试结果:
============初始内容==========
user1地址:demo4.User@7f31245a user2地址:demo4.User@6d6f6e28
user1的Hobby地址:demo4.Hobby@135fbaa4 user2的Hobby地址:demo4.Hobby@135fbaa4
user1:User{name='小明', age=18, Hobby=ID{hobbyName=唱歌}}
user2:User{name='小明', age=18, Hobby=ID{hobbyName=唱歌}}
============值赋完内容==========
user1的Hobby地址:demo4.Hobby@135fbaa4 user2的Hobby地址:demo4.Hobby@135fbaa4
user1的Hobby地址:User{name='小明', age=18, Hobby=ID{hobbyName=跳舞}}
user2的Hobby地址:User{name='小红', age=17, Hobby=ID{hobbyName=跳舞}}
个人小结:
- 浅拷贝简单来说就是将一个对象new了两次、但是和浅拷贝性质不一样、浅拷贝实际意义是将上一个对象拷贝一份、将其对象数据拷贝到新地址中、而最后达到两个对象的引用地址不一样。
- 浅拷贝的特点
- 对应基本数据类型的成员变量: 基础类型的拷贝,其中一个对象修改该值,不会影响另外一个、原因是 因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象 。
- 对应引用类型的成员变量:比如数组或者类对象、改变其中一个,会对另外一个也产生影响 、原因是因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。
弊端:浅拷贝只对本身要拷贝的对象作为拷贝、而不会将拷贝对象中的引用类型属性再次进行拷贝。
深拷贝 (Deep Copy
)
深拷贝 (
Deep Copy
)
案例:
Hobby类
public class Hobby implements Cloneable{
private String hobbyName;
public Hobby() {
}
public Hobby(String hobbyName) {
this.hobbyName = hobbyName;
}
public String getHobbyName() {
return hobbyName;
}
public void setHobbyName(String hobbyName) {
this.hobbyName = hobbyName;
}
public String toAddress() {
return super.toString();
}
@Override
public String toString() {
return "ID{" +
"hobbyName=" + hobbyName +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
User类
public class User implements Cloneable{
private String name;
private Integer age;
private Hobby hobby;
public User() {
}
public User(String name, Integer age, Hobby id) {
this.name = name;
this.age = age;
this.hobby = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Hobby getHobby() {
return hobby;
}
public void setHobby(Hobby hobby) {
this.hobby = hobby;
}
public String toAddress() {
return super.toString();
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", Hobby=" + hobby +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User) super.clone();
hobby = (Hobby)getHobby().clone();
return user;
}
}
测试类
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
Hobby hobby = new Hobby("唱歌");
User user1 = new User("小明",18,hobby);
User user2 = (User) user1.clone();
System.out.println("============初始内容==========");
System.out.println("user1地址:"+user1.toAddress()+"\t\tuser2地址:"+user2.toAddress());
System.out.println("user1的Hobby地址:"+user1.getHobby().toAddress()+"\t\t" +
"user2的Hobby地址:"+user2.getHobby().toAddress());
System.out.println("user1:"+user1.toString());
System.out.println("user2:"+user2.toString());
user2.setName("小红");
user2.setAge(17);
hobby.setHobbyName("跳舞");
user2.setHobby(hobby);
System.out.println("============值赋完内容==========");
System.out.println("user1的Hobby地址:"+user1.getHobby().toAddress()+"\t\t" +
"user2的Hobby地址:"+user2.getHobby().toAddress());
System.out.println("user1的Hobby地址:"+user1.toString());
System.out.println("user2的Hobby地址:"+user2.toString());
}
}
测试结果
============初始内容==========
user1地址:demo4.User@7f31245a user2地址:demo4.User@6d6f6e28
user1的Hobby地址:demo4.Hobby@135fbaa4 user2的Hobby地址:demo4.Hobby@45ee12a7
user1:User{name='小明', age=18, Hobby=ID{hobbyName=唱歌}}
user2:User{name='小明', age=18, Hobby=ID{hobbyName=唱歌}}
============值赋完内容==========
user1的Hobby地址:demo4.Hobby@135fbaa4 user2的Hobby地址:demo4.Hobby@45ee12a7
user1的Hobby地址:User{name='小明', age=18, Hobby=ID{hobbyName=唱歌}}
user2的Hobby地址:User{name='小红', age=17, Hobby=ID{hobbyName=跳舞}}
个人小结:
- 深拷贝除了拷贝对象本身、对象内部的属性引用也会进行拷贝、唯一麻烦的就是在对象内部的属性的对象要去实现
Cloneable
接口、还要从写clone()
方法、而本身对象的clone()方法中要对属性引用对象调用clone()方法将其赋值给这个引用对象属性 - 深拷贝的特点
- 对应基本数据类型的成员变量: 基础类型的拷贝,其中一个对象修改该值,不会影响另外一个、原因是 因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象 。(和浅拷贝一样)
- 对应引用类型的成员变量:比如数组或者类对象、改变其中一个, 不会对另外一个也产生影响 、原因是因为深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间 。
- 弊端
- 如有对象属性是一个对象、就非常麻烦、需要每个对象实现
Cloneable
接口、并从写clone()
方法、并在上一个对象的clone()
方法中去调用属性对象的clone()
将其拷贝后赋值给属性。 - 深拷贝的开销大
- 如有对象属性是一个对象、就非常麻烦、需要每个对象实现
浅拷贝和深拷贝总结
- 不管还是浅拷贝还是深拷贝、它们两的作用就是复制对象、复制后的对象和被复制的对象引用地址不一样、可以简单理解为将复制的对象放在一个新的内存区域中。
- 浅拷贝可以复制对象的基本类型和引用类型、只不过引用类型指向的是同一片内存空间
- 深拷贝可以复制对象的基本类型和引用类型、只不过引用类型指向的不是同一片内存空间
参考: https://www.jianshu.com/p/94dbef2de298
采用序列化对象的形式来实现深度克隆
什么是序列化、序列化就是将内存中的对象保存在硬盘中、当做一个文件、然后将文件读取出来变成内存中的对象、这个形式是反序列化。
@Test
public void fun_5() throws Exception {
/**
* 注意:
* User对象需要实现Serializable接口
*/
Hobby hobby = new Hobby();
User user1 = new User("小明",18,hobby); // 创建新对象
String projectPath = System.getProperty("user.dir"); // 获取当前项目路径
System.out.println(projectPath);
File f = new File(projectPath+"/obj.txt"); // 文件报存的本地地址
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f)); // 输出流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));// 输入流
oos.writeObject(user1); // 将对象序列化
User user2 = (User) ois.readObject();// 将对象反序列化
System.out.println("user1地址:"+user1.toAddress()+"\t\tuser2地址:"+user2.toAddress());
System.out.println("user1的Hobby地址:"+user1.getHobby().toAddress()+"\t\t" +
"user2的Hobby地址:"+user2.getHobby().toAddress());
ois.close(); // 释放资源
oos.close(); // 释放资源
}
对象(反)序列化、要实现Serializable接口、否则会报一个错误
java.io.NotSerializableException: pojo.User
·测试结果
user1地址:demo4.User@5fdef03a user2地址:demo4.User@2c13da15
user1的Hobby地址:demo4.Hobby@5ccd43c2 user2的Hobby地址:demo4.Hobby@77556fd
具体了解还请自行测试。