Java学习笔记--对象克隆
转自:Edward_qing_Lee 的专栏
http://blog.csdn.net/edward_qing_lee/article/details/8249102
一、java 方法参数 理解:
方法参数 可理解为: 对于输入的实参 进行了一份拷贝, (1) 若方法参数为基本类型,则在栈内存中开辟新的空间,所有的方法体内部的操作都是针对这个拷贝的操作,并不会影响原来输入实参的值 (2)若方法参数为引用类型,该拷贝与输入实参指向了同一个对象,方法体内部对于对象的操作,都是针对的同一个对象。
另外,除了在函数传值的时候是"引用传递",在任何用"="向对象变量赋值的时候都是"引用传递"。
下面举例说明:
package com.lqh.test; import java.util.*; public class HashtableAdd {
public static void main(String[] args) {
Hashtable<String, StringBuffer> ht = new Hashtable<String, StringBuffer>();
StringBuffer sb = new StringBuffer();
sb.append("abc,");
ht.put("1", sb);
sb.append("def,");
ht.put("2", sb);
sb.append("mno,");
ht.put("3", sb);
sb.append("xyz.");
ht.put("4", sb); int numObj = 0;
Enumeration it = ht.elements();
while (it.hasMoreElements()) {
System.out.print("get StringBufffer " + (++numObj)
+ " from Hashtable: ");
System.out.println(it.nextElement());
}
}
}
上面的例子的实际输出的结果是:
get StringBufffer 1 from Hashtable: abc,def,mno,xyz.
get StringBufffer 2 from Hashtable: abc,def,mno,xyz.
get StringBufffer 3 from Hashtable: abc,def,mno,xyz.
get StringBufffer 4 from Hashtable: abc,def,mno,xyz.
分析:向Hashtable传递 StringBuffer对象是只传递了这个StringBuffer对象的引用!每一次向Hashtable表中put一次 StringBuffer,并没有生成新的StringBuffer对象,只是在Hashtable表中又放入了一个指向同一StringBuffer对象的引用而已。对Hashtable表存储的任何一个StringBuffer对象(更确切的说应该是对象的引用)的改动,实际上都是对同一个 "StringBuffer"的改动。所以Hashtable并不能真正存储能对象,而只能存储对象的引用。也应该知道这条原则对与Hashtable相似的Vector, List, Map, Set等都是一样的。
二、Java Clone( )介绍
顾名思义,clone方法的含义就是克隆出一个一模一样的对象,这样是有两个对象的。
实现clone方法的步骤()
(1)实现Cloneable接口
(2)重载Object类中的clone()方法,重载时需定义为public
(3)在重载方法中,调用super.clone()
例如:
class CloneClass implements Cloneable {
public int aInt; public Object clone() {
CloneClass o = null;
try {
o = (CloneClass) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
(1)clone()方法是定义在java.lang.Object类中,该方法是一个protected的方法,所以重载时要把clone()方法的属性设置为public,这样其它类才能调用这个clone类的clone()方法
(2)实现Cloneable接口:Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出 CloneNotSupportedException异常。
三、浅克隆与深克隆
所谓浅克隆就是说被克隆的对象各个域都是基本类型,而不存在引用类型。如有存在引用类型的域,则需要进行深克隆。深克隆就是在重载clone()方法中,要对其引用类型的域也进行克隆。如下例:
package com.lqh.clone; public class Address implements Cloneable{ private String state;
private String province;
private String 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() {
StringBuilder sb = new StringBuilder();
sb.append("国家:" + state + ", ");
sb.append("省:" + province + ", ");
sb.append("市:" + city);
return sb.toString();
}
public Address(String state, String province, String city) {
super();
this.state = state;
this.province = province;
this.city = city;
} @Override
public Address clone() {
Address address = null;
try {
address = (Address) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return address;
}
}
package com.lqh.clone; public class Employee implements Cloneable{
private String name;
private int age;
private Address address; public Employee(String name, int age, Address address) {
super();
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 Employee clone(){
Employee employee = null;
try {
employee = (Employee) super.clone();
employee.address = address.clone(); //对引用类型的域进行克隆
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return employee;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:" + name + ",");
sb.append("年龄:" + age+ ", ");
sb.append("地址:" + address);
return sb.toString();
} public static void main(String[] args) {
System.out.println("克隆之前:");
Address address = new Address("中国", "吉林", "长春");
Employee employee1 = new Employee("明日科技", 12, address);
System.out.println("员工1信息:" + employee1 ); Employee employee2 = employee1.clone(); employee2.getAddress().setState("中国");
employee2.getAddress().setProvince("四川");
employee2.getAddress().setCity("成都"); System.out.println("克隆之后:");
System.out.println("员工2信息:" + employee2);
System.out.println("员工1信息:" + employee1);
}
}
Object类中clone()方法产生的过程是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,若不使用深克隆,即不对引用类型的域进行克隆,会导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。
不是所有的类都能实现深度clone的,StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个 final类,这也是说我们也不能用继承的办法间接实现StringBuffer的clone。如果一个类中包含有StringBuffer类型对象或和 StringBuffer相似类的对象,我们有两种选择:要么只能实现影子clone,要么就在类的clone()方法中加一句(假设是 SringBuffer对象,而且变量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原来的是:o.unCA = (UnCloneA)unCA.clone();
四、Clone中String和StringBuffer的区别
下面的例子中包括两个类,CloneC类包含一个String类型变量和一个StringBuffer类型变量,并且实现了clone()方法。在 StrClone类中声明了CloneC类型变量c1,然后调用c1的clone()方法生成c1的拷贝c2,在对c2中的String和 StringBuffer类型变量用相应的方法改动之后打印结果:
package clone; class CloneC implements Cloneable {
public String str;
public StringBuffer strBuff; public Object clone() {
CloneC o = null;
try {
o = (CloneC) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
} public class StrClone {
public static void main(String[] a) {
CloneC c1 = new CloneC();
c1.str = new String("initializeStr");
c1.strBuff = new StringBuffer("initializeStrBuff");
System.out.println("before clone,c1.str = " + c1.str);
System.out.println("before clone,c1.strBuff = " + c1.strBuff); CloneC c2 = (CloneC) c1.clone();
c2.str = c2.str.substring(0, 5);
c2.strBuff = c2.strBuff.append(" change strBuff clone");
System.out.println("=================================");
System.out.println("after clone,c1.str = " + c1.str);
System.out.println("after clone,c1.strBuff = " + c1.strBuff);
System.out.println("=================================");
System.out.println("after clone,c2.str = " + c2.str);
System.out.println("after clone,c2.strBuff = " + c2.strBuff);
}
} /* RUN RESULT
before clone,c1.str = initializeStr
before clone,c1.strBuff = initializeStrBuff
=================================
after clone,c1.str = initializeStr
after clone,c1.strBuff = initializeStrBuff change strBuff clone
=================================
after clone,c2.str = initi
after clone,c2.strBuff = initializeStrBuff change strBuff clone
*
*/
打印的结果可以看出,String类型的变量好象已经实现了深度clone,因为对c2.str的改动并没有影响到c1.str!难道Java把 Sring类看成了基本数据类型?其实不然,这里有一个小小的把戏,秘密就在于c2.str = c2.str.substring(0,5)这一语句!
实质上,在clone的时候c1.str与c2.str仍然是引用,而且都指向了同一个 String对象。但在执行c2.str = c2.str.substring(0,5)的时候,它作用相当于生成了一个新的String类型,然后又赋回给c2.str。这是因为String被 Sun公司的工程师写成了一个不可更改的类(immutable class),在所有String类中的函数都不能更改自身的值。
参考:http://www.blogjava.net/jerry-zhaoj/archive/2009/10/14/298141.html
Java学习笔记--对象克隆的更多相关文章
- Java学习笔记-对象与垃圾回收
Java存在垃圾回收机制,JVM会去回收垃圾,释放资源,而不是像C++一样有程序员去完成 垃圾回收机制的特点 垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源(例如数据库连接.网络IO等资源 ...
- Java学习笔记之---类和对象
Java学习笔记之---类和对象 (一)类 类是一个模板,它描述一类对象的行为和状态 例如:动物类是一个类,动物们都有属性:颜色,动物们都有行为:吃饭 public class Dog { Stri ...
- java学习笔记6--类的继承、Object类
接着前面的学习: java学习笔记5--类的方法 java学习笔记4--类与对象的基本概念(2) java学习笔记3--类与对象的基本概念(1) java学习笔记2--数据类型.数组 java学习笔记 ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- Java学习笔记(04)
Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...
- 0032 Java学习笔记-类加载机制-初步
JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...
- 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...
- 0028 Java学习笔记-面向对象-Lambda表达式
匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...
- 0025 Java学习笔记-面向对象-final修饰符、不可变类
final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...
随机推荐
- rsync同步目录及同步文件
最简单的只读同步工作. 一,服务端的配置 1,安装rsync(阿里云默认已有此程序) 略 2,生成文件rsyncd.conf,内容如下: #secrets file = /etc/rsyncd.sec ...
- 利用http实现文件的上传和下载
其他语言都比较方便,使用http上传.但是C++这样就差点,不过还好,Linux下有个curl的命令行工具,这是一个开源项目,底下有个子项目是libcurl,curl就是调用这个API实现的一系列ft ...
- USBSpirit(USB精灵)更新到1.2.300.105
USBSpirit(USB精灵)是CopyU!的内核引擎,CopyU!的主要功能均由该引擎提供,此次更新主要内容如下:(版本号:1.2.300.105) 1.[修复]:修复了几处引擎的资源泄露问题,提 ...
- JQuery 插件 - 弹窗:BlockUI
JQuery的弹窗插件,网上实在有很多做的比较好的,比如artDialog.layer,甚至EasyUI等等.这些在效果上做的非常好.但我觉得一个小小的弹窗提示,没有必要引用这些(其实是有点大材小用了 ...
- 使用 APPLY
原文地址:http://technet.microsoft.com/zh-cn/library/ms175156(v=SQL.105).aspx 使用 APPLY 运算符可以为实现查询操作的外部表表达 ...
- HDU_2018——母牛产小牛的问题,递推
Problem Description 有一头母牛,它每年年初生一头小母牛.每头小母牛从第四个年头开始,每年年初也生一头小母牛.请编程实现在第n年的时候,共有多少头母牛? Input 输入数据由多 ...
- spring 学习2
package com.asm; //...省略导入的相关类 public class MessageController implements Controller { public ModelAn ...
- servlet过滤器配置白名单、黑名单
1.web.xml配置 <filter> <description>过滤是否登陆</description> <filter-name>encoding ...
- 添加链接服务器 SQL SERVER
使用sql语句: exec sp_addlinkedserver @server='serverontest',@provider='sqloledb',@srvproduct='',@datasrc ...
- resin config 中文(resin.xml)
<!-- - Resin 3.1 配置文件. --> <resin xmlns="http://caucho.com/ns/resin" xmlns:resin= ...