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算法中,第一步是需要理解它的数据结构,在此基础上,涉及到 ...
随机推荐
- JavaScript学习之DIV层与图像
DIV层与图像 一.设计一个可定位的层 1.设置位置(position)和大小 (1)绝对定位(absolute):以页面边框为参照,只要设置好绝对位置,那么元素的位置会始终固定在距离边框某个位置的距 ...
- imx6 uboot lvds clock
在uboot中添加logo,lvds接口的lcd显示不正常,出现波动.网上说是lvds时钟频率的问题. 使用示波器测量之后,发现频率是60M,而lcd最大频率才46.8M. 因此就需要更改uboot中 ...
- chem02-- ajax登录
1.ajaxLogin.jsp <%@ page language="java" contentType="text/html; charset=UTF-8&quo ...
- Android 开发之如何保证Service不被杀掉(broadcast+system/app)
序言 最近项目要实现这样一个效果:运行后,要有一个service始终保持在后台运行,不管用户作出什么操作,都要保证service不被kill,这可真是一个难题.参考了现今各种定制版的系统和安全厂商牛虻 ...
- [BS-10] 统一设置app所有页面的“返回”按钮样式
统一设置app所有页面的“返回”按钮样式 如果想统一设置app所有页面的“返回”按钮样式,首先自定义WZNavigationController类继承UINavigationController类,然 ...
- 奥威Power-BI V11——凤凰涅槃,重磅来袭
时隔一年,奥威Power-BI重磅再推新版,在之前V10 WEB可视化前端分析工具基础上,更加注重用户体验这一块.V11站在用户的角度,以用户的视野去设计研发新版本,最终在前端框架的优化.移动设备的体 ...
- C# WebSocket 服务端示例代码 + HTML5客户端示例代码
WebSocket服务端 C#示例代码 using System; using System.Collections.Generic; using System.Linq; using System. ...
- MySQL 部分函数使用
1.DATE_ADD 参考博客:MySQL日期时间函数大全 转 例:DATE_ADD(date,INTERVAL expr type) 2.日期转字符串 DATE_FORMAT 参考博客:MYSQL中 ...
- 第四篇 SQL Server安全权限
本篇文章是SQL Server安全系列的第四篇,详细内容请参考原文. 权限授予主体访问对象,以执行某些操作.SQL Server有大量你可以授予给主体的权限,你甚至可以拒绝或回收权限.这听起来有点复杂 ...
- css3实现动画效果
一.动画效果的常用属性 实现动画效果需要借助css3的下列属性:transform,transion,animation(具体可以参见教材) 二.动画效果实例 1)文字闪烁的动画效果 /*文字的闪烁效 ...