Java.lang.Object.clone()分析

  

  首先,看一下源码:

1 public class Object  {
2     protected native Object clone() throws CloneNotSupportedException;
3 }

  由源代码我们会发现:

  第一:Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于Java中的非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息复制到新对象中,虽然这也实现了clone功能。(JNI是Java
Native Interface的 缩写。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java
虚拟机实现下。)

  第二:Object类中的 clone()方法被protected修饰符修饰。这也意味着如果要应用 clone()方 法,必须继承Object类,在 Java中所有的类是缺省继承 Object类的,也就不用关心这点了。然后重载 clone()方法。还有一点要考虑的是为了让其它类能调用这个 clone类的 clone()方法,重载之后要把 clone()方法的属性设置为 public。

  第三:Object.clone()方法返回一个Object对象。我们必须进行强制类型转换才能得到我们需要的类型。

  浅层复制与深层复制概念:

  浅层复制: 被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它所引用的对象。(概念不好理解,请结合下文的示例去理解)

  深层复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象引用的对象都复制一遍。

  Java中对象的克隆

  1)在派生类中实现Cloneable借口。

  2)为了获取对象的一份拷贝,我们可以利用Object类的clone方法。

  3)在派生类中覆盖积累的clone方法,声明为public。

  4)在派生类的clone方法中,调用super.clone()。

  实现Cloneable接口

  首先,看一下源码:  

1 public interface Cloneable {
2 }

  我们奇怪的发现Cloneable竟然是空的,那么我们为什么要实现Cloneable接口呢?其实Cloneable接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。

  程序示例分析:

 1 public class Person {
 2     private String name;
 3     private int age;
 4     public Person(){}
 5     public Person(String name,int age){
 6         this.name=name;
 7         this.age=age;
 8     }
 9     public Object clone(){
10         Object o=null;
11         try {
12             o=super.clone();
13         } catch (CloneNotSupportedException e) {
14             e.printStackTrace();
15         }
16         return o;
17     }
18     public String getName() {
19         return name;
20     }
21     public void setName(String name) {
22         this.name = name;
23     }
24     public int getAge() {
25         return age;
26     }
27     public void setAge(int age) {
28         this.age = age;
29     }
30 }

 1 public class PersonTest {
 2     public static void main(String[] args) {
 3         Person p1=new Person("zhangsan",18);
 4         Person p2=(Person)p1.clone();
 5         p2.setName("lis");
 6         p2.setAge(20);
 7         System.out.println("name="
 8             +p1.getName()+",age="+p1.getAge());
 9         //修改p2后,没有对p1产生影响。
10     }
11 }

  说明:

  1)为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。

  2)继承自java.lang.Object.clone()方法是浅层复制。一下代码可以证明之:

 1 public class Student implements Cloneable {
 2     private String name;
 3     private int age;
 4     private Professor pro;
 5     public Student(){}
 6     public Student(String name,int age,Professor pro){
 7         this.name=name;
 8         this.age=age;
 9         this.pro=pro;
10     }
11     public Object clone(){
12         Object o=null;
13         try {
14             //Object中的clone()识别出你要复制的是哪一个对象。
15             o=super.clone();
16         } catch (CloneNotSupportedException e) {
17             System.out.println(e.toString());
18         }
19         return o;
20     }
21     public String getName() {
22         return name;
23     }
24     public void setName(String name) {
25         this.name = name;
26     }
27     public int getAge() {
28         return age;
29     }
30     public void setAge(int age) {
31         this.age = age;
32     }
33     public Professor getPro() {
34         return pro;
35     }
36     public void setPro(Professor pro) {
37         this.pro = pro;
38     }
39 }
40 class Professor{
41     private String name;
42     private int age;
43     public Professor(){}
44     public Professor(String name,int age){
45         this.name=name;
46         this.age=age;
47     }
48     public String getName() {
49         return name;
50     }
51     public void setName(String name) {
52         this.name = name;
53     }
54     public int getAge() {
55         return age;
56     }
57     public void setAge(int age) {
58         this.age = age;
59     }
60 }

 1 public class StudentTest {
 2     public static void main(String[] args) {
 3         Professor p=new Professor("wangwu",50);
 4         Student s1=new Student("zhangsan",18,p);
 5         Student s2=(Student)s1.clone();
 6         s2.getPro().setName("maer");
 7         s2.getPro().setAge(40);
 8         System.out.println("name="+s1.getPro().getName()
 9                 +",age="+s1.getPro().getAge());
10         //name=maer,age=40
11     }
12 }

  那么我们如何实现深层复制的克隆,即在修改s2.Professor时不影响s1.Professor?代码改进如下:

 1 public class Student implements Cloneable {
 2     private String name;
 3     private int age;
 4     Professor pro;
 5     public Student(){}
 6     public Student(String name,int age,Professor pro){
 7         this.name=name;
 8         this.age=age;
 9         this.pro=pro;
10     }
11     public Object clone(){
12         Student o=null;
13         try {
14             //Object中的clone()识别出你要复制的是哪一个对象。
15             o=(Student)super.clone();
16         } catch (CloneNotSupportedException e) {
17             System.out.println(e.toString());
18         }
19         o.pro=(Professor)pro.clone();
20         return o;
21     }
22     public String getName() {
23         return name;
24     }
25     public void setName(String name) {
26         this.name = name;
27     }
28     public int getAge() {
29         return age;
30     }
31     public void setAge(int age) {
32         this.age = age;
33     }
34     public Professor getPro() {
35         return pro;
36     }
37     public void setPro(Professor pro) {
38         this.pro = pro;
39     }
40 }
41 class Professor implements Cloneable{
42     private String name;
43     private int age;
44     public Professor(){}
45     public Professor(String name,int age){
46         this.name=name;
47         this.age=age;
48     }
49     public Object clone(){
50         Object o=null;
51         try {
52             o=super.clone();
53         } catch (CloneNotSupportedException e) {
54             e.printStackTrace();
55         }
56         return o;
57     }
58     public String getName() {
59         return name;
60     }
61     public void setName(String name) {
62         this.name = name;
63     }
64     public int getAge() {
65         return age;
66     }
67     public void setAge(int age) {
68         this.age = age;
69     }
70 }

public class StudentTest {
    public static void main(String[] args) {
        Professor p=new Professor("wangwu",50);
        Student s1=new Student("zhangsan",18,p);
        Student s2=(Student)s1.clone();
        s2.getPro().setName("maer");
        s2.getPro().setAge(40);
        System.out.println("name="+s1.getPro().getName()
                +",age="+s1.getPro().getAge());
        //name=wangwu,age=50
    }
}

  利用串行化来实现深层复制

  把对象写到流中的过程是串行化(Serilization)过程,而把对象从流中读出来是并行化(Deserialization)过程。应当指出的是,写在流中的是对象的一个拷贝,而原来对象仍然存在JVM里面。

  在Java语言里深层复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流中,再从流中读出来,便可以重建对象。

  这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否设成transient,从而将之排除在复制过程之外。代码改进如下:

 1 public class Student implements Serializable {
 2     private String name;
 3     private int age;
 4     Professor pro;
 5     public Student(){}
 6     public Student(String name,int age,Professor pro){
 7         this.name=name;
 8         this.age=age;
 9         this.pro=pro;
10     }
11     public Object deepClone() throws IOException, ClassNotFoundException{
12         //将对象写到流中
13         ByteArrayOutputStream bo=new ByteArrayOutputStream();
14         ObjectOutputStream oo=new ObjectOutputStream(bo);
15         oo.writeObject(this);
16         //从流中读出来
17         ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
18         ObjectInputStream oi=new ObjectInputStream(bi);
19         return oi.readObject();
20     }
21     public String getName() {
22         return name;
23     }
24     public void setName(String name) {
25         this.name = name;
26     }
27     public int getAge() {
28         return age;
29     }
30     public void setAge(int age) {
31         this.age = age;
32     }
33     public Professor getPro() {
34         return pro;
35     }
36     public void setPro(Professor pro) {
37         this.pro = pro;
38     }
39 }
40 class Professor implements Serializable{
41     private String name;
42     private int age;
43     public Professor(){}
44     public Professor(String name,int age){
45         this.name=name;
46         this.age=age;
47     }
48     public String getName() {
49         return name;
50     }
51     public void setName(String name) {
52         this.name = name;
53     }
54     public int getAge() {
55         return age;
56     }
57     public void setAge(int age) {
58         this.age = age;
59     }
60 }

 1 public class StudentTest {
 2     public static void main(String[] args) throws IOException, ClassNotFoundException {
 3         Professor p=new Professor("wangwu",50);
 4         Student s1=new Student("zhangsan",18,p);
 5         Student s2=(Student)s1.deepClone();
 6         s2.getPro().setName("maer");
 7         s2.getPro().setAge(40);
 8         System.out.println("name="+s1.getPro().getName()
 9                 +",age="+s1.getPro().getAge());
10         //name=wangwu,age=50
11     }
12 }



  继续深究:{一下是个人未能想明白的一些问题,网络上也没能给出很好的解释}

  1、数组:(以int[]为例):

 1 public class ArrayClone {
 2     public static void main(String[] args) {
 3         int[] a1={1,2,3,4};
 4         int[] a2=a1.clone();
 5         System.out.println(Arrays.toString(a2));
 6         //[1, 2, 3, 4]
 7         String[] s1={"hello","china"};
 8         String[] s2=s1.clone();
 9         System.out.println(Arrays.toString(s2));
10         //[hello, china]
11         Object[] o1={new Object(),new Object()};
12         Object[] o2=o1.clone();
13         System.out.println(Arrays.toString(o2));
14         //[[email protected], [email protected]]
15     }
16 }

  我们发现Java数组有clone()方法,而且不需要我们去进行强制类型转换,Java底层是怎样实现数据结构这个功能的?

 1 public class ArrayClone {
 2     public static void main(String[] args) {
 3         Person p1=new Person("wangwu",18);
 4         Person p2=new Person("lisi",28);
 5         Person[] ps1={p1,p2};
 6         Person[] ps2=ps1.clone();
 7         ps2[0].setName("wanghao");
 8         ps2[0].setAge(22);
 9         System.out.println("name="+p1.getName()+",age="+p1.getAge());
10         //name=wanghao,age=22
11     }
12 }

  由测试可知,Java数组只具备浅层复制的功能。

  2、String类

1 public class StringClone {
2     public static void main(String[] args) {
3         String str1="wang";
4         //String str2=(String)str1.clone();
5         //编译错误,String类没有clone方法
6     }
7 }

  查看源代码,我们可知,String类并没有重载Object类的clone方法。虽然,String和Object都在java.lang包中,但是我们的测试类StringClone不在java.lang包中,因此,str.clone()时会出现编译错误。继续进行:

 1 public class Dog {
 2     private String name;
 3     private int age;
 4     public Dog(){}
 5     public Dog(String name,int age){
 6         this.name=name;
 7         this.age=age;
 8     }
 9     public static void main(String[] args) {
10         Dog dog1=new Dog("dog1",5);
11         Dog dog2=null;
12         try {
13             dog2=(Dog)dog1.clone();
14         } catch (CloneNotSupportedException e) {
15             // TODO Auto-generated catch block
16             e.printStackTrace();
17         }
18         System.out.println(dog2.getName()+","+dog2.getAge());
19     }
20     public String getName() {
21         return name;
22     }
23     public void setName(String name) {
24         this.name = name;
25     }
26     public int getAge() {
27         return age;
28     }
29     public void setAge(int age) {
30         this.age = age;
31     }
32 }

  我们惊奇的发现,dog1.clone();并没有出现变异错误,我们随便创建的类具有clone方法,这又是怎么回事?

  虽然没编译错误,但是运行时出错,如下所示:

1 java.lang.CloneNotSupportedException: com.clone.Dog
2     at java.lang.Object.clone(Native Method)
3     at com.clone.Dog.main(Dog.java:15)
4 Exception in thread "main" java.lang.NullPointerException
5     at com.clone.Dog.main(Dog.java:20)
时间: 2024-11-17 23:49:33

Java.lang.Object.clone()分析的相关文章

java.lang.Object底层代码分析-jdk1.8

首先先来说明两件事情 1.后面的一些博客都会对jdk底层的方法进行一些分析,因为我个人开发了2年多,发现有很多事情还不是清楚,当我今天再好好底层源码的时候还是有不少的收获,所以和大家分    享一下,我只要参考一下相关文档 jdk1.8api:https://docs.oracle.com/javase/8/docs/api/index.html 代码下载地址:https://gitee.com/luanmihun/java-jdk-study 2.为什么要分析jdk1.8的版本,只是一些个人的

java.lang.Object 之 clone() 使用

Java的所有类都默认继承 java.lang.Object 类,在 java.lang.Object 类中有一个方法 clone().JDK API的说明文档解释这个方法将返回Object对象的一个拷贝.要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用.二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息.对于Java中使用 clone() 方法有几点需要注意 1:被clone的类必须实现 Cloneable 接口,Clo

Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference xxx 的问题分析与解决方案

最近,公司要求开发一个APP,所以很苦逼的学习了几天 Android 的上手手册,但是实际运用于开发中还是捉襟见肘,困难重重:好在的是,部门还有几个专门搞安卓的大佬可以问问,哈哈 好了,进入今天的正题吧,前天开发过程中,遇到一个如“标题”所示的问题,百思不得其解!最终上网搜报错的原因,才找到问题的所在:.xml文件中运用到了不存在的标签--我的是因为粗心把<View>写成了<view>,加载的时候找不到该标签,所以页面一直运行不出来. 所以,该问题基本出在于布局页面有错(有的错误页

java.lang.Object.hashCode()的返回值到底是不是对象内存地址?

刚学Java的时候我也有过这种怀疑,但一直没有验证:最近在OSCHINA上看到有人在回答问题时也这么说,于是萌生了一探究竟的想法--java.lang.Object.hashCode()的返回值到底是不是对象内存地址? (顺带回顾一下JNI) hashCode契约 说到这个问题,大家的第一反应一定和我一样--去查Object.hashCode的源码,但翻开源码,看到的却是这样的(Oracle JDK 8): /** * Returns a hash code value for the obje

jdk源码每日一读 (一) java.lang.Object

jdk源码每日一读 (一) java.lang.Object 1. 类说明 Object是java继承体系的根,是每一个类的基类,所有的类都实现了Object类的所有方法. 2.重要方法 public final native Class<?> getClass() public native int hashCode(); public boolean equals(Object obj); protected native Object clone() throws CloneNotSup

Java总结篇系列:java.lang.Object

从本篇开始,将对Java中各知识点进行一次具体总结,以便对以往的Java知识进行一次回顾,同时在总结的过程中加深对Java的理解. Java作为一个庞大的知识体系,涉及到的知识点繁多,本文将从Java中最基本的类java.lang.Object开始谈起. Object类是Java中其他所有类的祖先,没有Object类Java面向对象无从谈起.作为其他所有类的基类,Object具有哪些属性和行为, 是Java语言设计背后的思维体现. Object类位于java.lang包中,java.lang包包

java.lang.Object

java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. Object类是Java中唯一没有父类的类. 其他所有的类,包括标准容器类,比如数组,都继承了Object类中的方法. Object类中的方法 构造方法:public Object() 文档中的类概览: Java中的每个类都具有定义在Object类中的这些方法. protected Object clone() Creates and returns

深入研究java.lang.Object类

一.概述:       Object类是所有Java类的祖先.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个类的方法.在不明确给出超类的情况下,Java会自动把Object作为要定义类的超类.      可以使用类型为Object的变量指向任意类型的对象.      Object类有一个默认构造方法pubilc Object(),在构造子类实例时,都会先调用这个默认构造方法.      Object类的变量只能用作各种值的通用持有者.要对他们进行任何专门的操作,都需要知道它

研究java.lang.Object类

一.概述:        Object类是所有Java类的祖先.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个类的方法. 在不明确给出超类的情况下,Java会自动把Object作为要定义类的超类.       可以使用类型为Object的变量指向任意类型的对象.       Object类有一个默认构造方法pubilc Object(),在构造子类实例时,都会先调用这个默认构造方法.       Object类的变量只能用作各种值的通用持有者.要对他们进行任何专门的操作,都