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关键字将其排除在复制过程中。

posted @ 2018-07-18 17:43  真某人  阅读(303)  评论(0编辑  收藏  举报