开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
在日常开发中,Java的引用类型在传递的时候是属于引用传递,那么我们想将引用对象完整的复制一份出来,并不能使用简单的
=赋值来实现,那么我们就需要对象克隆。
在Java中,想要复制一个基本类型很简单,使用=进行赋值就行了。如果我们要对对象进行克隆,采用简单的赋值,只是传递了地址,其实两者还是指向同一个对象,修改任何一个变量的值都会导致该对象的改变。
// 基本类型直接赋值,因为是值传递
int age = 22;
double altitude = 300;
int num = age
// 对象直接赋值,copyUserInfo与userInfo指向的是同一个对象,因为是引用传递
UserInfo userInfo = new UserInfo();
UserInfo copyUserInfo = userInfo;
克隆对象有两种实现方法
1. clone()
- 首先在类中实现
Cloneable接口- 被复制类中必须实现
Cloneable接口,该接口为标记接口,不含任何方法,就只是表示这个类可以被克隆 - 重写
equal()是为了后续更清晰的查看结果
- 被复制类中必须实现
public class User implements Cloneable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object obj) {
User user = (User) obj;
if (name.equals(user.name) && age == user.age) return true;
return false;
}
}
- 使用
clone()来克隆对象
我们可以看到输出的结果中,两个变量所指的对象值是完全一样的,但是两个变量所指的对象地址完全不同,为两个对象,那么对象克隆成功。
private static void cloneTest() {
User user = new User("aHua", 22);
try {
User userClone = (User) user.clone();
System.out.println("equal: " + (userClone.equals(user)));
System.out.println("==: " + (userClone == user));
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
但是该方法还不能实现完全的克隆,只能够克隆对象中的基本类型,不能克隆对象中引用类型,那么这种克隆被称为浅克隆,并不是完完全全的克隆。
深克隆和浅克隆的区别就在于支不支持对象中引用类型变量的克隆
2. Serializable
除了使用clone()可以克隆对象之外,还可以使用Serializable克隆对象,该方法克隆的对象则是深克隆,不论对象内存储的是什么类型,所有内容都将完全克隆,变成一个新的对象。
- 类中需要实现
Serializable接口- 类支持序列化必须实现
Serializable接口,标记接口,标记该类可被序列化
- 类支持序列化必须实现
public class User implements Cloneable, Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object obj) {
User user = (User) obj;
if (name.equals(user.name) && age == user.age) return true;
return false;
}
}
- 使用流克隆对象
private static void serializableTest() {
System.out.println("------- serializableTest ------");
User user = new User("aHua", 22);
try {
User copyUser = deepCopyObject(user);
System.out.println("user name: " + user.getName() + ", age: " + user.getAge());
System.out.println("copyUser name: " + copyUser.getName() + ", age: " + copyUser.getAge());
System.out.println("equal: " + (user.equals(copyUser)));
System.out.println("==: " + (user == copyUser));
} catch (IOException e) {
System.out.println("IOException: " + e);
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
private static <E> E deepCopyObject(E src) throws IOException, ClassNotFoundException {
if (src == null) return null;
// 将对象序列化成流
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(src);
// 重新将流序列化成对象
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream in = new ObjectInputStream(byteIn);
return (E) in.readObject();
}
根据输出结果看出Serializable也可以克隆对象,而且使用该方法克隆对象是深克隆,因为将原对象序列化成流,流和原对象就失去了关联,再将流重新反序列化成新对象,那么就新对象就是原对象的完全克隆。
使用Serializable克隆对象效率很低,但它是完全克隆的最好的办法
如有错误,欢迎在评论区指出!