JVM|浅析Integer类型传参值不变来理解Java值传参
浅析Integer类型传参值不变来理解Java值传参
以前对java值的引用传递有一些疑惑,将Integer和String传入方法中进行修改,但最后值却没有修改,现在经过不断的学习以后,对这里有了一些新的体会,现在总结一下。
代码过程 (1)先上代码
private void add(Integer i) {
i = i - 1;
}private void reverse(String s) {
s = "sey";
}public static void main(String[] args) {
Integer i = 1;
String s = "yes";
Test test = new Test();
test.add(i);
test.reverse(s);
// 打印值
System.out.println(String.format("i的值:%d", i));
System.out.println(String.format("s的值:%s", s));
}
(2)打印结果如下:
文章图片
可以看到值没有改变,接下来我来浅析一下这是为什么。
(3)反编译如下:
public class Test
{ public Test()
{
} private void add(Integer i)
{
i = Integer.valueOf(i.intValue() - 1);
} private void reverse(String s)
{
s = "sey";
} public static void main(String args[])
{
Integer i = Integer.valueOf(1);
String s = "yes";
Test test = new Test();
test.add(i);
test.reverse(s);
System.out.println(String.format("i的值:%d", new Object[] {
i
}));
System.out.println(String.format("s的值:%s", new Object[] {
s
}));
}
}
我们可以明显的看出由于java语法糖的缘故,Integer i = 1; 实质上是 Integer.valueOf(1)。接着深入Integer源码看一下:
文章图片
熟悉的朋友都会知道,Integer有一个-128-127的一个缓存,在这个区间内会直接从IntegerCache中获取缓存返回,超出这个区间则返回一个新的对象。
原理分析 本次我从虚拟机栈和堆的来进行探讨:
首先JVM模型如下:
文章图片
(1)显示具体字节码
执行 javap -c Test.class 命令分解方法代码,显示每个方法具体的字节码
public class Test {
public Test();
Code:
0: aload_0// 装载局部变量表[0]位置的变量(一般是this对象)
1: invokespecial #1// 初始化方法
4: return// 方法结束public static void main(java.lang.String[]);
Code:
0: iconst_1// 将常量1压入操作数栈
1: invokestatic#3// 装箱操作(Integer.valueOf()),返回一个对象并且压入栈顶
4: astore_1// 栈顶元素出栈,并将引用存入局部变量表[1]的位置
5: ldc#5// String yes 把常量池中的项压入栈
7: astore_2// 栈顶元素出栈,并将引用存入局部变量表[2]的位置
8: new#6// 创建Test对象(堆上分配内存,返回引用),并将引用压入栈顶
11: dup// 栈顶元素出栈,并将栈顶元素复制
12: invokespecial #7// 栈顶元素出栈,并且调用实例化init()方法
15: astore_3// 栈顶元素出栈,并将引用存在局部变量表[3]的位置
16: aload_3// 装载局部变量表[3]的引用 -----> 对应test
17: aload_1// 装载局部变量表[1]的引用 -----> 对应i
18: invokespecial #8// 调用实例化方法test,add()方法
21: aload_3// 装载局部变量表[3]的引用
22: aload_2// 装载局部变量表[2]的引用
23: invokespecial #9// 调用实例化方法test,reverse()方法
26: getstatic#10// Field java/lang/System.out:Ljava/io/PrintStream;
29: ldc#11// String i的值:%d
31: iconst_1
32: anewarray#12// class java/lang/Object
35: dup
36: iconst_0
37: aload_1
38: aastore
39: invokestatic#13// Method java/lang/String.format:(Ljava/lang/String;
[Ljava/lang/Object;
)Ljava/lang/String;
42: invokevirtual #14// Method java/io/PrintStream.println:(Ljava/lang/String;
)V
45: getstatic#10// Field java/lang/System.out:Ljava/io/PrintStream;
48: ldc#15// String s的值:%s
50: iconst_1
51: anewarray#12// class java/lang/Object
54: dup
55: iconst_0
56: aload_2
57: aastore
58: invokestatic#13// Method java/lang/String.format:(Ljava/lang/String;
[Ljava/lang/Object;
)Ljava/lang/String;
61: invokevirtual #14// Method java/io/PrintStream.println:(Ljava/lang/String;
)V
64: return
}
(2)我们知道Java虚拟机是线程私有的,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程,这次我们讨论虚拟机栈的这两个方法的栈帧:main(),add()。
文章图片
(3)由图所示,main对应一个栈帧,栈帧中的局部变量表元素i 分别指向堆中的i,s指向堆中的s。
文章图片
当在main()方法中执行到字节码的第 18行: invokespecial #8 // 调用实例化方法test,add()方法
时候因为Java是引用传递,所以会把局部变量表中的 i 的引用地址,传递给实例化对象test的add()方法中的i参数,同时add()方法被调用,压入add()方法的栈帧进入java虚拟机栈中,同时该栈帧拥有自己的局部变量表 i 指向堆中内存 i 。
文章图片
因为 i 此时是局部变量,仅仅存在add的栈帧中,当执行代码
i = i - 1;
(等同于执行了i = Integer.valueOf(i - 1) );
如图所示,在add()方法的栈帧中的局部变量表中 i 内存指向 i1。
文章图片
综上所诉,我们可以看出,原先main()方法的栈帧中的局部变量表 i 的内存地址指向并没有发生任何的改变,所以自然在打印的时候也不会发生任何的改变。仅仅只是add()栈帧中的局部变量表里面的 i 的指向堆中的内存地址发生了改变,并不会影响到main()方法局部变量表中的 i 。
总结 Java中的传递方式是引用传递,在方法中如果要修改变量的值,只能修改原本变量指向堆中内存的值,而不能通过在方法中改变对象的引用地址来进行修改。
public class Test {Integer x = 1;
Integer y = 2;
/**
* 交换变量
*/
private void swap(Integer i1, Integer i2) {
i1 = i1 ^ i2;
i2 = i1 ^ i2;
i1 = i1 ^ i2;
}private void swap(Test test) {
Integer x = test.x;
Integer y = test.y;
x = x ^ y;
y = x ^ y;
x = x ^ y;
test.x = x;
test.y = y;
}
public static void main(String[] args) {
Integer i = 1;
Integer j = 2;
Test test = new Test();
test.swap(i, j);
System.out.println(i);
System.out.println(j);
test.swap(test);
System.out.println(test.x);
System.out.println(test.y);
}
}
【JVM|浅析Integer类型传参值不变来理解Java值传参】这样就能成功交换变量的值了:结果如下
文章图片
推荐阅读
- jvm关于String
- jvm常见分析工具
- 08_JVM学习笔记_类命名空间解析
- 浅析(成人情趣用品智能无人自动售货机是新零售的下一个风口吗())
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 浅析唐.温庭筠《菩萨蛮.小山重叠金明灭》
- python日志重复输出
- 浅析栈溢出遇到的坑及绕过技巧
- jvm常用命令
- codility|codility 之 MissingInteger