首先我们来看下面这段代码:

public class Test1 {
String a = "123";
public static void change(Test1 test) {
test.a="abc";
}
public static void main(String[] args) {
Test1 test1=new Test1();
System.out.println(test1.a);
change(test1);
System.out.println(test1.a);
}
}

结果输出123 abc 相信大家都能做对这道题目。Java是按引用传递的,在函数里面可以修改对象的值。我们再看下面的代码:

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";
}
}

你认为会输出多少?总之我周围的好几个人都说会输出123 abc。因为在Java中String不是基本数据类型,会传递引用,所以在change方法里面可以修改str的值。 
可是实际的输出确实是123 123,是不是有点毁三观呢?

误区1:

有人说可能是这里String定义的方式有问题,因为这里的定义方式String str = “123”,可能存在问题,java会将其存放在字符串常量池中(当再次声明一个同样内容的字符串时将会使用字符串常量池中原来的那个内存,而不会重新分配) 
写成

JavaString str = new String(“123”);

str是指向堆区的内存会传递引用。可是结果依然打印 123 123

误区2:String是不可变的

还有人查看String文档发现

Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared.For example:

String str = "abc";

is equivalent to:

 char data[] = {'a', 'b', 'c'};
String str = new String(data);

可以看到官方文档解释了因为String是不可以改的。所以有人认为这就是在函数里面无法修改String值得原因。

请看下面的代码

public class Test3{
public static void main(String[] args) {
String str = new String("123");
str = new String("abc");
System.out.println(str);
}
}

按照这种理解,这里的String应该还打印123而不是abc。显然这不是瞎扯么。

文档中说StringBuffer是可变的我们用StringBuffer试试

public class Test4{
public static void main(String[] args) {
StringBuffer str = new StringBuffer("123");
System.out.println(str);
change(str);
System.out.println(str);
}
public static void change(StringBuffer str){
str = new StringBuffer("abc");
// str.append(abc);
}
}

依然打印123 123,可见根本不是这个原因,并且对可变不可变的理解有误。

传递引用?

于是疑问就产生了,传引用到底是什么意思? 
学过C/C++的人理解起来可能会容易一点,在一个函数里面修改一个变量的值就要传递该变量的地址。也就是说通过指针来修改(改变指针变量的值要通过二级指针)。

在Java里面传递引用到底是什么意思?实质上就是将该变量的地址传递了过来,这个角度来看实际上也是按值传递的。

上面的内存图表示new一个变量的时候真正的对象其实在堆区,栈上保存了一个指向堆区存放该对象内存的地址。于是在函数调用的时候传递引用也就是传递这个地址值得一个拷贝。通过这个地址值可以修改这个地址所在堆区的内容。切记,传递的这个地址是按值传递的。在函数里面修改这个值得结果就是失去对堆区对象的指向。函数调用完成后对象没有任何影响。像代码1那样,传递的test1对象的地址,因此test1对象在堆区的值都可以修改,str是他的属性当然也可以修改。

看下面的代码

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;
}
}

调用change函数将o置为null,对o对象毫无作用,再次打印o的值照样有值,因为传递的只是o对象地址的一个拷贝,在函数里面修改该变量的值对外面毫无影响。

因此,注意Java中所说的按引用传递实质上是传递该对象的地址,该地址其实是按值传递的,通过这个地址可以修改其指向内存处对象的值。改变该地址的值毫无意义,只会失去对真实对象的掌控。

再来解释一下产生上面误区2的原因。文档中说String不可变StringBuffer可变的意思是堆区的那片内存的可变性。 
对于String类,通过引用无法修改之前在堆区申请的那段内存,大小是固定的,也就是不能修改他的值,因为他的底层是char数组。当再次给变量new一个值时,他会指向另一个堆区内存,从表面上看也是改变了值。 
对于StringBuffer,程序员可以根据实际使用继续分配更多内存,例如调用append方法,这就是可变的意思。比如上面代码4中函数里面的注释那句是可以修改String的值的。



// added at 2015年11月2日10:44:27

附上stackoverflow上关于java传值还是传引用的回答:Is Java “pass-by-reference” or “pass-by-value”?

Java is always pass-by-value. Unfortunately, they decided to call pointers references, thus confusing newbies. Because those references are passed by value.

Java一直是值传递。不幸的是,他们决定把指针叫做引用,因此新人总是被搞晕。因为这些引用也是通过值传递的

原文转自 http://blog.csdn.net/cauchyweierstrass/article/details/49047217

原作者为 weiers. 请尊重原作者版权

你真的理解Java的按引用传递吗?的更多相关文章

  1. 你真的理解Java 注解吗?

    你真的理解Java 注解吗? 1.什么是注解? 官方解释: Java 注解用于为 Java 代码提供元数据.作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的.Java ...

  2. Java随谈(六)## 我们真的理解 Java 里的整型吗?

    我们真的理解 Java 里的整型吗 整型是我们日常生活中最常用到的基础数据类型,看这篇文章之前,我想问: 我们真的像自己认为的那么理解 Java 内的整型吗? 也许看完本篇文章你就有自己的答案. C ...

  3. 你真的理解Java中的try/catch/finally吗?

    看几个例子,回顾一下执行顺序 例子1 无异常,finally中的return会导致提前返回 public static String test() {    try {        System.o ...

  4. 你真的理解Java的this和super吗?

    你不知道的this 很多介绍java的书籍都说this指该对象本身.我们来看下面代码: class Base{ private int i = 3; public Base() { this.disp ...

  5. Java内存管理-你真的理解Java中的数据类型吗(十)

    勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 作为Java程序员,Java 的数据类型这个是一定要知道的! 但是不管是那种数据类型最 ...

  6. 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】

    目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...

  7. 深入理解Java枚举

    深入理解Java枚举 重新认识Java枚举 老实说,挺羞愧的,这么久了,一直不知道Java枚举的本质是啥,虽然也在用,但是真不知道它的底层是个啥样的 直到2020年4月28日的晚上20点左右,我才真的 ...

  8. Java内存模型原理,你真的理解吗?

    [51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关实现规则,环环相扣,希望读者看完这篇文章后能对 Java 内存模型体系产生一个相对清晰的理解,知其然知其所以 ...

  9. 《深入理解 java虚拟机》学习笔记

    java内存区域详解 以下内容参考自<深入理解 java虚拟机 JVM高级特性与最佳实践>,其中图片大多取自网络与本书,以供学习和参考.

随机推荐

  1. Angular+Grunt+Bower+Karma+Protractor (Atom)

    1. 配置bower 1.安装bower npm install -g bower 2.创建.bowerrc文件 { "directory": "src/bower&qu ...

  2. Linux安全基础:网络配置命令

    1.ifconfig查看和配置网络接口信息(1)设ip地址ifconfig eth0 192.168.0.1(2)暂时关闭或启用网卡ifconfig eth0 downifconfig eth0 up ...

  3. JSON解析和XML解析对比

    JSON解析和XML解析是较为普遍的两种解析方式,其中JSON解析的市场分额更大.本文系统的分析两种解析方式的区别,为更好地处理数据作准备.由于目前阶段主要是做移动开发,所以本文所描述的JSON解析和 ...

  4. xcode svn commit is not under version control (1) & git commit

    使用Xcode提交一个第三方库时,由于包含资源文件,总是提交不了,提示报错:XXX commit is not under version control (1) 网上查了下,得知 xcode对于sv ...

  5. 深入理解RunLoop

    网上看的一篇文章,写的真好,我得多看几次好好理解理解 膜拜大神,转载至此便于学习查看. 此处标明原文链接:http://blog.ibireme.com/2015/05/18/runloop/    ...

  6. 关于UIScollView 中的contentOffset 的理解

    大家对UIScollView 中的contentOffset 一直有疑问, 虽然看是一个简单的问题, 实际上并不简单. 当时我也有好多疑问, 后来在网上找了一下资料, 发现没有找到合理的解释, 因此自 ...

  7. linux版基金看板

    程序员的吊丝们,还在害怕上班时偷偷看基金被老板发现吗?今天你们的福利来了,专属程序员吊丝一族的礼物,linux版基金看板. 优点: 1.自定义设置关注基金 2.linux系统,让别人可以以为你一直都在 ...

  8. vim中tab转为空格

    :set ts=4:set expandtab:%retab!

  9. 职业规划:管理vs技术

    "每个人都身怀天赋,但如果用会不会爬树能力来评判一只鱼,那它这辈子都会觉得自己是条蠢鱼" - 阿尔伯特.爱因斯坦 我想我为这篇博客已经准备了很长时间.但是看起了我还一直挣扎我该往哪 ...

  10. 设计模式C#实现(十三)——享元模式(蝇量模式)

    意图 0 适用性 1 结构 2 实现 3 效果 4 参考 5 意图 运用共享技术有效地支持大量细粒度的对象. 适用性 当以下情况都成立时使用: 一个程序使用了大量的对象 完全由于使用大量对象造成很大存 ...