- 定义
比如A被B浅拷贝后,B的所有变量和A的所有变量相同,而且B所有对其他对象的引用任然指向原来的对象,也就是对像浅拷贝只会对主对象(就是A)本身进行拷贝,但不会对主对象里面的对象拷贝,A和B里面的对象引用相同,属于共享状态。
简单说就是,支付至所考虑的对象,而不复制它引用的对象。
- 浅拷贝前提
首先对象浅拷贝需要实现接口Cloneable
Cloneable接口
空接口
Cloneable是标记型的接口(空接口),它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就CloneNotSupportedException异常。可以理解为Cloneable接口发挥的是标记功能,自定义类型需要用户自己标记出哪些类是可以clone的,这个标记就是去实现Cloneable接口,实现了Cloneable接口后就表明该类创建的对象可以被克隆。而要想使一个类具备拷贝实例的功能,除了要实现Cloneable接口,还必须重写Object类的clone()方法。
- 代码详解
Group类实现:
super.clone()他会把原对象完整的拷贝过来包括其中的引用,属于欠拷贝
接下里是Group和Person类,用于浅拷贝
package com.consumer.test;
public class Group implements Cloneable{
String groupName;
Person person;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
package com.consumer.test;
public class Person implements Cloneable{
String name;
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
- 具体实现样例类ShallowCopy
public class ShallowCopy {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.setName("zhangSan");
person.setSex("17");
Group group = new Group();
group.setGroupName("1号组");
group.setPerson(person);
Group groupCopy = (Group) group.clone();
System.out.println("浅拷贝后:");
System.out.println(groupCopy.getGroupName());
System.out.println(groupCopy.getPerson().getName());
System.out.println(groupCopy.getPerson().getSex());
System.out.println("修改Person信息后:");
person.setName("liSi");
person.setSex("28");
group.setGroupName("copy组");
System.out.println(groupCopy.getGroupName());
System.out.println(groupCopy.getPerson().getName());
System.out.println(groupCopy.getPerson().getSex());
}
}
运行结果
从结果发现浅拷贝后复制得到的对象引用的person会随着原对象的改变而变化,但是直接属性String不会随着原对象的修改儿变化。
浅拷贝特点总结:
1.复制得到的对象本身是新对象
2.对象里面的基本数据会复制, 基本数据不存在引用;特殊的String类型,有深拷贝表现;
String 存在于堆内存、常量池;这种比较特殊, 本身没有实现 Cloneable, 传递是引用地址;
由本身的final性, 每次赋值都是一个新的引用地址,原对象的引用和副本的引用互不影响。
因此String就和基本数据类型一样,表现出了"深拷贝"特性.
3.对象里面的复杂数据类型会进行浅拷贝, 指向的同一个引用地址
深拷贝:DeepGroup
package com.consumer.test;
public class DeepGroup implements Cloneable{
String groupName;
Person person;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
protected Object clone() throws CloneNotSupportedException {
DeepGroup deepGroup = (DeepGroup)super.clone();
// 本来是浅复制的,现在讲Person镀锌复制一份重新set进来,因为都是基本类型,都支持深拷贝
deepGroup.setPerson((Person) deepGroup.getPerson().clone());
return deepGroup;
}
}
Person
package com.consumer.test;
public class Person implements Cloneable{
String name;
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 具体深拷贝实例:
package com.consumer.test;
public class DeepGroup implements Cloneable{
String groupName;
Person person;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
protected Object clone() throws CloneNotSupportedException {
DeepGroup deepGroup = (DeepGroup)super.clone();
// 本来是浅复制的,现在讲Person镀锌复制一份重新set进来,因为都是基本类型,都支持深拷贝
deepGroup.setPerson((Person) deepGroup.getPerson().clone());
return deepGroup;
}
}
运行结果:
深拷贝结果
我们发现深拷贝后,复制得到的对象不会受到原来对象的修改而变化的影响了。
深拷贝总结:
所有属性都是一份拷贝, 跟原数据不会有任何耦合(不存在引用共享),我们目前是一层层浅拷贝,实现深拷贝,如果嵌套层次很多会很臃肿,当然我们可以序列化深拷贝: 不需要递归让所有对象实现cloneable接口, 方便简洁。
贴上深拷贝工具类:(此工具类的拷贝类必须都是实现了Serializable接口,支持序列化,否则会报错)
package com.consumer.test;
import java.io.*;
public class CloneUtils {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T obj) {
T cloneObj = null;
try {
//写入字节流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
//返回生成的新对象
cloneObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}
序列化实现深拷贝实现:
package com.consumer.test;
import com.alibaba.fastjson.JSONObject;
public class DeepTest {
public static void main(String[] args) throws Exception {
Person person = new Person();
person.setName("zhangSan");
person.setSex("17");
DeepGroup deepGroup = new DeepGroup();
deepGroup.setGroupName("1号组");
deepGroup.setPerson(person);
// 序列化——深拷贝
// 相当于重写字节流, 再创建新对象, 跟原对象没有任何引用共享, 无需嵌套重现 Cloneable.clone(), 只需要实现 Serializable (每个子类)
System.out.println("----------------- 序列化-深拷贝测试1 ------------------");
// 工具类
DeepGroup deepGroupCopy = CloneUtils.deepClone(deepGroup);
System.out.println("deepGroup == deepGroupCopy: " (deepGroup == deepGroupCopy));
System.out.println("deepGroup.person == deepGroupCopy.person: " (deepGroup.getPerson() == deepGroupCopy.getPerson()));
System.out.println("deepGroup.person.name == deepGroupCopy.person.name: " (deepGroup.getPerson().getName() == deepGroupCopy.getPerson().getName()));
System.out.println(JSONObject.toJSONString(deepGroup));
System.out.println(JSONObject.toJSONString(deepGroupCopy));
System.out.println("----------------- 序列化-深拷贝测试2 ------------------");
person.setName("liSi");
person.setSex("132");
System.out.println(JSONObject.toJSONString(deepGroup));
System.out.println(JSONObject.toJSONString(deepGroupCopy));
}
}
运行结果:
我们发现序列化可以实现深拷贝,没有问题,到此ok。
- 你究竟是害怕剑姬还是害怕满是破绽的自己。