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算法中,第一步是需要理解它的数据结构,在此基础上,涉及到 ...
随机推荐
- Selenium2学习-010-WebUI自动化实战实例-008-Selenium 操作下拉列表实例-Select
此文主要讲述用 Java 编写 Selenium 自动化测试脚本编写过程中,对下拉列表框 Select 的操作. 下拉列表是 Web UI 自动化测试过程中使用率非常高的,通常有两种形式的下拉列表,一 ...
- Windows-007-进程相关命令(netstat、tasklist、taskkill、tskill)实战实例图文详解
本节主要讲述 Windows 系统下,nestat.tasklist.tskill 三个 CMD 命令的参数,及使用方法:以及如何利用三者结合查看进程信息和结束进程.敬请亲们参阅,希望能对亲们有所帮助 ...
- c#中匿名函数lamb表达式
c#中匿名函数lamb表达式 实例一:(其实,这样都是些语法糖) using System; using System.Collections.Generic; using System.Linq; ...
- 优化Linux内核参数/etc/sysctl.conf sysctl 《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》
优化Linux内核参数/etc/sysctl.conf sysctl <高性能Linux服务器构建实战:运维监控.性能调优与集群应用> http://book.51cto.com/ar ...
- APICloud上openFrameGroup把菜单挡住了,怎么处理?
问:openFrameGroup把菜单挡住了,怎么处理? 试了sendFrameToBack没反应,又不能页面自己openFrameGroup,不知道该怎么办.而且用另外的页面先openFrameGr ...
- Ubuntu中Apache修改DocumentRoot(修改网站根目录)
今天配置好Apache+PHP+MySQL但是apache默认DocumentRoot是/var/www想把它改到我Windows下进行测试的k:/wwwroot把 apache2.conf 翻了好几 ...
- linux的5个查找命令_转
转自:http://www.ruanyifeng.com/blog/2009/10/5_ways_to_search_for_files_using_the_terminal.html 在Linux中 ...
- mysql centeros 安装
http://www.cnblogs.com/xiaoluo501395377/archive/2013/04/07/3003278.html linux mysql允许远程连接 1.登录数据库:my ...
- Hadoop学习笔记: HDFS
注:该文内容部分来源于ChinaHadoop.cn上的hadoop视频教程. 一. HDFS概述 HDFS即Hadoop Distributed File System, 源于Google发表于200 ...
- 使用Adreno Profiler分析android游戏
有时候我们需要对自己的游戏或者别人的游戏进行分析,比如我们需要了解一个引擎的大体渲染的流程,这个时候我们可以借助一些工具,在PC上我们可以使用Microsoft PIX.Intel GPA.Nvidi ...