在方法中声明类 - 最终关键字

时间:2013-04-15 05:35:59

标签: java

在方法中给出以下内部类(IsSomething):

public class InnerMethod {

private int x;


public class Something {
    private int y;

    public void printMyNumber(double x)
    {
        class IsSomething extends Something {

            public void print() {
                System.out.println(x);
            }

        }
    }
}

}

为什么X变量必须是FINAL才能使其工作..? (我说的是关于“printMyNumber”函数的X参数。)

3 个答案:

答案 0 :(得分:1)

匿名类中的方法实际上无法访问局部变量和方法参数。相反,当实例化匿名类的对象时,对象方法引用的最终局部变量和方法参数的副本将作为实例变量存储在对象中。匿名类对象中的方法确实可以访问那些隐藏的实例变量。

来自JLS:

Any local variable, formal method parameter or exception handler 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.

答案 1 :(得分:1)

区别在于局部变量与类成员变量之间。成员变量在封闭对象的生命周期中存在,因此可以由内部类实例引用。但是,局部变量仅在方法调用期间存在,并且由编译器以不同方式处理,因为它的隐式副本是作为内部类的成员生成的。如果不声明局部变量fi​​nal,可以改变它,导致细微的错误,因为内部类仍然引用该变量的原始值。

Final local variables

  

我知道制作局部变量或者一个局部变量有两个原因   参数最终。第一个原因是您不想要您的代码   更改局部变量或参数。很多人都认为   是一种糟糕的风格来改变方法内部的参数   代码不清楚。作为一种习惯,一些程序员会制作所有参数   "最终"防止自己改变它们。我不这样做,   因为我发现它使我的方法签名有点难看。

     

当我们想要访问本地变量或时,第二个原因就出现了   内部类中的参数。这就是实际原因   据我所知,最终的局部变量和参数是   在JDK 1.1中引入了Java语言。

public class Access1 {
  public void f() {
    final int i = 3;
    Runnable runnable = new Runnable() {
    public void run() {
      System.out.println(i);
    }
    };
  }
}

在run()方法中,如果我们在外部类中使它成为最终的,我们只能访问i。要理解推理,我们必须

  

看一下编译器的作用。它生成两个文件Access1.class   和Access1 $ 1.class。当我们用JAD对它们进行反编译时,我们得到:

public class Access1 {
  public Access1() {}
  public void f() {
    Access1$1 access1$1 = new Access1$1(this);
  }
}

class Access1$1 implements Runnable {
  Access1$1(Access1 access1) {
    this$0 = access1;
  }
  public void run() {
    System.out.println(3);
  }
  private final Access1 this$0;
}

由于i的值是最终的,编译器可以"内联"它进入内在

  

类。令我感到不安的是,局部变量必须是最终的   在看到上述内容之前,由内部类访问。

     

当局部变量的值可以改变为不同的值   内部类的实例,编译器将其添加为数据成员   内部类,并让它在构造函数中初始化。该   这背后的根本原因是Java没有指针,   C的方式。

     

考虑以下课程:

public class Access2 {
  public void f() {
    for (int i=0; i<10; i++) {
    final int value = i;
    Runnable runnable = new Runnable() {
      public void run() {
        System.out.println(value);
      }
    };
    }
  }
} 

这里的问题是我们每次进行for循环时都必须创建一个新的本地数据成员,所以我今天想到了

  

编码时,是将上面的代码改为:

public class Access3 {
  public void f() {
    Runnable[] runners = new Runnable[10];
    for (final int[] i={0}; i[0]<runners.length; i[0]++) {
    runners[i[0]] = new Runnable() {
      private int counter = i[0];
      public void run() {
        System.out.println(counter);
      }
    };
    }
    for (int i=0; i<runners.length; i++)
    runners[i].run();
  }
  public static void main(String[] args) {
    new Access3().f();
  }
}

我们现在不必申报额外的最终本地变量。事实上,这不是真的

  

int []我就像一个指向int的常见C指针?我花了4年时间   看到这个,但是如果你听到这个想法,我想听听你的意见   在其他地方。

答案 2 :(得分:0)

这是因为本地类实例的生命周期可能比定义类的方法的执行时间长得多。因此,本地类必须具有它使用的所有局部变量的私有内部副本(这些副本由编译器自动生成)。确保局部变量和私有副本始终相同的唯一方法是坚持局部变量是最终的。