[讨论] 关于Local Class可以访问有final关键词修饰的local variables

chong_zh 2013-07-04
之前没有用到过,一直没有注意到,今天了解到Local Class可以访问final local variables,很好奇其在JVM中的实现,特来求大牛指点。

简单描述一下,例如下面的代码,在匿名内部类中访问了calculateArea方法的参数,由于两个参数都是final的,所以这段代码是可以正常执行的。
Class Rectangle
{
    public int calculateArea(final int w, final int h)
    {
        postTask(new Runnable()
        {
            public void run()
            {
                writeToFile(w*h);
            }
        });
    }

}


令人好奇的是这一feature是怎么实现的,calculateArea的参数保存在它的方法栈上,而writeToFile这一句执行时已经是另一个线程的另一条方法栈,好奇JVM用一个怎样的机制完美实现该feature。
RednaxelaFX 2013-07-05
方法内部类(包括local class、anonymous class之类)可以访问final修饰的外部变量或参数,这是Java的非常弱的闭包的功能。

这功能在Java里是纯语言层面的,在JVM里没有任何特殊支持。也就是说,这个功能在把Java源码编译到Class文件的过程中就已经解除语法糖变成很一般的东西,JVM不需要任何特别的实现就可以支持它的运行。

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.1.3
Java Language Specification, Java SE 7 Edition, 8.1.3 写道
Any local variable, formal parameter, or exception parameter used but not declared in an inner class must be declared final.

Any local variable used but not declared in an inner class must be definitely assigned (§16) before the body of the inner class.


具体来说,之所以设计成只能捕获final的变量或参数,就是因为final的话capture-by-value和capture-by-reference的效果会是一样的,用户无法分辨语言实际是采用哪种办法实现的。

实际上Java实现楼主的例子是类似这样:
class Rectangle {
  public int calculateArea(final int w, final int h) {
    postTask(new Rectangle$1(w, h));
  }
}

class Rectangle$1 implements Runnable {
  private int w$;
  private int h$;

  Rectangle$1(int w, int h) {
    this.w$ = w;
    this.h$ = h;
  }

  public void run() {
    writeToFile(this.w$ * this.h$);
  }
}

也就是用了capture-by-value的实现方式:闭包(这里是内部类)捕获的外部变量的值在创建闭包实例时被拷贝到了闭包实例里。

要体现capture-by-reference的效果,那语言就必须允许捕获非final的变量,然后对变量的值进行修改。那就得用更繁琐的实现方式了。
chong_zh 2013-07-05
茅塞顿开,Thanks very much~
deyimsf 2013-08-09
RednaxelaFX 写道

private String a1="1111";
private final String a2="2222";

这两个变量都会往常量池放一份,除了a2以后不能再修改外,跟a1还有啥区别?请大神解惑。
RednaxelaFX 2013-08-09
应该说这俩成员所指向的初始值都被常量池(以及全局驻留字符串池)所引用;这俩成员自身是作为对象的成员字段放在对象内的。
然后就没啥了,a1赋初值后还可以变,a2不能变。仅此而已。
Global site tag (gtag.js) - Google Analytics