深入分析java传参
来源:互联网 发布:linux 保存退出 编辑:程序博客网 时间:2024/06/05 20:50
概述
java中的参数传递问题可以根据参数的类型大致可以分为三类:传递基本类型,传递String类型,传递引用类型,至于最终是否可以归纳为值传递和引用传递,根据每个人的理解不同,答案不同,此处不做强调。
传递基本类型
public class Test1 { public static void main(String[] args) { int n = 3; System.out.println("Before change, n = " + n); changeData(n); System.out.println("After changeData(n), n = " + n); } public static void changeData(int n) { n = 10; }}
结果:Before change, n = 3
After changeData(n), n = 3
解析(比较简单不结合字节码分析):
1.线程调用main方法,创建栈帧A,局部变量表有n=3
2.main方法中调用changeDate方法,传入参数n=3,线程创建栈帧B,将10赋给n后,局部变量表有n=10
3.changeDate方法执行完毕,栈帧B弹出,输出栈帧A中n的值为3
传递String类型
public class Test2 { public static void main(String[] args) { String str = new String("String"); System.out.println("Before change, str = " + str); changeData(str); System.out.println("After changeData(n), str = " + str); } public static void changeData(String str) { str = "newString"; }}
结果:Before change, str = String
After changeData(n), str = String
指令码为(将上述代码两条输出语句删除后进行编译,反汇编,为了突出主要过程):
public static void main(java.lang.String[]); 0: new #2 // class java/lang/String 3: dup 4: ldc #3 // 返回常量池中字符串的引用,并且入栈 6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 9: astore_1 10: aload_1 11: invokestatic #5 // Method changeData:(Ljava/lang/String;)V 14: return public static void changeData(java.lang.String); 0: ldc #6 // 返回常量池中字符串的引用,并且入栈 2: astore_0 3: return}
解析: 1.new,dup,Idc,invokespecial,astore_1:在栈帧A中完成了实例化一个String对象,并将一个指向该对象的引用存入了局部变量表的操作
2.aload_1,invokestatic:调用changeDate方法,传入引用,创建栈帧B
3.Idc,astore_0,return:在栈帧B中完成了将指向常量池中"newString"字符串的引用压入操作数栈并且将该引用存入局部变量表的操作,之后栈帧B弹出
4.栈帧A局部变量表中那个引用依然指向String对象,其值依然为String
传递引用类型
public class Test3 { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello "); System.out.println("Before change, sb = " + sb); changeData(sb); System.out.println("After changeData(n), sb = " + sb); } public static void changeData(StringBuffer strBuf) { strBuf.append("World!"); }}
结果:Before change, sb = Hello
After changeData(n), sb = Hello World!
指令码为(将上述代码两条输出语句删除后进行编译,反汇编,为了突出主要过程):
public static void main(java.lang.String[]); 0: new #2 // class java/lang/StringBuffer 3: dup 4: ldc #3 // String Hello 6: invokespecial #4 // Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V 9: astore_1 10: aload_1 11: invokestatic #5 // Method changeData:(Ljava/lang/StringBuffer;)V 14: return public static void changeData(java.lang.StringBuffer); stack=2, locals=1, args_size=1 0: aload_0 1: ldc #6 // String World! 3: invokevirtual #7 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/ StringBuffer; 6: pop 7: return}
解析(说明下和String传参区别的地方):
在changeDate方法中有了aload操作,也就是将传递来的引用压入了操作数栈,并且之后的Idc,invokevirtual操作说明对该引用指向的对象进行了相关操作,很显然在栈帧B弹出时,栈帧A局部变量表中的引用指向的对象发生了变化。
总结
回头看一下:综合来看基本变量和String变量传参,对传入参数进行改变的时候,都没有用到传入的参数值(也就是没有aload操作),直接将基本类型值或者常量池中字面量引用赋值给变量。怎么看都有些别扭,因为String本质上是一个类和基本类型中终究是不同的,我的理解是:String类既然设计成final类,暗示string变量的复用带来的正面效果大于由于不能改变String变量而必须存入一个新的string字符串的负面效果,那么为了复用,对于String变量的赋值语句在编译时便进行了特殊处理,在常量池中找是否已经存在该字符串,如果有,返回引用,达到复用的目的,如果没有,将字符串放入常量池返回该引用为了下次复用。而对于其他引用变量传参,当栈帧B要对传入参数进行改变的时候,都会进行aload操作,由于jvm是基于栈的字节码执行,aload的参数只能是栈帧A中引用的复制,这点区别于C,由于C是基于寄存器的操作,其指针传递,操作是的是指针变量本身,可以用一个经典的引用交换实例区分,网上有举例,不在累述,以上。
- 深入分析java传参
- 深入分析java前景
- Java HttpClient深入分析
- 深入JAVA源码分析
- 深入分析Java多态性
- 【Java】BlockingQueue深入分析
- java hashmap深入分析
- Java 枚举深入分析
- java乱码深入分析
- 深入分析 java注解
- java 深入分析ConcurrentHashMap
- 深入分析Java中的数据结构
- JAVA泛型深入分析
- 深入分析Java ClassLoader原理
- Java Random类深入分析
- 深入分析Java ClassLoader原理
- 深入分析Java ClassLoader原理
- 深入分析Java ClassLoader原理
- ubuntu系统安装流程
- 6卡驱动win7系统,挖矿6卡驱动
- 多线程
- 洛谷1631 序列合并
- 操作系统 第10章
- 深入分析java传参
- lintcode---转换成回文串(leetcode---Shortest Palindrome)
- ImageLoader--pizifusheng
- 进程间通信方式
- 李航博士的《浅谈我对机器学习的理解》
- http协议&Tomcat
- 购物车页面
- 网路状态判断
- okHttp拦截器的使用