官方的文档中标明:结论是值传递。
Java总是值传递的,但是sun称“一个对象的地址”为引用,当我们传递一个对象的值的时候,我们传递的是它的引用,这很容易迷惑初学者。
在此,问题的关键是“引用”这个词在表达“通过引用传递”时与我们通常在Java中的“引用”一词完全不同的意思。
在Java中引用通常意味着对一个对象的引用。但在编程语言理论中技术术语中,引用、值传递意味着一个存储着变量的存储单元的引用。
引用传递:
当一个参数传递给一个函数的时候,调用函数获得一个原始值的引用,而不是他的值的一个拷贝。如果这个函数修改了他的参数在调用代码中的值也会被改变,因为参数与参数都是用的相同的内存槽。很确定的Java只有一种传值方式(有助于保持事情的简单性)
对此,举个例子来体会一下:你不能用Java直接写一个交换函数去交换两个作为参数传递的值,不论他们是对象还是单纯的值。因为Java是值传递,包括指向一个方法的对象的指针。
Java是值传递的。我们所以为的按引用传递,其实传递的还是引用中地址的值(并且是它的一个拷贝),也可以理解为指针的值。让我们所疑惑的,只是“引用”这个词的含义,在Java中的引用和我们以为的引用是有所不同的。
对上述是不是懵懵懂懂,那具体可以看看下述文章。
首先,我们不要纠结值传递和引用传递的字面意义,容易陷入所谓的“一切传引用其实本质上是传值”这种无意义论战。
基本类型,值就直接保存在变量中。而引用类型,变量中保存的只是实际对象的地址。一般称这种变量为引用,引用指向实际对象,实际对象中保存着内容。参考下图:
这里搞清楚赋值运算符(=)的作用
num = 20;
str = "java";
对于基本类型num,赋值运算符会直接改变变量的值,原来的值被覆盖掉。
对于引用类型str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变
如上图,"hello"字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收)
调用方法时发生了什么?参数传递基本上就是赋值操作。
第一个例子:基本类型
void foo(int value){
value = 100;
}
foo(num);//num没有被改变
第二个例子:没有提供改变自身方法的引用类型
void foo(String text){
text = "windows";
}
foo(str);//str 也没有被改变
第三个例子:提供了改变自身方法的引用类型
StringBuilder sb = new StringBuilder("ipone");
void foo(StringBuilder builder){
builder.append("4");
}
foo(sb);//sb被改变了,变成了"iphone4"。
第四个例子:提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。
StringBuilder sb = new StringBuilder("ipone");
void foo(StringBuilder builder){
builder = new StringBuilder("ipad");
}
foo(sb);//sb没有被改变,还是iphone。
重点理解是第三四个
第三个例子:
builder.append("4")之后
第四个例子:
builder = new StringBuilder("ipad");
局部变量和方法参数在jvm中的存储方法是相同的,都是在栈上开辟空间来存储的,随着进入方法开辟,退出方法回收。以32位JVM为例,boolean,byte,short,char,int,float以及引用都是分配4字节空间,long,double分配8字节空间。对于每个方法来说,最多占用多少空间是一定的,这在编译时就可以计算好。
Java对对象采用的不是引用调用,实际上,对象引用进行的是值传递。
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
总结一下java中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型和布尔型)
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用一个新的对象