*最近秋招面试,遇到一道题。Java中是值传递还是应用传递,回来在网上搜了一大圈,查了一些书,有说是值传递的,有说是两者都有的。
自己整理了一下,再加上自己的理解。**
首先我们要搞清楚什么是值传递和应用传递:
(1)值传递
在方法的调用中,实参会把它的值传递给形参,形参只是用实参的值初始化一个临时的存储单元,因此形参与实参虽然有着相同的值,但是却有着不同的存储单元,因此对形参的改变不会影响实参的值(我在学初学C语言的时候遇到过这种,当时一直都搞不明白)。
(2)引用传递
在方法调用中,传递的是对象(或者说对象的地址),这时形参与实参的对象指向用一块存储单元,此时形参的修改会影响实参的值。
在Java语言中原始数据类型在传递参数时都是按值传递,而包装类型在传递参数时是按引用传递的。
下面通过一个例子来介绍按值传递和按应用传递的区别:
public class Test01 {
public static void testPassParameter(StringBuffer ss1,int n){
ss1.append(",world");//引用
n++;//值
System.out.println("ss1= " + ss1);
System.out.println("n=" + n);
}
public static void main(String[] args) {
int i = 1;
StringBuffer s1 = new StringBuffer("Hello");
testPassParameter(s1,i);
System.out.println("s1= " + s1);
System.out.println("i= " + i);
}
}
运行结果:
ss1= Hello,world
n=2
s1= Hello,world
i= 1
上图是值传递与引用传递的过程图,应用传递其实是把对象的地址作为参数的。
简单的说就是假设1和“Hello,world”存储的地址是0X1234567和0XFFFFFF1,在用方法testPassParameter时,由于i为基本类型,因此参数是按值传递的,此时会创建一个副本,该副本与 i 有相同的值,把这个副本作为参数覆给n,作为传递的参数。而StringBuffer由于是一个类,因此按应用传递,传递的是“Hello”所存储的地址,如上图所示,在testPassParameter内部修改的是n的值,这个值与i是没有关系的。但是在修改ss1时,修改的时这个地址指向的字符串,由于形参ss1与实参s1指向的是同一块存储区域,因此修改ss1的值后,s1指向的字符串的值也就被修改了。
可以得出一个结论:Java中处理8中剧本的数据类型用的是值传递,处理其他类型用的是引用传递。那String和它们的包装类呢?我们都知道,String和7种基本数据类型的包装类是一群特殊的群体,在这也不例外。
public class Test02 {
public static void testString(String str,Integer n){
str = str + ",world";
n++;
System.out.println("str=" + str);
System.out.println("n=" + n);
}
public static void main(String[] args) {
String st1 = "Hello";
Integer i = 1;
testString(st1,i);
System.out.println("st1=" + st1);
System.out.println("i=" + i);
}
}
运行结果:
str=Hello,world
n=2
st1=Hello
i=1
对此,可以得出在Java中基本数据类型以及它们的包装类和String类型是值传递的,而其他类型都是引用传递的,但是有必要理解的一点“引用传递也是按值传递的”。下面给出一个例子说明:
public class Test04 {
public static void test(StringBuffer ss1, StringBuffer ss2){
ss1.append(",world");
ss2 = ss1;
}
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer("Hello");
StringBuffer s2 = new StringBuffer("Hello");
test(s1,s2);
System.out.println("s1=" + s1);
System.out.println("s2=" + s2);
}
}
运行结果:
s1=Hello,world
s2=Hello
运行结果出人意料,这时怎么回事呢?假设是s1和s2指向字符串的地址分别为0X1234567和0XFFFFFF1,那么在调用方法test时,就会传递两个地址值,分别为0X1234567和0XFFFFFF1,而且这两个值是按值传递的。因此,在调用方法ss1.append(",world")时,会修改指向ss1所指向的字符串的值,从而修改s1的值。但是在执行到ss2=ss1时,只会修改ss2的值对s2毫无影响。下面给出方法调用的处理过程:
由上图可以看出,在传递参数时相当于传递了两个地址,在调用ss1.append(",world")时,修改了这个地址所指向的字符串的值,而在调用ss2 = ss1 时,相当于修改了test中局部变量ss2,是与ss1没有关系的。
总结:
(1)在Java中基本数据类型以及它们的包装类和String类型是值传递的,而其他类型都是引用传递的。
(2)引用传递也是按值传递的。