概念
引用拷贝:只是拷贝源对象的地址,并不生成一个新的对象
浅拷贝:创建一个新对象,新对象和源对象不等,但是新对象的属性和源对象相同。
深拷贝:拷贝源对象的所有值,而不是地址
引用拷贝
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
}
测试:
public class test {
public static void main(String[] args) {
User user = new User(1,"jarvis");
User user2 = user;
System.out.println(user.getName());
System.out.println(user.getName());
user.setName("sojrs");
System.out.println(user.getName());
System.out.println(user.getName());
}
}
======
jarvis
jarvis
sojrs
sojrs
浅拷贝
- 如果属性是基本类型(int,double,long,boolean等),拷贝的就是基本类型的值;
- 如果属性是引用类型,拷贝的就是内存地址(即复制引用但不复制引用的对象) ,因此如果其中一个对象的属性,就会影响到另一个对象。
Book类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
private int id;
private String name;
}
User 类实现Cloneable接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Cloneable{
private int id;
private String name;
private Book book;
@Override
public User clone() throws CloneNotSupportedException {
return (User)super.clone();
}
}
测试
public class test {
public static void main(String[] args) {
User user = new User(1,"jarvis",new Book(1,"三国"));
User user2 = null;
try {
user2 = user.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(user==user2); //false 说明克隆对象和源对象是不同的对象
System.out.println(user.getName()==user2.getName()); //true 两个对象的name都是一样的,都指向常量池中的同一个字符串
System.out.println(user.getBook()==user2.getBook()); //true
user.getBook().setName("三国演义");
System.out.println(user.getBook().getName()); //三国演义
System.out.println(user2.getBook().getName()); //三国演义 user.book的属性改变了,影响到了user2
}
}
Cloneable实现深拷贝
在对引用数据类型进行拷贝的时候,创建新的对象,并且复制其内的成员变量。
如果使用重写clone()方法实现深拷贝,那么要将类中所有自定义引用变量的类也去实现Cloneable接口实现clone()方法
Book类实现Cloneable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book implements Cloneable{
private int id;
private String name;
@Override
public Book clone() throws CloneNotSupportedException {
return (Book)super.clone();
}
}
User类通过Cloneable实现深拷贝
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Cloneable{
private int id;
private String name;
private Book book;
@Override
public User clone() throws CloneNotSupportedException {
User user2 = (User)super.clone(); //对user进行浅拷贝,获得user2
user2.name = new String(this.name); //将user2的name指向一个新的字符串
user2.book = this.getBook().clone(); //将user2的book执行一个新的book
return user2;
}
}
测试
public class test {
public static void main(String[] args) {
User user = new User(1,"jarvis",new Book(1,"三国"));
User user2 = null;
try {
user2 = user.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(user==user2); //false
System.out.println(user.getName()==user2.getName()); //false
System.out.println(user.getBook()==user2.getBook()); //false
}
}
Cloneable实现深拷贝的缺点:如果引用数量或者层数太多了,会很麻烦
序列化实现深拷贝
对象序列化的时候会写到一个byte[] (或json等其他媒介)中,反序列化的时候从byte[]中读取,然后会重新创建所有的对象。
Book类实现Serializable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book implements Serializable {
private int id;
private String name;
}
User类实现Serializable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private int id;
private String name;
private Book book;
protected User deepClone() throws IOException, ClassNotFoundException {
User user2 = null;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//对象的序列化输出
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(this);
objectOutputStream.close();
//
ByteArrayInputStream inputStream = new ByteArrayInputStream((outputStream.toByteArray()));
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
//生成新对象
user2 = (User)objectInputStream.readObject();
objectInputStream.close();
outputStream.close();
return user2;
}
}
测试
public class test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
User user = new User(1,"jarvis",new Book(1,"三国"));
User user2 = user.deepClone();
System.out.println(user==user2); //false
System.out.println(user.getName()==user2.getName()); //false
System.out.println(user.getBook()==user2.getBook()); //false
}
}