Java实现克隆的几种方式
- 实现Cloneable接口,重写Object类中的clone()方法
- 实现Serializable接口,通过对象的序列化和反序列化实现克隆
实现Cloneable接口演示
public class Student implements Cloneable {
private String name;//姓名
private int age;//年龄
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
//setter and getter
...
}
测试方法
public class TestClone {
public static void main(String[] args) {
Student s1 = new Student();//创建学生对象
s1.setName("小麦");
s1.setAge(12);
try {
Student s2 =(Student) s1.clone();//调用重写后的clone()方法进行对象克隆
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);//结果为false
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
可以看出克隆的结果确实是实现了克隆对象,但在测试方法中加入如下代码
System.out.println(s1.getName().hashCode());//771191
System.out.println(s2.getName().hashCode());//771191
我的输出结果为771191,说明两个对象的name属性都指向了同一个对象,并没有新创建对象,这实际上就是典型的浅克隆
深克隆实现演示
实体类Person代码,实现Serializable接口
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;//姓名
private int age;//年龄
private Car car;
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
//setter and getter
...
}
克隆工具类
public class CloneUtil {
private CloneUtil(){
throw new AssertionError();
}
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) throws Exception{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
}
//ByteArrayInputStream和ByteArrayOutputStream无需调用close方法,是基于内存的流,只要垃圾回收器清理对象就能够释放资源
}
实体类Car,实现Serializable接口
public class Car implements Serializable {
private static final long serialVersionUID = 11L;
private String brand;//品牌
private int maxSpeed;//最高时速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
//setter and getter
...
}
测试类
public class CloneTest {
public static void main(String[] args) {
Person p1 = new Person("小东",12,new Car("Benz",10));
try {
Person p2 = CloneUtil.clone(p1);
p2.getCar().setBrand("BYD");
System.out.println(p1.getCar().hashCode());//13648335
System.out.println(p2.getCar().hashCode());//1935637221
//修改克隆的Person对象p2关联的的汽车对象的品牌属性
//原来的Person对象p1关联的汽车不会受到影响
//因为在克隆Person对象时关联的汽车对象也被克隆了
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里是通过序列化和反序列化的方式实现深克隆,要克隆的对象要实现Serializable接口,测试方法中通过获取两个对象中Car属性的hashCode可知这种方式实现的克隆不止Person对象新创建了一份,属性同样也是新创建的,也就是两个对象中的属性都已相对独立。
其实也可以通过在浅克隆重写的clone()方法中,实现属性的clone()操作,以上述为例,Person类中的Car同样可以实现新创建对象的效果,两种方式实现深克隆均非真正意义上的深克隆,原因是Car对象中的brand最终指向的仍会是同一个对象,要实现真正意义上的深克隆几乎不可能实现。
写到这里有人可能会说:“你用两个例子都有很多不一样,怎么能确定第一种方式的克隆去克隆Person实例,返回的Car对象是同一个?”
所以我在第一个例子中的测试类中加入如下代码
Person p1 = new Person("haha",12,new Car("hehe",122));
Person p2 = (Person)p1.clone();
System.out.println(p1);
System.out.println(p2);
System.out.println(p1.getCar().hashCode());//984849465
System.out.println(p2.getCar().hashCode());//984849465
结果显示,属性值的哈希值确实相同,也就是没有创建新实例。
纪念一下自己的第一篇博文。