Java原型模式之浅拷贝-深拷贝

一、是什么?


浅拷贝:对值类型的成员变量进行值的复制,对引用类型的成员变量仅仅复制引用,不复制引用的对象

深拷贝:对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制

内部机制:

(1)关于Object类的clone方法

默认实现为“浅拷贝”,重写Object类中的clone方法。Java中全部类的父类都是Object类,Object类中有一个clone方法。作用是返回对象的一个拷贝,可是其作用域是protected类型的,一般的类无法调用,因此Prototype类须要将clone方法的作用域改动为public类型。

(2)关于Java.lang.Cloneable接口

在java语言有一个Cloneable接口,它的作用仅仅有一个。就是在执行时通知虚拟机能够安全地在实现了此接口的类上使用clone方法。在java虚拟机中,仅仅有实现了这个接口的类才干够被拷贝。否则在执行时会抛出CloneNotSupportedException异常。

二、怎么用?

(一)浅拷贝

浅拷贝运用:假设你改变一个很基本类型的值时,原对象的值不要求改变时就用浅拷贝。就是一直处于覆盖的状态。

比如:

packagelc.clone.shadow;
public classShadowClone implements Cloneable
{
    // 基本类型
    private int a;
    // 非基本类型
    private String b;
    // 非基本类型
    private int[] c;
    // 重写Object.clone()方法,并把protected改为public
    @Override
    public Object clone()
    {
        ShadowClone sc = null;
        try
        {
            sc = (ShadowClone) super.clone();
        } catch (CloneNotSupportedException e)
        {
            e.printStackTrace();
        }
        return sc;
    }
    public int getA()
    {
        return a;
    }
    public void setA(int a)
    {
        this.a = a;
    }
    public String getB()
    {
        return b;
    }
    public void setB(String b)
    {
        this.b = b;
    }
    public int[] getC()
    {
        return c;
    }
    public void setC(int[] c)
    {
        this.c = c;
    }
}
測试类Test.java

packagelc.clone.shadow;
public class Test
{
    public static void main(String[] args)throws CloneNotSupportedException
    {
        ShadowClone c1 = new ShadowClone();
        //对c1赋值
        c1.setA(100) ;
        c1.setB("clone1") ;
        c1.setC(new int[]{1000}) ;

        System.out.println("克隆前: c1.a="+c1.getA() );
        System.out.println("克隆前: c1.b="+c1.getB() );
        System.out.println("克隆前: c1.c[0]="+c1.getC()[0]);
       System.out.println("-----------") ;

        //克隆出对象c2,并对c2的属性A,B,C进行改动

        ShadowClone c2 = (ShadowClone)c1.clone();

        //对c2进行改动
        c2.setA(50) ;
        c2.setB("clone2");
        int []a = c2.getC() ;
        a[0]=500 ;
        c2.setC(a);

        System.out.println("克隆后: c1.a="+c1.getA() );
        System.out.println("克隆后: c1.b="+c1.getB() );
        System.out.println("克隆后: c1.c[0]="+c1.getC()[0]);
       System.out.println("---------------") ;

        System.out.println("克隆后: c2.a=" + c2.getA());
        System.out.println("克隆后: c2.b=" + c2.getB());
        System.out.println("克隆后: c2.c[0]=" + c2.getC()[0]);
    }
}
结果:
克隆前: c1.a=100
克隆前: c1.b=clone1
克隆前: c1.c[0]=1000
-----------
克隆后: c1.a=100
克隆后: c1.b=clone1
克隆后: c1.c[0]=500
---------------
克隆后: c2.a=50
克隆后: c2.b=clone2
克隆后: c2.c[0]=500
<span style="font-size: 14pt; font-family: SimSun; background-color: rgb(255, 255, 255);"> </span>

问题出现了,我指改动了克隆后的对象c2.c的值,但c1.c的值也改变了,与c2的值相等.下面针对浅克隆得出结论:基本类型是能够被克隆的,但引用类型仅仅是copy地址,并没有copy这个地址指向的对象的值,这使得两个地址指向同一值,改动当中一个,当然还有一个也就变了。

由此可见,浅克隆仅仅适合克隆基本类型,对于引用类型就不能实现克隆了。

(二)深拷贝

   利用序列化实现深度拷贝:

把对象写到流里的过程是序列化(Serialization)过程;而把对象从流中读出来的过程则叫反序列化(Deserialization)过程。

应当指出的是。写到流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。

    
在Java语言里深度克隆一个对象。经常能够先使对象实现Serializable接口,然后把对象(实际上仅仅是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便能够重建对象。

被克隆对象.DeepClone.java

packagelc.clone.deep;
importjava.io.Serializable;
<span style="color:#cc0000;">//要实现深克隆必须实现Serializable接口</span>
public classDeepClone implements Serializable
{
    private int a;
    private String b;
    private int[] c;
    public int getA()
    {
        return a;
    }
    public void setA(int a)
    {
        this.a = a;
    }
    public String getB()
    {
        return b;
    }
    public void setB(String b)
    {
        this.b = b;
    }
    public int[] getC()
    {
        return c;
    }
    public void setC(int[] c)
    {
        this.c = c;
    }
}
測试类Test.java

packagelc.clone.deep;
importjava.io.ByteArrayInputStream;
importjava.io.ByteArrayOutputStream;
importjava.io.IOException;
importjava.io.ObjectInputStream;
importjava.io.ObjectOutputStream;
public class Test
{
    public static void main(String[] args)throws CloneNotSupportedException
    {
        Test t = new Test();
        DeepClone dc1 = new DeepClone();
        // 对dc1赋值
        dc1.setA(100);
        dc1.setB("clone1");
        dc1.setC(new int[] { 1000 });
        System.out.println("克隆前: dc1.a=" + dc1.getA());
        System.out.println("克隆前: dc1.b=" + dc1.getB());
        System.out.println("克隆前: dc1.c[0]=" + dc1.getC()[0]);
       System.out.println("-----------");
        DeepClone dc2 = (DeepClone)t.deepClone(dc1);
        // 对c2进行改动
        dc2.setA(50);
        dc2.setB("clone2");
        int[] a = dc2.getC();
        a[0] = 500;
        dc2.setC(a);
        System.out.println("克隆前: dc1.a=" + dc1.getA());
        System.out.println("克隆前: dc1.b=" + dc1.getB());
        System.out.println("克隆前: dc1.c[0]=" + dc1.getC()[0]);
       System.out.println("-----------");
        System.out.println("克隆后: dc2.a=" + dc2.getA());
        System.out.println("克隆后: dc2.b=" + dc2.getB());
        System.out.println("克隆后: dc2.c[0]=" + dc2.getC()[0]);
    }
   <span style="color:#cc0000;"> //用序列化与反序列化实现深克隆</span>
    public Object deepClone(Object src)
    {
        Object o = null;
        try
        {
            if (src != null)
            {
    <span style="color:#cc0000;">// 将对象写到流里</span>
                ByteArrayOutputStream baos =new ByteArrayOutputStream();
                ObjectOutputStream oos = newObjectOutputStream(baos);
                oos.writeObject(src);
                oos.close();
   <span style="color:#cc0000;"> // 将对象从流里读出来</span>
                ByteArrayInputStream bais = newByteArrayInputStream(baos
                        .toByteArray());
                ObjectInputStream ois = newObjectInputStream(bais);
                o = ois.readObject();
                ois.close();
            }
        } catch (IOException e)
        {
            e.printStackTrace();
        } catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        return o;
    }
}

结果:
克隆前: dc1.a=100
克隆前: dc1.b=clone1
克隆前: dc1.c[0]=1000
-----------
克隆前: dc1.a=100
克隆前: dc1.b=clone1
克隆前: dc1.c[0]=1000
-----------
克隆后: dc2.a=50
克隆后: dc2.b=clone2
克隆后: dc2.c[0]=500<span style="font-size: 14pt; font-family: SimSun; background-color: rgb(255, 255, 255);"> </span>

深克隆后:改动dc1或者dc2,不管是基本类型还是引用类型,他们的值都不会随着一方改变还有一方也改变。

这样做的前提就是对象以及对象内部全部引用到的对象都是可序列化的。否则。就须要细致考察那些不可序列化的对象可否设成transient,从而将之排除在复制过程之外。

  浅拷贝显然比深拷贝更easy实现。由于Java语言的全部类都会继承一个clone()方法。而这个clone()方法所做的正式浅拷贝。

  有一些对象。比方线程(Thread)对象或Socket对象。是不能简单复制或共享的。

无论是使用浅拷贝还是深拷贝,仅仅要涉及这种间接对象,就必须把间接对象设成transient而不予复制;或者由程序自行创建出相当的同种对象,权且当做复制件使用。

三、对照

通过以上对浅拷贝和深拷贝的简单介绍,预计在脑子中已经了解了大概,接下来就通过对照来彻底消除对它们的疑惑吧!

从上图中进行对照就能够明确事实上质:浅拷贝指向的是同一个引用对象。而深拷贝指向的是两个全然一个样的引用对象。所以假设不想让引用对象跟着改变,就必须用深拷贝。

假设仅仅是单纯的值类型那么两者皆能够,那就在今后的项目中实践吧。

四、总结

综上所述不管是浅拷贝还是深拷贝仅仅要攻克了问题就是好的拷贝,所以在今后的实践中要体会它们的价值。并让每一个的价值都发挥最大化。

时间: 2024-10-01 05:58:50

Java原型模式之浅拷贝-深拷贝的相关文章

java 原型模式之浅拷贝

浅拷贝:java Ojbect类提供的clone只是拷贝本对象,其对象内部的数组和引用对象等都不拷贝,还是指向原生对象的内部元素地址. 类引用的成员变量必须满足两个条件才不会被拷贝:1.是类的成员变量而不是方法内变量:2必须是一个可变的引用对象,而不是一个原始类型或者不可变对象(包括int.long.char等及其对象类型[Integer.Long].String) 测试代码: import java.util.ArrayList; import java.util.List; public c

java原型模式

原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式结构图 通俗来说:原型模式就是深拷贝和浅拷贝的实现. 浅拷贝 只实现了值拷贝,对于引用对象还是指向原来的对象. 父类实现clone方法,子类没有实现clone方法,其效果是浅拷贝. 父类实现clone方法,子类也实现clone方法,本来我想应该是深拷贝了,没想到也是浅拷贝. package com.prototype; import java.io.Serializable; public

Java原型模式(Prototype模式)

Prototype模式定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建. 如何使用原型模式 因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单.以勺子为例: public abstract class Abstrac

Java 原型模式(克隆模式)

  Java 的设计模式有 23 种,前段时间小编已经介绍了单例模式,由于我们在学习 Spring 的时候在 bean 标签的学习中碰到了今天要讲的原型模式,那么小编就已本文来介绍下原型模式. 原型模式  在java中我们知道通过new关键字创建的对象是非常繁琐的(类加载判断,内存分配,初始化等),在我们需要大量对象的情况下,原型模式就是我们可以考虑实现的方式.  原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样.而且对于原型对象没有任何

Java原型模式(思维导图)

图1 原型模式[点击查看大图] 1,原型对象 public class Prototype implements Cloneable{ public Prototype clone(){ Prototype prototype=null; try{ prototype=(Prototype)super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return prototype; } } class

java语言实现创建型设计模式—原型模式(Prototype)

一.描述 原型模式是通过一个原型对象来标明要创建的对象的类型,然后用复制这个原型对象的方法来拷贝创建更多的同类型对象.例如我们在程序的动态运行过程中有了一个对象,这个对象中包含了一系列的有效数据,我们此时需要一个和该对象完全相同的新对象,并且在拷贝之后,新旧对象之间没有任何联系,对任何一个对象的更改都不影响另一个对象. 在java中所有类都默认继承自java.lang.Object类,在这个Object类中有一个clone()方法,该方法将返回Object对象的一个拷贝. 我们让需要被拷贝的类实

iOS与Java原型设计模式,欢迎扫二维码加入订阅号进行讨论

什么是原型模式 什么时候用到原型模式 原型模式有那些优缺点 oc与java语法上原型模式有那些异同及简单的例子 首先看什么是原型模式: java:Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.(用原型实例制定创建对象的种类,并且创建一个新的对象从原型对象拷贝而来.) iOS:客户端知道抽象prototype

原型模式之基础

一.是什么? 定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.(官方定义) 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype.Prototype类需要具备以下两个条件: 1.实现Cloneable接口.在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法.在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedExce

设计模式解密(18)- 原型模式

1.简介 定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象. 功能:①是通过克隆来创建新的对象实例:②是为克隆出来的新的对象实例复制原型实例属性的值. 本质:通过克隆来创建新的对象实例. 英文:Prototype 类型:创建型 2.类图及组成 (引)类图: 组成: Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们都要实现这里定义的克隆方法. ConcretePrototype:实现Prototype接口的类,这些类真正实现了克隆自身的功能. C