传递与引用
Java中所有函数调用本质上都是值传递。首先我们来看这样一段程序:
public class Test { public static void main(String[] args) { int i = 10; String str = new String("World"); char[] ch = { 'H', 'e', 'l', 'l', 'o' }; change(i, str, ch); System.out.println(i); System.out.println(str); ; System.out.println(ch); } public static void change(int i, String str, char[] ch) { i = 20; str = "Changed"; ch[0] = 'c'; } }
输出结果为:
10
World
cello
这里涉及到的就是Java传递和引用的问题。Java中所有的函数本质上都是值传递,也就是说函数的形参是实参的一份副本。如果实参是基本类型,那么形参就是基本类型的值的副本;如果实参是引用类型,那么形参就是引用类型的句柄的副本。我们可以把值当做一个仓库,把引用类型句柄当做一把钥匙。这样我们再来看上面的几行代码。
变量 i 是基本类型,在执行函数 change(int i, String str, char []ch) 时,只是把 i 的值(10)的副本传给了形参中的 i ,可以想象成是在原来的仓库旁边有建了个仓库,往新仓库里放了20袋大米,但是新仓库里的东西和原来放10袋大米的仓库没有关系,所以最终打印的还是原来仓库中的10袋大米。
接下来先说一下char数组,在执行change 函数的时候,同样是将char数组的句柄传递给形参。上边我们说可以把句柄想象成钥匙,那在这里我们就试试。执行change的时候,我们把钥匙的副本(注意无论是值还是句柄,传递的都是副本)传给了ch,它用这把复制的钥匙一样可以打开原来存放 hello 的仓库,所以它打开了仓库,把 'h' 换成了 'c' ,最终打印的时候我们就看到了 cello 的结果。
至于String有些同学大概会有疑问,不是说除了四类八种之外都是引用类型么,那么String也是引用类型,那为什么str 的值没有改变呢?
确实,String属于引用类型,但是让我们来看看String类的一部分定义:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
从Java中String的定义我们可以看出,String类及其成员变量都是final类型,即不能被更改的,这就相当于你拿了配好的钥匙副本但是却搬不动仓库里的东西,什么都动不了,所以在最后打印的时候str 的结果还是 World 不会变。