Java 的参数传递

一、传值和传址(引用)

Java 的参数传递分为传递基本数据类型(传值)和传递引用数据类型(传址)

public class Demo {

public void fun(Test test, int i) {
test.name = "test2";
i = 2;
System.out.println(i);
}

public static void main(String[] args) {
Demo demo = new Demo();
Test test = new Test();
int i = 1;
demo.fun(test, i);
System.out.print(test.name + " and " + i);
}
}

class Test {
String name = "test";
}
2
test2 and 1

对于基本数据类型,当我们修改传递的值,原来的值不会改变,所以传递的是值的拷贝。值的拷贝在一块儿新的内存中。

对于引用数据类型,当我们通过传递的引用变量修改对象的属性,原引用指向的对象也将改变,所以没有将对象的数据拷贝传递,传递的是指向对象的引用变量,即对象的地址。当然,引用变量是值传递,引用变量的拷贝在一块儿新的内存中。

正常来说,传值更符合正常思路。我把值传给你,你使用它,不会影响我的值。

因为传址的特点,可以将大数据存放到对象中,传递对象引用,减少参数(局部变量表)的大小以提高栈帧的空间利用率。

二、String 的参数传递

public class Demo {

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

public void fun(String str, char[] ch) {
str = "world";
ch[0] = 'd';
}

public static void main(String[] args) {
Demo demo = new Demo();
demo.fun(demo.str, demo.ch);
System.out.print(demo.str + " and ");
System.out.print(demo.ch);
}
}
hello and dbc
  1. 局部变量 str 和全局变量 str 这 2 个名称相同变量,但内存位置不同的值都是 0x1001(假设)
  2. 对象 test 调用 fun 方法,将实参 test.str(全局变量 str 存放对象 “hello” 的内存地址)和 test.ch(全局变量 ch 存放字符数组对象的内存地址),传给 fun 方法的形参 str 引用和 ch 引用。
  3. 局部变量 str 存放对象 “hello” 的内存地址。"world" 匿名对象, str = "world"; 等价于 str = new String("world") ,相当于将在堆中重新分配一个内存空间存放 “world”,局部变量 str 指向 “world”。局部变量 str 的生命周期和方法相同,只是名称和全局变量 str 相同

以上两步操作在 JVM 中如下图所示,内存地址为假设。

image

  1. char ch[] 与 char[] ch 等价。语句 ch[0] = 'd'; 将替换字符数组对象中下标为 0 的字符,即改变了对象的内容。test.str 没有改变,还是 “hello”,test.ch 从 “abc” 改为 “dbc”。

IDEA 调试图如下:

image

2.1、理解引用存放地址

Book a == new Book();// 对象 1
Book b == new Book();// 对象 2
b = a;

问:那个对象会被回收?
答:对象 2,因为将引用 a 中存放对象 1 的地址赋值给了引用 b,所以引用 b 将 “指向” 对象 1,对象 2 将被回收。

三、总结

可以将地址视为值,所以传值和传址都可以视为传值。

四、延伸阅读