ImageVerifierCode 换一换
格式:DOCX , 页数:15 ,大小:24.42KB ,
资源ID:11703520      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/11703520.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(JAVA中的指针引用及对象的clone.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

JAVA中的指针引用及对象的clone.docx

1、JAVA中的指针引用及对象的cloneJAVA中的指针,引用及对象的clone倪大鹏(userid),简介:Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,本文会试图澄清这一概念。并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象。本文会让你了解什么是影子clone与深度clone,认识它们的区别、优点及缺点。本文的标签:j2se标记本文!发布日期:2002 年 12 月 23 日级别:初级访问情况6481 次浏览建议:1(查看或添加评论)平均分 (共 14 个评分 )看到这个标题

2、,是不是有点困惑:Java语言明确说明取消了指针,因为指针往往是在带来方便的同时也是导致代码不安全的根源,同时也会使程序的变得非常复杂难以理解,滥用指针写成的代码不亚于使用早已臭名昭著的GOTO语句。Java放弃指针的概念绝对是极其明智的。但这只是在Java语言中没有明确的指针定义,实质上每一个new语句返回的都是一个指针的引用,只不过在大多时候Java中不用关心如何操作这个指针,更不用象在操作C的指针那样胆战心惊。唯一要多多关心的是在给函数传递对象的时候。如下例程:package reference;class Obj String str = init value; public Stri

3、ng toString() return str; public class ObjRef Obj aObj = new Obj(); int aInt = 11; public void changeObj(Obj inObj) inObj.str = changed value; public void changePri(int inInt) inInt = 22; public static void main(String args) ObjRef oRef = new ObjRef(); System.out.println(Before call changeObj() meth

4、od: + oRef.aObj); oRef.changeObj(oRef.aObj); System.out.println(After call changeObj() method: + oRef.aObj); System.out.println(=Print Primtive=); System.out.println(Before call changePri() method: + oRef.aInt); oRef.changePri(oRef.aInt); System.out.println(After call changePri() method: + oRef.aInt

5、); /* RUN RESULTBefore call changeObj() method: init valueAfter call changeObj() method: changed value=Print Primtive=Before call changePri() method: 11After call changePri() method: 11*/这段代码的主要部分调用了两个很相近的方法,changeObj()和changePri()。唯一不同的是它们一个把对象作为输入参数,另一个把Java中的基本类型int作为输入参数。并且在这两个函数体内部都对输入的参数进行了改动。

6、看似一样的方法,程序输出的结果却不太一样。changeObj()方法真正的把输入的参数改变了,而changePri()方法对输入的参数没有任何的改变。从这个例子知道Java对对象和基本的数据类型的处理是不一样的。和C语言一样,当把Java的基本数据类型(如int,char,double等)作为入口参数传给函数体的时候,传入的参数在函数体内部变成了局部变量,这个局部变量是输入参数的一个拷贝,所有的函数体内部的操作都是针对这个拷贝的操作,函数执行结束后,这个局部变量也就完成了它的使命,它影响不到作为输入参数的变量。这种方式的参数传递被称为值传递。而在Java中用对象的作为入口参数的传递则缺省为引用

7、传递,也就是说仅仅传递了对象的一个引用,这个引用的概念同C语言中的指针引用是一样的。当函数体内部对输入变量改变时,实质上就是在对这个对象的直接操作。除了在函数传值的时候是引用传递,在任何用向对象变量赋值的时候都是引用传递。如:package reference;class PassObj String str = init value;public class ObjPassValue public static void main(String args) PassObj objA = new PassObj(); PassObj objB = objA; objA.str = change

8、d in objA; System.out.println(Print objB.str value: + objB.str); /* RUN RESULTPrint objB.str value: changed in objA*/第一句是在内存中生成一个新的PassObj对象,然后把这个PassObj的引用赋给变量objA,第二句是把PassObj对象的引用又赋给了变量objB。此时objA和objB是两个完全一致的变量,以后任何对objA的改变都等同于对objB的改变。即使明白了Java语言中的指针概念也许还会不经意间犯下面的错误。Hashtable真的能存储对象吗?看一看下面的很简单的

9、代码,先是声明了一个Hashtable和StringBuffer对象,然后分四次把StriingBuffer对象放入到Hashtable表中,在每次放入之前都对这个StringBuffer对象append()了一些新的字符串:package reference;import java.util.*;public class HashtableAdd public static void main(String args) Hashtable ht = new Hashtable(); StringBuffer sb = new StringBuffer(); sb.append(abc,);

10、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 S

11、tringBufffer 1 from Hashtable: abc,get StringBufffer 2 from Hashtable: abc,def,get StringBufffer 3 from Hashtable: abc,def,mno,get StringBufffer 4 from Hashtable: abc,def,mno,xyz.那么你就要回过头再仔细看一看上一个问题了,把对象时作为入口参数传给函数,实质上是传递了对象的引用,向Hashtable传递StringBuffer对象也是只传递了这个StringBuffer对象的引用!每一次向Hashtable表中put一次

12、StringBuffer,并没有生成新的StringBuffer对象,只是在Hashtable表中又放入了一个指向同一StringBuffer对象的引用而已。对Hashtable表存储的任何一个StringBuffer对象(更确切的说应该是对象的引用)的改动,实际上都是对同一个StringBuffer的改动。所以Hashtable并不能真正存储能对象,而只能存储对象的引用。也应该知道这条原则对与Hashtable相似的Vector, List, Map, Set等都是一样的。上面的例程的实际输出的结果是:/* RUN RESULTget StringBufffer 1 from Hashtab

13、le: 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.*/类,对象与引用Java最基本的概念就是类,类包括函数和变量。如果想要应用类,就要把类生成对象,这个过程被称作类的实例化。有几种方法把类实例化成对象,最常用的就是用new操作符。类实例化成对象后,就意味着要在内存中占据一块空间存放实例。想要对这块

14、空间操作就要应用到对象的引用。引用在Java语言中的体现就是变量,而变量的类型就是这个引用的对象。虽然在语法上可以在生成一个对象后直接调用该对象的函数或变量,如:new String(Hello NDP).substring(0,3)/RETURN RESULT: Hel但由于没有相应的引用,对这个对象的使用也只能局限这条语句中了。1. 产生:引用总是在把对象作参数传递的过程中自动发生,不需要人为的产生,也不能人为的控制引用的产生。这个传递包括把对象作为函数的入口参数的情况,也包括用进行对象赋值的时候。2. 范围:只有局部的引用,没有局部的对象。引用在Java语言的体现就是变量,而变量在Jav

15、a语言中是有范围的,可以是局部的,也可以是全局的。3. 生存期:程序只能控制引用的生存周期。对象的生存期是由Java控制。用new Object()语句生成一个新的对象,是在计算机的内存中声明一块区域存储对象,只有Java的垃圾收集器才能决定在适当的时候回收对象占用的内存。4. 没有办法阻止对引用的改动。什么是clone?在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不

16、能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。怎样应用clone()方法?一个很典型的调用clone()代码如下:class CloneClass implements C

17、loneable 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。另一个值得请注意的是重载

18、了clone()方法。最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法。下面再详细的解释一下这几点。应该说第三点是最重要的,仔细观察一下Object类的clone()一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能。对于第二点,也要观察Object类中的clo

19、ne()还是一个protected属性的方法。这也意味着如果要应用clone()方法,必须继承Object类,在Java中所有的类是缺省继承Object类的,也就不用关心这点了。然后重载clone()方法。还有一点要考虑的是为了让其它类能调用这个clone类的clone()方法,重载之后要把clone()方法的属性设置为public。那么clone类为什么还要实现Cloneable接口呢?稍微注意一下,Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Obj

20、ect的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。以上是clone的最基本的步骤,想要完成一个成功的clone,还要了解什么是影子clone和深度clone。什么是影子clone?下面的例子包含三个类UnCloneA,CloneB,CloneMain。CloneB类包含了一个UnCloneA的实例和一个int类型变量,并且重载clone()方法。CloneMain类初始化UnCloneA类的一个实例b1,然后调用clone()方法生成了一个b1的拷贝b2。最后考察一下b

21、1和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 = nul

22、l; 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.u

23、nCA); 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(a

24、fter clone,b2.unCA = + b2.unCA); /* RUN RESULT:before clone,b1.aInt = 11before clone,b1.unCA = 111=after clone,b1.aInt = 11after clone,b1.unCA = 222=after clone,b2.aInt = 22after clone,b2.unCA = 222*/输出的结果说明int类型的变量aInt和UnCloneA的实例对象unCA的clone结果不一致,int类型是真正的被clone了,因为改变了b2中的aInt变量,对b1的aInt没有产生影响,也就是

25、说,b2.aInt与b1.aInt已经占据了不同的内存空间,b2.aInt是b1.aInt的一个真正拷贝。相反,对b2.unCA的改变同时改变了b1.unCA,很明显,b2.unCA和b1.unCA是仅仅指向同一个对象的不同引用!从中可以看出,调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。大多时候,这种clone的结果往往不是我们所希望的结果,这种

26、clone也被称为影子clone。要想让b2.unCA指向与b2.unCA不同的对象,而且b2.unCA中还要包含b1.unCA中的信息作为初始信息,就要实现深度clone。怎么进行深度clone?把上面的例子改成深度clone很简单,需要两个改变:一是让UnCloneA类也实现和CloneB类一样的clone功能(实现Cloneable接口,重载clone()方法)。二是在CloneB的clone()方法中加入一句o.unCA = (UnCloneA)unCA.clone();程序如下:package clone.ext;class UnCloneA implements Cloneable

27、 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

28、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) Clon

29、eB 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 = 11before clone,b1.unCA = 111=

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1