关键字final有什么用?

时间:2010-12-29 18:00:30

标签: java android final

在下面的代码中,如果我从EditText中删除关键字final,我在第(6)行中遇到错误,我将EditText对象(et)传递给intent ...我必须知道final关键字的重要性...

final EditText et=(EditText)findViewById(R.id.t);
        Button b=(Button)findViewById(R.id.b1);
        b.setOnClickListener(new Button.OnClickListener(){
            public void onClick(View v)<br>
            {
            Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
            startActivity(on);
            }
        });

7 个答案:

答案 0 :(得分:21)

最终本质上意味着变量et不会在任何时候重新分配并保持不变。这意味着内部类(如侦听器)可以相信它不会被其他可能导致各种麻烦的线程重新分配。

final也可用于修改方法或类定义,这意味着该方法不能被子类覆盖,或者该类无法扩展。

答案 1 :(得分:15)

这是因为你在这里使用了闭包。这意味着内部类使用了内部类的上下文。要使用它,变量应该被声明为final,以便不被更改。

查看更多here

答案 2 :(得分:3)

阅读this article以了解所涉及的实施细节:

  

这种限制的原因   如果我们发现一些亮光就会变得明显   如何实施本地类。   匿名本地类可以使用本地   变量因为编译器   自动给出一个班级   用于保存副本的私有实例字段   该类使用的每个局部变量。   编译器还添加了隐藏   每个构造函数的参数   初始化这些自动创建   私人领域。因此,一个本地班   实际上并不访问本地   变量,但仅仅是它自己的私有   他们的副本。唯一的方法就是这样   工作正确是如果当地的   变量被声明为final,所以   他们保证不会改变。   有了这个保证到位,   当地的班级确信它的   变量的内部副本   准确反映当地的实际情况   变量

编辑:

  

柏林布朗说:“我发布了一个反编译版本   匿名的内部阶级。但是要   老实说,我还是不明白为什么   编译器必须拥有该信息。   即使该领域被宣布为最终,   该字段仍然可以为null。我认为   这是Java怪癖中的一个,你   必须声明该字段   最终...因为这就是它的方式。   没有明确的理由为什么“

原因是要确保用户意识到闭包“关闭”变量而不是值。我们假设没有要求最终的局部变量。然后我们可以编写如下代码:

public void doIt() {
    for(int i = 0; i < 3; ++i) {
        runnables.add(new Runnable() {
            @Override
            public void run() {
                System.out.println(i);
            }
        });
    }
    run(runnables); // run each runnable
}

您认为输出是什么?如果你认为它会是“0 1 2”你会错的,因为Runnable关闭了“变量”i而不是那个时间点的i的“值”,因此输出将是“2 2 2”。在这里可以做些什么来实现预期的行为?两种解决方案:要么依赖用户了解闭包的工作方式,要么在语言层面以某种方式强制执行闭包。这是语言设计师使用的第二种选择。

public void doIt() {
    for(int i = 0; i < 3; ++i) {
        final int j = i; // notice the final local variable
        runnables.add(new Runnable() {
            @Override
            public void run() {
                System.out.println(j);
            }
        });
    }
    run(runnables);
}

JFTR,我并不是说第二个选项是“走向”的方式,只是在用于匿名内部类之前将局部变量标记为final是对我来说是一个很大的障碍。当然,YMMV。 : - )

答案 3 :(得分:2)

最终使变量et只允许分配一次。它还会更改变量的范围,并允许函数onClick可见性等。如果没有最终结果,则在onClick函数中看不到et。

答案 4 :(得分:2)

JAVA中“final”关键字的目的可以在三个级别定义,分类,方法,变量

Java final variable: If you make any variable as final, you cannot change the value of final variable (It will be constant).

Java final method: If you make any method as final, you cannot override it.

Java final class: If you make any class as final, you cannot extend it.

答案 5 :(得分:0)

Here is a link讨论final关键字。根据{{​​3}}(第4.12.4节):

  

变量可以声明为final。最终变量只能分配一次。如果分配了最终变量,则编译时错误,除非在分配之前它是明确未分配的(第16段)。

答案 6 :(得分:0)

它还有助于理解Java如何创建匿名内部类。 Java如何实现该特定类。

Java需要该变量为final,因为编译器必须在编译时知道该对象的类型。然后编译器将在匿名内部类实现中生成一个字段(在本例中为'et')。

如果类型不是final,那么编译器将如何确定如何构建内部类实现。基本上,通过声明字段final,您将为Java编译器提供更多信息。

无法帮助编译器的代码,不会编译您的匿名内部类:

Object et;
et = a ? new Object() : new EditText();

...

使用上面的代码,Java编译器无法真正构建匿名内部类。

您的代码:

final EditText et =(EditText)findViewById(R.id.t);

...

new Button.OnClickListener$1(){
            public void onClick(View v)<br>
            {
            Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
            startActivity(on);
            }
        });

... java编译器将创建一个字节码类,这是您提供的内部类块的实现,可能看起来像这样。

public class OnClickListener$1 {

 private final EditText et ; <---- this is important
 public OnClickListener$1(final et) {
    this.et = et;
 }
 public void onClick(View v)<br>
                {
                Intent on=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+et.getText()));
                startActivity(on);
                }
}

您可以通过查找匿名字节码类$ 1并对该字节码文件进行反编译来测试我提供的伪代码。