只要适用于Java,就使用"最终"修饰符
在Java中,有一种做法是声明每个变量(本地或者类),参数final(如果确实存在)。
尽管这使代码更加冗长,但这有助于易于阅读/掌握代码,并且由于意图被明确标记,因此还可以防止错误。
我们对此有何想法,我们会怎么做?
解决方案
我从来没有遇到过在变量上使用final关键字阻止我犯错的情况,所以目前我认为这是浪费时间。
除非有真正的理由这样做(例如,我们想就该变量为最终变量提出具体观点),否则我不愿意这样做,因为我发现它会使代码的可读性降低。
但是,如果找不到它会使代码更难阅读或者编写更长的时间,则一定要这样做。
编辑:作为一种澄清(并试图赢回不赞成票),我并不是说不要将常量标记为final,而是要不要做类似的事情:
public String doSomething() { final String first = someReallyComplicatedExpressionToGetTheString(); final String second = anotherReallyComplicatedExpressionToGetAnother(); return first+second; }
(我认为)这只会使代码更难阅读。
还值得记住的是,所有final操作都是防止我们重新分配变量,它不会使变量不变或者类似的事情。
在使用final关键字之前,我们确实需要了解它的全部用法。它可以应用于变量,字段,方法和类并对其产生不同的影响
ID建议我们查看下面链接到的文章,以了解更多详细信息。
最终关键词
我几乎不使用final方法或者类,因为我喜欢允许人们覆盖它们。
否则,仅当它是"公共/私有静态最终类型SOME_CONSTANT;"时,才使用final。
有效的Java的标题为"支持不可变对象"。将字段声明为final可以朝此方向迈出一些小步,但是,真正不可变的对象当然不止于此。
如果我们知道对象是不可变的,则可以在许多线程/客户端之间共享它们以供读取,而无需担心同步,并且更容易推断程序的运行方式。
我认为这都与良好的编码风格有关。当然,我们可以编写好的,健壮的程序,而无需在任何地方使用很多final
修饰符,但是当我们考虑时...
将" final"添加到所有不应该改变的事物上,只会缩小我们(或者下一个程序员,在代码上工作)会误解或者滥用导致代码生成的思维过程的可能性。至少当他们现在想要更改我们以前不可变的内容时,它应该会发出一些响声。
刚开始时,在代码中看到很多final
关键字看起来有些尴尬,但是很快我们将停止注意到单词本身,而会简单地认为,这将永远不会改变点对点(你可以从我这里拿走;-)
我认为这是个好习惯。我并不是一直在使用它,但是只要有可能,就可以标记" final"(最终),我会去做。
Final与Java中的变量一起使用时,可以替代C ++中的常量。因此,当final和static用于变量时,它变得不可变。同时使迁移的C ++程序员很高兴;-)
与引用变量一起使用时,尽管可以操纵该对象,但它不允许我们重新引用该对象。
当final与方法一起使用时,它不允许子类重写该方法。
一旦用法非常明确,应谨慎使用。它主要取决于设计,因为在该方法上使用final不利于多态。
当我们确定该变量的值将/将永远不会被更改时,应仅将其用于变量。还要确保遵循SUN鼓励的编码约定。例如:final int COLOR_RED = 1; (大写字母由下划线分隔)
对于引用变量,仅当我们需要对特定对象的不变引用时才使用它。
关于可读性部分,请确保在使用final修饰符时注释起着非常重要的作用。
在事件侦听器中使用匿名本地类,这是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 } }; }
我将其用于内部和外部方法中的常量。
我有时仅将其用于方法,因为我不知道子类是否不想覆盖给定的方法(无论出于何种原因)。
关于类,仅对于某些基础结构类,我使用了最终类。
如果将函数参数写入函数内部,则IntelliJ IDEA会警告我们。因此,我已经停止使用final作为函数参数。我也没有在Java Runtime库中看到它们。
强烈建议将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); }
一些链接:
- 最后的故事(《 Hardcore Java》一书的免费章节)
- 一些最终模式
- 确定分配
我一直在使用" final"作为对象属性。
当在对象属性上使用时,final
关键字具有可见性语义。基本上,设置最终对象属性的值是在构造函数返回之前进行的。这意味着只要我们不让this引用转义构造函数,并且对所有属性都使用final,就可以保证对象(在Java 5语义下)被正确构造,并且由于它也是不可变,可以安全地发布到其他线程。
不可变的对象不仅与线程安全有关。它们还使我们可以更轻松地推断程序中的状态转换,因为可以更改的空间是有意为之的,并且如果始终如一地使用,则将其完全限制为仅应更改的内容。
有时我也会使方法定型,但不那么频繁。我很少把课程定下来。我通常这样做是因为我几乎不需要。我通常不太多使用继承。我更喜欢使用接口和对象组合,这也使它适合于我发现通常更易于测试的设计。当我们编写接口而不是具体类的代码时,在测试时无需使用继承,就象使用jMock这样的框架一样,使用接口创建模拟对象比使用具体类要容易得多。
我想我应该把大部分课程都定下来,但是我还没有习惯。
我必须阅读很多代码以完成我的工作。缺少实例变量上的final是使我烦恼的最重要的事情之一,这使理解代码变得不必要地困难。为了我的钱,局部变量的最终结果会导致混乱而不是清晰。语言的设计应使其成为默认语言,但我们必须忍受错误。有时,这对于使用if-else树进行循环和明确赋值特别有用,但是大多数情况下,它往往表明方法过于复杂。
痴迷于:
- 最终字段-将字段标记为最终字段会迫使它们在构造结束时进行设置,从而使该字段引用不变。这样可以安全地发布字段,并且可以避免在以后的读取中进行同步。 (请注意,对于对象引用,只有字段引用是不可变的-对象引用所引用的内容仍然可以更改,并且会影响不可变性。)
- 最终静态字段-尽管我在以前使用静态最终字段的许多情况下都使用枚举。
考虑但要谨慎使用:
- 最终课程-框架/ API设计是我唯一考虑的情况。
- 最终方法-与最终类基本相同。如果我们使用的是疯狂的模板方法模式,并且将东西标记为final,则可能是我们对继承的依赖过多,而对委派的依赖则不够。
除非感到肛门,否则忽略:
- 方法参数和局部变量-我很少这样做,主要是因为我很懒,并且发现它会使代码混乱。我将完全承认,我将不修改的标记参数和局部变量是"正确的"。我希望它是默认设置。但是事实并非如此,我发现遍历Final的代码更加难以理解。如果我使用别人的代码,则不会删除它们,但是如果我正在编写新代码,则不会放入它们。一个例外是必须标记最终内容以便可以访问它来自一个匿名内部类。
我从未在局部变量上使用它们,添加的冗长性毫无意义。即使我们不认为应该重新分配该变量,也不会对下一个更改该代码的人产生太大的影响,否则代码将以其他方式更改,并且由于代码已被更改,因此使其最终定稿的任何原始目的可能不再有效。如果只是为了清楚起见,我相信它会由于冗长的负面影响而失败。
成员变量几乎也是如此,因为除了常量的情况外,它们几乎没有好处。
它也与不变性没有关系,因为关于不变性的最好指示就是它是这样记录的,并且/或者没有可以改变对象的方法(这与使类为final是唯一保证不变的方法)。它是不可变的)。
但是,那只是我的意见:-)
我将Eclipse设置为在所有未修改的字段和属性上添加final。使用Eclipse的"保存动作",在保存文件时添加了这些最终修饰符(以及其他功能),效果很好。
强烈推荐。
查看我关于Eclipse Save Actions的博客文章。
显然,final应该用于常量并强制不变性,但是方法还有另一个重要用途。
有效的Java对此有一个整体(第15项),指出了意外继承的陷阱。实际上,如果我们没有为继承设计和编写类,则从该类继承会带来意想不到的问题(该项目提供了一个很好的例子)。因此,建议我们在不希望从其继承的任何类和/或者方法上使用final。
这可能看起来很苛刻,但这是有道理的。如果要编写供其他人使用的类库,那么我们不希望它们从非为它设计的东西继承而来,那么我们将自己锁定在该类的特定实现中以实现向后兼容。如果我们在团队中进行编码,则没有什么可以阻止团队中的另一个成员(如果确实需要)删除决赛。但是关键字使他们思考自己在做什么,并警告他们继承的类不是为此而设计的,因此应格外小心。
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
,我非常教条。其中包括方法参数,局部变量,很少包括值对象字段。我到处声明最终变量的三个主要原因:
- 声明意图:通过声明最后一个变量,我表示此变量只能写入一次。对其他开发人员来说,这是一个微妙的暗示,对编译器而言,这是一个重要的暗示。
- 强制使用一次性变量:我相信每个变量在生活中应该只有一个目的。通过仅给每个变量一个目的,可以减少调试时达到该特定变量目的的时间。
- 允许优化:我知道编译器曾经有一些性能增强的技巧,这些技巧特别依赖于变量引用的不变性。我想认为其中一些旧的性能技巧(或者新的技巧)将由编译器使用。
但是,我确实认为,最终类和方法几乎没有最终变量引用有用。与这些声明一起使用时,final
关键字只是为自动测试和以我们从未料过的方式使用代码提供了障碍。
即使对于局部变量,知道将其声明为final也意味着我不必担心以后会更改引用。这意味着在调试时,稍后再看到该变量时,我相信它引用的是同一对象。寻找错误时,我不必担心这件事。
一个额外的好处是,如果将99%的变量声明为final,那么实际上是变量的少数变量会更好。
同样,最终的结果使编译器可以发现更多可能愚蠢的错误,否则这些错误可能不会引起注意。
对于论点,我认为不需要它们。 Mostley他们只是在伤害阅读能力。重新分配参数变量非常愚蠢,以至于我应该非常确信它们无论如何都可以当作常量来对待。
Eclipse将最终的颜色涂成红色的事实使我更容易发现代码中的变量声明,而我认为这在大多数情况下都提高了可读性。
我试图强制执行所有变量都应为最终变量的规则,这是没有极端理由的。回答"这个变量是什么?"要容易得多。问我们是否只需要找到初始化并有信心就可以了。
实际上,现在几天来,我对非最终变量感到非常紧张。这就像是把刀悬挂在头顶的线中,还是只是把它放在厨房的抽屉里……之间的区别。
最终变量只是获取值的一种好方法。
非最终变量绑定到某些易错算法的一部分。
一个不错的功能是,在大多数情况下,对于算法而言,在选项之外使用变量时,解决方法是改写一种方法,这通常可以显着改善代码。
我已经编码了一段时间,并在可能的情况下使用final。经过一段时间(对于变量,方法参数和类属性),我可以说我的变量中有90%(或者更多)实际上是最终变量。我认为在我们不想修改变量时(在以前我曾见过这种情况,有时很痛苦)的好处是,代码中增加了键入内容和" final"关键字。
话虽这么说,如果我要设计一种语言,除非使用其他关键字进行修改,否则我将使每个变量都成为最终变量。
我想对于类和方法,final很少。这是一个或者多或者少复杂的设计选择,除非类是一个实用程序类(在这种情况下,我们应该只有一个私有构造函数)。
在需要时,我还使用Collections.unmodifiable ...创建无法修改的列表。