`
35687638
  • 浏览: 9731 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

从局部内部类说起

    博客分类:
  • JAVA
JVM 
阅读更多
先上测试代码:
public class TestCompileConstant {
    public static void main(String[] args) {
        List list = method2();
        Field[] fields = list.getClass().getDeclaredFields();
        Constructor[] cons = list.getClass().getDeclaredConstructors();
        for(Constructor con:cons){
            System.out.println(con.toString());
        }
        for(Field field:fields){
            System.out.println(field.toString());
        }
    }
    static List method2(){
         Random ran = new Random();
        final int a = ran.nextInt();
        final int b = 5;
        final int bb = ran.nextInt();
         final int[] cc = new int[12];
            for (int i = 0; i < cc.length; i++) {
                 cc = i;
            }
            System.out.println(a);
            return new AbstractList(){
                public Object get(int i){
                    if(i<0||i>=cc.length)
                        return b;
                    return cc[i];
                }

           @Override
                  public int size() {
                    switch(2){
                      case 1:
          //            case a:        //is not compile-time constant
                       break;
                      }
                    int c = bb;
                    return a;
           }
            };
    }
}



奇怪的地方:
List list = method2();
list.get(1) = 1;  //???????
照理说 method2()的返回值list get()方法返回是一个本该失效的局部变量cc,那么是什么使cc能够存活下来呢?

第一天: 粗看int[] 为对象,那么应该存在heap区,当方法结束后,内部类list 中仍有应用所以没有被gc .
问题: int[] 是对象没错,那么换一个基本类型呢?局部变量b一样可以被返回怎么解释?

第二天: b 为一个compile-time constant 即编译时常量,当运行时b是作为literal存在的,即运行时代码应为:  if(i<0||i>cc.length) return 5;
问题: 从这个角度似乎能解释问题了,那么运行时常量 a 又如何呢?

第三天: 从测试代码看,a也可以被正常返回,通过查阅资料理解为: jvm处理一个 .class ,将对像放入heap区;具体执行的方法在java栈的java帧中;类的基本信息存放在方法区,其中也包括常量池.故推论,final a 存入常量池,方法结束java帧销毁但与常量无关,故可以被访问.

第四天: 常量池中只有 compile-time constant , 像a这样的运行时常量不在此列,迷茫中...

第五天: 真相大白,局部内部类凡是引用外部的运行时常量,jvm都会自动为其创建一个final field,及一个相应的构造方法. 关于为什么compile-time constant 不需要,请看第二天内容.
这也说明为什么内部类中只能访问外部的final属性,它需要自己也copy一份作为自己的field,而使用final修饰符就不用担心两者的不一致.

具体可见test code 的输出内容,利用反射我们可以得到 list 的内部结构:

test.newTest.TestCompileConstant$2(int,int)
public java.lang.Object test.newTest.TestCompileConstant$2.get(int)
public int test.newTest.TestCompileConstant$2.size()
private final int test.newTest.TestCompileConstant$2.val$bb
private final int test.newTest.TestCompileConstant$2.val$a


over....
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics