java中的拷贝(一)
摘自:http://blog.csdn.net/tounaobun/article/details/8491392
假如说你想复制一个简单变量。很简单:
- int apples = 5;
- int pears = apples;
不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。
但是如果你复制的是一个对象,情况就有些复杂了。
假设说我是一个beginner,我会这样写:
- class Student {
- private int number;
- public int getNumber() {
- return number;
- }
- public void setNumber(int number) {
- this.number = number;
- }
- }
- public class Test {
- public static void main(String args[]) {
- Student stu1 = new Student();
- stu1.setNumber(12345);
- Student stu2 = stu1;
- System.out.println("学生1:" + stu1.getNumber());
- System.out.println("学生2:" + stu2.getNumber());
- }
- }
打印结果:
- 学生1:12345
- 学生2:12345
这里我们自定义了一个学生类,该类只有一个number字段。
我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)
再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,
难道真的是这样吗?
我们试着改变stu2实例的number字段,再打印结果看看:
- stu2.setNumber(54321);
- System.out.println("学生1:" + stu1.getNumber());
- System.out.println("学生2:" + stu2.getNumber());
打印结果:
- 学生1:54321
- 学生2:54321
这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?
原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,
这样,stu1和stu2指向内存堆中同一个对象。如图:

那么,怎样才能达到复制一个对象呢?
是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。
该方法的签名是:
protected native Object clone() throws CloneNotSupportedException;
因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。
要想对一个对象进行复制,就需要对clone方法覆盖。
一般步骤是(浅复制):
1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)
下面对上面那个方法进行改造:
- class Student implements Cloneable{
- private int number;
- public int getNumber() {
- return number;
- }
- public void setNumber(int number) {
- this.number = number;
- }
- @Override
- public Object clone() {
- Student stu = null;
- try{
- stu = (Student)super.clone();
- }catch(CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return stu;
- }
- }
- public class Test {
- public static void main(String args[]) {
- Student stu1 = new Student();
- stu1.setNumber(12345);
- Student stu2 = (Student)stu1.clone();
- System.out.println("学生1:" + stu1.getNumber());
- System.out.println("学生2:" + stu2.getNumber());
- stu2.setNumber(54321);
- System.out.println("学生1:" + stu1.getNumber());
- System.out.println("学生2:" + stu2.getNumber());
- }
- }
打印结果:
- 学生1:12345
- 学生2:12345
- 学生1:12345
- 学生2:54321
如果你还不相信这两个对象不是同一个对象,那么你可以看看这一句:
- System.out.println(stu1 == stu2); // false
上面的复制被称为浅复制(Shallow Copy),还有一种稍微复杂的深度复制(deep copy):
我们在学生类里再加一个Address类。
- class Address {
- private String add;
- public String getAdd() {
- return add;
- }
- public void setAdd(String add) {
- this.add = add;
- }
- }
- class Student implements Cloneable{
- private int number;
- private Address addr;
- public Address getAddr() {
- return addr;
- }
- public void setAddr(Address addr) {
- this.addr = addr;
- }
- public int getNumber() {
- return number;
- }
- public void setNumber(int number) {
- this.number = number;
- }
- @Override
- public Object clone() {
- Student stu = null;
- try{
- stu = (Student)super.clone();
- }catch(CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return stu;
- }
- }
- public class Test {
- public static void main(String args[]) {
- Address addr = new Address();
- addr.setAdd("杭州市");
- Student stu1 = new Student();
- stu1.setNumber(123);
- stu1.setAddr(addr);
- Student stu2 = (Student)stu1.clone();
- System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
- System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
- }
- }
打印结果:
- 学生1:123,地址:杭州市
- 学生2:123,地址:杭州市
乍一看没什么问题,真的是这样吗?
我们在main方法中试着改变addr实例的地址。
- addr.setAdd("西湖区");
- System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
- System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
打印结果:
- 学生1:123,地址:杭州市
- 学生2:123,地址:杭州市
- 学生1:123,地址:西湖区
- 学生2:123,地址:西湖区
这就奇怪了,怎么两个学生的地址都改变了?
原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:
- package abc;
- class Address implements Cloneable {
- private String add;
- public String getAdd() {
- return add;
- }
- public void setAdd(String add) {
- this.add = add;
- }
- @Override
- public Object clone() {
- Address addr = null;
- try{
- addr = (Address)super.clone();
- }catch(CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return addr;
- }
- }
- class Student implements Cloneable{
- private int number;
- private Address addr;
- public Address getAddr() {
- return addr;
- }
- public void setAddr(Address addr) {
- this.addr = addr;
- }
- public int getNumber() {
- return number;
- }
- public void setNumber(int number) {
- this.number = number;
- }
- @Override
- public Object clone() {
- Student stu = null;
- try{
- stu = (Student)super.clone(); //浅复制
- }catch(CloneNotSupportedException e) {
- e.printStackTrace();
- }
- stu.addr = (Address)addr.clone(); //深度复制
- return stu;
- }
- }
- public class Test {
- public static void main(String args[]) {
- Address addr = new Address();
- addr.setAdd("杭州市");
- Student stu1 = new Student();
- stu1.setNumber(123);
- stu1.setAddr(addr);
- Student stu2 = (Student)stu1.clone();
- System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
- System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
- addr.setAdd("西湖区");
- System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
- System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
- }
- }
打印结果:
- 学生1:123,地址:杭州市
- 学生2:123,地址:杭州市
- 学生1:123,地址:西湖区
- 学生2:123,地址:杭州市
这样结果就符合我们的想法了。
最后我们可以看看API里其中一个实现了clone方法的类:
java.util.Date:
- /**
- * Return a copy of this object.
- */
- public Object clone() {
- Date d = null;
- try {
- d = (Date)super.clone();
- if (cdate != null) {
- d.cdate = (BaseCalendar.Date) cdate.clone();
- }
- } catch (CloneNotSupportedException e) {} // Won't happen
- return d;
- }
java中的拷贝(一)的更多相关文章
- 总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇
本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 总结java中创建并写文件的5种方式-JAVA IO基础总结第一篇 总结java从文件中读取数据的6种方法-JAVA IO基础总结第二篇 ...
- java中的拷贝(二)深克隆
浅拷贝(Object类中的clone()方法)是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝. 深拷贝(或叫深克隆) 则是对对象及该对象关联的对象内容, ...
- Java中对象拷贝的两种方式
引用的拷贝 //引用拷贝 private static void copyReferenceObject(){ Person p = new Person(23, "zhang") ...
- java中的拷贝文件FileChannel
以前用Java拷贝文件,只知道写byte数组循环拷贝,今天知道了可以用FileChannel进行拷贝,上代码: 下边是传统的byte数组拷贝方法 </pre><pre name=&q ...
- Java中的Html解析:使用jsoup
包:jsoup-1.10.2.jar import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import o ...
- 一文了解 Java 中的构造器
摘要:Java 也采用了构造器,并且还提供了一个垃圾收集器(garbage collector),当不再使用内存资源的时候,垃圾收集器会自动将其释放. 本文分享自华为云社区<一文带你了解 Jav ...
- Java学习-045-目录中文件拷贝
挺晚的了,直接上码.敬请各位小主参阅,若有不足之处,敬请指正,非常感谢! 目录文件拷贝源码: /** * <strong>目录拷贝</strong><br> * & ...
- JAVA中JavaBean对象之间拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...
- 关于Java中的HashMap的深浅拷贝的测试与几点思考
0.前言 工作忙起来后,许久不看算法,竟然DFA敏感词算法都要看好一阵才能理解...真是和三阶魔方还原手法一样,田园将芜,非常可惜啊. 在DFA算法中,第一步是需要理解它的数据结构,在此基础上,涉及到 ...
随机推荐
- 导航栏和里面的View设置的是同一颜色值,实际运行又不一样.
导航栏和里面的View设置的是同一颜色值,实际运行又不一样.如何保证两者的颜色一致呢? 答案就是:( navigationBar.translucent = NO; ) 去除 导航条的分割线(黑 ...
- rabbitMq 转自 http://gaoyangang.iteye.com/blog/1566600
rabbitMq 转自 http://gaoyangang.iteye.com/blog/1566600
- inline-block元素的空白间距
inline-block元素的空白间距 html代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&quo ...
- t-sql中字符串前加N代表什么意思
比如 select @status = N'stopped' 那么其中的字符串 stopped 前面为什么要加 N 呢?而且我们发现有些地方加 N 与否都没有影响,有些地方又必须加 N. N 在这里表 ...
- docker:从 tomcat 容器连接到 mysql 容器
docker 中的容器互联是一个较为复杂的话题,详细内容将在后续章节中介绍. 续前 2 个章节的内容,我们创建了一个 mysql 容器和一个 tomcat 容器,可以使用 「docker ps」来查看 ...
- PISA
http://www.oecd.org/pisa/ 国际学生评估 PISA类测试:一种国际性的科学的评价方法,可强化对考生知识面.综合分析.创新素养方面的考查. 评估内容 第一次PISA评估于2000 ...
- Windows Security 学习笔记
对于Windows 在 Security 方面的学习. 纯兴趣. UNIX 的另外开一条路线学习. 话说今天查gpedit.msc的资料的时候发现 M$ 官网上怎么连个文档都没有. 后来才点了 gpe ...
- Git随记
gulp 打包 把dist下的提交到构建环境 git status 看哪些发生了变化 git add . 添加当前文件夹下的文件 git commit -am "change file&qu ...
- jquery的ajax同步和异步
之前一直在写JQUERY代码的时候遇到AJAX加载数据都需要考虑代码运行顺序问题.最近的项目用了到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出去 ...
- SQLSERVER:通过sys.tables实现批量删表、快速统计多表记录和
SQLSERVER:通过sys.tables实现批量删表,或者回滚表 begin try drop table #temp10 end try begin catch end catch select ...