你是C/C++或是VB转过来的吗看在Java中过多考虑引用(指针)和值之间的关系是步入歧途,这正是Java的设计者极力避免你考虑的问题。
你需要明白的是:
1、Java中所有方法的参数的传递都是逗值传递地;
2、Java中所有对象类型的变量的逗值地,本质上说,包含了Java堆中的实际对象的地址,你可以大体认为Java的变量对应了C/C++中的指针(其实这里面有更复杂的机制)。事实上,Java并不像C/C++一样明确的区分逗值语义地与逗引用语义地,Java栈中也不会存放任何对象的实体(这点与C/C++不同,C/C++栈中可以存放对象实体),所有的Java对象都是在堆中。
概念上的区别在于,我这里提到的逗变量地是指Java栈中的内容,对应你说的逗引用地;我提到的逗对象地是指Java堆中的实体,对应你说的逗值地。而一般Java教材中提到的逗值传递地,是指这些逗变量地的内容的传递,不是Java堆中的对象实体的传递。
你用字符串来做实验,并推广为所有Java对象的做法,并不是特别合适。Java的String类型有特殊的处理:所有编译期认识的字符串,都会被放到常量池,于是下面的语句:
a = "s";
b = "s";
a和b并不像其它对象一样有创建的动作,都是直接指向常量池中的"s",所以你可以得到a==b。而下面的语句:
a = new String("s");
b = new String("s");
是分别在Java堆中创建了2个对象,此时a!=b。
本质上说,对于基本数据类型(整数、字符等),Java的符号==,用于判断二者的值是否相等;对于对象类型,Java的符号==,用于判断两个变量是否是逗同一个对象地,equals()方法才是用于判断两个对象是否相等。
你希望实现的swap逻辑,在Java中通常认为是无法实现的。拿你这个例子来说,swapValue()中的tmpValue无论怎么更改,只是改变tmpValue自己的内容(即不断指向不同的对象),并不会改变value中的内容(始终指向同一个对象)。这也是为什么Java最初说自己永远是值传递。你只有改变tmpValue指向的对象的值(通过调用这个对象的方法或是更改它的属性),使用value访问时,才能看到这些改变。
为了弥补这个缺陷,C#才加入了ref关键字,允许传入变量的引用(如果参考C/C++,C#传递的实际是二级指针,它的内容是栈中的变量的地址)。
static void operate(String str) {
str += "def";
}
static void mapAdd(Map
map.put("aa", "aa");
}
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = "abc";
operate(str1);
// str1指向了堆中new出的对象abc,像operate传递时,应该传递的是str1的引用
// 在方法中操作后,str1应该指向到操作后的新的对象,也就是abcdef
System.out.println(str1);
operate(str2);
// str2为栈中的对象,传递的就是abc数值,方法操作完后,新的值仅仅是方法中的局部变量
// 值未带出到main方法中来,所以str2还应该是abc
System.out.println(str2);
Map
map.put("bb", "bb");
// 这里没有疑问,mapAdd中传递的map为对象,传递的是引用,操作后map指向了操作后堆中的map值
// 操作后,main方法中map中有两队值。
mapAdd(map);
for (String str : map.keySet()) {
System.out.println(str);
}
}