最近做Java联系时候,碰到了关于Java值传递的习题,递归函数调用的时候什么时候传值的疑问,感觉还是很容易弄混淆其中的定义的。看了一些blog和知乎上的回答,在这里总结一下。
java中方法参数传递方式是按值传递。所谓值传递,就是将实际参数值的副本(复制品)传入方法内,而自己本身不会受到任何影响。
如果参数是基本类型,传递的是基本类型的字面量值的拷贝。
如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝。
参数传递基本上就是赋值操作。
基本类型 和 引用类型 的不同
int num = 10;
String str = "hello";
如图所示,num是基本类型,值就直接保存在变量中。而str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为”引用”,引用指向实际对象,实际对象中保存着内容。
一般来说,对象、数组、String是引用类型。
对于基础类型
public class Test {
public static void main(String args[]) {
int a = 1;
int b = 2;
change(a,b);
System.out.print("交换结束后:a="+a +",b=" +b);
}
public static void change(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("在swap方法中:a="+a +",b=" +b);
}
}
输出:
在swap方法中:a=2,b=1
交换结束后:a=1,b=2
其内存年图如下:
改变的只是传入change方法中的副本而已。
第一个例子:基本类型
void foo(int value) {
value = 100;
}
foo(num); // num 没有被改变
对于引用类型
应用类型比较复杂,我们一步一步来看。
第二个例子:没有提供改变自身方法的引用类型
String str ="HUST";
void foo(String text) {
text = "windows";
}
foo(str); // str 也没有被改变
内存变化如下:
因为传参仅仅是把自己的地址值赋予过去了。String很特殊,是不可变类,任何改变都是在常量池中重新创建一个字符串。
第三个例子:提供了改变自身方法的引用类型
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder.append("8");
}
foo(sb); // sb 被改变了,变成了"iphone8"。
内存变化如下图:
第四个例子:提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder = new StringBuilder("ipad");
}
foo(sb); // sb 没有被改变,还是 "iphone"。
内存变化如下图:
swap(Object obj1, Object obj2)能否实现呢?
不可能实现,因为Java是值传递,obj1 ,obj2,没法换。
Object obj1 = new Object();
Object obj2 = new Object();
swap(Object obj1, Object obj2)
main栈区如何也不会改变的。
再来看一道题,最后输出什么?
public class Example{
String str=new String("hello");
char[]ch={'a','b'};
public static void main(String args[]){
Example ex=new Example();
ex.change(ex.str,ex.ch);
System.out.print(ex.str+" and ");
System.out.print(ex.ch);
}
public void change(String str,char ch[]){
str="test ok";
ch[0]='c';
}
}
输出:hello and cb