JAVA之路_假克隆、浅克隆、深克隆
一.JAVA假克隆
Java中,对于基本类型,可以用“=”进行克隆,而对于引用类型却不能简单的使用“=”进行克隆,这与JAVA的内存使用空间有关,JAVA在栈中保存基本类型和引用变量,在堆中保存对象。对于引用变量而言,使用“=”将修改引用,而不是复制堆中的对象,此时两个引用对象将指向同一个对象,因此如果对一个变量修改则会修改另一个对象。
public class Employee { private String name; private int age; //省略get和set方法 @Override public String toString() { return "姓名:" + name + ", 年龄:" + age; } } public class Test { public static void main(String[] args) { System.out.println("克隆之前:"); Employee employee1 = new Employee(); employee1.setName("芋头1"); employee1.setAge(12); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("克隆之后:"); Employee employee2 = employee1; employee2.setName("芋头2"); employee2.setAge(114); System.out.println("员工2的信息:"); System.out.println(employee2); System.out.println("员工1的信息:"); System.out.println(employee1); } }
输出:
克隆之前:
员工1的信息:
姓名:芋头1, 年龄:12
克隆之后:
员工2的信息:
姓名:芋头2, 年龄:114
员工1的信息:
姓名:芋头2, 年龄:114
可以看出,employee1和employ2两个引用变量同时指向一个对象,当修改employee2的域时,employee11的域也被修改,因此是假克隆。
二、浅克隆
protect Object clone()
通常需要改写该方法并把访问权限限定为public,该方法对于类中的每个域,如果只包含基本类型和不可变的引用类型,如string,或者对象在其生命周期内不可变化,则可以用浅克隆来复制对象。
public class Address { private String state; private String province; private String city; //省略get和set @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("国家:" + state + ", "); sb.append("省:" + province + ", "); sb.append("市:" + city); return sb.toString(); } } public class Employee implements Cloneable { private String name; private int age; private Address address; //省略get和set @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("姓名:" + name + ", "); sb.append("年龄:" + age + "\n"); sb.append("地址:" + address); return sb.toString(); } @Override public Employee clone() { Employee employee = null; try { employee = (Employee) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return employee; } } public class Test { public static void main(String[] args) { System.out.println("克隆之前:"); Address address = new Address("中国", "吉林", "长春"); Employee employee1 = new Employee("明日科技", 12, address); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("克隆之后:"); Employee employee2 = employee1.clone(); employee2.getAddress().setState("中国"); employee2.getAddress().setProvince("四川"); employee2.getAddress().setCity("成都"); employee2.setName("西南交通大学"); employee2.setAge(114); System.out.println("员工2的信息:"); System.out.println(employee2); System.out.println("员工1的信息:"); System.out.println(employee1); } }
输出:
克隆之前:
员工1的信息:
姓名:明日科技, 年龄:12
地址:国家:中国, 省:吉林, 市:长春
克隆之后:
员工2的信息:
姓名:西南交通大学, 年龄:114
地址:国家:中国, 省:四川, 市:成都
员工1的信息:
姓名:明日科技, 年龄:12
地址:国家:中国, 省:四川, 市:成都
我们发现,employee类中又包含了Adress类adress的引用,我们知道,clone方法默认的是浅克隆,即不会克隆对象引用的对象,而只是简单地复制这个引用。所以在上例中,adress对象在内存中只有一个,employee1和employee2都指向它,任何一个对象对它的修改都会影响另一个对象。所以adress的值也被修改了。
三,深克隆
一种就是在引用类型中添加克隆方法。如对上面的浅克隆代码改成:
在Adress类中增加
protected Address clone() { Address address = null; try { address = (Address) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return address; }
Employee中:
public Employee clone() { Employee employee = null; try { employee = (Employee) super.clone(); employee.address = address.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); }
输出:
克隆之前:
员工1的信息:
姓名:明日科技, 年龄:12
地址:国家:中国, 省:吉林, 市:长春
克隆之后:
员工2的信息:
姓名:西南交通大学, 年龄:114
地址:国家:中国, 省:四川, 市:成都
员工1的信息:
姓名:明日科技, 年龄:12
地址:国家:中国, 省:吉林, 市:长春
实现了深克隆
一个方法自然是重写clone方法,添加如order.items=(LineItems)items.clone()的语句,也就是人为地添加对引用对象的复制。这个方法的缺点是如果引用对象有很多,或者说引用套引用很多重,那么太麻烦了。业界常用的方法是使用串行化然后反串行化的方法来实现深克隆。由于串行化后,对象写到流中,所有引用的对象都包含进来了,所以反串行化后,对等于生成了一个完全克隆的对象。
这个方法的要求是对象(包括被引用对象)必须事先了Serializable接口,否则就要用transient关键字将其排除在复制过程中。