clone()方法是Object的native方法。protected native Object clone() throws CloneNotSupportedException;  声明为protected,表明子类必须重新实现该方法,除非是与Obeject类在一个包里,后者是不可能的。而实际上,作为native方法clone()已经有一份field to field的浅拷贝实现,实际上是不需要一定重写的。这种情况下,需要的做法就是覆写clone()方法,在方法里通过super.clone()调用Object的clone()。

而Cloneable是标记型接口,实现了Cloneable才可以实现clone()方法。否则使用clone()方法会报错。

下面是ArrayList的clone()

    /**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}

这里为何不是简单的super.clone()浅拷贝呢?因为成员变量是复杂类型时(涉及成员变量为对象的引用),就需要深拷贝。

下面做个小实验,先使用浅拷贝,验证普通的成员变量是ok的:

package a;

public class CloneTest  implements Cloneable {
private int v_a;
public void setV_a(int v) {
v_a = v;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void print_v_a() {
System.out.println(v_a);
}
public static void main(String[] args) throws CloneNotSupportedException {
CloneTest ct0 = new CloneTest();
ct0.setV_a(66);
CloneTest ct1 = (CloneTest)ct0.clone();
ct1.print_v_a();
ct0.setV_a(88);
ct1.print_v_a();
ct0.print_v_a();
}
}

先设置ct0的v_a对象为66,然后ct1对象是ct0的拷贝,打印ct1的v_a,也为66,说明拷贝成功。 之后重新设置ct0的值为88,ct1的值没变还是66。

package a;

import java.util.Arrays;

class A {
private int v;
public void setV(int v) {
this.v = v;
}
public void p_v() {
System.out.println(v);
}
} public class CloneTest implements Cloneable {
private A v_a;
public void setV_a(A v) {
v_a = v;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void print_v_a() {
v_a.p_v();
}
public static void main(String[] args) throws CloneNotSupportedException {
CloneTest ct0 = new CloneTest();
A a = new A();
a.setV(66);
ct0.setV_a(a);
CloneTest ct1 = (CloneTest)ct0.clone();
ct1.print_v_a();
a.setV(88);
ct0.setV_a(a);
ct1.print_v_a();
}
}

上面这个例子就体现出了浅拷贝的弱点,输出为66 88。

ct1是浅拷贝的ct0,此时ct0的v_a(成员变量,A对象)的v值为66。拷贝后,ct1的v_a(A对象的v)输出也是66。然后重设ct0的v_a(A对象的v)为88,再输出ct1的v_a(A对象的v),竟然也是88。说明二者的引用指向的是同样的堆内存。

浅拷贝情况下,两个对象的成员变量(A对象)引用的是同一个堆内存,并没有完全实现拷贝后内存独立。

这种情况就需要深拷贝。

例如文章最开始提到的ArrayList的clone()的写法。

java的clone()、浅拷贝与深拷贝的更多相关文章

  1. 【转】JAVA中的浅拷贝和深拷贝

    原文网址:http://blog.bd17kaka.net/blog/2013/06/25/java-deep-copy/ JAVA中的浅拷贝和深拷贝(shallow copy and deep co ...

  2. java中的浅拷贝和深拷贝

    复制 将一个对象的引用复制给另一个对象,一共有三种方式.第一种方式是直接赋值,第二种方式是浅复制,第三种方式是深复制. 1.直接赋值 在Java中,A a1 = a2,这实际上复制的是引用,也就是说 ...

  3. java中的浅拷贝与深拷贝

    浅拷贝: package test; class Student implements Cloneable { private int number; public int getNumber() { ...

  4. Java对象的浅拷贝和深拷贝&&String类型的赋值

    Java中的数据类型分为基本数据类型和引用数据类型.对于这两种数据类型,在进行赋值操作.方法传参或返回值时,会有值传递和引用(地址)传递的差别. 浅拷贝(Shallow Copy): ①对于数据类型是 ...

  5. java引用类型的浅拷贝与深拷贝理解

    1.浅拷贝 只会复制地址值,也就是同一个对象两个引用,只是复制了一个引用而已. 2.深拷贝 重新在堆里创建一个新对象给新引用,连同地址值也不一样. 首先要知道Object的clone()方法, pub ...

  6. 【Java】 Java中的浅拷贝和深拷贝

    先抛出结论: 浅拷贝是引用拷贝,A对象拷贝B以后,A对象和B对象指向同一块内存地址,改变A对象的属性值会触发B对象属性的改变,有安全风险 深拷贝是对象拷贝,A对象拷贝B以后,A对象和B对象指向不同的额 ...

  7. Java 数组的浅拷贝和深拷贝

    浅拷贝: 在堆内存中不会分配新的空间,而是增加一个引用变量和之前的引用指向相同的堆空间. int[] a = {1,2,3,4,5}; int[]b = a; public class Test { ...

  8. java 浅拷贝和深拷贝 对象克隆clone

    分一下几点讨论: 为什么要克隆? 如何实现克隆 浅克隆和深克隆 解决多层克隆问题 总结 一:为什么要克隆? 大家先思考一个问题,为什么需要克隆对象?直接new一个对象不行吗? 答案是:克隆的对象可能包 ...

  9. 浅拷贝和深拷贝(谈谈java中的clone)

    clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有 ...

  10. Java中的clone方法-理解浅拷贝和深拷贝

    最近学到Java虚拟机的相关知识,更加能理解clone方法的机制了 java中的我们常常需要复制的类型有三种: 1:8种基本类型,如int,long,float等: 2:复合数据类型(数组): 3:对 ...

随机推荐

  1. 新技能 get —— Python 断点续传下载文件

    from urllib.request import urlretrieve import sys import os prev_reported_download_percent = None # ...

  2. CocoaPods详解之(一)----使用篇

    CocoaPods详解之----使用篇 作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/18737437 一.什么是Coc ...

  3. erlang应用发布

    http://blog.csdn.net/zhangxinrun/article/details/6993892 参考“转载1”和“转载2”就可以了,但需要注意以下两点: 1.如果用rebar - c ...

  4. 学习鸟哥的Linux私房菜笔记(1)——Linux系统入门

    今天在阿里云申请了一个centos系统的云服务器,以前对linux了解的只是皮毛,记了几个命令还给忘了,整了半天都弄不好,作为一个做过javaweb开发的coder实在是惭愧啊,决定从今天开始学习Li ...

  5. cordova-plugin-android-update安卓版本更新插件使用

    原文:cordova-plugin-android-update安卓版本更新插件使用 安装插件,使用方法官方都有. 安卓7.0以上要在AndroidMainfest.xml里加一句 <uses- ...

  6. Arcgis api for javascript学习笔记(4.5版本)-三维地图的飞行效果

    其实就只是用到了 view.goTo()  函数,再利用 window.setInterval()  函数(定时器)定时执行goTo().代码如下: <!DOCTYPE html> < ...

  7. 继承之重写prototype

    function Ff(){} //undefined Ff.prototype={a:"ss"} //Object {a: "ss"} var f1= new ...

  8. Matlab Tricks(十八)—— 矩阵间元素距离的计算

    两个矩阵间元素(向量)距离的度量,首先想到的是遍历,循环的方式,显然 matlab 下的编程并不推荐,matlab 下矩阵向量化编程效率尤高. 先考虑两个向量距离的计算: ∥x−y∥2=∥x∥2+∥y ...

  9. 写一个去除AI2XAML注释及多余数字位数的WPF窗体程序

    原文:写一个去除AI2XAML注释及多余数字位数的WPF窗体程序 使用正则表达式去除多余注释及冗余数字位,关键代码:            string pattern = @"/b(/d+ ...

  10. 局部QEventLoop帮助QWidget不消失(也就是有一个局部事件循环始终在运行,导致程序被卡住那里,但仍可以接受事件。说白了就是有一个while语句死活不肯退出,直到收到退出信号)

    熟悉的陌生人 Qt 是事件驱动的,所以当你用Qt的时候,几乎时时刻刻和 QEventLoop 打交道.,只是你可能没有意识到: QCoreApplicaton::exec() QApplication ...