欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JAVA之路_假克隆、浅克隆、深克隆

程序员文章站 2022-04-16 21:15:29
一.JAVA假克隆 Java中,对于基本类型,可以用“=”进行克隆,而对于引用类型却不能简单的使用“=”进行克隆,这与JAVA的内存使用空间有关,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关键字将其排除在复制过程中。