Java中的深克隆和浅克隆

  • 引用传递
public static void main(String[] args){
    //引用传递
    Student stu1 = new Student();
    Student stu2 = stu1;
    System.out.println(stu1);
    System.out.println(stu2);
}

输出结果为:

test_java.Student@1540e19d
test_java.Student@1540e19d

两个student地址相同 也就是说他们是同一个对象 这就是引用的传递 也可称为引用的拷贝 

  • 浅拷贝

浅拷贝是按位拷贝对象,他会创建一个新的对象(参考上面的引用传递 并没有创建新的对象),新的对象有原始对象属性值的精确拷贝。如果属性是基本数据类型,拷贝的就是基本数据类型的值,如果属性是引用类型,拷贝的就是引用类型的引用地址。所以,如果基本数据类型改变,不会影响另一个对象,如果引用类型的数据发生改变,则另一个对象也会改变。

 浅拷贝的条件:必须实现Cloneable接口,并且覆写clone()方法

Student类:

//学生类实现克隆的接口 才可以使用clone方法 克隆对象

class Student implements Cloneable{

    private String name;
    private int age;

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return super.toString();
        //return "name: "+name+" age: "+age;
    }

    @Override
    protected Object clone(){

        Student stu = null;

        try {
            stu = (Student)super.clone();
        }catch(CloneNotSupportedException e){

            e.printStackTrace();
        }

        return stu;

    }

public static void main(String[] args){
    Student stu1 = new Student();
    stu1.setName("李三");
    stu1.setAge(21);

    Studnet stu2 = stu1;
    
    System.out.println(stu1);
    System.out.println(stu1);
}

输出的结果是:

test_java.Student@677327b6
test_java.Student@14ae5a5

从输出结果可以看出,两个student的对象地址不一样,实现了对象的拷贝

但是,在这里需要注意到,类中有一个String类型的饮用对象name,这里的String是同对象一起被拷贝过去了呢?

还是说两个student引用的依然是同一个name对象呢?测试一下:

System.out.println(stu1.getName().hashCode());
System.out.println(stu2.getName().hashCode());

输出结果:

1224543
1224543

可以看出,二者的name对象依然是同一个对象,这也就是典型的浅拷贝。

  • 深拷贝

深拷贝会拷贝所有的属性,并且拷贝属性指向的动态分配的内存。当对象和它引用的对象一起拷贝是即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

 

static class Body implements Cloneable{
    public Head head;
    public Body() {}
    public Body(Head head) {this.head = head;}

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Body newBody =  (Body) super.clone();
        newBody.head = (Head) head.clone();
        return newBody;
    }

}
static class Head implements Cloneable{
    public  Face face;

    public Head() {}
    public Head(Face face){this.face = face;}
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
} 
public static void main(String[] args) throws CloneNotSupportedException {

    Body body = new Body(new Head());

    Body body1 = (Body) body.clone();

    System.out.println("body == body1 : " + (body == body1) );

    System.out.println("body.head == body1.head : " +  (body.head == body1.head));


}

输出结果:

body == body1 : false 
body.head == body1.head : false

由此可见, body和body1内的head引用指向了不同的Head对象, 也就是说在clone Body对象的同时, 也拷贝了它所引用的Head对象, 进行了深拷贝。

讨论

经过了上面的实践,可以得出结论:要实现深拷贝一个对象,不光该对象要实现Cloneable接口,实现clone方法;该对象中的引用对象也要实现Cloneable接口,并且实现clone方法。

那么,按照上面的结论, Body类组合了Head类, 而Head类组合了Face类,要想深拷贝Body类,必须在Body类的clone方法中将Head类也要拷贝一份,但是在拷贝Head类时,默认执行的是浅拷贝,也就是说Head中组合的Face对象并不会被拷贝。

只能说这不是彻底的深拷贝,但他还是属于深拷贝。也就是两个对象之间还有些许的联系,没有完全的独立。

clone在平时项目的开发中可能用的不是很频繁,但是区分深拷贝和浅拷贝会让我们对java内存结构和运行方式有更深的了解。至于彻底深拷贝,几乎是不可能实现的,原因已经在上一节中进行了说明。深拷贝和彻底深拷贝,在创建不可变对象时,可能对程序有着微妙的影响,可能会决定我们创建的不可变对象是不是真的不可变。clone的一个重要的应用也是用于不可变对象的创建。
 

alibaba的规范手册

【强制】关于基本数据类型与包装数据类型的使用标准如下: 
1) 所有的 POJO 类属性必须使用包装数据类型。 
2) RPC 方法的返回值和参数必须使用包装数据类型。 
3) 所有的局部变量推荐使用基本数据类型。 
说明: POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE 问题,或者入库检查,都由使用者来保证。 
正例: 数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。反例: 某业务的交易报表上显示成交总额涨跌情况,即正负 x%, x 为基本数据类型,调用的RPC 服务,调用不成功时,返回的是默认值,页面显示: 0%,这是不合理的,应该显示成中划线-。所以包装数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。

【推荐】慎用 Object 的 clone 方法来拷贝对象。 
说明: 对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象的拷贝。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值