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而不予复制;或者由程序自行创建出相当的同种对象,权且当做复制件使用。
三、对照
通过以上对浅拷贝和深拷贝的简单介绍,预计在脑子中已经了解了大概,接下来就通过对照来彻底消除对它们的疑惑吧!
从上图中进行对照就能够明确事实上质:浅拷贝指向的是同一个引用对象。而深拷贝指向的是两个全然一个样的引用对象。所以假设不想让引用对象跟着改变,就必须用深拷贝。
假设仅仅是单纯的值类型那么两者皆能够,那就在今后的项目中实践吧。
四、总结
综上所述不管是浅拷贝还是深拷贝仅仅要攻克了问题就是好的拷贝,所以在今后的实践中要体会它们的价值。并让每一个的价值都发挥最大化。
Java原型模式之浅拷贝-深拷贝的更多相关文章
- Java原型模式之基础
一.是什么? 定义:用原型实例指定创建对象的种类,而且通过拷贝这些原型创建新的对象.(官方定义) 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype. Prototype类须要 ...
- Prototype 原型模式 复制 浅拷贝 clone MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Java原型模式
原型模式 原型模式也称克隆模式.原型模式jian ming zhi yi,就是先创造出一个原型,然后通过类似于Java中的clone方法,对对象的拷贝,克隆类似于new,但是不同于new.new创造出 ...
- Java 原型模式(克隆模式)
Java 的设计模式有 23 种,前段时间小编已经介绍了单例模式,由于我们在学习 Spring 的时候在 bean 标签的学习中碰到了今天要讲的原型模式,那么小编就已本文来介绍下原型模式. 原型模 ...
- Java 原型模式
http://www.cnblogs.com/itTeacher/archive/2012/12/02/2797857.html http://www.cnblogs.com/java-my-life ...
- Java 基础 - System.arraycopy() 浅拷贝 深拷贝
ref: https://blog.csdn.net/balsamspear/article/details/85069207 https://blog.csdn.net/balsamspear/ar ...
- 设计模式:原型模式介绍 && 原型模式的深拷贝问题
0.背景 克隆羊问题:有一个羊,是一个类,有对应的属性,要求创建完全一样的10只羊出来. 那么实现起来很简单,我们先写出羊的类: public class Sheep { private String ...
- 【设计模式】Java设计模式精讲之原型模式
简单记录 - 慕课网 Java设计模式精讲 Debug方式+内存分析 & 设计模式之禅-秦小波 文章目录 1.原型模式的定义 原型-定义 原型-类型 2.原型模式的实现 原型模式的通用类图 原 ...
- 重学 Java 设计模式:实战原型模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 老板你加钱我的代码能飞 程序员这份工作里有两种人:一类是热爱喜欢的.一类是仅当成工作 ...
随机推荐
- The user specified as a definer ('root'@'%') does not exist 解决方法
mysql> grant all privileges on *.* to root@"%" identified by "."; Query OK, r ...
- codeforces_302D
D. Yaroslav and Time time limit per test 2 seconds memory limit per test 256 megabytes input standar ...
- Vue简易博客总结
项目结构: 首先,编写博客的导航栏组件BlogHeader.vue: <template> <nav> <ul> <li> <router-lin ...
- ls 命令还能这么玩?看一下这 20 个实用范例
Linux中一个基本命令是ls.没有这个命令,我们会在浏览目录条目时会遇到困难.这个命令必须被每个学习Linux的人知道. ls是什么 ls命令用于列出文件和目录.默认上,他会列出当前目录的内容.带上 ...
- php第二十四节课
三级联动 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- Bookshelf 2(poj3628,01背包,dp递推)
题目链接:Bookshelf 2(点击进入) 题目解读: 给n头牛,给出每个牛的高度h[i],给出一个书架的高度b(所有牛的高度相加>书架高度b),现在把一些牛叠起来(每头牛只能用一次,但不同的 ...
- linux diff3-比较3个文件不同的地方
推荐:更多Linux 文件查找和比较 命令关注:linux命令大全 diff3命令用于比较3个文件,将3个文件的不同的地方显示到标准输出. 语法 diff3(选项)(参数) 选项 -a:把所有的文件都 ...
- js之条件判断
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- reading/writing files in Python
file types: plaintext files, such as .txt .py Binary files, such as .docx, .pdf, iamges, spreadsheet ...
- 匹配 C 语言样式字符串
#include <stdio.h> char haha[] = "nihaoma" "niubi" "\"hello worl ...