JAVA对象克隆可能会出现的问题
首先,区分一下拷贝和克隆:
拷贝:当拷贝一个变量时,原始变量与拷贝变量引用的是同一个对象。当改变一个变量所引用的对象,则会对另一个变量造成影响。
克隆:当克隆一个对象时,是重新的创建了和该对象内容相同的对象。
clone方法是Object类受保护(preteced)方法,用户编写的代码不能直接调用。只有相同的类才能克隆其本身。
问题:
如果待克隆的对象中的所有数据域都是数值或基本类型,这样的克隆没有问题。
但是,如果在对象中包含了子对象的引用,拷贝的结果就会使得两个域引用同一个子对象,此时,原始对象和克隆对象将共享这一部分信息。
这样,当克隆对象改变这部分时,就会造成原始对象中数据的改变。
默认的克隆操作都是浅拷贝,它并没有克隆包含在对象中的内部对象。
进行浅拷贝会发生什么?
如果原始对象与浅克隆对象共享的子对象是不可变的,这样就不会产生问题。
但是,更多的情况是子对象是可变的。这样,就必须重写clone方法,以实现克隆子对象的深度拷贝。
对于待克隆的对象,需要作出一些判断:
(1)默认的克隆方法是否能够满足要求;
(2)默认的克隆方法是能够通过调用可变子对象的clone方法进行修补;
(3)是否不应该使用clone。
如果选1或2,类必须:
实现Cloneable接口,并使用public访问修饰符重新定义clone方法。
下面举例说明克隆机制:
import java.util.Date;
import java.util.GregorianCalendar;
public class TestClone {
public static void main(String[] args) {
try {
/**原始对象**/
Employee origin = new Employee("Tom", 10000);
origin.setHireDay(2014, 10, 10);
/**克隆Employee对象**/
Employee copy = origin.clone();
copy.raiseSalary(10);
copy.setHireDay(2015, 11, 11);
System.err.println(origin); /**Tom,10000.0,Mon Nov 10 00:00:00 CST 2014**/
System.err.println(copy); /**Tom,11000.0,Fri Dec 11 00:00:00 CST 2015**/
/**
* 可以看出数据域属于数值或基本类型时,克隆不会对其值造成影响,
* 而类似Date这样的对象在进行克隆时就需要特别注意,必须进行深度克隆,将子对象也进行克隆
*/
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Employee implements Cloneable {
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s) {
this.name = n;
this.salary = s;
this.hireDay = new Date();
}
public Employee clone() throws CloneNotSupportedException {
/** 调用Object的clone方法 **/
Employee cloned = (Employee) super.clone();
/** 克隆可变的域对象 对可变的子对象进行克隆 **/
cloned.hireDay = (Date) hireDay.clone();
/**如果注释掉上面hireDay的克隆,最终将会得到如下结果,原始对象和克隆对象都指向同一个对象**/
/**Tom,10000.0,Fri Dec 11 00:00:00 CST 2015**/
/**Tom,11000.0,Fri Dec 11 00:00:00 CST 2015**/
return cloned;
}
public void setHireDay(int year, int month, int day) {
Date newHireday = new GregorianCalendar(year, month, day).getTime();
hireDay.setTime(newHireday.getTime());
}
public void raiseSalary(double byPrecent){
double raise = salary * byPrecent / 100;
salary += raise;
}
/**重写Object类的toString方法**/
public String toString(){
return new StringBuffer().append(name).append(",").append(salary).append(",")
.append(hireDay).toString();
}
}
JAVA对象克隆可能会出现的问题的更多相关文章
- Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么 ...
- (转)Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
原文地址:http://blog.csdn.net/kenthong/article/details/5758884 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的 ...
- Java对象克隆详解
原文:http://www.cnblogs.com/Qian123/p/5710533.html 假如说你想复制一个简单变量.很简单: int apples = 5; int pears = appl ...
- java对象克隆复制
原文链接:https://blog.csdn.net/ztchun/article/details/79110096 自己先简单描述总结一下:当想要将一个对象中已有的值直接给另外一个对象的时候,其实并 ...
- java对象克隆以及深拷贝和浅拷贝
1.什么是"克隆"? 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不 ...
- JAVA对象克隆
1> 为了获取对象的一份拷贝,我们可以利用Object类的clone()方法. 2> 在派生类中覆盖基类的clone(),并声明为public.3> 在派生类的clone()方法中, ...
- Java提高篇——对象克隆(复制)
假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short, ...
- Java对象的克隆和深浅问题
Java实现克隆的方式 Java实现克隆的方式有如下两种, 推荐采用实现Cloneable接口的方式 实现Cloneable接口, 重写clone方法, 调用父类的clone方法 还有另一种方法, 不 ...
- Java对象和集合的拷贝/克隆/复制
昨天同事遇到了一个奇怪的问题,他需要将一个JavaBean拷贝一份,然后对新创建的Bean进行操作.但是他对新的Bean操作后,会影响旧的Bean的值.当听到这个问题的时候,我第一反应就是他的拷贝方法 ...
随机推荐
- public、protected、default、private区别
public.protected.default.private: 修饰符 本类 同包 子类 其他 public √ √ √ √ protected √ √ √ × default √ √ × × p ...
- 【转】使用virtualenv在ubuntu上搭建python 3开发环境
ubuntu 13.04默认的python版本是2.7的,想在其上做python3的开发会遇到问题.比如要使用pip安装软件包时,默认安装的就是python2的包.如果想安装python3的包,就需要 ...
- Vue国际化处理 vue-i18n 以及项目自动切换中英文
1. 环境搭建 命令进入项目目录,执行以下命令安装vue 国际化插件vue-i18n npm install vue-i18n --save 2. 项目增加国际化翻译文件 在项目的src下添加lang ...
- Go 语言 goto 语句
Go 语言的 goto 语句可以无条件地转移到过程中指定的行. goto语句通常与条件语句配合使用.可用来实现条件转移, 构成循环,跳出循环体等功能. 但是,在结构化程序设计中一般不主张使用goto语 ...
- 线程停止与volatile
1.使用标志位停止线程 在Java中希望停止线程,可以使用设置标志位的方法,如下例所示: class SimpleTask implements Runnable{ private boolean s ...
- 在Spring Boot中使用数据库事务
我们在前面已经分别介绍了如何在Spring Boot中使用JPA(初识在Spring Boot中使用JPA)以及如何在Spring Boot中输出REST资源(在Spring Boot中输出REST资 ...
- linux系统性能监控--内存利用率
Linux提供了对物理内存进行合理.高效的访问并可以访问潜在的海量虚存的技术.虚存通常稍多于操作系统实际拥有的内存容量,以便将较少使用的数据卸载到磁盘存储器上,同时又呈现出系统拥有大量物理内存的假象. ...
- Zookeeper的安装部署
1.Zookeeper的安装部署 7.1 Zookeeper工作机制 7.1.1.Zookeeper集群角色 Zookeeper集群的角色: Leader 和 follower (Observer ...
- 安卓高级6 玩转AppBarLayout,更酷炫的顶部栏 Toolbar
原文大神地址:http://www.jianshu.com/p/d159f0176576 上一篇文章[<CoordinateLayout的使用如此简单 >]上一篇文章<Coordin ...
- 从Dynamics CRM2011到Dynamics CRM2016的升级之路
CRM的产品更新特别快,特别是最近的几个版本,很多客户依旧停留在2011甚至是4.0,也经常会听到有人问2011能不能升级至最新版,2013能不能升级至最新版,本文将简单演示下从2011升级到2016 ...