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();
}
}
学习群64弍46衣3凌9,资料群69似64陆0吧3
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关键字将其排除在复制过程中。