Java克隆--深克隆与浅克隆的区别
克隆,就是复制一个对象的副本,而克隆又分浅克隆和深克隆。浅克隆是指克隆得到的对象基本类型的值改变了,而源对象的值不会变。但如果被克隆对象引用类型的值改变了,那么源对象的值同样会改变,因为引用类型在栈内存中存放的是一个引用地址,被克隆对象后也和源对象的引用地址一样,都是指向同样的内存空间的值。所以在克隆时,任何一个对象的值的改变都会令另外的值改变,所以这种情况下要用深克隆。
要注意的是要克隆的对象的泪必须继承cloneable接口。浅克隆的特点是只克隆该对象本体,它的优缺点就是一改皆改;深克隆的特点是新建对象,与源对象互不干扰,而它的优缺点也是互不干扰和麻烦。通常情况下我们使用克隆的时候都只使用浅克隆。
对克隆可以简单的理解为:当克隆一个对象时,把属性的值和方法都一起拷贝的是浅拷贝,而方法的值可以设置不同的是深克隆。深克隆要进行反序例化,先把内容输出到内存,再从内存读取。
浅克隆:
package com.lk.B;
public class Address {
private String state;
private String province;
private String city;
public Address(String state,String province,String city){
this.state = state;
this.province = province;
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
// TODO Auto-generated method stub
StringBuffer sb = new StringBuffer();
sb.append("国家:"+this.state+",");
sb.append("省:"+this.province+",");
sb.append("市:"+this.city);
return sb.toString();
}
}
package com.lk.B;
public class Employees implements Cloneable{
private String name;
private int age;
private Address address;
public Employees(String name,int age,Address address){
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
// TODO Auto-generated method stub
StringBuffer sb = new StringBuffer();
sb.append("姓名:"+name+",");
sb.append("年龄:"+age+"\n");
sb.append("地址"+address);
return sb.toString();
}
@Override
protected Employees clone() {//原本返回值应该是Object,但是这里返回的是Employees,为协变类型
//即子类覆盖(即重写)基类方法时,返回的类型可以是基类方法返回类型的子类。
// TODO Auto-generated method stub
Employees employee = null;
try {
employee = (Employees) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return employee;
}
}
package com.lk.B;
public class Test2 {
public static void main(String[] args) {//此方法并没有克隆成功,原因是对于引用类型不能使用浅克隆
// TODO Auto-generated method stub
System.out.println("克隆之前:");
Address address = new Address("中国", "黑龙江", "哈尔滨");
Employees employee1 = new Employees("阿坤", 21, address);
System.out.println("员工1的信息:");
System.out.println(employee1);
System.out.println("克隆之后:");
Employees employee2 = employee1.clone();
employee2.getAddress().setState("中国");
employee2.getAddress().setProvince("上海");
employee2.getAddress().setCity("杨浦");
employee2.setName("lk");
employee2.setAge(22);
System.out.println("员工2的信息:");
System.out.println(employee2);
System.out.println("员工1的信息:");
System.out.println(employee1);
}
}
输出:
/*
克隆之前:
员工1的信息:
姓名:阿坤,年龄:21
地址国家:中国,省:黑龙江,市:哈尔滨
克隆之后:
员工2的信息:
姓名:lk,年龄:22
地址国家:杨浦,省:上海,市:哈尔滨
员工1的信息:
姓名:阿坤,年龄:21
地址国家:杨浦,省:上海,市:杨浦
*/
此时并没有克隆成功,因为对于引用类型不能使用浅克隆,需要使用深克隆
实现深克隆有两种方法,这里先介绍第一种方法:一次克隆各个可变的引用类型:即只要有类中包含了引用类型就将那个引用类型进行克隆,在这个例子里,即需要对Address类也需要克隆,由于Address中都是基本类型(String在这里我先叫做基本类型,因为String比较特殊)
深克隆:
package com.lk.B;
public class Address implements Cloneable{
private String state;
private String province;
private String city;
public Address(String state,String province,String city){
this.state = state;
this.province = province;
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
// TODO Auto-generated method stub
StringBuffer sb = new StringBuffer();
sb.append("国家:"+this.state+",");
sb.append("省:"+this.province+",");
sb.append("市:"+this.city);
return sb.toString();
}
@Override
protected Address clone() {
// TODO Auto-generated method stub
Address address = null;
try {
address = (Address) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return address;
}
}
package com.lk.B;
public class Employees implements Cloneable{
private String name;
private int age;
private Address address;
public Employees(String name,int age,Address address){
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
// TODO Auto-generated method stub
StringBuffer sb = new StringBuffer();
sb.append("姓名:"+name+",");
sb.append("年龄:"+age+"\n");
sb.append("地址"+address);
return sb.toString();
}
@Override
protected Employees clone() {//原本返回值应该是Object,但是这里返回的是Employees,为协变类型
//即子类覆盖(即重写)基类方法时,返回的类型可以是基类方法返回类型的子类。
// TODO Auto-generated method stub
Employees employee = null;
try {
employee = (Employees) super.clone();
employee.address = address.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return employee;
}
}
package com.lk.B;
public class Test2 {
public static void main(String[] args) {//深克隆,克隆成功
// TODO Auto-generated method stub
System.out.println("克隆之前:");
Address address = new Address("中国", "黑龙江", "哈尔滨");
Employees employee1 = new Employees("阿坤", 21, address);
System.out.println("员工1的信息:");
System.out.println(employee1);
System.out.println("克隆之后:");
Employees employee2 = employee1.clone();
employee2.getAddress().setState("中国");
employee2.getAddress().setProvince("上海");
employee2.getAddress().setCity("杨浦");
employee2.setName("lk");
employee2.setAge(22);
System.out.println("员工2的信息:");
System.out.println(employee2);
System.out.println("员工1的信息:");
System.out.println(employee1);
}
}
/*
克隆之前:
员工1的信息:
姓名:阿坤,年龄:21
地址国家:中国,省:黑龙江,市:哈尔滨
克隆之后:
员工2的信息:
姓名:lk,年龄:22
地址国家:中国,省:上海,市:杨浦
员工1的信息:
姓名:阿坤,年龄:21
地址国家:中国,省:黑龙江,市:哈尔滨 */
注意:使用重写clone方法时必须要实现Cloneable接口,否则会出现:java.lang.CloneNotSupportedException异常
Java克隆--深克隆与浅克隆的区别的更多相关文章
- 浅谈Java中的深克隆和浅克隆(阿里面试)
在最近的秋招中,阿里和多益网络都问到了这个问题,虽然很简单,但是我还是想总结一下,感兴趣的可以看一下我的个人博客网站(Spring+MyBatis+redis+nginx+mysql)(适合菜鸟),最 ...
- java克隆机制
看了下面博客就很明白了 http://www.cnblogs.com/Qian123/p/5710533.html#_label0 java对象创建方式有三种: 1.通过new对象 2.通过java克 ...
- JavaScript的深克隆与浅克隆
JS数据类型分为两类: 基本类型(Number.Boolean.Undefined.Null.String.Symbol(ES6新加,此处不讨论))与引用类型(Object).原始类型存储的是对象的实 ...
- Java中Set Map List 的区别
java中set map list的区别: 都是集合接口 简要说明 set --其中的值不允许重复,无序的数据结构 list --其中的值允许重复,因为其为有序的数据结构 map--成对的数据结构 ...
- Java中Comparable和Comparator接口区别分析
Java中Comparable和Comparator接口区别分析 来源:码农网 | 时间:2015-03-16 10:25:20 | 阅读数:8902 [导读] 本文要来详细分析一下Java中Comp ...
- 转:Java中abstract和interface的区别
转自:Java中abstract和interface的区别 abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java ...
- Java abstract class 和 interface 的区别
Java abstract class 和 interface 的区别 1. abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制 2. 以Door的抽象概 ...
- java虚拟机和Dalvik虚拟机的区别
java虚拟机和Dalvik虚拟机的区别: java虚拟机Dalvik虚拟机 java虚拟机基于栈. 基于栈的机器必须使用指令来载入和操作栈上数据,所需指令更多更多dalvik虚拟机是基于寄存器的 j ...
- Java中this与super的区别【6】
若有不正之处,请多多谅解并欢迎批评指正,不甚感激.请尊重作者劳动成果: 本文原创作者:pipi-changing本文原创出处:http://www.cnblogs.com/pipi-changing/ ...
随机推荐
- IIS 7 WAS服务不可用
在 Windows Server 2008 上使用 %windir%\system32\inetsrv\appcmd.exe list wp 命令,得到如下错误: ERROR ( message:WA ...
- 为什么要尽量少使用iframe
Iframes 阻塞页面加载 及时触发 window 的 onload 事件是非常重要的.onload 事件触发使浏览器的 “忙” 指示器停止,告诉用户当前网页已经加载完毕.当 onload 事件加载 ...
- InteractivePNG
在as3中很多时候需要只能选中png中可视区域,即透明区域“感觉可以穿透”.
- 初始化css代码需要注意的
(从已经死了一次又一次终于挂掉的百度空间人工抢救出来的,发表日期 2014-05-06) 写在所有css代码之前,对网页中所有同类元素的一个样式规则代码或者一些基础性公用元素的样式规则代码. 1.空白 ...
- 关于div的居中的问题
(从已经死了一次又一次终于挂掉的百度空间人工抢救出来的,发表日期2014-01-11) div水平和垂直居中,text-align和vertical-align不起作用,因为标签div没有这两个属性, ...
- svn 冲突解决
svn: E155015: Aborting commit: '$path + $file' remains in conflict 解决步骤 1.svn resolved 'file'执行后结果Re ...
- [置顶] Android AlarmManager实现不间断轮询服务
在消息的获取上是选择轮询还是推送得根据实际的业务需要来技术选型,例如对消息实时性比较高的需求,比如微博新通知或新闻等那就最好是用推送了.但如果只是一般的消息检测比如更新检查,可能是半个小时或一个小时一 ...
- Python学习入门基础教程(learning Python)--5.1 Python下文件处理基本过程
Python下的文件读写操作过程和其他高级语言如C语言的操作过程基本一致,都要经历以下几个基本过程. 1. 打开文件 首先是要打开文件,打开文件的主要目的是为了建立程序和文件之间的联系.按程序访问文件 ...
- C++学习笔记之输入、输出和文件
一.流的概念 数据从内存的一个地址移动到另一个地址称为数据流动——流操作 流操作是通过缓冲区(buffer)机制实现的. 缓冲区:内存的一块区域——用作文件与内存交换数据. 数据从文件中读出:文件 → ...
- javascript 操作 excel 全攻略
最近做一个项目,用到了javascript操纵excel以生成报表,下面是标有详细注解的实例 <html> <head><script language="ja ...