- 浏览: 3558046 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (1491)
- Hibernate (28)
- spring (37)
- struts2 (19)
- jsp (12)
- servlet (2)
- mysql (24)
- tomcat (3)
- weblogic (1)
- ajax (36)
- jquery (47)
- html (43)
- JS (32)
- ibatis (0)
- DWR (3)
- EXTJS (43)
- Linux (15)
- Maven (3)
- python (8)
- 其他 (8)
- JAVASE (6)
- java javase string (0)
- JAVA 语法 (3)
- juddiv3 (15)
- Mule (1)
- jquery easyui (2)
- mule esb (1)
- java (644)
- log4j (4)
- weka (12)
- android (257)
- web services (4)
- PHP (1)
- 算法 (18)
- 数据结构 算法 (7)
- 数据挖掘 (4)
- 期刊 (6)
- 面试 (5)
- C++ (1)
- 论文 (10)
- 工作 (1)
- 数据结构 (6)
- JAVA配置 (1)
- JAVA垃圾回收 (2)
- SVM (13)
- web st (1)
- jvm (7)
- weka libsvm (1)
- weka屈伟 (1)
- job (2)
- 排序 算法 面试 (3)
- spss (2)
- 搜索引擎 (6)
- java 爬虫 (6)
- 分布式 (1)
- data ming (1)
- eclipse (6)
- 正则表达式 (1)
- 分词器 (2)
- 张孝祥 (1)
- solr (3)
- nutch (1)
- 爬虫 (4)
- lucene (3)
- 狗日的腾讯 (1)
- 我的收藏网址 (13)
- 网络 (1)
- java 数据结构 (22)
- ACM (7)
- jboss (0)
- 大纸 (10)
- maven2 (0)
- elipse (0)
- SVN使用 (2)
- office (1)
- .net (14)
- extjs4 (2)
- zhaopin (0)
- C (2)
- spring mvc (5)
- JPA (9)
- iphone (3)
- css (3)
- 前端框架 (2)
- jui (1)
- dwz (1)
- joomla (1)
- im (1)
- web (2)
- 1 (0)
- 移动UI (1)
- java (1)
- jsoup (1)
- 管理模板 (2)
- javajava (1)
- kali (7)
- 单片机 (1)
- 嵌入式 (1)
- mybatis (2)
- layui (7)
- asp (12)
- asp.net (1)
- sql (1)
- c# (4)
- andorid (1)
- 地价 (1)
- yihuo (1)
- oracle (1)
最新评论
-
endual:
https://blog.csdn.net/chenxbxh2 ...
IE6 bug -
ice86rain:
你好,ES跑起来了吗?我的在tomcat启动时卡在这里Hibe ...
ES架构技术介绍 -
TopLongMan:
...
java public ,protect,friendly,private的方法权限(转) -
贝塔ZQ:
java实现操作word中的表格内容,用插件实现的话,可以试试 ...
java 读取 doc poi读取word中的表格(转) -
ysj570440569:
Maven多模块spring + springMVC + JP ...
Spring+SpringMVC+JPA
3.java 中的 clone
3.1. 什么是 "clone" ?
在实际编程过程中,我们常常要遇到这种情况:有一个对象
A,在某一时刻
A中已经包含了一些有效值,此时可能会需要一个和
A完全相同新对象
B,并且此后对
B 任何改动都不会影响到
A中的值,也就是说,
A与
B是两个独立的对象,但
B的初始值是由
A对象确定的。在
Java语言中,用简单的赋值语句是不能满足这种需
求的。要满足这种需求虽然有很多途径,但实现
clone()方法是其中最简单,也是最高效的手段。
Java的所有类都默认继承
java.lang.Object类,在
java.lang.Object类中有一个方法
clone()。
JDK API的说明文档解释这个方法将返回
Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用
new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。
3.2. 怎样应用clone() 方法?
一个很典型的调用
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;
}
}
有三个值得注意的地方,
一是希望能实现 clone功能的 CloneClass类实现了 Cloneable接口,这个接口属于 java.lang包, java.lang包已经被缺省的导入类中,所以不需要写成 java.lang.Cloneable。
另一个值得请注意的是重载了 clone()方法。最 后在 clone()方法中调用了 super.clone(),这也意味着无论 clone类的继承结构是什么样的, super.clone()直接或间接调 用了 java.lang.Object类的 clone()方法。下面再详细的解释一下这几点。
应该说第三点是最重要的,仔细观察一下 Object类的 clone()一个 native方法, native方法的效率一般来说都是远高于 java中的非 native方法。这也解释了为什么要用 Object中 clone()方法而不是先 new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了 clone功能。对于第二点,也要观察 Object类中的 clone()还是一个 protected属性的方法。这也意味着如果要应用 clone()方 法,必须继承 Object类,在 Java中所有的类是缺省继承 Object类的,也就不用关心这点了。然后重载 clone()方法。还有一点要考虑的是为 了让其它类能调用这个 clone类的 clone()方法,重载之后要把 clone()方法的属性设置为 public。 、
那么 clone类为什么还要实现 Cloneable接口呢?稍微注意一下, Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且 这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方 法(也就是调用了 super.Clone() 方法),那么 Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。
以上是 clone的最基本的步骤,想要完成一个成功的 clone,还要了解什么是 "影子 clone"和 "深度 clone"。
3.3 什么是影子clone ?
下面的例子包含三个类
UnCloneA,
CloneB,
CloneMain。
CloneB类包含了一个
UnCloneA的实例和一个
int类型变量,并且
重载
clone()方法。
CloneMain类初始化
UnCloneA类的一个实例
b1,然后调用
clone()方法生成了一个
b1的拷贝
b2。最后考察
一下
b1和
b2的输出:
package clone;
class UnCloneA {
private int i;
public UnCloneA(int ii) { i = ii; }
public void doublevalue() { i *= 2; }
public String toString() {
return Integer.toString(i);
}
}
class CloneB implements Cloneable{
public int aInt;
public UnCloneA unCA = new UnCloneA(111);
public Object clone(){
CloneB o = null;
try{
o = (CloneB)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
}
public class CloneMain {
public static void main(String[] a){
CloneB b1 = new CloneB();
b1.aInt = 11;
System.out.println("before clone,b1.aInt = "+ b1.aInt);
System.out.println("before clone,b1.unCA = "+ b1.unCA);
CloneB b2 = (CloneB)b1.clone();
b2.aInt = 22;
b2.unCA.doublevalue();
System.out.println("=================================");
System.out.println("after clone,b1.aInt = "+ b1.aInt);
System.out.println("after clone,b1.unCA = "+ b1.unCA);
System.out.println("=================================");
System.out.println("after clone,b2.aInt = "+ b2.aInt);
System.out.println("after clone,b2.unCA = "+ b2.unCA);
}
}
/** RUN RESULT:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 222
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222
*/
输出的结果说明
int类型的变量
aInt和
UnCloneA的实例对象
unCA的
clone结果不一致,
int类型是真正的被
clone了,因为改变了
b2中的
aInt变量,对
b1的
aInt没有产生影响,也就是说,
b2.aInt与
b1.aInt已经占据了不同的内存空间,
b2.aInt是
b1.aInt的一个真正拷贝。相反,对
b2.unCA的改变同时改变了
b1.unCA,很明显,
b2.unCA和
b1.unCA是仅仅指向同一个对象的
不同引用!从中可以看出,调用
Object类中
clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内
容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致
clone后的非基本类型变量和原始对
象中相应的变量指向的是同一个对象。
大多时候,这种
clone的结果往往不是我们所希望的结果,这种
clone也被称为
"影子
clone"。要想让
b2.unCA指向与
b2.unCA不同的对象,而且
b2.unCA中还要包含
b1.unCA中的信息作为初始信息,就要实现深度
clone。
3.4 怎么进行深度clone ?
把上面的例子改成深度
clone很简单,需要两个改变:一是让
UnCloneA类也实现和
CloneB类一样的
clone功能(实现
Cloneable接
口,重载
clone()方法)。二是在
CloneB的
clone()方法中加入一句
o.unCA = (UnCloneA)unCA.clone();
程序如下:
package clone.ext;
class UnCloneA implements Cloneable{
private int i;
public UnCloneA(int ii) { i = ii; }
public void doublevalue() { i *= 2; }
public String toString() {
return Integer.toString(i);
}
public Object clone(){
UnCloneA o = null;
try{
o = (UnCloneA)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
}
class CloneB implements Cloneable{
public int aInt;
public UnCloneA unCA = new UnCloneA(111);
public Object clone(){
CloneB o = null;
try{
o = (CloneB)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
o.unCA = (UnCloneA)unCA.clone();
return o;
}
}
public class CloneMain {
public static void main(String[] a){
CloneB b1 = new CloneB();
b1.aInt = 11;
System.out.println("before clone,b1.aInt = "+ b1.aInt);
System.out.println("before clone,b1.unCA = "+ b1.unCA);
CloneB b2 = (CloneB)b1.clone();
b2.aInt = 22;
b2.unCA.doublevalue();
System.out.println("=================================");
System.out.println("after clone,b1.aInt = "+ b1.aInt);
System.out.println("after clone,b1.unCA = "+ b1.unCA);
System.out.println("=================================");
System.out.println("after clone,b2.aInt = "+ b2.aInt);
System.out.println("after clone,b2.unCA = "+ b2.unCA);
}
}
/** RUN RESULT:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 111
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222
*/
可以看出,现在
b2.unCA的改变对
b1.unCA没有产生影响。此时
b1.unCA与
b2.unCA指向了两个不同的
UnCloneA实例,而且在
CloneB b2 = (CloneB)b1.clone();调用的那一刻
b1和
b2拥有相同的值,在这里,
b1.i = b2.i = 11。
要知道不是所有的类都能实现深度
clone的。例如,如果把上面的
CloneB类中的
UnCloneA类型变量改成
StringBuffer类型,看一下
JDK API中关于
StringBuffer的说明,
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对象,
Integer,
Double等是一个例外,它
clone后的表现好象也实现了深度
clone,虽然这只是一个假象,但却大大方便了我们的编程。
Clone中
String和
StringBuffer的区别
应该说明的是,这里不是着重说明
String和
StringBuffer的区别,但从这个例子里也能看出
String类的一些与众不同的地方。
下面的例子中包括两个类,
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类中的函数都不能更改自身的值。下面给出很简单的一个例子:
package clone; public class StrTest { public static void main(String[]
args) { String str1 = "This is a test for immutable"; String str2 =
str1.substring(0,8); System.out.println("print str1 : " + str1);
System.out.println("print str2 : " + str2); } } /* RUN RESULT print
str1 : This is a test for immutable print str2 : This is */
例子中,虽然
str1调用了
substring()方法,但
str1的值并没有改变。类似的,
String类中的其它方法也是如此。当然如果我们把最上面的例子中的这两条语句
c2.str = c2.str.substring(0,5);
c2.strBuff = c2.strBuff.append(" change strBuff clone");
改成下面这样:
c2.str.substring(0,5);
c2.strBuff.append(" change strBuff clone");
去掉了重新赋值的过程,
c2.str也就不能有变化了,我们的把戏也就露馅了。但在编程过程中只调用
c2.str.substring(0,5); 语句是没有任何意义的。
应该知道的是在
Java中所有的基本数据类型都有一个相对应的类,象
Integer类对应
int类型,
Double类对应
double类型等等,这些类也
与
String类相同,都是不可以改变的类。也就是说,这些的类中的所有方法都是不能改变其自身的值的。这也让我们在编
clone类的时候有了一个更多的
选择。同时我们也可以把自己的类编成不可更改的类。
发表评论
-
snmp
2020-04-13 11:07 428https://www.iteye.com/blog/zhan ... -
snmp
2020-04-10 21:33 566https://blog.csdn.net/qq_333141 ... -
服务器监控软件
2019-12-31 11:07 513[ERROR] org.hyperic.sigar.Sigar ... -
多数据源
2019-12-23 22:09 455https://gitee.com/baomidou/dyna ... -
mybatis多数据源
2019-12-23 18:09 449https://blog.csdn.net/qq_288042 ... -
springboot ueditor
2019-12-17 18:26 381https://blog.csdn.net/u01216982 ... -
java支持多数据源
2019-12-13 15:59 456spxcms是否支持多数据源 ... -
java日志
2019-12-10 12:01 297https://blog.csdn.net/peng_wei_ ... -
spring 多数据源
2019-12-06 09:55 432https://www.jb51.net/article/10 ... -
idea
2019-12-04 17:13 411https://blog.csdn.net/dengachao ... -
手机大屏
2019-11-30 16:02 349http://demo.demohuo.top/modals/ ... -
quarz配置
2019-11-08 11:48 462https://blog.csdn.net/BryantLmm ... -
mysql同步
2019-11-06 12:20 352https://blog.csdn.net/baidu_418 ... -
nginx配置多个服务
2019-11-04 20:35 775https://blog.csdn.net/everljs/a ... -
h5 加壳
2019-11-04 16:05 630https://jingyan.baidu.com/artic ... -
jeui 前端框架
2019-10-22 14:30 1208http://www.jemui.com/demo/ http ... -
jeui 维护
2019-10-22 14:29 2http://www.jemui.com/demo/ htt ... -
jeui 维护
2019-10-22 14:29 2http://www.jemui.com/demo/ -
jeui 维护
2019-10-22 14:29 2http://www.jemui.com/demo/ -
jeui 维护
2019-10-22 14:29 2http://www.jemui.com/demo/
相关推荐
Java中的clone方法详解_动力节点Java学院,动力节点口口相传的Java黄埔军校
### Java中的`clone`方法详解:浅拷贝与深拷贝 #### 一、引言 在Java中,`clone`方法提供了一种快速复制对象的方式。它属于`Object`类的一部分,但需要显式地在子类中声明并实现`Cloneable`接口才能正常使用。本文...
Java中的克隆(Clone)机制是一种创建对象副本的方法,它允许程序员复制一个对象的状态,而不会影响原始对象。克隆在编程中常用于创建对象的独立副本,使得新副本与原对象之间相互独立,对其中一个对象的修改不会...
Java中的clone方法详解 在Java语言中,clone方法是一个非常重要的概念,它允许对象被复制,从而创造出一个新的对象。下面我们将详细介绍Java中的clone方法,并讨论它的实现机制和应用场景。 什么是clone方法 ...
Java中的`clone`方法是Java语言提供的一种复制对象的机制,它允许创建一个现有对象的副本,这个副本具有与原始对象相同的状态,但它们是独立的实体,对其中一个对象的修改不会影响另一个。`clone`方法是Java `Object...
Java 中 clone() 的使用方法 Java 中的 clone() 方法是对象的复制方法,其主要作用是创建一个与原对象相同的新对象。下面将详细介绍 Java 中 clone() 方法的使用方法。 什么是 clone() 方法? clone() 方法是 ...
Java中的数组复制(clone与arraycopy)代码详解 Java中的数组复制是通过clone和arraycopy两个方法来实现的。clone方法是Object类的protected方法,用于创建对象的副本,而arraycopy是System类的静态方法,用于将一...
在Java中,克隆主要涉及到`Object`类中的`clone()`方法,以及两种不同的克隆类型:浅克隆和深克隆。 一、克隆的原理与应用 `clone()`方法的工作原理是在堆上创建一个新的对象,这个新对象的内存分配与源对象相同,...
Java中的`Object`类是所有类的根,这意味着无论你定义的任何自定义类,如果没有显式地声明继承自其他类,那么它们都会隐式地继承`Object`类。`Object`类提供了基本的方法,这些方法是所有Java对象共有的。下面我们将...
首先,`clone()`方法是Java中的一个内置功能,它允许我们创建一个对象的浅拷贝。对于基本类型的数组,`clone()`会创建一个新的数组,然后复制原数组的所有元素到新数组中。然而,对于引用类型的数组(如对象数组),...
这个压缩包文件"详解Java中Object 类的使用.rar"包含了对Java中Object类的深入探讨,通过阅读其中的"详解Java中Object 类的使用.txt"文本,我们可以了解到关于Object类的一些关键知识点。 1. **对象的创建与类型...
【Java原型模式详解】 原型模式(Prototype Pattern)是一种创建型设计模式,它的主要目标是通过复制现有的对象来创建新对象,以减少重复的构造过程,提高性能。在Java中,原型模式通常涉及到对象的克隆操作,即...
在Java中,浅拷贝可以通过实现`Cloneable`接口并重写`clone()`方法来完成。以下是一个简单的浅拷贝示例: ```java public class Person implements Cloneable { private String name; private int age; private ...
在Java编程语言中,List接口是集合框架的重要组成部分,它属于Collection接口的子接口。List接口主要用于存储有序的、可重复的元素序列。与Set不同,List允许元素重复,并且保持插入时的顺序。本篇将详细介绍Java中...
### Java 高级特性详解 #### 一、`hashCode` `hashCode` 方法是 `Object` 类中的一个方法,用于返回对象的哈希码值。在 Java 中,哈希码经常被用于实现散列表(如 `HashMap` 和 `HashSet`)。为了确保散列表的正确...
Java 函数习惯用法详解 Java 函数习惯用法是 Java 编程中非常重要的一部分,它直接影响着程序的效率和可读性。下面将对 Java 函数习惯用法进行详细的解释和总结。 一、equals() 函数 equals() 函数是 Java 中最...
Java中的`clone()`方法是`Object`类的一个成员方法,用于实现对象的复制。然而,使用`clone()`默认只会进行浅复制。要实现深复制,我们需要进行以下操作: - 在派生类中重写`clone()`方法,并声明为`public`,...