你真的理解Java中的值传递与引用传递吗?

【部分参考自《Java程序员面试笔试宝典》——个人觉得这本书真的很不错】

在Java语言中提供了两种参数传递的方式:值传递和引用传递。

【实参和形参】个人理解为,实参就是在调用这个方法时,真实,实际传递给方法进行各种操作的参数。形参,即为形式参数,也就是在编写该方法时,声明方法需要接收到什么类型的什么参数。

【值传递】在方法调用时,实参会把它的值传递给形参,形参只是用实参的值进行初始化形成一个临时的存储单元,因此形参与实参实际虽然有着相同的值,但是却有着不同的存储单元,因此对形参的改变不会影响实参的值。

【引用传递】在方法调用中,传递的是对象(也可以看作是对象的地址),这时实参与形参的对象指向了同一块存储单元,因此对形参的修改,会改变形参指向的存储单元中的内容,而实参也正是指向这块存储单元的,这样对形参的修改就会影响实参的值。其实按引用传递其实与传递指针类似,是把对象的地址作为参数的。

【例1】下面程序运行的结果是什么?

package test;

public class Test9 {
	
	public static void testPassParameter(StringBuffer ss1, int n){
		ss1.append(" World");	//引用
		n = 8;	//值
	}
	
	public static void main(String[] args) {
		int i = 1;
		StringBuffer s1 = new StringBuffer("Hello");
		testPassParameter(s1,i);
		System.out.println(s1);
		System.out.println(i);
	}
	
}

【例1代码运行结果】


【例1解析】为了便于理解,假设1和“Hello”存储的地址为0X12345678和0XFFFFFF12。在调用方法testPassParameter方法时,由于i为基本数据类型,因此参数是按值传递的,此时会创建一个i的副本,该副本与i有相同的值,把这个副本作为参数赋值给n,作为传递的参数。而StringBuffer由于是一个类,因此按引用传递,传递的是它的引用(传递的是存储“Hello”的地址),在testPassParameter方法内部修改的是n的值,这个值与i是没有关系的。但是在修改ss1时,修改的是ss1这个地址指向的字符串,由于形参ss1和实参s1指向的是同一块存储控件,因此修改ss1后,s1指向的字符串也被修改了。具体如图1所示:


【深入理解引用传递】在Java语言中处理8中基本数据类型用的是值传递,其他所有类型(包括8中基本数据类型的包装类)都是引用传递,但是由于8种基本数据类型的包装类型都是不可变量,因此就增加了对“按引用传递”的理解难度。

扫描二维码关注公众号,回复: 1883727 查看本文章

【例2】下面程序的运行结果是什么

package test;

public class Test10 {
	
	public static void changeStringBuffer(StringBuffer ss1, StringBuffer ss2){
		ss1.append(" World");
		ss2 = ss1;
	}
	
	public static void main(String[] args) {
		Integer a = 1;
		Integer b = a;
		b++;
		System.out.println(a);
		System.out.println(b);
		System.out.println("==============我是华丽的分割线================");
		StringBuffer s1 = new StringBuffer("Hello");
		StringBuffer s2 = new StringBuffer("Hello");
		changeStringBuffer(s1, s2);
		System.out.println(s1);
		System.out.println(s2);
	}
	
}

【运行结果】


【例2解析】

      对于上面两个输出“1”和“2”,Integer是按引用传递的这没错,之所以b的值改变后a的值没有改变,是由于Integer类(其实不仅仅是Integer,所有包装类)是不可变类。(是指当创建了这个类的实例后,就不允许修改它的值了,也就是说,一个对象一旦被创建出来,在其整个生命周期中,它的成员变量就不能被修改了。即只允许程序读,不允许程序修改。)由于Integer是不可变类,因此没有提供改变它值的方法,在例2中,在执行完语句b++后,由于Integer是不可变类,因此此时会创建一个新的值为2的Integer实例赋值给b,此时b与a其实已经不再指向同一块存储单元,也就没有任何关系了。

     后面两个输出也是按引用传递的,为了理解后面两个输出结果,首先必须理解“引用也是按值传递的”这一要点。为了便于理解,假设s1和s2指向字符串的地址为0X12345678和0XFFFFFF12,那么在调用函数changeStringBuffer方法时,传递s1和s2的引用就i可以理解为传递了两个地址0X12345678和0XFFFFFF12,而且这两个地址是按值传递的(即传递了两个值,ss1为0X12345678,ss2为0XFFFFFF12),在调用方法ss1.append(" World");时,会修改ss1所指向的字符串的值,因此会修改调用者的s1的值,得到的输出结果为“Hello World”。但是在执行ss2=ss1时,只会修改ss2的值而对s2毫无影响,因此s2的值在调用前后保持不变。从下图中我们可以看出,在传递参数时,相当于传递了两个地址,在调用ss1.append(" World")时,修改了这个地址所指向字符串的值,而在调用ss2=ss1时,相当于修改了方法changeStringBuffer内部局部变量ss2,这个修改与ss1没有关系。


猜你喜欢

转载自blog.csdn.net/weixin_36378917/article/details/80680287