Java中的值传递和引用传递
从代码例子中思考
//例1
1 public void method1(){
2 int x=0;
3 this.change(x);
4 System.out.println(x);
5 }
6 public void change(int i){
7 i=1;
8 }
很显然的,在mothod1中执行了change(x)后,x的值并不会因为change方法中将输入参数赋值为1而变成1,也就是说在执行change(x)后,x的值z依然是0。这是因为x传递给change(int i)的是值。这就是最简单的传值。
进行一点变化的例2
//例2
1 public void method1(){
2 StringBuffer x=new StringBuffer("Hello");
3 this.change(x);
4 System.out.println(x);
5 }
6 public void change(StringBuffer i){
7 i.append(" world!");
8 }
看起来没什么变化,但是这次mothed1中执行了change (x)后,x的值不再是"Hello"了,而是变成了"Hello world!"。这是因为x传递给change(i)的是x的引用。这是最经典的传引用。
似乎有些奇怪了,两段程序没有特别的不同,可是为什么一个传的是值而另一个传的是引用呢?
让我们从内存的存储方式看一下x和I之间到底是什么关系。
在执行到第2行的时候,变量x指向一个存放着int 0的内存地址。
变量x---->[存放值0]
执行第3行调用change(x)方法的时候,内存中是这样的情形:x把自己值在内存中复制一份,然后变量i指向这个被复制出来的0。
变量x---->[存放值0]
↓进行了一次值复制
变量x---->[存放值0]
这时候再执行到第7行的时候,变量i的被赋值为7,而这一步的操作已经跟x没有任何关系了。
变量x---->[存放值0]
变量x---->[存放值7]
再看例2
变量x---->[存放值"Hello"]
接下来执行第三行change(x),注意,这里就与例1有了本质的不同:调用change(x)时,变量i也指向了x指向的内存空间,而不是指向x的一个拷贝。
变量x \
-->[存放值"Hello"]
变量x /
于是,第7行对i调用append方法,改变i指向的内存空间的值,x的值也就随之改变了。
变量x \
-->[追加为"Hello World!"]
变量x /
对于参数传递,如果是简单数据类型,那么它传递的是值拷贝,对于类的实例它传递的是类的引用。
再看一个例子:
1 void method1() {
2 StringBuffer x = new StringBuffer("Hello");
3 change1(x);
4 System.out.println(x);
5 }
6
7 void method2() {
8 StringBuffer x = new StringBuffer("Hello");
9 change2(x);
10 System.out.println(x);
11 }
12
13 void change1(StringBuffer sb) {
14 sb.append(" world!");
15 }
16
17 void change2(StringBuffer sb) {
18 sb = new StringBuffer("hi");
19 sb.append(" world!");
20 }
调用method1(),屏幕打印结果为:“Hello world!”,调用method2(),我们认为结果应该是"hi world",因为sb传进来的是引用。可是实际执行的结果是"Hello"!
现在分析一下调用change2()
程序执行到第8行,x指向一个存放着"Hello"的内存空间。
变量x---->[存放值"Hello"]
第9行调用change2,将sb指向x指向的内存空间,也就是传入x的引用。
变量x \
-->[存放值"Hello"]
变量x /
到这里为止还没有什么异样,接下来执行18行,这里就出现了类似传入值拷贝的变化:new 方法从内存中开辟了一块新的空间存放串"hi",同时sb指向了这块空间。
变量x---->[存放值"Hello"]
x原有的引用被切断;
变量x---->[另一块存放"hi"的空间],接下来再对sb进行append已经和x没有任何关系了。