Java is Pass by Value | Java只有值传递

[b]in C and JAVA, arguments are passed by value.[/b]

[color=red][b]Java Language Spec中其实已经阐述过Java中都是值传递:[/b][/color]
JLS8.4.1. Formal Parameters(形式参数) : [url]http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.1[/url][quote]When the method or constructor is invoked (§15.12), [color=red]the [b]values[/b] of the actual argument expressions initialize newly created parameter variables[/color], each of the declared type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.
[b]当方法被调用时,会在方法执行前用实际参数的值创建并初始化一个新的参数变量,作为形式参数传给被调用方法。[/b][/quote]


[b][color=red]概念:[/color][/b]
[b]值传递:[/b]
这个好理解,C/C++/Java等语言中体系下对pass by value的理解都是一致的:调用带参方法foo时,传递给方法形参的是实参的值的副本。所以,foo对传入的参数值的修改仅是对副本的修改,实参值不会因为foo对形参的修改而改变:
  	
void foo(int j) {
j = 0; /* modifies the copy of the argument received by the function */
}

int main(void) {
int k=10;
foo(k);
/* k still equals 10 */
}

public static void main(String[] args) {
int i = 10;
foo(i);
System.out.println(i); //i还是10
}

static void foo(int j) {
j = 0;
}

[b]引用传递:[/b]
说引用传递前先说引用。reference的概念,在C语言中是没有,C++和Java中引入了reference的概念,但两种语言中reference的的概念是有本质区别的。
C++[quote]Src:http://www.cnblogs.com/kingln/archive/2008/03/29/1129118.html
C++中的引用就是个别名,对引用的任何操作就是对被引用物的操作。如下例子:
int m;
int &n = m;
n是m的一个引用(reference),m是被引用物(referent)。 n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。
C++中的引用遵从如下规则:
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
C++引用示例程序

//k被初始化为i的引用。
//语句k = j并不能将k修改成为j的引用,只是把k的值改变成为6。
//由于k是i的引用,所以i的值也变成了6。
int i = 5;
int j = 6;
int &k = i;
k = j; // k和i的值都变成了6;
[/quote]
Java中reference的概念就不分析了,就是指向堆中引用类型对象实例的栈中变量。总之,Java中的reference和C/C++的指针更像一些,和C++的reference很不一样。
通过对引用的分析,说说引用传递的概念:
C中没有引用传递这一说(值传递/地址传递(指针传递)),而所谓“引用传递”的提法,就是出自C++(出了个 & nickname 这样一个reference概念,而通过该种方式传参既区别于值传递,又区别于指针传递,完了就取了个名叫引用传递)。C++中的引用传递在swap()例子中可以得到典型的体现:

void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
cout<<a<<’ ‘<<b<<’\n’;
}
int main(){
int x=1;
int y=2;
swap(x,y);
cout<<x<<’ ‘<<y<<’\n’;
return 0;
}
输出为2,1;2,1。C++之引用只是别名,对作为形参的引用的任何操作就是对实参本身的操作。


[b][color=red]Java中为什么有reference的概念,却没有引用传递那?[/color][/b](基本数据类型肯定是值传递,所以下面只谈引用数据类型作为参数传递的情况)
首先,假设Java存在引用传递。既然“引用传递”,肯定如C++一样,是传引用本身;这个引用传递的阐述肯定应该是:将方法外部的引用(变量)作为实参,直接传递给被调用方法的形参。因为方法内外持有的是同一个引用(变量),那么肯定会得出以下共识:
1 方法内部对引用的重新赋值,会在方法外体现(内外都指向了一个新的对象);
2 方法内部对引用所指向对象的属性的变更,会在方法外体现。
可是实际情况是:
[color=red][b]方法内部对内部引用的重新赋值,并不会改变外部引用所指向的对象;[/b]
方法内部对内部引用所指向对象的属性的变更,改变了外部引用所指向对象的属性值。[/color]
假设中的共识1是与我们实际看到的结果相违背的!所以说,java中不存在引用传递。
[b]混淆源自C++/Java中同样都叫reference;争执源自不去考虑两者“引用”概念的区别。[/b]


[b][color=red]Java中引用数据类型作为参数传递的方式[/color][/b]
如上面所引用JLS中的阐述所说,当传递引用(变量)时,使用引用的值(就是所指向的对象实例的地址)创建并初始化了一个新的引用变量,将该新的引用变量传给了被调用方法。从本质上来说,这就是值传递!
[img]http://www.javaworld.com/javaworld/javaqa/2000-05/images/03-qa-0512-pass1.gif[/img]


Java中的reference,更像是C/C++中的指针。但Java中引用数据类型作为参数传递的方式,是明显不同于指针传递的:Java中作为形参的引用(变量),其存放的内容是实参所指向对象实例的地址值;而C/C++中的指针传递,作为形参的指针,其存放的内容是实参的地址值。
但从本质上来说,他们都是传递地址值的值传递。
而C++中的引用,其“一旦创建必须初始化,一旦初始化就不能改变其引用关系”的特点,导致了对引用的操作无论何时都是对被引用物的操作(我们不去深究C++中的引用是否分配了内存,因为在C++程序员之间这都是一个没有定论的话题 ISO C++ 3.7 It is unspecified whether or not a reference requires storage),所以C++的引用传递,传递的就是实参本身。这也正是引用传递这个概念想要表达的东西:传的就是实参本身,被调用方法的任何操作,都是对实参本身的操作。[quote]C++ : Is there any way to find the address of a reference?
[url]http://stackoverflow.com/questions/1950779/is-there-any-way-to-find-the-address-of-a-reference[/url]References don't have their own addresses, although references may be implemented as pointers, there is no need or guarantee of this.[/quote]


[size=medium][b][color=red]总之,java只有值传递:
当传递的是基本数据类型时,地球人都认同是值传递;
当传递的是引用数据类型时,传递的不是该引用(变量)本身(所以不是引用传递)!而是该引用变量的一个副本,而副本的产生遵从的就是值传递的原则(原引用变量中的所指向对象地址信息直接赋给该副本),所以归根结底还是值传递。[/color][/b]
[/size]


[b]参考资料 Sources:
Does Java pass by reference or pass by value?
[url]http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html[/url]
Is “C” Pass by Value or Pass by Reference?
[url]http://geeki.wordpress.com/2010/10/13/is-c-pass-by-value-or-pass-by-reference/[/url]
C++值传递、指针传递、引用传递的区别:
[url]http://www.cnblogs.com/yjkai/archive/2011/04/17/2018647.html[/url]
[/b]


[b]Understanding Weak References:[/b]
[url]http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html[/url][quote]引用从强到弱:
[b]string > soft > weak > phantom[/b]
weakly-referenced objects will probably get collected by the JVM as soon as they become eligible for GC (and the WeakReference cleared). A weak reference to a would look like new WeakReference<Object>(a). [b]Weak references are useful in the case that you want a cache whereby the data is only needed if the keys exist as strongly-reachable elsewhere (e.g. HttpSessions)[/b]
softly-referenced objects will probably hang around in the JVM [b]until it absolutely needs to recover memory(如,内存不足时)[/b]. [b]Soft references are useful for caches where the values are long-lived but can collected if necessary[/b].[/quote]


Example:
Tips:面试时遇到该类问题,为了避免混淆,如果原题的实参和方法形参名字是一样的,可以改成不一样的再做,以免自己脑子混乱起来(如这里的例一实参为s,形参为str;例二实参为a,b,形参为x,y)

public class JavaIsPassByValue {
public static void main(String[] args) {
String s = "a";
say(s);
System.out.println(s);
}
static void say(String str) {
str = str + "b";
}
}
结果:a
说明:设字符串常量"a"分配在地址为0x0001的Method Area里、字符串常量"ab"分配在地址为0x0002的Method Area里。
[img]http://dl.iteye.com/upload/attachment/0073/1571/66ac6fd0-8dc0-3700-8261-12688a3083ee.png[/img]



public class TestStringBuffer {

public static void main(String[] args) {

StringBuffer a=new StringBuffer("A");
StringBuffer b=new StringBuffer("B");
method(a,b);
System.out.println(a+","+b);
}
public static void method(StringBuffer x,StringBuffer y){
x.append(y);
y=x;
}
}
结果:AB,B
说明:[b]设存放有A的StringBuffer对象分配在地址为0x1的堆空间,存放有B的StringBuffer对象分配在地址为0x2的堆空间[/b]:
[img]http://dl.iteye.com/upload/attachment/184922/d7225e14-2362-3199-98eb-ffcb529dd35a.png[/img]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值