克隆(复制)在Java中是一种常见的操作,目的是快速获取一个对象副本。 克隆分为深克隆和浅克隆。 浅克隆 :创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
1,克隆的分类
深克隆与浅克隆的区别就是,浅克隆不会克隆原对象中的引用类型,仅仅拷贝了引用类型的指向。深克隆则拷贝了所有。也就是说深克隆能够做到原对象和新对象之间完全没有影响。
下面举例说明:
1,浅克隆,基本数据类型会拷贝值,引用类型拷贝的是引用。
import java.io.Closeable;
import java.io.IOException;
public class Person implements Cloneable {
private String name;
private int age;
private String address;
private BodyParam param;
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public BodyParam getParam() {
return param;
}
public void setParam(BodyParam param) {
this.param = param;
}
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
return person;
}
public String toString(){
return "Person is [name="+name+"age="+age+"address="+address+"param"+param+"]";
}
}
@Test
public void testClone() throws CloneNotSupportedException {
Person person = new Person();
person.setName("mjdai");
person.setAge(18);
person.setAddress("shenzhen");
BodyParam bodyParam = new BodyParam();
bodyParam.setHand("hand");
bodyParam.setFoot("foot");
person.setParam(bodyParam);
System.out.println(person.toString());
// 浅克隆 拷贝的是引用
Person closePerson = person.clone();
System.out.println(closePerson.toString());
}
结果如下所示:
Connected to the target VM, address: '127.0.0.1:59782', transport: 'socket'
person is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
clonePerson is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
Disconnected from the target VM, address: '127.0.0.1:59782', transport: 'socket'
2,深克隆,深克隆需要采用流的写入写出实现:
在需要被克隆的对象中实现如下方法。
// 深拷贝
protected Person deepClone() throws IOException, ClassNotFoundException {
// 将当前对象写入流中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 将对象从流中读取出来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Person)ois.readObject();
}
测试方法:
@Test
public void testClone() throws CloneNotSupportedException, IOException,
ClassNotFoundException {
Person person = new Person();
person.setName("mjdai");
person.setAge(18);
person.setAddress("shenzhen");
BodyParam bodyParam = new BodyParam();
bodyParam.setHand("hand");
bodyParam.setFoot("foot");
person.setParam(bodyParam);
System.out.println("person is :"+person.toString());
// 浅克隆 拷贝的是引用
Person clonePerson = person.clone();
System.out.println("clonePerson is :"+clonePerson.toString());
// 浅克隆 拷贝的是引用
Person deepClosePerson = person.deepClone();
System.out.println("deepClosePerson is :"+deepClosePerson.toString());
}
测试结果:
Connected to the target VM, address: '127.0.0.1:61976', transport: 'socket'
person is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
clonePerson is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
deepClosePerson is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@46daef40]
Disconnected from the target VM, address: '127.0.0.1:61976', transport: 'socket'
需要注意的是如果没有实现序列化的话,是会报错的:
测试去掉引用对象的序列化:
public class BodyParam implements Cloneable, Serializable
去掉Serializable,测试结果如下:
Connected to the target VM, address: '127.0.0.1:51295', transport: 'socket'
person is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
clonePerson is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
java.io.NotSerializableException: BodyParam
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at Person.deepClone(Person.java:54)
at TestClone.testClone(TestClone.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
恢复public class BodyParam implements Cloneable, Serializable
去掉public class Person implements Cloneable ,Serializable中的Serializable
结果如下:
Connected to the target VM, address: '127.0.0.1:56254', transport: 'socket'
person is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
clonePerson is :Person is [name=mjdaiage=18address=shenzhenparamBodyParam@6536e911]
java.io.NotSerializableException: Person
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at Person.deepClone(Person.java:54)
at TestClone.testClone(TestClone.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Disconnected from the target VM, address: '127.0.0.1:56254', transport: 'socket'
Process finished with exit code -1
如上就是java克隆的简单测试验证,当然序列化可以使用其他的方式。