java引用与参数传递

来源:互联网 发布:道路横断面效果图软件 编辑:程序博客网 时间:2024/06/06 04:54
网上都在讲参数传递是一种拷贝,拷贝的变化不会影响原值,当然这是对的。还有一种说法,叫JAVA里只有一种参数传递方式,值传递,这也是对的。但是我总觉得这些说法容易让新人犯迷糊,因为有一个很常见的现象,很多时候拷贝也能改变原始对象的属性。所以有些人又把参数传递跟基本类型传递分开去解释这个问题,拆开显然是不对的。

先来看这段非常简单的代码:
package main;public class Main {public static void main(String[] args) {TestObject a = new TestObject();TestObject b = new TestObject();System.out.println(a);  //打印结果为:main.TestObject@15db9742System.out.println(b);  //打印结果为:main.TestObject@6d06d69cSystem.out.println(a.equals(b)); //打印结果为:falseSystem.out.println(b.testString);//打印结果为:no.0a = b;System.out.println(a); //打印结果为 :main.TestObject@6d06d69cSystem.out.println(b); //打印结果为 :main.TestObject@6d06d69cSystem.out.println(a.equals(b)); //打印结果为:truea.testString = "no.1";System.out.println(b.testString); //打印结果为:no.1}}package main;public class TestObject {public String testString = "no.0";}
使用 TestObject a = new TestObject(); 的时候,这个a,事实上就是一个引用(可以理解为C语言中的指针,是一个指向堆中真实对象的地址)。
所以,很自然,如果a 和 b 两个引用相同(指向同一个对象),运行a.testString = "no.1"之后,通过b获得的对象也改变了。

接下来我们看参数传递:
package main;public class Main {public static void main(String[] args) {// TODO 自动生成的方法存根TestObject a = new TestObject();TestObject b = new TestObject();System.out.println(a);  //打印结果为:main.TestObject@15db9742System.out.println(b);  //打印结果为:main.TestObject@6d06d69cSystem.out.println(a.equals(b)); //打印结果为:falseSystem.out.println(b.testString);//打印结果为:no.0a = b;System.out.println(a); //打印结果为 :main.TestObject@6d06d69cSystem.out.println(b); //打印结果为 :main.TestObject@6d06d69cSystem.out.println(a.equals(b)); //打印结果为:truea.testString = "no.1";System.out.println(b.testString); //打印结果为:no.1//------------------------------------------------------System.out.println(a);//打印结果为 :main.TestObject@6d06d69cchange(a);            //打印结果为 :main.TestObject@6d06d69cSystem.out.println(b.testString);//打印结果为 :no.2}private static void change(TestObject s) {// TODO 自动生成的方法存根System.out.println(s);s.testString = "no.2";}}package main;public class TestObject {public String testString = "no.0";}
我们都知道,s是a的拷贝。现在s变了,a也变了,这是为什么?其实,s是一个引用,s,a,b指向了同一个对象。所以我们通过s改变这个对象的时候,通过a,b,s获取到的对象都会改变。所以,JAVA里面通过参数传递,去改变对象是“可行的”,当然这个可行要打一个引号,因为我们不推荐这么做。

再来看这样的参数传递:
package main;public class Main {public static void main(String[] args) {// TODO 自动生成的方法存根TestObject a = new TestObject();TestObject b = new TestObject();System.out.println(a);  //打印结果为:main.TestObject@15db9742System.out.println(b);  //打印结果为:main.TestObject@6d06d69cSystem.out.println(a.equals(b)); //打印结果为:falseSystem.out.println(b.testString);//打印结果为:no.0a = b;System.out.println(a); //打印结果为 :main.TestObject@6d06d69cSystem.out.println(b); //打印结果为 :main.TestObject@6d06d69cSystem.out.println(a.equals(b)); //打印结果为:truea.testString = "no.1";System.out.println(b.testString); //打印结果为:no.1//------------------------------------------------------System.out.println(a);//打印结果为 :main.TestObject@6d06d69cchange(a);            //打印结果为 :main.TestObject@6d06d69cSystem.out.println(b.testString);//打印结果为 :no.2//------------------------------------------------------System.out.println(a); //打印结果为 :main.TestObject@6d06d69cchange2(a);System.out.println(a); //打印结果为 :main.TestObject@6d06d69cSystem.out.println(b.testString);//打印结果为 :no.3}private static void change(TestObject s) {System.out.println(s); //打印结果为 :main.TestObject@6d06d69cs.testString = "no.2";}private static void change2(TestObject s) {System.out.println(s);//打印结果为 :main.TestObject@6d06d69cs.testString = "no.3";TestObject c = new TestObject();s = c;System.out.println(s);//打印结果为 :main.TestObject@7852e922s.testString = "no.4";System.out.println(s.testString);//打印结果为 :no.4}}package main;public class TestObject {public String testString = "no.0";}
看结果,最终,s改变了吗?s改变了,s变成了main.TestObject@7852e922。那a变了吗?a没有变,a还是main.TestObject@6d06d69c。s是不是拷贝?当然是拷贝,s变了,但是a没有改变。
其实,当s是main.TestObject@6d06d69c的时候,通过s.testString = "no.3";改变s指向的对象的属性的时候,a,b,s由于指向了同一个对象,所以看起来a也变了。当s变为main.TestObject@7852e922的时候,由于s和a,b已经不再指向同一个对象了,所以,s变了,a,b指向的对象不会有改变。

基本类型就不讲了,网上都是讲基本类型的参数传递拷贝不变性的。总结一下:
1.参数传递是拷贝,没问题。
2.JAVA里面的传递只有一种,值传递,没问题。
3.通过引用去改变堆中对象属性的时候,只要通过一个引用改变指向对象的属性,指向同一个对象的所有引用,看起来都会变。所以有时候看起来,方法内和方法外的引用一起改变了。

所以,在JAVA编写过程中,尽量不要使用public属性,而是要使用get,set方法。因为如果在你的函数中,通过引用改变了一些类的属性,就会使得程序变得混乱而不可读。为了后期维护的方便,请不要直接在调用的方法中通过引用改变引用指向对象的属性。

于此同时,克隆也是一样的道理,浅克隆中,克隆的对象如果存在引用,将会使得调用出现异常,因为两个克隆对象中如果拥有同一个引用属性,当你调用这个引用的时候,就会出现克隆的属性一起改变的情况,克隆的两个对象确实是两个对象,但是他们的引用属性却指向了同一个对象。如果他们指向的对象无法被修改,那么浅克隆完全是符合要求的。但是如果要通过这个引用去修改指向的那个对象,这其实是一定程度上违反了JAVA的基本原则(对扩展开放,对修改关闭)。
原创粉丝点击