在讨论上面问题之前,我们先来看看函数的实参为形参赋值时,传递的到底是什么东西?实际上实参赋值给形参时,是将自己的一份拷贝传递到函数内部。这就不难理解,不管是“传址”还是“传值”,本质上都是传值,但传递值的类型是不相同的。对于普通基本类型,就是这个数值的拷贝,所以函数内部对其进行修改,不会影响传递的实参的值;而对于指针来说,函数内部对其修改,影响的是指针指向的那片内存区域,但实参指针的值也没有发生变化(指针还是指向那片内存区域)。
java中不存在指针,但实际上java中对象的引用(句柄)就是指针。 由此可见,java中所谓的“传址”其实也是一种传值(或者说java中没有传址)。我们给方法“传址”时实际上是传递的是实参的地址的一个拷贝,它跟我们的实参(这里把他们暂时都理解为指针)所指向的地址虽然相同,但他们却是两个不同的实体。所以当我们在方法中对形参进行重新赋值时,改变的只是形参所指向的地址,而实参所指向的地址没有被改变,所以其内容不变。
上面两段是整体总结,下面我们来看一下设计到的知识点:
1.Java中的参数传递有传值和传址两种;
2.基本类型和String型作为参数时,为传值方式,只把值传入方法,不管在方法中怎么处理这个参数,原值不变;
3.其他引用类型作为参数时,为传址方式,将指向内存中的地址传入方法,方法中此内存地址中的值发生变化时,原值也会改变;
4.例外:
(1)如果引用类型的对象通过传址方式将其指向内存中的地址传入方法后,方法中使用new关键字重新给参数赋值时,会在内存中重新开辟空间,参数指向新的内存空间,此时参数和原对象指向的就不是同一个地址了,参数值的变化不会改变原值;
(2)String型是引用类型,但是String型作为参数,是传值方式,可以通过以下两种方式来理解:
<1>String本质上是基本类型的char[],基本类型作为参数时,为传值方式;
<2> 字符串在内存中是存储在堆中的一个常量,String对象指向内存中这个常量的地址,通过传址方式将地址传入方法后,方法中如果通过字符串给参数赋值,则会重新在堆中创建一个字符串常量,并指向这个地址,原值依然指向原来的字符串常量地址,参数值的变化不会改变原值,如果通过new关键字给参数赋值,参见 (1)中的解释。
下面为大家一一详解:
一、别名的问题
“别名”意味着多个句柄都试图指向同一个对象,若有人向那个对象写入一点什么东西,就会产生别名问题。如果其他句柄所有者不希望那个对象改变,那就要失望了。在编写程序的过程中,我们可以避免多个句柄指向同一个对象,但是一旦准备将句柄作为一个自变量或参数传递,别名问题就会自动重现。下面就是讲如何克服这个问题。
二、制作本地副本
在讲制作本地副本之前我需要强调一下,我们无须在传值和传址上纠结,如果知道他们的区别后,即可以灵活的运用,这个在实际的编程的过程中为我们带来的好处远远大于弊端。只有我们明确的需要,句柄传递后的使用过程中,不能对外面的对象产生影响,才考虑下面的方式。
对象的克隆就是我们这里要讲解的,这也是本地副本最常见的一种用途。只需要简单的使用clone()方法即可。这个方法在基础类object中定义成了protected模式,但在希望克隆的任何衍生类中,必须将其覆盖为public模式。
采用clone()方法复制对象,我们通常把这种方法叫做“简单复制”或者“浅层复制”。因为他只是复制了一个对象的表面部分。实际对象除了包含这个表面以外,还包括句柄指向的所有对象,以及那些对象又指向的其他所有对象。由此类推,这便是对象网或对象关系网。如果可以复制下所有这张网,便叫做全面复制。
三、如何实现clone()
为了使一个对象的克隆能力成功圆满,需要实现Cloneable接口。这个接口使人感觉奇怪,因为他是一个空的! interface Cloneable{}
之所以要实现这个空接口,显然不是因为准备上塑造成一个Cloneable,以及调用其他的某个方法。而是为了实现一种标记的功能。首先,可能有一个上塑造句柄指向一个基础类型,而且不知道他是否真的能克隆那个对象,在这种情况下,可用instanceof调查句柄是否确实实现克隆功能;其次,我们可能不愿意所有对象类型都能克隆,所以Object.clone()会验证一个类是否真的实现了Cloneable接口,判断类是否真的可以Clone()。
在Object.clone()正式开始操作之前,首先会检查一个类是否实现Cloneable。如果已经实现,则Object.clone()会检查原先的对象有多大,在为新对象腾出足够大的内存,将所有二进制位从原来的对象复制到新的对象,这就是“按位复制”。
注意:java对“是否等价”的测试并不对所对比对象的内部进行检查,从而核实他们的值是否相同。==和!=运算符知识简单的对比句柄内容。若句柄的地址相同,就认为句柄指向同样的对象,所以认为他们是“等价”的。所以运算符真正检测的是“由于别名问题,句柄是否指向同一个对象?”。
在克隆的过程中,第一个部分都应该调用super.clone()。这样做的目的就是希望复制整条对象链,达到深层复制的目的。试图深层复制合成对象时会遇到一个问题。必须假定成员对象中的 clone()方法也能依次对自己的句柄进行深层复制,以此类推。这使我们的操作变得复杂。为了能正常实现深层复制,必须对所有类中的代码进行控制,或者至少全面掌握深层复制中需要涉及的类,确保它们自己的深层复制能正确进行。
四、再论String类
String 类的对象被设计成“不可变”。若查阅联机文档中关于 String 类的内容,就会发现类中能够修改 String 的每个方法实际都创建和返回了一个崭新的 String 对象,新对象里包含了修改过的信息——原来的 String 是原封未动的。
由于 String 对象是不可变的,所以能够根据情况对一个特定的 String 进行多次别名处理。因为它是只读的,所以一个句柄不可能会改变一些会影响其他句柄的东西。因此,只读对象可以很好地解决别名问题。通过修改产生对象的一个崭新版本,似乎可以解决修改对象时的所有问题,就象 String 那样。但对某些操作来讲,这种方法的效率并不高。一个典型的例子便是为 String 对象覆盖的运算符“+”。为了解决这个问题,java又引入了StringBuffer(可变字符串)。无须像string对象那样,在变动的过程中,需要差生很多垃圾中间字符串对象。
相关推荐
java中clone的详细用法,分浅拷贝和深拷贝,并分别有详细的实例介绍。从原理分析。
对java clone的一些讲解,以及我个人的一些心得等等,
Java中的clone方法详解_动力节点Java学院,动力节点口口相传的Java黄埔军校
java中clone的详细用法,分浅拷贝和深拷贝,并分别有详细的实例介绍。从原理分析。
现在Clone已经不是一个新鲜词语了,伴随着“多莉”的产生这个词语确实很“火”过一阵子,在java中也有这么一个概念,它可以让我们很方便的“制造”出一个对象的副本来,下面来具体看看java中的Clone机制是如何工作的...
详细的描述了Java中 clone方法使用
用 Java 语言编写的 初学者可学习的clone()
Java深浅clone测试代码 流拷贝 Cloneable
详细描述了java基础中的数组与方法的应用技术,以及面向对象的过程思想,有助于java初学者的入门学习。
主要介绍了JAVA对象clone方法代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
java code clone
提供了20道高难度的Java Object类面试题及详细答案解析,涵盖了equals()、hashCode()、toString()、clone()、finalize()等方法的重写和应用,以及对象的比较、克隆、标识哈希码等概念。适合准备Java面试的开发者深入...
clone的用法 希望有帮助,仅供参考 通过例子的分析,可以对克隆的方法有所深入了解
主要介绍了Java中的数组复制(clone与arraycopy)代码详解,本文并未全部介绍数组复制的几种方式,仅对clone和copy的相关内容进行了解析,具有一定参考价值,需要的朋友可以了解下。
主要介绍了Java利用序列化实现对象深度clone的方法,实例分析了java序列化及对象克隆的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
darts-clone-java 用Java编写的Double-ARray Trie System克隆。 该库基于称为“快速高效”库的 。入门设置要使用Maven添加依赖项,请使用以下命令: < dependency> < groupId>...
Clone 属性的相关内容 Clone 属性的相关内容
10道Java面试题及详细解答 1.Java中多态的实现方式是什么? 2. Java中如何避免空指针异常? 3. Java中垃圾回收的机制是什么?...8. Java中如何防止对象的clone? 9. Java中什么是泛型? 10. Java中如何实现单例模式?
clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。下面通过本文给大家介绍java中的clone方法,感兴趣的朋友一起看看吧