java.lang.Cloneable
和java.io.Serializable
一样属于标记型接口,没有定义任何方法和属性。
一个类想要使用克隆方法
重写clone()方法
,因为Object的clone()的修饰符是protected;
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
实现Cloneable接口
,否则会抛出CloneNotSupportedException异常。
克隆类型
浅拷贝
,拷贝对象时仅拷贝对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象深拷贝
,不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象
举个栗子
这里使用User、Phone举例。
测试浅拷贝、深拷贝所用的Main方法如下:
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 预置数据
Phone phone1 = new Phone();
String number = "11111111111";
phone1.setPhoneNum(number);
String name = new String("dkangel");
int age = 24;
User user1 = new User(name, age, phone1);
User user2 = (User) user1.clone();
System.out.println("对象是否相等:" + (user1 == user2));
System.out.println("类的类型是否相等:" + (user1.getClass() == user2.getClass()));
System.out.println("age属性是否相等:" + (user1.getAge() == user2.getAge()));
System.out.println("name属性是否相等:" + (user1.getName() == user2.getName()));
System.out.println("对象的对象属性Phone是否相等:" + (user1.getPhone() == user2.getPhone()));
}
}
浅拷贝
只拷贝对象本身,Phone不需要实现Cloneable接口和重写clone()方法。
public class Phone {
private String phoneNum;
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
}
public class User implements Cloneable {
private String name;
private int age;
private Phone phone;
public User(String name, int age, Phone phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
user2是user1的浅拷贝,只拷贝user1对象本身,未拷贝成员变量phone,所以两者phone对象相等
深拷贝
不仅拷贝对象本身,而且拷贝对象所指向的所有对象,所以Phone类也要支持clone。
public class Phone implements Cloneable{
private String phoneNum;
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
User.java 这里只粘贴clone()方法,其他和浅克隆保持一致
public class User implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User) super.clone();
user.phone = (Phone) user.getPhone().clone();
return user;
}
}
user2是user1的深拷贝,拷贝user1本身的同时也拷贝一份phone1对象,两者phone对象不再相等
结合JVM内存分区理解下浅拷贝和深拷贝
看上图可知,JVM内存分为五个部分堆、方法区、程序计数器、虚拟机栈、本地方法栈,在这里我们只关注
- 存放对象的
堆
- 存放基本数据类型和对象引用的
虚拟机栈
- 存放类基本信息的
方法区
浅拷贝解读
模式下从上到下解读CloneTest的main方法,得出如下对象在JVM中的分布图。
这里采用句柄模式
访问对象(还有直接地址模式),简单解读下
new出来的对象phone1存入堆的实例池里;字符串number由于使用引号创建,所以存到堆中的字符串常量池;字符串name使用new创建,所以存入String实例池;age属于基本数据类型所以存入虚拟机栈;user1在堆中分配内存,通过clone对象user1本身的数据创建对象user2,user2的属性name/age/phone都与user1共用。
深拷贝解读
通过深拷贝创建的user2,不再与user1对象共用phone属性。深拷贝不仅拷贝了user1本身,也拷贝了user1的phone1对象。
这里只粘贴了与浅拷贝不同的地方,user2的phone属性由指向phone1改为指向phone2
如若有错误还望不吝指出,peace