从字节码角度理解一些java 问题
来源:互联网 发布:在线编程网站 编辑:程序博客网 时间:2024/06/11 07:12
1、非静态内部类对象持有外部的引用:
public class ExternalClass{ class InsideClass{ } ExternalClass(){ InsideClass inside = new InsideClass(); }}
javac 会编译出两个ExternalClass$InsideClass.class 与ExternalClass.class
使用javap -c 查看这两个文件:
public class ExternalClass { ExternalClass(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: new #2 // class ExternalClass$InsideClass 7: dup 8: aload_0 9: invokespecial #3 // Method ExternalClass$InsideClass."<init>":(LExternalClass;)V 12: astore_1 13: return}
ExternalClass.class 可以看出在
aload_0先将自己的引用this,压入栈中。
然后调用了一个内部类带参数的构造方法,将this 传了过去。
Compiled from "ExternalClass.java"class ExternalClass$InsideClass { final ExternalClass this$0; ExternalClass$InsideClass(ExternalClass); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LExternalClass; 5: aload_0 6: invokespecial #2 // Method java/lang/Object."<init>":()V 9: return}
ExternalClass
0: aload_0 取出局部变量表中的首位(默认是当前对象this),压入操作数栈中
1: aload_1 取出局部变量表中的第二位(构造方法中的:ExternalClass 对象), 压入操作数栈中。
2:putfield #1 给#1的赋值指令,结合操作数栈中的值,解释为:取出 操作数栈栈顶的值(aload_1 放入的),赋值给 操作数栈中对象(aload_0放入的),的#1 变量中
ps:备注静态的内部类,是没有this$0,不持有外部类对象引用
内部类访问局部变量,局部变量要定义为final
源码为:public class ExternalClass{ interface InsideClass { public void test(); } ExternalClass(final int i){ new InsideClass(){ @Override public void test(){ int a = i; } }; }}
ExternalClass 的字节码为:
Compiled from "ExternalClass.java"public class ExternalClass { ExternalClass(int); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: new #2 // class ExternalClass$1 7: dup 8: aload_0 9: iload_1 10: invokespecial #3 // Method ExternalClass$1."<init>":(LExternalClass;I)V 13: pop 14: return}
从ExternalClass 首先可以看到 匿名内部类的构造方法是有两个传参(ExternalClass, int)
aload_0 将局部变量表中的第0 位(this),压入操作数栈中。
iload_1 将局部变量表中的第1位(即参数int),压入操作数栈中。
在看ExternalClass$1.class
Compiled from "ExternalClass.java"class ExternalClass$1 implements ExternalClass$InsideClass { final int val$i; final ExternalClass this$0; ExternalClass$1(ExternalClass, int); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LExternalClass; 5: aload_0 6: iload_2 7: putfield #2 // Field val$i:I 10: aload_0 11: invokespecial #3 // Method java/lang/Object."<init>":()V 14: return public void test(); Code: 。。。。}
ExternalClass
将传参 int 给 全局变量val$i;
到此在匿名内部类中 有一个 int 变量
外部类的方法中有一个int 变量
为了保持这两个值的一致性,需要同时定义为final
final 变量的初始化
众说周知,final 变量只能在
1、定义的时候赋值
2、构造方法中赋值
3、构造代码块 中赋值
为什么只有这三处可以赋值呢? 与普通变量与静态变量的区别是什么?
public class Test { public final int a = 100; public final int b; public int c = 6; public static final int d = 0; { b = 0; }}
javap 反编译后 对应的结果为:
public final int a; descriptor: I //变量类型为int flags: ACC_PUBLIC, ACC_FINAL //变量是一个public final 类型的 ConstantValue: int 100 //设置此final 值为 100 public final int b; descriptor: I flags: ACC_PUBLIC, ACC_FINAL public int c; descriptor: I flags: ACC_PUBLIC public static final int d; descriptor: I flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL ConstantValue: int 0 //设置此 静态 final 字段的值为 0 public Test(); descriptor: ()V flags: ACC_PUBLIC Code: //除了static final 变量d, 其他变量实际进行赋值的地方。 stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: bipush 100 //将100 放入栈顶 7: putfield #2 // Field a:I 将栈顶数据(100)取出,赋值给 #2 即a 10: aload_0 11: bipush 6 //将 6放入栈顶 13: putfield #3 // Field c:I c = 6 16: aload_0 17: iconst_0 // 将0 放入栈顶 18: putfield #4 // Field b:I b = 0 21: return LineNumberTable: line 1: 0 line 2: 4 line 4: 10 line 7: 16 line 8: 21
从字节码可以看出:
1、static final && final变量是在 定义时真正去赋值的
2、final 与其他的普通变量 实际上会在 构造方法中再次进行赋值
3、构造代码块中的代码实际是在构造方法中执行的
ps:
static 普通的静态变量,会在静态代码块中执行,例如:
public static int c = 6;
实际上的初始化位置为:
“`
public static int c;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 6 //将6 放入栈中
2: putstatic #4 // Field c:I 将6赋值给c
5: return
LineNumberTable:
line 4: 0
“`
http://www.importnew.com/13107.html
- 从字节码角度理解一些java 问题
- 从字节码角度分析java泛型数组的问题
- 从字节码角度理解JVM异常处理机制的原理
- Java 函数调用是传值还是传引用? 从字节码角度来看看!
- Java 函数调用是传值还是传引用? 从字节码角度来看看!
- 从资源配置的角度理解IT领域的一些场景
- 从阅读Java字节码来解决一些疑难杂症
- 理解java字节码
- 从字节码角度看String的连接操作
- 从字节码角度分析接口中的成员域
- Java 从设计者的角度理解Java IO流
- 小例子:从计算机的角度理解问题
- 用汇编语言角度来理解C语言的一些问题
- 编辑从字节码和 JVM 的角度解析 Java 核心类 String 的不可变特性
- 从Java的角度理解Ext的extend
- 从操作系统进程的角度理解 Java 虚拟机
- 从C语言程序员角度理解Java中的 interface
- 从内存的角度理解java中的继承
- static TA和user TA
- java浏览器下载文件
- Universalimageloader图片加载框架缓存本地图片的使用
- 用户体验似乎已是明日黄花,因为已经万事俱备了-是真的吗?
- apache kafka系列之在zookeeper中存储结构
- 从字节码角度理解一些java 问题
- 加载较大的本地文件作为背景图时 防止内存溢出
- Ubuntu下自动更新Firefox的Adobe flash player
- call与apply
- PHP面向对象三大特性之封装基础
- Java后端WebSocket的Tomcat实现(转载)
- Git基本操作
- CSS布局 ——display,position, float
- 【Tomcat9源码分析】Container、Pipeline和Vavle设计