`

转:java.lang.Object.clone()分析

阅读更多

文章转载自:http://www.cnblogs.com/gw811/archive/2012/10/07/2712252.html

 

首先,看一下源码:

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

 由源代码我们会发现:

  第一: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接口 

  首先,看一下源码:

public interface Cloneable { 
 }

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

 

程序示例分析:

public class Person {
    private String name;
    private int age;
    public Person(){}
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    public Object clone(){
        Object o=null;
        try {
            o=super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class PersonTest {
    public static void main(String[] args) {
        Person p1=new Person("zhangsan",18);
        Person p2=(Person)p1.clone();
        p2.setName("lis");
        p2.setAge(20);
        System.out.println("name="
            +p1.getName()+",age="+p1.getAge());
        //修改p2后,没有对p1产生影响。
    }
}

 说明:

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

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

public class Student implements Cloneable {
    private String name;
    private int age;
    private Professor pro;
    public Student(){}
    public Student(String name,int age,Professor pro){
        this.name=name;
        this.age=age;
        this.pro=pro;
    }
    public Object clone(){
        Object o=null;
        try {
            //Object中的clone()识别出你要复制的是哪一个对象。
            o=super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Professor getPro() {
        return pro;
    }
    public void setPro(Professor pro) {
        this.pro = pro;
    }
}
class Professor{
    private String name;
    private int age;
    public Professor(){}
    public Professor(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
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=maer,age=40
    }
}

 

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

public class Student implements Cloneable {
    private String name;
    private int age;
    Professor pro;
    public Student(){}
    public Student(String name,int age,Professor pro){
        this.name=name;
        this.age=age;
        this.pro=pro;
    }
    public Object clone(){
        Student o=null;
        try {
            //Object中的clone()识别出你要复制的是哪一个对象。
            o=(Student)super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        o.pro=(Professor)pro.clone();
        return o;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Professor getPro() {
        return pro;
    }
    public void setPro(Professor pro) {
        this.pro = pro;
    }
}
class Professor implements Cloneable{
    private String name;
    private int age;
    public Professor(){}
    public Professor(String name,int age){
        this.name=name;
        this.age=age;
    }
    public Object clone(){
        Object o=null;
        try {
            o=super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
} 
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,从而将之排除在复制过程之外。代码改进如下:

public class Student implements Serializable {
    private String name;
    private int age;
    Professor pro;
    public Student(){}
    public Student(String name,int age,Professor pro){
        this.name=name;
        this.age=age;
        this.pro=pro;
    }
    public Object deepClone() throws IOException, ClassNotFoundException{
        //将对象写到流中
        ByteArrayOutputStream bo=new ByteArrayOutputStream();
        ObjectOutputStream oo=new ObjectOutputStream(bo);
        oo.writeObject(this);
        //从流中读出来
        ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi=new ObjectInputStream(bi);
        return oi.readObject();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Professor getPro() {
        return pro;
    }
    public void setPro(Professor pro) {
        this.pro = pro;
    }
}
class Professor implements Serializable{
    private String name;
    private int age;
    public Professor(){}
    public Professor(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class StudentTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Professor p=new Professor("wangwu",50);
        Student s1=new Student("zhangsan",18,p);
        Student s2=(Student)s1.deepClone();
        s2.getPro().setName("maer");
        s2.getPro().setAge(40);
        System.out.println("name="+s1.getPro().getName()
                +",age="+s1.getPro().getAge());
        //name=wangwu,age=50
    }
}

 

继续深究:{一下是个人未能想明白的一些问题,网络上也没能给出很好的解释}
  1、数组:(以int[]为例):

public class ArrayClone {
    public static void main(String[] args) {
        int[] a1={1,2,3,4};
        int[] a2=a1.clone();
        System.out.println(Arrays.toString(a2));
        //[1, 2, 3, 4]
        String[] s1={"hello","china"};
        String[] s2=s1.clone();
        System.out.println(Arrays.toString(s2));
        //[hello, china]
        Object[] o1={new Object(),new Object()};
        Object[] o2=o1.clone();
        System.out.println(Arrays.toString(o2));
        //[java.lang.Object@1fc4bec, java.lang.Object@dc8569]
    }
}

 

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

public class ArrayClone {
    public static void main(String[] args) {
        Person p1=new Person("wangwu",18);
        Person p2=new Person("lisi",28);
        Person[] ps1={p1,p2};
        Person[] ps2=ps1.clone();
        ps2[0].setName("wanghao");
        ps2[0].setAge(22);
        System.out.println("name="+p1.getName()+",age="+p1.getAge());
        //name=wanghao,age=22
    }
}

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

 

2、String类:

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

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

public class Dog {
    private String name;
    private int age;
    public Dog(){}
    public Dog(String name,int age){
        this.name=name;
        this.age=age;
    }
    public static void main(String[] args) {
        Dog dog1=new Dog("dog1",5);
        Dog dog2=null;
        try {
            dog2=(Dog)dog1.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(dog2.getName()+","+dog2.getAge());
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

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

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

java.lang.CloneNotSupportedException: com.clone.Dog
     at java.lang.Object.clone(Native Method)
     at com.clone.Dog.main(Dog.java:15)
Exception in thread "main" java.lang.NullPointerException
     at com.clone.Dog.main(Dog.java:20)

 

 

分享到:
评论

相关推荐

    java源码阅读之java.lang.Object

    Java源码阅读之java.lang.Object Java中的Object类是所有类的父类,任何类都默认继承Object。Object类提供了多种方法,以下是其中一些重要的方法: 1. clone方法:保护方法,实现对象的浅复制,只有实现了...

    《Java设计模式》课后习题参考答案-刘伟(20180723).pdf

    - java.lang.Object#clone(),创建并返回此对象的副本。 e) 单例模式(Singleton) 单例模式保证一个类只有一个实例,并提供一个全局访问点。在JDK中的例子有: - java.lang.Runtime#getRuntime(),获取Java运行...

    Java rt.jar 源码分析

    3. **内部实现细节**:例如,`java.lang.Object`的`clone()`方法是如何工作的,`java.util.ArrayList`和`java.util.LinkedList`的性能差异,`java.io.InputStream`和`java.io.OutputStream`的抽象设计等。...

    Java面试题及答案-共60道.docx

    Object 是 Java 中最顶级的父类,提供了 equals、hashCode、toString、wait、notify、clone、getClass 等方法。 二十一、指针 Java 中有指针,但是隐藏了,开发人员无法直接操作指针,由 JVM 来操作指针。Java 中...

    Java 动态代理Proxy应用和底层源码分析.pdf

    通过以上分析,我们可以了解到Java动态代理是如何通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口实现的,以及它的基本应用场景和技术原理。这对于理解和使用AOP、RPC等高级特性有着...

    JDK中的设计模式

    在JDK中,`java.lang.Object#clone()`是一个典型示例,它支持浅克隆,要求实现`java.lang.Cloneable`接口。 ##### 单例模式 单例模式确保一个类只有一个实例,并提供一个全局访问点。JDK中的一些例子是: - `java...

    重难点之java_javalang包.pdf

    《Java编程中的java.lang包详解》 在Java编程语言中,`java.lang`包是所有Java程序的基础,它包含了进行基本操作和对象创建所必需的类和接口。这个包的重要性在于它是每个Java应用程序的默认导入包,无需显式导入...

    Java 60 道面试题及答案.docx

    1. Java.lang 2. Java.io 3. Java.sql 4. Java.util 5. Java.awt 6. Java.net 7. Java.math 十二、顶级父类 1. Object 十三、Object类常用方法 1. Equals 2. Hashcode 3. toString 4. wait 5. notify 6. clone 7...

    Java Object类认识

    `Object`类位于Java的核心库`java.lang`包中,它提供了基本的方法来支持对象的创建、比较和字符串表示。以下是`Object`类的一些关键方法: 1. **构造器**:虽然`Object`类没有显式的构造器,但每个类在创建时都会...

    编程语言java高级应用.pdf

    2. java.lang.Object类:Object类是Java语言中所有类的根类,其他类都直接或间接地继承自Object类。Object类定义了几个重要的方法:clone()、equals(Object obj)、finalize()、toString()等。 - clone()方法:用于...

    JAVA中常用类的常用方法.docx

    JAVA 中有许多常用的类,每个类都有其特定的方法,本文将对 java.lang.Object 类、String 类和 StringBuffer 类的常用方法进行讲解。 一、java.lang.Object类 java.lang.Object 类是 JAVA 中所有类的父类,它提供...

    java Clone

    `clone`方法存在于Java的`java.lang.Object`基类中,所有Java类都默认继承自`Object`类,因此所有类都具备`clone`能力。 首先,要使用`clone`方法,我们需要确保类实现了`Cloneable`接口。`Cloneable`接口并没有...

    commons.lang ArrayUtils类的中文api

    ### 基于commons.lang ArrayUtils类的知识点详解 #### 一、ArrayUtils类简介 `ArrayUtils`类是Apache Commons Lang库中的一个重要工具类,主要用于处理数组的各种操作。相较于Java标准库中的数组操作,`ArrayUtils`...

    Java 60道面试题和答案.docx

    17. **常用包**:Java的常用包包括java.lang、java.io、java.sql、java.util、java.awt、java.net和java.math等。 18. **最顶级父类**:Java中最顶级的父类是Object,所有类都直接或间接继承自Object。 19. **...

    Java中clone方法共6页.pdf.zip

    这个方法来源于`java.lang.Object`类,所有Java类都默认继承了这个方法。本资料"Java中clone方法共6页.pdf.zip"可能包含了关于如何理解和使用`clone()`方法的详细解释,以及它在实际开发中的应用示例。 `clone()`...

    JAVA常见的设计模式+源码+ppt+pdf

    Java中`Object`类提供的`clone()`方法就是原型模式的应用。 6. **适配器模式**:将一个类的接口转换成客户希望的另一个接口。适配器使原本不兼容的类可以一起工作。在Java IO中,`InputStreamReader`和`...

Global site tag (gtag.js) - Google Analytics