使用“final"修饰符,只要在Java中适用

在Java中,有一种惯例,将每个变量(局部变量或类)声明为final参数(如果它们确实是final的话)。

虽然这会使代码更加冗长,但这有助于容易阅读/掌握代码,也可以防止错误,因为意图被清晰地标记出来。

你对此有何看法?

71621 次浏览

我从来没有遇到过在变量上有最后一个关键字就能阻止我犯错误的情况,所以目前我认为这是一种巨大的浪费时间。

除非有真正的理由这样做(比如你想要明确指出变量是final的),否则我宁愿不这样做,因为我发现这样做会使代码的可读性降低。

但是,如果您没有发现它会使代码更难阅读或更长时间地编写,那么无论如何都可以使用它。

编辑:作为一个澄清(并试图赢回反对票),我不是说不要将常量标记为最终值,我是说不要做这样的事情:

public String doSomething() {
final String first = someReallyComplicatedExpressionToGetTheString();
final String second = anotherReallyComplicatedExpressionToGetAnother();


return first+second;
}

在我看来,这只是让代码更难阅读。

同样值得记住的是,所有 final会阻止你重新分配一个变量,它不会使它成为不可变或类似的东西。

在使用final关键字之前,您确实需要了解它的全部用途。它可以应用于变量、字段、方法和类,并对它们产生不同的影响

我建议你看看下面链接的文章,了解更多细节。

< a href = " http://archive。today/ZIwdk" rel="noreferrer">Final Word On the Final关键字 . today/ZIwdk" rel="noreferrer">最后的字

我很少在方法或类上使用final,因为我喜欢允许人们重写它们。

否则,我只在它是public/private static final type SOME_CONSTANT;时使用finally

有效的Java有一个条目写着“偏爱不可变对象”。将字段声明为final可以帮助您在这方面迈出一小步,但是对于真正的不可变对象来说,当然还有更多的事情要做。

如果您知道对象是不可变的,那么可以在多个线程/客户端之间共享它们以供读取,而无需担心同步问题,并且更容易推断程序的运行方式。

我认为这一切都与良好的编码风格有关。当然,你可以在不使用大量final修饰符的情况下编写良好、健壮的程序,但当你考虑它时…

final添加到不应该更改的所有内容中,只会缩小您(或下一个程序员,处理您的代码)误解或误用导致您的代码的思想过程的可能性。至少当他们现在想要改变你以前不变的东西时,它应该敲响一些警钟。

一开始,在你的代码中看到很多final关键字看起来有点尴尬,但很快你就会停止注意这个词本身,只会简单地想,that-thing-will-never-change-from-this-point-on(你可以从我这里拿走它;-)

我认为这是很好的练习。我并没有一直使用它,但是当我可以并且有意义地将一些东西标记为final时,我会这样做。

当在Java中与变量一起使用时,Final提供了c++中常量的替代品。所以当final和static被用于一个变量时,它就变成了不可变的。同时让迁移的c++程序员非常开心;-)

当与引用变量一起使用时,它不允许您重新引用对象,尽管对象可以被操作。

当final与方法一起使用时,它不允许子类重写该方法。

一旦用法非常清楚,就应该谨慎使用。这主要取决于设计,因为在方法上使用final不利于多态性。

只有当你非常确定变量的值不会/不应该被改变时,才应该将它用于变量。还要确保您遵循SUN鼓励的编码约定。int COLOR_RED = 1;(大写字母以下划线分隔)

对于引用变量,只在需要对特定对象的不可变引用时使用它。

关于可读性部分,在使用最终修饰语时,注释起着非常重要的作用。

为事件监听器使用匿名本地类,这是Java中的常见模式。 final关键字最常见的用法是确保偶数监听器可以访问作用域内的变量

但是,如果您发现自己被要求在代码中放入大量的最终语句。这可能是你做错事的好暗示。

上面的文章给出了这样一个例子:

public void doSomething(int i, int j) {
final int n = i + j; // must be declared final


Comparator comp = new Comparator() {
public int compare(Object left, Object right) {
return n; // return copy of a local variable
}
};
}

我将它用于方法内部和外部的常量。

我有时只将它用于方法,因为我不知道子类是否不想覆盖给定的方法(无论出于什么原因)。

至于类,我只对一些基础类使用了final类。

IntelliJ IDEA会在函数参数被写入函数内部时发出警告。我已经停止使用final作为函数参数了。我在java运行库中也没有看到它们。

强烈鼓励使用final 为常量。然而,我不会将它用于方法或类(或者至少考虑一下),因为它使测试更加困难,如果不是不可能的话。如果你绝对必须让一个类或方法成为final,确保这个类实现了一些接口,这样你就可以有一个模拟实现相同的接口。

final修饰符,特别是对于变量,是一种让编译器强制执行通常合理的约定的方法:确保一个(局部或实例)变量被精确地赋值一次(不多不少)。通过确保在使用变量之前明确地赋值,可以避免NullPointerException的常见情况:

final FileInputStream in;
if(test)
in = new FileInputStream("foo.txt");
else
System.out.println("test failed");
in.read(); // Compiler error because variable 'in' might be unassigned

通过防止变量被多次赋值,可以防止过宽的作用域。而不是这样:

 String msg = null;
for(int i = 0; i < 10; i++) {
msg = "We are at position " + i;
System.out.println(msg);
}
msg = null;

我们鼓励你这样做:

 for(int i = 0; i < 10; i++) {
final String msg = "We are at position " + i;
System.out.println(msg);
}

一些链接:

我一直使用final作为对象属性。

final关键字在对象属性上使用时具有可见性语义。基本上,设置最终对象属性的值发生在构造函数返回之前。这意味着只要你不让this引用逃脱构造函数,并且你为所有 you属性使用final,你的对象(在Java 5语义下)就能保证被正确构造,而且由于它也是不可变的,它可以安全地发布到其他线程。

不可变对象不仅仅是关于线程安全。它们也让你更容易推断程序中的状态转换,因为可以改变的空间是故意的,如果使用一致,完全限制在应该改变的地方。

我有时也把方法设为final,但不经常。我很少把课程定为期末考试。我通常这样做,因为我很少需要这样做。我通常不怎么使用继承。我更喜欢使用界面和对象组合——这也有助于我发现通常更容易测试的设计。当你为接口而不是具体类编写代码时,你不会需要在测试时使用继承,就像在jMock这样的框架中一样,用接口创建模拟对象比用具体类要容易得多。

我想我应该把大部分课程都定为期末考试,但我还没有养成这个习惯。

为了工作,我必须阅读大量的代码。在实例变量上缺少final是最让我恼火的事情之一,这使得理解代码变得不必要的困难。在我看来,局部变量的final会导致更多的混乱而不是清晰。该语言的设计应该使其成为默认值,但我们不得不接受这个错误。有时它很有用,特别是对于循环和if-else树的明确赋值,但大多数情况下它往往表明你的方法太复杂了。

为:

  • Final字段——将字段标记为Final将强制在构造结束时设置它们,从而使该字段引用不可变。这允许安全发布字段,并且可以避免以后读取时需要同步。(注意,对于一个对象引用,只有字段引用是不可变的-对象引用引用的东西仍然可以改变,这影响了不可变性。)
  • 最终静态字段-虽然我现在使用枚举的许多情况下,我曾经使用静态最终字段。

考虑但审慎地使用:

  • 最终类——框架/API设计是我唯一考虑的情况。
  • Final方法——基本上与Final类相同。如果你疯狂地使用模板方法模式,并把东西标记为final,你可能太依赖继承而不是委托。

忽略,除非感觉肛门:

  • 方法参数和局部变量——我很少这样做,很大程度上是因为我很懒,我发现它会使代码变得混乱。我完全承认,标记我不打算修改的参数和局部变量是“正确的”。我希望这是默认设置。但它不是,我发现代码更难理解的期末。如果我在别人的代码中,我不会把它们取出来但如果我在写新代码,我就不会把它们放进去。一个例外是,您必须将某些内容标记为final,以便从匿名内部类中访问它。

我从不在局部变量上使用它们,没有什么意义。即使您不认为变量应该被重新赋值,这对于下一个改变代码的人来说也没有什么区别,而且由于代码正在被更改,任何使其最终的原始目的都可能不再有效。如果只是为了清晰,我认为它失败了,因为冗长的负面影响。

对于成员变量也是如此,因为它们提供的好处很少,除了常量的情况。

它也与不可变无关,因为不可变的最好指标是它被这样记录并且/或没有可以改变对象的方法(这与使类为final是保证它是不可变的唯一方法)。

但是,嘿,这只是我的观点:-)

我设置Eclipse在所有未修改的字段和属性上添加final。使用Eclipse“save actions”在保存文件时添加这些最终修饰符(以及其他东西),效果非常好。

强烈推荐。

查看Eclipse Save Actions中的我的博客

最后显然应该用于常量,并强制不变性,但在方法上还有另一个重要的用途。

有效的Java有一个关于此的完整项目(项目15),指出了意外继承的陷阱。实际上,如果您没有为继承而设计和记录您的类,那么从它继承可能会带来意想不到的问题(该项目提供了一个很好的例子)。因此,建议在任何不打算继承的类和/或方法上使用最后

这可能看起来很严厉,但也有道理。如果你正在编写一个类库供其他人使用,那么你就不希望他们继承不是为它设计的东西——为了向后兼容,你将把自己锁定在类的特定实现中。如果你在一个团队中编码,没有什么可以阻止团队的其他成员删除最后,如果他们真的必须这样做的话。但是关键字使他们思考他们正在做什么,并警告他们,他们继承的类不是为它设计的,所以他们应该格外小心。

Final应该总是用于常量。当定义变量的规则很复杂时,它甚至对于短期存在的变量(在单个方法中)也很有用。

例如:

final int foo;
if (a)
foo = 1;
else if (b)
foo = 2;
else if (c)
foo = 3;
if (d)        // Compile error:  forgot the 'else'
foo = 4;
else
foo = -1;
将类标记为final也可以使一些方法绑定发生在编译时而不是运行时。 考虑下面的“v2.foo()”——编译器知道B不能有子类,所以foo()不能被重写,所以要调用的实现在编译时是已知的。如果类B没有被标记为final,那么v2的实际类型可能是某个扩展了B并重写了foo()的类
class A {
void foo() {
//do something
}
}
final class B extends A {
void foo() {
}
}
class Test {
public void t(A v1, B v2) {
v1.foo();
v2.foo();
}
}

另一个注意事项是,许多人将final混淆为实例变量的内容不能更改,而不是引用不能更改。

对于声明每个可能的变量final,我非常武断。这包括方法参数、局部变量,以及很少的值对象字段。我有三个在任何地方声明最终变量的主要原因:

  1. 声明意图:通过声明最后一个变量,我是在声明这个变量只被写入一次。这对其他开发人员是一个微妙的提示,而对编译器则是一个很大的提示。
  2. 强制使用一次性变量:我认为每个变量在生活中应该只有一个目的。通过只赋予每个变量一个用途,可以减少调试时了解特定变量用途所需的时间。
  3. 允许优化:我知道编译器过去有一些性能增强技巧,这些技巧特别依赖于变量引用的不可变性。我认为这些旧的性能技巧(或新技巧)将被编译器使用。

然而,我确实认为final类和方法远不如final变量引用有用。final关键字,当与这些声明一起使用时,只是为自动化测试和以您从未预料到的方式使用代码提供了障碍。

即使对于局部变量,知道它被声明为final意味着我不需要担心引用稍后会被更改。这意味着当调试时,我看到这个变量,我相信它引用的是同一个对象。这是我在寻找bug时少需要担心的一件事。 额外的好处是,如果99%的变量都被声明为final,那么少数真正是变量的变量就会更加突出。 此外,final可以让编译器发现更多可能被忽略的愚蠢错误

对于争论,我认为不需要。大多数情况下,它们只会损害可读性。重新分配一个参数变量是如此疯狂的愚蠢,我应该相当有信心,他们可以被视为常量。

Eclipse将最终结果显示为红色,这使得在代码中更容易发现变量声明,我认为这在大多数情况下提高了可读性。

我试图强制执行规则,任何和所有变量都应该是最终的,没有一个非常有效的理由不这样做。如果你只需要找到初始化并确信这就是初始化,那么回答“这个变量是什么?”的问题就容易得多了。

实际上,我现在对非最终变量相当紧张。这就像把刀挂在你的头上,还是把它放在厨房抽屉里的区别……

final变量是标记值的好方法。

一个非最终变量被绑定到一些容易出错的算法的一部分。

一个很好的特性是,当一个算法无法选择使用变量时,大多数情况下解决方案是编写一个方法,这通常会显著改善代码。

我已经编写了一段时间的代码,并尽可能使用final。这样做了一段时间后(对于变量、方法参数和类属性),我可以说90%(或更多)的变量实际上是最终的。我认为在你不想修改变量的时候不修改变量的好处(我以前看到过,有时这很痛苦)是为了在你的代码中额外的输入和额外的“final”关键字。

也就是说,如果我要设计一种语言,我将使每个变量都是final,除非被其他一些关键字修改。

对于类和方法,我不太使用final。这是一个或多或少复杂的设计选择,除非您的类是一个实用程序类(在这种情况下,您应该只有一个私有构造函数)。

我也使用Collections.unmodifiable…在我需要的时候创建不可修改的列表

听起来反对使用final关键字的最大参数之一是“这是不必要的”,而且它“浪费空间”。

如果我们承认“final”的许多好处,同时承认它需要更多的输入和空间,我认为Java应该默认将变量设置为“final”,并且如果编码器想要的话,就要求将变量标记为“mutable”。

选择为每个方法中的每个参数键入final会让编码员和代码读者感到非常恼火。

一旦愤怒超出合理范围,切换到Scala,默认情况下参数是最终的。

或者,您总是可以使用代码样式工具,它将自动为您完成这些工作。所有ide都实现了它们或作为插件。