关于Cloneable接口和clone方法

1、使用



创建对象有两种方式: new 和 clone

当一个对象创建过程复杂,我们是否可以根据已有的对象直接来克隆一份,而不必关系创建的细节呢(原型模式)。

1.1 Java Object根类默认提供了clone方法:

protected native Object clone() throws CloneNotSupportedException;

一个本地方法,protected权限: 这样做是为避免我们创建每一个类都默认具有克隆能力  

1.2 实现Cloneable接口

我们要使用一个对象的clone方法,必须Cloneable接口,这个接口没有任何实现,跟 Serializable一样是一种标志性接口

如果不实现Cloneable接口,会抛出CloneNotSupportedException异常

重写clone方法,使用public修饰(否则外部调用不到),调用父类的clone方法

如下:

public class CloneModel implements  Cloneable{
    private String name;

    private int age;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "CloneModel{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=" + age +
                ‘}‘;
    }
}

1.3、Object.clone() 与 构造方法

我们看一个例子:

CloneModel类:

public class CloneModel implements  Cloneable{
    private String name;

    private int age;

    public CloneModel(){
        System.out.println("will new a instance");
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "CloneModel{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=" + age +
                ‘}‘;
    }
}

当我们调用此对象的clone方法时,构造方法并没有被调用,所以我说创建一个对象new和clone是两条路

public static void main(String[] args) throws CloneNotSupportedException {
        CloneModel cloneModel = new CloneModel();
        System.out.println(cloneModel.clone());
    }

打印:

CloneModel{name=‘null‘, age=0}

2、重写clone方法原则



x.clone != x将会是true;

x.clone().getClass()==x.getClass()将会是true(不是绝对的,但建议这么做)

x.clone().equals(x)将会是true(不是绝对的,但建议这么做)

3、浅克隆和深克隆



3.1 默认clone方法时浅克隆

Object默认的clone方法实际是对域的简单拷贝,对于简单数据类型,是值的拷贝;

对于复杂类型的字段,则是指针地址的拷贝,clone后的对象和原对象指向的还是一个地址空间

所以说默认的clone方法时浅克隆。

例子:

class Model2{
    int height;
}

public class CloneModel implements  Cloneable{
    private String name;

    private int age;

    private Model2 model2;

    public CloneModel() {
        this.model2 = new Model2();
    }

    public Model2 getModel2() {
        return model2;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "CloneModel{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=" + age +
                ‘}‘;
    }
}

clone之后比较复杂对象是否相等

public static void main(String[] args) throws CloneNotSupportedException {
        CloneModel cloneModel1 = new CloneModel();
        CloneModel cloneModel2 = (CloneModel)cloneModel1.clone();
        System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
    }

执行返回:true

3.2 如何实现深克隆

还是上面的例子,我们改下代码

class Model2 implements Cloneable{
    int height;

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("clone Model2");
        return super.clone();
    }
}

public class CloneModel implements  Cloneable{
    private String name;

    private int age;

    private Model2 model2;

    public CloneModel() {
        this.model2 = new Model2();
    }

    public Model2 getModel2() {
        return model2;
    }

    public void setModel2(Model2 model2) {
        this.model2 = model2;
    }

    @Override
    public CloneModel clone() throws CloneNotSupportedException {
        CloneModel cloneModelTemp = (CloneModel)super.clone();
        cloneModelTemp.setModel2((Model2)cloneModelTemp.getModel2().clone());
        return cloneModelTemp;
    }

    @Override
    public String toString() {
        return "CloneModel{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=" + age +
                ‘}‘;
    }
}

再次测试下:

public static void main(String[] args) throws CloneNotSupportedException {
        CloneModel cloneModel1 = new CloneModel();
        CloneModel cloneModel2 = cloneModel1.clone();
        System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
    }

执行返回:false

这么做就要在super.clone的基础上 继续对非基本类型的对象递归的再次clone.

显然这么方式是繁琐的且不可靠的。

有没有其他的方式呢?有 序列化

3.3 序列化实现深度克隆

(1) 使用java自身的序列化转为二进制数 ,再反序列化为对象

上面的例子改造下

import java.io.Serializable;

class Model2 implements Serializable {
    int height;
}

public class CloneModel implements Serializable {
    private String name;

    private int age;

    private Model2 model2;

    public CloneModel() {
        this.model2 = new Model2();
    }

    public Model2 getModel2() {
        return model2;
    }

}

测试代码:

import com.yangfei.test.CloneModel;

import java.io.*;

public class YfTest {
    public static <T extends Serializable> T deepCloneObject(T object) throws IOException {
        T deepClone = null;
        ObjectInputStream ois = null;
        try(ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
        )
        {
            oos.writeObject(object);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos
                    .toByteArray());
            ois = new ObjectInputStream(bais);
            deepClone = (T)ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois != null){
                ois.close();
            }
        }
        return deepClone;
    }

    public static void main(String[] args) throws IOException {
        CloneModel cloneModel1 = new CloneModel();
        CloneModel cloneModel2 = deepCloneObject(cloneModel1);
        System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
    }
}

测试输出:false

(2)其他序列化方式,如对象序列化json字符串再反序列化为对象

我们使用google的gson来进行序列化,上代码

import com.google.gson.Gson;

import java.io.Serializable;

class Model2 implements Serializable {
    int height;
}

public class CloneModel implements Serializable {
    private String name;

    private int age;

    private Model2 model2;

    public CloneModel() {
        this.model2 = new Model2();
    }

    public Model2 getModel2() {
        return model2;
    }

    public CloneModel deepClone() {
        Gson gson = new Gson();
        return gson.fromJson(gson.toJson(this), CloneModel.class);
    }

}

测试代码:

public static void main(String[] args) throws IOException {
        CloneModel cloneModel1 = new CloneModel();
        CloneModel cloneModel2 = cloneModel1.deepClone();
        System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2());
    }

执行返回:false

性能对比测试:

上代码:

public static void main(String[] args) throws IOException {
        CloneModel cloneModel1 = new CloneModel();
        long time1 = System.currentTimeMillis();
        for(int i=0;i<1000;i++){
//            CloneModel cloneModel2 = cloneModel1.deepClone();
            CloneModel cloneModel2 = deepCloneObject(cloneModel1);
        }
        long time2 = System.currentTimeMillis();
        System.out.println((time2-time1)+"ms");
    }

  

循环1000次

Serializable耗时:118ms

json耗时:167ms

对比 Serializable gson
易用性 对象要实现Serializable,依赖的子元素依然要实现此接口,不易扩展 无要求,额外的工具控制,易用使用
性能对比 1000次clone耗时118ms,稍好 1000次clone耗时167ms

原文地址:https://www.cnblogs.com/yangfei629/p/11392034.html

时间: 2024-10-10 20:04:55

关于Cloneable接口和clone方法的相关文章

clone方法是如何工作的

clone()是java.lang.Object类下面的一个很难处理的方法,clone()的作用很简单,提供一种克隆机制创建对象的副本,对于如何实现它已成为一个棘手的事,同时还长期被广受批评.不管怎样,我们不去争论历史,现在我们将尝试学习clone方法是怎样工作的.说实在的,想理解克隆机制并不简单,甚至有经验的java程序员也很难解释可变对象的克隆是如何运作的.深克隆(deep copy)与浅克隆(shallow copy)的区别.这文章分为三部分,我们首先看clone方法是如何工作的,第二部分

Cloneable接口和Object的clone()方法

http://www.cnblogs.com/xrq730/p/4858937.html 为什么要克隆 为什么要使用克隆,这其实反映的是一个很现实的问题,假如我们有一个对象: public class SimpleObject implements Cloneable { private String str; public SimpleObject() { System.out.println("Enter SimpleObject.constructor()"); } public

使用clone( )和Cloneable接口

由Object类定义的绝大部分方法在本书其他部分讨论.而一个特别值得关注的方法是clone( ).clone( )方法创建调用它的对象的一个复制副本.只有那些实现Cloneable接口的类能被复制. Cloneable接口没有定义成员.它通常用于指明被创建的一个允许对对象进行位复制(也就是对象副本)的类.如果试图用一个不支持Cloneable接口的类调用clone( )方法,将引发一个CloneNotSupportedExcepti on异常.当一个副本被创建时,并没有调用被复制对象的构造函数.

Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么要对对象进行克隆.Java中所有的对象都是保存在堆中,而堆是供全局共享的.也就是说,如果同一个Java程序的不同方法,只要能拿到某个对象的引用,引用者就可以随意的修改对象的内部数据(前提是这个对象的内部数据通过get/set方法曝露出来).有的时候,我们编写的代码想让调用者只获得该对象的一个拷贝(也

java Object对象的clone方法

参考copy链接:http://blog.csdn.net/bigconvience/article/details/25025561 在看原型模式,发现要用到clone这个方法,以前和朋友聊过,没怎么看过,刚好要用,就看看了. 源码解释: /** * Creates and returns a copy of this object. The precise meaning * of "copy" may depend on the class of the object. The

谨慎重载clone方法

本文涉及到的概念 1.浅拷贝和深拷贝 2..clone方法的作用和使用方式 3.拷贝构造器和拷贝工厂 1.浅拷贝和深拷贝 浅拷贝 一个类实现Cloneable接口,然后,该类的实例调用clone方法,返回一个新的实例. 新的实例与原来的实例是不同的对象. 新的实例的各个非引用类型的成员变量值与原来的实例的成员变量值相同. 对于引用类型的成员变量,新实例的成员变量和旧实例的成员变量,都是指向相同的对象,引用值相同,这种称作浅拷贝(shallow copying) public class Car

详解Java中的clone方法

转载自:http://blog.csdn.net/zhangjg_blog/article/details/18369201 Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象 2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是

clone()方法、深复制和浅复制

clone方法 Java中没有明确提供指针的概念和用法,而实质上没个new语句返回的都是一个指针的引用,只不过在大部分情况下开发人员不需要关心如何去操作这个指针而已. 在实际编程中,经常会遇到从某个已有对象A创建出另一个与A具有相同状态的对象B,并且B的修改不会影响到A的情况,例如Prototype(原型)模式中,就需要clone一个对象实例. 仅仅通过简单的复制操作显然无法达到这个目的,而Java提供了一个简单有效的clone()方法来满足这个需求. Java中所有的类默认都继承自Object

Java clone()方法使用说明

Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象. 比如函数参数类型是自定义的类时,此时便是引用传递而不是值传递.下面是举例: Java代码   public class A { public String name; } Java代码   public class testClone { public void changeA(A