一、何为克隆

在Java的体系中,数据类型分为基本数据类型引用数据类型

基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通过赋值运算实现,比如

int a = 1;
int b = a;

引用类型的克隆的实现方式有以下两种:

1)实现Cloneable接口,重写clone() 方法,修改clone() 方法的修饰符为public。其分为浅克隆深克隆

2)  实现Serializable接口,对实例进行序列化,通过二进制流反序列化。其为真正的克隆

注:若类没实现Cloneable接口,调用对象的clone方法会抛出CloneNotSupportedException异常

二、重写clone实现克隆

克隆就是获得当前对象的副本,其与当前对象是内存地址不同的两个对象。

浅克隆指的是当前对象的实例成员为引用类型时,副本的该实例成员只是复制了其引用值,指向同一个对象。

深克隆则是对该引用指向的对象进行克隆,然后引用指向了该副本,指向不同的对象。

浅克隆导致了一个问题就是,对实例或克隆副本做出的改变会影响彼此,从而导致错误的结果,下面的例子可以说明:

汽车类 Car,包含一个引用类型成员brand:

/**
* Car类
* 实现Cloneable接口,重写clone方法
* @author zhangyj
*
*/
public class Car implements Cloneable{
//使用年限
private int year;
//品牌
private Brand brand; public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public Brand getBrand() {
return brand;
}
public void setBrand(Brand brand) {
this.brand = brand;
} //构造器
public Car(int year, Brand brand) {
this.year = year;
this.brand = brand;
} /**
* 浅克隆,调用父类的clone方法
*/
@Override
public Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
} /**
* 深克隆,其引用类型成员也要调用clone
* @return
*/
public Car deepClone() {
Car car = null;
try {
car = (Car)super.clone();
car.setBrand((Brand)brand.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return car;
} @Override
public String toString() {
return "[year=" + year + ", brand=" + brand + "]";
}
}
/**
* 品牌类
* @author zhangyj
*
*/
class Brand implements Cloneable{
//名称
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
protected Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
} public Brand(String name) {
this.name = name;
} @Override
public String toString() {
return "Brand [name=" + name + "]";
}
}

测试类 CarCloneTest :

public class CarCloneTest {
public static void main(String[] args) {
Brand brand = new Brand("BMW");
Car car = new Car(5, brand);
Car carClone;
carClone = (Car) car.clone(); //(1)
//carClone = (Car) car.deepClone(); //(2) System.out.println("******************************************");
System.out.println("car "+car);
System.out.println("carClone "+carClone); System.out.println("******************************************");
System.out.println("将car年限改为 7,品牌名称改为Banz");
brand.setName("Banz");
car.setYear(7); System.out.println("******************************************");
System.out.println("car "+car);
System.out.println("carClone "+carClone);
}
}

浅克隆测试,运行(1)代码,得到结果如下:

******************************************
car [year=5, brand=Brand [name=BMW]]
carClone [year=5, brand=Brand [name=BMW]]
******************************************
将car年限改为 7,品牌名称改为Banz
******************************************
car [year=7, brand=Brand [name=Banz]]
carClone [year=5, brand=Brand [name=Banz]]

可见当修改car中brand对象的名称为Banz时,carClone也跟着改变,可见其引用的brand对象为同一个,显然这不是我们想要的,而深克隆就解决了这个问题。

深克隆测试,运行(2)代码,得到结果如下:

******************************************
car [year=5, brand=Brand [name=BMW]]
carClone [year=5, brand=Brand [name=BMW]]
******************************************
将car年限改为 7,品牌名称改为Banz
******************************************
car [year=7, brand=Brand [name=Banz]]
carClone [year=5, brand=Brand [name=BMW]]

三、序列化实现深克隆

序列化对象必须实现Serializable接口,使对象可序列化。

public class Car implements Serializable{
private static final long serialVersionUID = 7883197573658810857L;
private int year; //使用年限
private Brand brand; //品牌 public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public Brand getBrand() {
return brand;
}
public void setBrand(Brand brand) {
this.brand = brand;
} public Car(int year, Brand brand) {
this.year = year;
this.brand = brand;
} @Override
public String toString() {
return "[year=" + year + ", brand=" + brand + "]";
}
}
class Brand implements Serializable{
private static final long serialVersionUID = -6505899377489945908L;
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Brand(String name) {
this.name = name;
} @Override
public String toString() {
return "Brand [name=" + name + "]";
}
}

克隆工具类 CloneUtil:

public final class CloneUtil {
private CloneUtil() { throw new AssertionError(); } /**
* 序列化实现深克隆
* @param t 泛型对象
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T deepClone(T t) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objOut;
ObjectInputStream objIn;
T tClone = null ;
try {
objOut = new ObjectOutputStream(out);
objOut.writeObject(t); //将对象以二进制流形式写入
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
objIn = new ObjectInputStream(in);
tClone = (T)objIn.readObject(); //反序列化读取对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return tClone;
}
}

测试类 CarCloneTest:

public class CarCloneTest {
public static void main(String[] args) {
Brand brand = new Brand("BMW");
Car car = new Car(5, brand);
Car carClone;
// carClone = (Car) car.clone(); //(1)
// carClone = (Car) car.deepClone(); //(2)
carClone = CloneUtil.deepClone(car); System.out.println("******************************************");
System.out.println("car "+car);
System.out.println("carClone "+carClone); System.out.println("******************************************");
System.out.println("将car年限改为 7,品牌名称改为Banz");
brand.setName("Banz");
car.setYear(7); System.out.println("******************************************");
System.out.println("car "+car);
System.out.println("carClone "+carClone);
}
}

运行测试程序,得到结果如下:

******************************************
car [year=5, brand=Brand [name=BMW]]
carClone [year=5, brand=Brand [name=BMW]]
******************************************
将car年限改为 7,品牌名称改为Banz
****************************************** 
car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]

可见通过反序列化对象进行克隆也能得到我们想要的结果。

以上!

【Java一看就懂】浅克隆和深克隆的更多相关文章

  1. Java必备技能:clone浅克隆与深克隆

    介绍 一直以来只知道Java有clone方法,该方法属于Object的,对于什么是浅克隆与深克隆就比较模糊了,现在就来补充学习一下. 概念 浅拷贝(浅克隆)复制出来的对象的所有变量都含有与原来的对象相 ...

  2. 深入理解Java的浅克隆与深克隆

    前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个对象,并对相应的属性进行数据,这样也能实现克隆的目的. 但当对象属性 ...

  3. Java的浅克隆与深克隆

    前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个对象,并对相应的属性进行数据,这样也能实现克隆的目的. 但当对象属性 ...

  4. java浅克隆和深克隆,序列化和反序列化实现深克隆(封装序列化和反序列化操作)

    本篇博客内容: 一.浅克隆(ShallowClone)和深克隆(DeepClone) 二.序列化和反序列化实现深克隆 三.封装序列化和反序列化操作 ObjectOutputStream + 内存流By ...

  5. 保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java、Golang两种客户端教学Case)

    保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java.Golang两种客户端教学Case)   目录 什么是AMQP 和 JMS? 常见的MQ产品 安装RabbitM ...

  6. c#:浅克隆和深克隆,序列化和反序列化

    一.浅克隆和深克隆(浅复制和深复制)浅克隆和深克隆最典型的应用是数据集对象DataSet的Clone和Copy方法.Clone()方法用来复制DataSet的结构,但是不复制DataSet的数据,实现 ...

  7. 一看就懂的Android APP开发入门教程

    一看就懂的Android APP开发入门教程 作者: 字体:[增加 减小] 类型:转载   这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤 ...

  8. mysql取出现在的时间戳和时间时间戳转成人类看得懂的时间

    mysql取出现在的时间戳和时间时间戳转成人类看得懂的时间,我们在mysql里面他封装了一个内置的时间戳转化的函数,比如我们现在的时间戳是:1458536709 ,"%Y-%m-%d&quo ...

  9. 一看就懂的ReactJs入门教程(精华版)

    一看就懂的ReactJs入门教程(精华版) 现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和 ...

随机推荐

  1. springmvc报406错误

    springmvc出现406,无非就两种情况,第一,百分之九十是json包没加进来,第二,百分之十,就是@ResponseBody返回的的数据,在请求的URL中不能有  .html的后缀

  2. 一个简单的Springmvc应用开发例子

            SpringMVC应用的配置步骤:         1,将所有的jar包导入到lib文件夹下:             jar在spring框架包-->libs-->所有的 ...

  3. 经典卷积神经网络(LeNet、AlexNet、VGG、GoogleNet、ResNet)的实现(MXNet版本)

    卷积神经网络(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现. 其中 文章 详解卷 ...

  4. android自定义View的绘制原理

    每天我们都会使用很多的应用程序,尽管他们有不同的约定,但大多数应用的设计是非常相似的.这就是为什么许多客户要求使用一些其他应用程序没有的设计,使得应用程序显得独特和不同. 如果功能布局要求非常定制化, ...

  5. R语言︱ 数据库SQL-R连接与SQL语句执行(RODBC、sqldf包)

    要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗"贝壳"~ --------------------------- 数据库是极其重要的R语言数据导入源 ...

  6. R语言︱情感分析—词典型代码实践(最基础)(一)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 笔者寄语:词典型情感分析对词典要求极高,词典中 ...

  7. Windows PE入门基础知识:Windows PE的作用、命名规则、启动方式、启动原理

    Windows PE的全名是WindowsPreinstallationEnvironment(WinPE)直接从字面上翻译就 是"Windows预安装环境".微软的本意是:Win ...

  8. stl string 容器的使用

    string 是基本的字符串序列容器,对应数据结构中的串,和vector<char>也类似,但功能更多 string 容器的使用 1,string 的构造函数是. string() 2,s ...

  9. OTG驱动分析(一)

    前一段时间弄了2个礼拜的OTG驱动调试,感觉精神疲惫啊.主要原因还是自己对OTG功能不了解造成的.现在终于完成但是对实质原理还有些模糊.所以自己重新总结一下.因为自己是菜鸟,所以用菜鸟的白话方式分析. ...

  10. 百度地图JavaScript API经纬度查询-MAP

    百度地图JavaScript API经纬度查询-MAP-ABCDEFGHIJKMHNOPQRSTUVWXYZ: 搜索:<input type="text" size=&quo ...