你真的真的理解Java的按引用传递吗?
2015-11-05 17:01
435 查看
今天在博客上看到《你真的理解Java的按引用传递吗?》这篇博文,就好奇进去看了一下,结果发现,其实说了半天,并没有特别清楚的解释。尤其是对于传递String类型时的例子时,有点发蒙。
接触Java也有好几年了,本来以为这些简单自己早就懂了,结果在看到最后一个例子时,直接就把答案给猜错了。
public class Test5{
public static void main(String[] args) {
Object o = new Object();
System.out.println(o);
change(o);
System.out.println(o);
}
public static void change(Object o){
o = null;
}
} 上面这个demo的结果是:
先不解析上面这个demo,我们拿下面这个来说明:public class AAA {
private String str = "123";
public static void main(String[] args) {
AAA aaa = new AAA();
System.out.println(aaa.str);
aaa.change(aaa);
System.out.println(aaa.str);
System.out.println("-------------------------------");
AAA bbb = new AAA();
System.out.println(bbb.str);
bbb.change2(bbb);
System.out.println(bbb.str);
}
public void change(AAA a) {
a.str = "abc";
}
public void change2(AAA a) {
a = new AAA();
a.str = "abc";
}
} 答案是什么呢:
为什么不一样,按照在书上看到的——“java传递的是引用,也就是对象的地址”,那change方法把源对象的str属性给改了,为什么change2不能改呢?
第一副图,对应对象aaa,第二副图对应对象bbb。
第一副:首先new了一个对象aaa,里面有一个str,指向字符串“123”的地址。然后调用change(aaa)后,由于java方法传递的是地址值的拷贝,所以参数a对象也是指向了aaa原来的堆空间。然后更改str,把参数a中str的指向了“abc”所在空间,即把aaa的str的地址给换成了“abc”,所以最后在输出的时候就会输出“abc”了。
第二副:首先new了一个对象bbb,里面有一个str,指向字符串“123”的地址。然后调用change(bbb)后,由于java方法传递的是地址值的拷贝,所以参数a对象也是指向了aaa原来的堆空间。但是,方法里调用了b = new AAA();,这样就把b指向的地址换成了一个新对象的地址,同时里面有一个str,由于也是“123”,所以指向的是同一个“123”所属的地址。然后更改str,只是把参数对象a的str的地址给换成了“abc”(双线粗箭头),但是bbb对象中的str仍然没有变化,所以最后在输出的时候还是输出“123”了。
所以说在传递对象类型时,就看在方法里参数的地址在修改其他属性前有没有发生改变:
如果形参的(引用)地址发生了改变,那对原对象就没有影响;
如果形参的(引用)地址未改变,但属性有改变,原对象的属性会有随之改变;
如果属性先改变,形参的(引用)地址后改变,那原对象的属性在地址改变前有修改的会随之改变。
所以我们会经常看到下面的这样的例子:
public static void main(String[] args) {
BBB bbb = new BBB();
List<Object> list = new ArrayList<Object>();
System.out.println(list.size());
bbb.change(list);
System.out.println(list.size());
}
public void change(List<Object> list) {
for (int i = 0; i < 3; i++) {
list.add("00"+i);
}
} 最后输出list.size=3,因为list的地址没有发生变化,所以方法中对list增删对象,原对象就会有变化。
所以现在再看传递String的这个方法,是不是就简单多了。因为在方法中参数的地址改变了,所以就不会影响到源对象了。所以还是输出“123”。
public class Test2{
public static void main(String[] args) {
String str = "123";
System.out.println(str);
change(str);
System.out.println(str);
}
public static void change(String str){
str = "abc";
}
}
那再回头看看文章里第一个例子是不是就也清楚了。
接触Java也有好几年了,本来以为这些简单自己早就懂了,结果在看到最后一个例子时,直接就把答案给猜错了。
public class Test5{
public static void main(String[] args) {
Object o = new Object();
System.out.println(o);
change(o);
System.out.println(o);
}
public static void change(Object o){
o = null;
}
} 上面这个demo的结果是:
java.lang.Object@2a139a55 java.lang.Object@2a139a55而不是
java.lang.Object@2a139a55 null仔细研究了一下,现在终于是弄懂了。原来以前都是自以为懂了。
先不解析上面这个demo,我们拿下面这个来说明:public class AAA {
private String str = "123";
public static void main(String[] args) {
AAA aaa = new AAA();
System.out.println(aaa.str);
aaa.change(aaa);
System.out.println(aaa.str);
System.out.println("-------------------------------");
AAA bbb = new AAA();
System.out.println(bbb.str);
bbb.change2(bbb);
System.out.println(bbb.str);
}
public void change(AAA a) {
a.str = "abc";
}
public void change2(AAA a) {
a = new AAA();
a.str = "abc";
}
} 答案是什么呢:
为什么不一样,按照在书上看到的——“java传递的是引用,也就是对象的地址”,那change方法把源对象的str属性给改了,为什么change2不能改呢?
第一副图,对应对象aaa,第二副图对应对象bbb。
第一副:首先new了一个对象aaa,里面有一个str,指向字符串“123”的地址。然后调用change(aaa)后,由于java方法传递的是地址值的拷贝,所以参数a对象也是指向了aaa原来的堆空间。然后更改str,把参数a中str的指向了“abc”所在空间,即把aaa的str的地址给换成了“abc”,所以最后在输出的时候就会输出“abc”了。
第二副:首先new了一个对象bbb,里面有一个str,指向字符串“123”的地址。然后调用change(bbb)后,由于java方法传递的是地址值的拷贝,所以参数a对象也是指向了aaa原来的堆空间。但是,方法里调用了b = new AAA();,这样就把b指向的地址换成了一个新对象的地址,同时里面有一个str,由于也是“123”,所以指向的是同一个“123”所属的地址。然后更改str,只是把参数对象a的str的地址给换成了“abc”(双线粗箭头),但是bbb对象中的str仍然没有变化,所以最后在输出的时候还是输出“123”了。
所以说在传递对象类型时,就看在方法里参数的地址在修改其他属性前有没有发生改变:
如果形参的(引用)地址发生了改变,那对原对象就没有影响;
如果形参的(引用)地址未改变,但属性有改变,原对象的属性会有随之改变;
如果属性先改变,形参的(引用)地址后改变,那原对象的属性在地址改变前有修改的会随之改变。
所以我们会经常看到下面的这样的例子:
public static void main(String[] args) {
BBB bbb = new BBB();
List<Object> list = new ArrayList<Object>();
System.out.println(list.size());
bbb.change(list);
System.out.println(list.size());
}
public void change(List<Object> list) {
for (int i = 0; i < 3; i++) {
list.add("00"+i);
}
} 最后输出list.size=3,因为list的地址没有发生变化,所以方法中对list增删对象,原对象就会有变化。
所以现在再看传递String的这个方法,是不是就简单多了。因为在方法中参数的地址改变了,所以就不会影响到源对象了。所以还是输出“123”。
public class Test2{
public static void main(String[] args) {
String str = "123";
System.out.println(str);
change(str);
System.out.println(str);
}
public static void change(String str){
str = "abc";
}
}
那再回头看看文章里第一个例子是不是就也清楚了。
相关文章推荐
- java常用类库之数组操作类
- 利用java集合模拟斗地主(发牌)
- JAVA编程思想之内部类
- 浅谈Java多进程程序的运行模式
- Java中的ReentrantLock和synchronized两种锁定机制的对比
- Spring、编码剖析Spring管理Bean的原理
- java synchronized详解
- Spring、编码剖析Spring管理Bean的原理
- myeclipse远程调试
- java排序
- 关于Java中使用外部的程序的方法(待完善)
- java中浅拷贝与深拷贝
- JAVA IO流 基础
- java增删改查JDBC封装类
- Spring 定时任务quartz配置及代码示例详解
- java增删改查JDBC封装类
- Java语言基础之封装类
- java服务端解决js跨域的问题
- JAVA学习_02: 包装类、拆箱和装箱详解
- java中volatile关键字的含义