知识点总结
示例代码
浅克隆
案例1:浅克隆实现简易对象克隆,只包含值类型的成员变量
/**
* 浅克隆的一般步骤:
*
* 1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)
*
* 2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)
*/
public class CloneTest1 {
private static class Student implements Cloneable {
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public Object clone() {
Student stu = null;
try {
stu = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
public static void main(String args[]) {
Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = (Student) stu1.clone();
System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
stu2.setNumber(54321);
System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
System.out.println(stu1 == stu2); // false
}
}
案例2:浅克隆实现包含值类型成员变量时暴露的一些问题
package com.netease.cloud.scaffold.clone;
/**
* 浅克隆
*
* 在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
*
* 简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
*/
public class CloneTest2 {
private static class Address {
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
private static class Student implements Cloneable {
private int number;
private Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public Object clone() {
Student stu = null;
try {
stu = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
public static void main(String args[]) {
Address addr = new Address();
addr.setAddr("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setAddr(addr);
Student stu2 = (Student) stu1.clone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddr());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddr());
System.out.println(stu1 == stu2);//false
System.out.println(stu1.getAddr() == stu2.getAddr());//true
addr.setAddr("西湖区");
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddr());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddr());
}
}
深克隆
案例1:深克隆解决了浅克隆在包含值类型成员变量时克隆的问题
package com.netease.cloud.scaffold.clone;
/**
* 深度克隆:如果希望克隆引用类型的成员变量,则需要该成员变量也实现Cloneable接口
*/
public class CloneTest3 {
private static class Address implements Cloneable {
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
protected Object clone() {
Address add = null;
try {
add = (Address) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return add;
}
}
private static class Student implements Cloneable {
private int number;
private Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public Object clone() {
Student stu = null;
try {
stu = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
stu.setAddr(((Address)addr.clone()));
return stu;
}
}
public static void main(String args[]) {
Address addr = new Address();
addr.setAddr("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setAddr(addr);
Student stu2 = (Student) stu1.clone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddr());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddr());
System.out.println(stu1 == stu2);//false
System.out.println(stu1.getAddr() == stu2.getAddr());//true
addr.setAddr("西湖区");
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddr());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddr());
}
}
案例2:序列化的方式解决多层克隆问题
/**
* 序列化的方式实现克隆的功能
*
* 实现对象克隆有两种方式:
*
* 1). 实现Cloneable接口并重写Object类中的clone()方法;
* 2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
*
* 注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,
* 这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时
* 候暴露出来总是优于把问题留到运行时。
*
*/
public class Outer implements Serializable {
private static final long serialVersionUID = 369285298572941L; //最好是显式声明ID
public Inner inner;
public void setInner(Inner inner) {
this.inner = inner;
}
public Inner getInner() {
return inner;
}
//Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化]
public Outer myclone() {
Outer outer = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
outer = (Outer) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return outer;
}
private static class Inner implements Serializable {
private static final long serialVersionUID = 872390113109L; //最好是显式声明ID
public String name = "";
public Inner(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Inner的name值为:" + name;
}
}
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = new Inner("test1");
outer.setInner(inner);
Outer outerNew = outer.myclone();
outerNew.getInner().setName("test2");
System.out.println(outer.getInner().toString());
System.out.println(outerNew.getInner().toString());
}
}