这是书P67页参数一节内容,结合P90 引用类型和P124 JVM内存布局我们来理解一下值传递和引用传递。
首先我们先简单理解一下P124的JVM内存布局:
我们要关心的有以下两个:
JVM Stacks虚拟机栈
元数据区(JDK1.8以前叫永久代/方法区)
首先元数据区存放了类元信息,静态属性,字段,方法等,是线程公有的。以前又名方法区,所以可以狭义的理解是存放方法各属性的一个内存区域。其中
特别的,String字符串存放在元数据区的常量池中(String底层是通过StringBuilder进行append然后进行toString方法的 因而每次修改String 都将不再是同一个对象了 为了方便String的复用,当一个新String生成 会放入常量池,如果栈中使用到了它,便返回其引用 这也是为什么String是final修饰的原因)
然后是虚拟机栈,虚拟机栈是一个执行方法的内存区域,线程私有,通过栈帧将不同的方法划分,整个执行区可以看作一个栈,最开始的肯定是main方法入栈,然后main方法的类内方法入栈,类内方法在操作栈执行完毕以后,方法返回地址会返回局部变量表中的值给调用处。
然后就能开始我们今天的主题,值传递和引用传递了:
package TestDemo;
public class TestDemo{
private static int intStatic=222;
private static String stringStatic="old string";
private static StringBuilder stringBuilderStatic=new StringBuilder("old stringBuilder");
public static void main(String[] args) {
method(intStatic);//值传递 方法A
method(stringStatic);//引用传递 方法B
method(stringBuilderStatic,stringBuilderStatic);//引用传递 方法C
System.out.println(intStatic);
System.out.println(stringStatic);
System.out.println(stringBuilderStatic);
}
public static void method(int intStatic){
intStatic=777;
}
public static void method(String stringStatic){
stringStatic="new string";
}
public static void method(StringBuilder stringBuilderStatic1,StringBuilder stringBuilderStatic2){
//直接使用参数引用修改对象
stringBuilderStatic1.append(".method.first-");
stringBuilderStatic2.append("method.second-");
}
}
首先看方法A:
这是一个值传递,由于我们知道,
基本数据类型的存储到执行都是直接从方法区(元数据区)复制到虚拟机栈中的局部变量表执行的,所以main方法中的222会复制一份到method方法中,但是method方法的栈帧和main方法的栈帧是不同的,因而在method方法栈帧中对222修改成777并不影响main方法中的222,除非方法返回地址作出明确要求(狭义理解成将其当成返回值返回)
看方法B:
引用数据类型的存储到执行都是从方法区取得引用变量的引用(类比于C中的指针,参考P90页的引用类型分析) 因而在栈帧中的局部变量表持有的是对stringStatic字符串的引用,任何修改都会是对原字符串的修改,但由于String底层很特殊,存放在常量池中,是final修饰的,所以也不会修改main栈帧中的值。
看方法C: