一、是什么?


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

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

内部机制:

(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而不予复制;或者由程序自行创建出相当的同种对象,权且当做复制件使用。

三、对照

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

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

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

四、总结

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

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

  1. Java原型模式之基础

    一.是什么? 定义:用原型实例指定创建对象的种类,而且通过拷贝这些原型创建新的对象.(官方定义) 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype. Prototype类须要 ...

  2. Prototype 原型模式 复制 浅拷贝 clone MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. Java原型模式

    原型模式 原型模式也称克隆模式.原型模式jian ming zhi yi,就是先创造出一个原型,然后通过类似于Java中的clone方法,对对象的拷贝,克隆类似于new,但是不同于new.new创造出 ...

  4. Java 原型模式(克隆模式)

      Java 的设计模式有 23 种,前段时间小编已经介绍了单例模式,由于我们在学习 Spring 的时候在 bean 标签的学习中碰到了今天要讲的原型模式,那么小编就已本文来介绍下原型模式. 原型模 ...

  5. Java 原型模式

    http://www.cnblogs.com/itTeacher/archive/2012/12/02/2797857.html http://www.cnblogs.com/java-my-life ...

  6. Java 基础 - System.arraycopy() 浅拷贝 深拷贝

    ref: https://blog.csdn.net/balsamspear/article/details/85069207 https://blog.csdn.net/balsamspear/ar ...

  7. 设计模式:原型模式介绍 && 原型模式的深拷贝问题

    0.背景 克隆羊问题:有一个羊,是一个类,有对应的属性,要求创建完全一样的10只羊出来. 那么实现起来很简单,我们先写出羊的类: public class Sheep { private String ...

  8. 【设计模式】Java设计模式精讲之原型模式

    简单记录 - 慕课网 Java设计模式精讲 Debug方式+内存分析 & 设计模式之禅-秦小波 文章目录 1.原型模式的定义 原型-定义 原型-类型 2.原型模式的实现 原型模式的通用类图 原 ...

  9. 重学 Java 设计模式:实战原型模式

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 老板你加钱我的代码能飞 程序员这份工作里有两种人:一类是热爱喜欢的.一类是仅当成工作 ...

随机推荐

  1. Fiddler——抓包工具的使用

    fiddler安装 pc端安装fiddler,自行从百度下载即可 Fiddler是强大且好用的Web调试工具之一,它能记录客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输 ...

  2. Erwin 带注释(comment )

    1. Database>Pre & Post Script > Model-level %ForEachTable() { alter TABLE %TableName COMME ...

  3. axios方法get及post代码示例

    show: function(){ //get方式 //赋值给变量self var self = this; var url = "hotcity.json"; axios.get ...

  4. 100 道 Linux 笔试题,能拿 80 分就算大神!

    本套笔试题共100题,每题1分,共100分.(参考答案在文章末尾) 1. cron 后台常驻程序 (daemon) 用于: A. 负责文件在网络中的共享 B. 管理打印子系统C. 跟踪管理系统信息和错 ...

  5. for循环,字典遍历(二)

    #通过列表值,定义一个字典,来获取key和value str_list = [1,3,5,7,9,'i',9,'o',7,'i'] str_dict = {} for i in str_list: # ...

  6. zip相关知识梳理(一)

    zip相关知识梳理(一) 经过对zip文件的长时间研究,对zip文件进行相关知识进行梳理,虽然网上很多牛人对其做了相关基础解析,但是对于特殊情况没有进行说明,比如超过4G的zip文件该以什么格式进行编 ...

  7. Linux 下 Bash 脚本对拍

    背会... #!/bin/bash i= while true ;do ./maker > data.in ./a <data.in> data.out ./b <data.i ...

  8. centOS目录结构介绍

    Linux / CentOS目录结构 /: 根目录,一般根目录下只存放目录,不要存放文件,/etc./bin./dev./lib./sbin应该和根目录放置在一个分区中 /bin:/usr/bin: ...

  9. python基础面试题30问(附带答案)

    1.     闭包 定义:闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)(想想Erlang的外层函数传入一个参数a, 内层函数依旧传入一个参数b, 内层函数使用a和b, 最后 ...

  10. RequestMapping_HiddenHttpMethodFilter 过滤器

    [REST] 1.REST:即Representational State Transfer.(资源)表现层状态转化.是目前最流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便,所以得 ...