<转载>Java值传递与引用传递

主要内容引用自: http://www.zhihu.com/question/20628016
Java里只有“值传递”,没有“引用传递”
# 谷强强 写道
值传递和引用传递,属于函数调用时参数的求值策略(Evaluation Strategy),这是对调用函数时,求值和传值的方式的描述,而非传递的内容的类型(内容指:是值类型还是引用类型,是值还是指针)。

值类型/引用类型,是用于区分两种内存分配方式,值类型在调用栈上分配,引用类型在堆上分配。(不要问我引用类型里定义个值类型成员或反之会发生什么,这不在这个本文的讨论范畴内,而且你看完之后,你应该可以自己想明白)。

值类型/引用类型描述内存分配方式,值传递/引用传递描述参数求值策略,两者之间无任何依赖或约束关系。
 写道
在函数调用过程中,调用方提供实参,

这些实参可以是常量: Call(1);
也可以是变量: Call(x);
也可以是他们的组合: Call(2 * x + 1);
也可以是对其它函数的调用:Call(GetNumber());

但是所有这些实参的形式,都统称为表达式(Expression)。

求值(Evaluation)即是指对这些表达式的简化并求解其值的过程。

求值策略(值传递和引用传递)的关注的点在于,这些表达式在调用函数的过程中,求值的时机、值的形式的选取等问题。求值的时机,可以是在函数调用前,也可以是在函数调用后,由被调用者自己求值。这里所谓调用后求值,可以理解为Lazy Load或On Demand的一种求值方式。
 
求值策略求值时机传值方式
值传递(pass by value)调用前值的副本(原始对象的副本)
引用传递(pass by reference)调用前原值(原始对象)
名传递(pass by name)调用后与值无关的一个名
则 值传递与引用传递 的区别:
 值传递引用传递
根本区别创建副本(copy)不创建副本
所以在函数中无法改变对象在函数中可以改变对象
写道
这里的改变不是指mutate, 而是change,指把一个变量指向另一个对象,而不是指仅仅改变属性或是成员什么的.
所以说Java是Pass by value,原因是它调用时Copy,实参不能指向另一个对象,而不是因为被传递的东西本质上是个Value.
 
写道
这些行为,与参数类型是值类型还是引用类型无关。

对于值传递,无论是值类型还是引用类型,都会在调用栈上创建一个副本,不同是,对于值类型而言,这个副本就是整个原始值的复制。而对于引用类型而言,由于引用类型的实例在堆中,在栈上只有它的一个引用(一般情况下是指针),其副本也只是这个引用的复制,而不是整个原始对象的复制.

这便引出了值类型和引用类型(这不是在说值传递)的最大区别:值类型用做参数会被复制,但是很多人误以为这个区别是值类型的特性。其实这是值传递带来的效果,和值类型本身没有关系。只是最终结果是这样。

综上所述,对于Java的函数调用方式最准确的描述是:参数藉由值传递方式,传递的值是个引用。

由于这个描述太绕,而且在字面上与Java总是传引用的事实冲突。于是对于Java,Python、Ruby、JavaScript等语言使用的这种求值策略,起了一个更贴切名字,叫Call by sharing。这个名字诞生于40年前。
call-by-reference: You are able to assign a new reference to a method parameter variable in a method, and it will be visible by the caller of the method so after calling the method, the caller will get the new reference (new object).

call-by-sharing: You are able to assign a new reference to a method parameter variable in a method, BUT it will NOT be visible by the caller of the method so after calling the method, the caller will still get the same old reference which was passed as a method parameter to the method.

 

    Java代码

public class ReferenceTest {
	
	public static void main(String[] args) {
		
		StringBuffer str = new StringBuffer("aaa");
		change1(str);
		System.out.println(str); // aaa
		change11(str);
		System.out.println(str); // aaabbb
		change12(str);
		System.out.println(str); // aaabbbbbb
		
		String ss = "111111";
		change2(ss);
		System.out.println(ss); // 111111
		
		int no = 123;
		change3(no);
		System.out.println(no); // 123
		
		Long lon = 1000000L;
		change4(lon);
		System.out.println(lon); // 1000000
		
		/**
		 * 在Java中, 对于函数而言, 在函数内部可见的只有形参(实参的COPY), 实参是不可见的. 则在函数内部, 是没有任何方法能修改实参,使指向到一个新的对象.
		 * 
		 * 因此, 如果形参是基本数据类型, 则函数执行完成后, 实参的值是不会也无法被改变的.
		 * 
		 * 如果形参是引用类型, 则在形参指向到一个新的对象之前的修改, 都会影响到实参(因为此时形参和实参都指向到同一个对象).反之亦然.
		 * 
		 */
	}
	
	public static StringBuffer change1(StringBuffer sb) {		
		sb = new StringBuffer("bbb");  
		//sb = null;
		return sb;
	}
	
	public static StringBuffer change11(StringBuffer sb) {
		sb.append("bbb");
		return sb;
	}
	
	public static StringBuffer change12(StringBuffer sb) {
		sb.append("bbb");
		sb = new StringBuffer("ccc");
		sb.append("ddd");
		return sb;
	}
	
	public static String change2(String str) {
		str += "1231231";
		return str;
	}
	
	public static int change3(int num) {
		num += 10;
		return num;
	}
	
	public static Long change4(Long l) {
		l = l - 11111111111L;
		return l;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值