从字节码角度理解一些java 问题

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 InsideClass.classfinalthis 0 全局变量, 并且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:
    。。。。
}

ExternalClass 1ExternalClassthis 0;
将传参 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值