Java 基础 --- Java变量储存机制及参数传递
JVM内存机制
Primitive type的储存和值传递
primitive type的储存
- primitive type的局部变量是储存在JVM stack上, 首先JVM创建一个名为A的变量, 存在于局部变量表中,然后去栈中查找是否存有字面量值为10的内容,如果有就直接把A指向这个地址. 如果没有,JVM会在栈中开辟一块空间来存储10这个内容.
- 当写下
int A = 10
时:
- 当写下
int A = 10, int B = 10
时:
- 当
int A = 10, int B = 10, B = 20;
primitive type的值传递
- 如下图,在执行main方法时,JVM会在栈顶加进一个栈帧,此栈帧包括一个A=10的变量。在执行passPrimitive方法时,JAM会再加入一个新的栈帧,此栈帧包括一个B=10的变量,当执行B=100时,在passPrimitive栈帧中的B值会被改变,但在main栈帧中的A值不会被改
- Before: A is 10
- B is 100
- After: A is 10
Reference Type的储存和值传递
Reference Type的储存
- reference包括除基础类型之外的其他所有类型,变量中储存的时一个引用(在栈中存一个可以指向heap的引用),具体过程如下
Reference Type的传递
- 会改变实参
- Before: S1’ID is 100
- In The method: S1’ID is 200
- After: S1’ID is 200
- 当执行第21行(Student s1 = new Student())时
- 当执行第24行时(进入Test方法)
总结:
- 在Java中没有引用传递,只有值传递。在Java中“值”的概念分为两种,一个是基础类型的实际数值,一个是引用,但是对于任何一种变量Java都拿不到地址(引用变量只是储存的是引用, 但是变量本身的地址是拿不到的, 也就是说Java中对对象的引用本身做不了任何操作,比如替换成另外一个引用)所以不存在引用传递。反之,在C++/C中对于任何一个变量(基础变量,类,指针变量等)除了可以拿到变量里的值以外还可以拿到变量的地址,所以有值传递,引用传递,指针传递之分
关于包装类的参数传递问题: Integer as example
- 虽然所有的包装类都是引用传递,但是在函数中传递包装类并不能改变实参
- 因为所有的包装类都是不可变类, 也就是用final修饰, 比如
final int value
. 所以当对象被重新赋值时,会生成新的对象
- 根据Integer的缓存机制,如果在-128-127之间 就从常量池中获取一个Integer对象返回;如果不在范围内 会new Integer(value)返回
public class ValuePassing {
public static void fun1(Integer i) {
System.out.println("在fun1中赋值前i的地址是: " + System.identityHashCode(i));
i = 10; //从常量池中获取一个Integer对象,或者new Integer(value)
//赋值之后,地址发生改变
System.out.println("在fun1中赋值后i的地址是: " + System.identityHashCode(i));
}
public static void main(String[] args) {
Integer i = 5;
System.out.println("在main中i的地址是: " + System.identityHashCode(i));
fun1(i);
i = 20;
//赋值之后,地址发生改变
System.out.println("在main中i重新赋值之后的地址是: " + System.identityHashCode(i));
}
}
output:
在main中i的地址是: 1072591677
在fun1中赋值前i的地址是: 1072591677
在fun1中赋值后i的地址是: 1523554304
在main中i重新赋值之后的地址是: 1175962212
关于缓存:
public class ValuePassing {
public static void fun1(Integer i, Integer j) {
System.out.println("在fun1中赋值前i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
//对i和j同时赋予一样的值后(在-128-127之间),地址一样
i = 10;
j = 10;
System.out.println("-----------------------------------------------");
System.out.println("在fun1中赋值后i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
}
public static void main(String[] args) {
Integer i = 5;
Integer j = 20;
System.out.println("在main中i的地址是: " + System.identityHashCode(i));
System.out.println("在main中j的地址是: " + System.identityHashCode(j));
System.out.println("-----------------------------------------------");
fun1(i, j);
}
}
output:
在main中i的地址是: 1072591677
在main中j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值前i的地址是: 1072591677
在fun1中赋值后j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值后i的地址是: 1175962212
在fun1中赋值后j的地址是: 1175962212
public class ValuePassing {
public static void fun1(Integer i, Integer j) {
System.out.println("在fun1中赋值前i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
//不在-128-127之间地址则不一样
i = 1000;
j = 1000;
System.out.println("-----------------------------------------------");
System.out.println("在fun1中赋值后i的地址是: " + System.identityHashCode(i));
System.out.println("在fun1中赋值后j的地址是: " + System.identityHashCode(j));
}
public static void main(String[] args) {
Integer i = 5;
Integer j = 20;
System.out.println("在main中i的地址是: " + System.identityHashCode(i));
System.out.println("在main中j的地址是: " + System.identityHashCode(j));
System.out.println("-----------------------------------------------");
fun1(i, j);
}
}
output:
在main中i的地址是: 1072591677
在main中j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值前i的地址是: 1072591677
在fun1中赋值后j的地址是: 1523554304
-----------------------------------------------
在fun1中赋值后i的地址是: 1175962212
在fun1中赋值后j的地址是: 918221580