从字节码角度理解一些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}

ExternalClassInsideClass.classfinalthis0 全局变量, 并且jvm 自动生成一个 带有参数的构造方法,
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:    。。。。}

ExternalClass1ExternalClassthis0;
将传参 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

阅读全文
0 0
原创粉丝点击