首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在method - Final关键字中声明类

在method - Final关键字中声明类
EN

Stack Overflow用户
提问于 2013-04-15 13:35:59
回答 3查看 501关注 0票数 3

给定方法中的以下内部类(IsSomething):

代码语言:javascript
复制
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参数。)

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-15 13:39:06

区别在于局部变量和类成员变量。成员变量存在于封闭对象的生存期内,因此它可以被内部类实例引用。然而,局部变量只在方法调用期间存在,并且由编译器以不同的方式处理,因为它的隐式副本是作为内部类的成员生成的。在不声明局部变量final的情况下,可以更改它,由于内部类仍然引用该变量的原始值,因此会导致细微的错误。

Final local variables

我知道将局部变量或参数设为

有两个原因。第一个原因是您不希望您的代码更改局部变量或参数。很多人认为更改方法中的参数是一种糟糕的风格,因为这会使代码变得不清楚。作为一种习惯,一些程序员将他们的所有参数都设为“最终”,以防止他们自己更改它们。我不这样做,因为我发现这让我的方法签名有点难看。

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

代码语言:javascript
复制
public class Access1 {
  public void f() {
    final int i = 3;
    Runnable runnable = new Runnable() {
    public void run() {
      System.out.println(i);
    }
    };
  }
}

在run()方法中,只有当我们在外部类中将其设为final时,才能访问i。为了理解其中的原因,我们必须

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

代码语言:javascript
复制
public class Access1 {
  public Access1() {}
  public void f() {
    Access1$1 access1$1 = new Access1$1(this);
  }
}

代码语言:javascript
复制
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的值是final,编译器可以将其“内联”到内部

类。直到我看到上面的内容,内部类才能访问局部变量,这让我感到不安。

当局部变量的值因内部类的不同实例而改变时,编译器会将其添加为内部类的数据成员,并在构造函数中对其进行初始化。这背后的根本原因是Java没有指针,就像C语言所具有的那样。

考虑下面的类:

代码语言:javascript
复制
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循环时,我们都必须创建一个新的本地数据成员,所以我今天有一个想法

在编码时,将上述代码更改为以下代码:

代码语言:javascript
复制
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[] i就像是一个指向一个整数的普通C指针?我花了4年时间才发现这一点,但如果你在其他地方听说过这个想法,我很想听听你的意见。

票数 1
EN

Stack Overflow用户

发布于 2013-04-15 13:38:34

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

来自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
EN

Stack Overflow用户

发布于 2013-04-15 13:44:34

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16008272

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档