在java中使用“final”修饰符

在Java中,有一种做法是声明每个variables(本地或类),参数最终如果是真的。 虽然这使得代码变得更加冗长,但是这有助于易于阅读/掌握代码,并且可以防止错误,因为意图是明确标记的。

你对此有什么想法?你跟随什么?

我认为这一切都与良好的编码风格。 当然,你可以在不使用任何地方的final修饰语的情况下编写出function强大的程序,但是当你考虑到它的时候…

添加final所有不应该改变的东西只是缩小了你(或者下一个程序员,处理你的代码)的错误理解或误用导致你的代码的思维过程的可能性。 至less当他们现在想要改变你以前不可改变的事情的时候,应该敲响一些钟声。

起初,在你的代码中看到很多final关键字看起来很尴尬,但是很快你就会停下来注意到这个词本身,并且会简单地认为, 这个东西永远不会改变这一点 (你可以把它从我身上;-)

我认为这是很好的做法。 我不是一直都在使用它,但是当我能够做到这一点时,有必要为final标签做点事情。

迷恋:

  • 最后的领域 – 标记领域作为最后的力量他们被设置结束build设,使该领域的参考不可变的。 这允许安全地发布字段并且可以避免在稍后的读取中同步的需要。 (请注意,对于对象引用,只有字段引用是不可变的 – 对象引用引用的内容仍然可以更改,并影响不可变性。)
  • 最终静态字段 – 虽然我现在使用枚举现在很多情况下,我曾经使用静态最终字段。

考虑一下,但明智地使用:

  • 最终课程 – 框架/ APIdevise是我考虑的唯一案例。
  • 最后的方法 – 基本上和最终的课程一样。 如果你使用模板方法模式,如疯狂和最后标记的东西,你可能过于依赖inheritance,而不是足够的委派。

忽略除非感觉肛门:

  • 方法参数和局部variables – 我很less这样做主要是因为我很懒,我发现它混乱的代码。 我将完全承认,我不会修改的标记参数和局部variables是“较为”。 我希望这是默认的。 但事实并非如此,我发现整个决赛更难以理解代码。 如果我在别人的代码中,我不会把它们拉出来,但是如果我正在编写新的代码,我不会把它们放进去。一个例外是,你必须标记最后的东西,所以你可以访问它来自一个匿名的内部类。

在使用之前,您确实需要了解最终关键字的完整用法。 它可以应用于variables,字段,方法和类,并对其有不同的影响

我build议检查下面链接到文章的更多细节。

最后的词在最后的关键字

final修饰符,特别是对于variables,是让编译器执行一个通常是合理的约定的方法:确保一个(本地或实例)variables恰好被赋值一次(不多不less)。 通过确保variables在使用之前被明确赋值,可以避免常见的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 

通过防止一个variables被分配不止一次,你不鼓励跨越范围的范围。 而不是这个:

  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); } 

一些链接:

  • 最后的故事 (“Hardcore Java”的免费章节)
  • 一些最终模式
  • 明确的分配

我对于宣布每一个可能的variablesfinal都很教条。 这包括方法参数,局部variables以及很less的值对象字段。 我有三个主要的理由来宣布各地的变数:

  1. 声明意图:通过声明一个最终variables,我声明这个variables只能被写入一次。 这对其他开发者来说是一个微妙的暗示,也是编译器的一个重要提示。
  2. 实施一次性variables:我相信每个variables在生活中应该只有一个目的。 通过给每个variables只有一个目的,你可以减less在debugging的时候需要花费的时间。
  3. 允许优化:我知道编译器曾经有性能增强技巧,它依赖于variables引用的不变性。 我喜欢想一些这些旧的性能技巧(或新的)将由编译器使用。

不过,我认为最后的类和方法不如最终variables引用有用。 与这些声明一起使用的final关键字只是为自动化testing提供了障碍,并以您从未预料到的方式使用您的代码。

有效的Java有一个项目,说:“赞成不变的对象”。 将字段声明为final可以帮助您向这个方向迈出一小步,但当然还有更多真正不可变的对象。

如果你知道对象是不可变的,那么它们可以在许multithreading/客户端之间被共享,而不用担心同步问题,并且可以很容易地推断出程序是如何运行的。

我从来没有在一个variables的最后一个关键字阻止我犯了一个错误的情况下,所以目前我认为这是一个巨大的浪费时间。

除非有真正的理由这样做(因为在你想对这个variables做一个特定的决定是最终的)我宁愿不这样做,因为我发现它使得代码更不可读。

但是,如果你没有发现它使得代码难以阅读或者更长的时间来写,那就千方百计去做。

编辑:作为一个澄清(并试图赢得回票),我不是说不要把常量标记为final,我是说不要做这样的事情:

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

它只是使代码(在我看来)更难以阅读。

还有一点值得记住, 所有的最终做法都是防止你重新分配一个variables,它不会使它不可变或者类似的东西。

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

例如:

 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来表示对象的属性。

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

不可变对象不仅仅是线程安全。 它们也使得对于程序中状态转换的推理变得容易很多,因为可以改变的空间是故意的,如果一致地使用,则只能彻底限制应该改变的东西。

我有时也会使方法最终,但并不经常。 我很less做最后的课程。 我通常这样做是因为我没有必要。 我一般不会多使用inheritance。 我更喜欢使用接口和对象组合,这也使得我发现的devise通常更容易testing。 当你编写代码到接口而不是具体的类,那么当你像使用jMock这样的框架testing的时候,你不需要inheritance,使用接口创build模拟对象比使用具体的类更容易。

我想我应该让我的大部分课程都是最终的,但是我还没有进入这个习惯。

我必须为我的工作阅读很多代码。 实例variables的最后缺失是让我困扰的最重要的事情之一,并且使得代码难以理解。 对于我的钱来说,局部variables的最终结果会导致比清晰度更多的混乱。 这个语言应该被devise成是默认的,但我们必须忍受这个错误。 有时它特别适用于循环和if-else树的明确赋值,但大多数情况下它往往表明你的方法太复杂了。

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

有效的Java有一个完整的项目(项目15)指出了意外inheritance的陷阱。 实际上,如果你没有devise和logging你的类inheritance,inheritance它可以给出意想不到的问题(该项目是一个很好的例子)。 因此,这个build议是你对任何不想被inheritance的类和/或方法使用final

这可能看起来很严厉,但这是有道理的。 如果你正在编写一个类库供别人使用,那么你不希望它们inheritance那些没有为它devise的东西 – 你将自己locking到类的特定实现中,以便进行后向兼容。 如果你在一个团队编码,没有什么可以阻止团队的另一个成员,如果他们真的必须去除最后 。 但关键词让他们思考自己在做什么,并警告说他们所从事的课程并非为此而devise的,所以应该特别小心。

另一个需要注意的是,很多人混淆了final,意味着实例variables的内容不能改变,而不是参考不能改变。

听起来像使用最后一个关键字最大的争论之一是“这是不必要的”,它“浪费空间”。

如果我们承认许多重要的post所指出的“最终”的许多好处,在承认需要更多的input和空间的同时,我认为Java应该使variables默认为“最终”,并且要求事物被标记为“如果编码器希望它是可变的。

select为每个方法中的每个参数键入final都会对编码器和代码阅读器产生太大的刺激。

一旦刺激超出了合理的切换到Scala的参数是默认最终的。

或者,你总是可以使用代码样式工具,这将自动为你做。 所有IDE都将它们实现或作为插件。

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

当与引用variables一起使用时,它不允许重新引用该对象,尽pipe该对象可以被操纵。

当使用最终方法时,不允许该方法被子类覆盖。

一旦使用非常清楚,应该小心使用。 这主要取决于devise,因为在方法上使用final将不利于多态。

当你确定variables的值将永远不会被改变时,你应该只使用它作为variables。 还要确保遵循SUN所鼓励的编码惯例,例如:final int COLOR_RED = 1; (大写由下划线分隔)

有了一个引用variables,只有当我们需要一个对一个特定对象的不可变引用时才使用它。

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

我从不在局部variables上使用它们,添加的冗长性没有多大意义。 即使你不认为这个variables应该被重新分配,那么下一个修改那个代码的代码就没有什么区别,而且由于代码正在被修改,任何原来的目的都不再是有效的。 如果只是为了清楚起见,我认为它由于冗长的负面影响而失败。

几乎相同的情况也适用于成员variables,因为它们提供的好处很less,除了常量的情况。

它也不会影响不可变性,因为最好的指标是不变的,因为它被logging为和/或没有可以改变对象的方法(这样,使得类最终是唯一的方法来保证它是不可变的)。

但是,嘿,这只是我的意见:-)

我将Eclipse设置为在所有未修改的字段和属性上添加final。 这很好用Eclipse的“保存动作”,当保存文件时添加了这些最终修饰符(除其他外)。

强烈推荐。

看看我的博客文章的Eclipse保存操作。

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

否则,如果它是public/private static final type SOME_CONSTANT; ,我才最终使用public/private static final type SOME_CONSTANT;

即使是局部variables,知道它被声明为最终意味着我不需要担心后面的参考被改变。 这意味着在debugging时我会看到这个variables,我相信它指的是同一个对象。 这是我在寻找错误时需要担心的一件事。 一个好处是,如果99%的variables被声明为最终的,那么真正variables的几个variables就更好了。 另外,最后让编译器find一些可能会被忽略的可能的愚蠢错误。

对于争论,我认为他们不需要。 莫斯利他们只是伤害了可读性。 重新分配一个参数variables是非常愚蠢的,我应该很自信,他们可以被视为常量无论如何。

Eclipse为最终红色添加颜色的事实使得更容易在代码中findvariables声明,我认为这大大提高了可读性。

我试图强制规则,任何和所有的variables应该是最终的,没有一个极端的有效理由不。 回答“这个variables是什么? 问题,如果你只需要find启动,并有信心就是这样。

我现在实际上在一天之内非常紧张地围绕非最终variables。 就好像把刀子挂在一根线上,或者把它拿到厨房抽屉里一样。

最后的variables只是一个很好的方法来标记值。

一个非finalvariables绑定到一些容易出错的algorithm的一部分。

一个很好的特性是,当大多数情况下使用一个variables来替代一个algorithm的variables的时候,这个方法就是编写一个方法,而这个方法通常会显着改善代码。

我已经编写了一段时间,并尽可能使用最终。 做了一段时间(对于variables,方法参数和类属性),我可以说,我的variables的90%(或更多)实际上是最终的。 我认为不用修改variables的好处(我曾经看到过,而且有时候是一种痛苦)会为代码中额外的input和额外的“最终”关键字付出代价。

这就是说,如果我devise一种语言,我会使每个variables最终,除非由其他关键字修改。

思考,我不会使用最后的类和方法。 这是一个或多或less复杂的deviseselect,除非你的类是一个实用类(在这种情况下,你应该只有一个私有构造函数)。

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

对事件监听器使用匿名本地类,这是Java中的一种常见模式。 final关键字的最常见用法是确保范围内的variables可以被偶数侦听器访问。

但是,如果您发现自己被要求在代码中input大量的最终声明。 这可能是一个很好的暗示,你做错了什么。

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

 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 } }; } 

我用它来表示常量内外的方法。

我只有时使用它的方法,因为我不知道如果一个子类不会重写给定的方法(无论什么原因)。

至于类,只有一些基础设施类,我使用了最后一堂课。

如果函数参数写入函数内部,IntelliJ IDEA会向您发出警告。 所以,我已经停止使用最后的函数参数。 我也没有看到它们在java运行时库中。

强烈build议使用final 作为常量 。 但是,我不会将它用于方法或类(或者至less考虑一下),因为这会使testing变得更加困难,甚至不可能。 如果你绝对必须做一个类或方法最后,确保这个类实现一些接口,所以你可以有一个模拟实现相同的接口。

标记类final还可以使一些方法绑定发生在编译时而不是运行时。 考虑下面的“v2.foo()” – 编译器知道B不能有一个子类,所以foo()不能被重载,所以调用的实现在编译时是已知的。 如果B类没有标记为final,那么v2的实际types可能是一些扩展了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(); } }