什么时候应该使用final方法参数和局部variables?

我发现了几个参考( 例如 ),build议尽可能使用final ,我想知道这是多么重要。 这主要是在方法参数和局部variables的上下文中,而不是最终的方法或类。 对于常量来说,这是显而易见的。

一方面,编译器可以进行一些优化,并使程序员的意图更清晰。 另一方面,它增加了冗长,优化可能是微不足道的。

我应该努力记住这件事吗?

迷恋:

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

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

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

忽略除非感觉肛门:

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

我应该努力记住这件事吗?

不,如果你使用Eclipse,因为你可以configuration一个保存动作来为你自动添加这些最终修饰符。 那么你就可以less花点功夫

“最终”的开发时间效益至less和运行时间效益一样重要。 它告诉未来编辑的代码有关你的意图。

标记一个类“final”意味着你没有在devise或实现类的时候努力正确地处理扩展。 如果读者可以对class级进行修改,并想删除“最终”修饰语,他们可以自行承担风险。 确保class级能够很好地处理延期,这取决于他们。

标记variables“final”(并在构造函数中赋值)对dependency injection非常有用。 它表明variables的“协作者”性质。

在抽象类中标记方法“final”是有用的。 它清楚地描述了扩展点的位置。

我发现标记方法参数和当地人作为final是有用的重构援助时,有问题的方法是一个难以理解的混乱几页长。 final ,我们可以宽泛地看到编译器(或者IDE)抛出什么“不能分配给最终variables”的错误,而且你可能会发现为什么称为“data”的variables结束为null,即使有几条(过时的)不能发生。

然后你可以修改一些错误,把重用的variablesreplace成更接近于使用点的新variables。 然后你会发现你可以将方法的所有部分包装在范围大括号中,而突然间,你就是一个IDE按键,而不是“提取方法”,而你的怪物才会更容易理解。

如果你的方法不是一个不可维护的残骸,那么我认为在最终决定什么东西时可能是有价值的,以防止人们把它变成沉船; 但如果这是一个简短的方法(请参阅:不可维护),那么您可能会添加大量冗长的内容。 特别是,Java函数签名非常难以容纳80个字符,而不需要每个参数增加6个字符!

那么,这一切都取决于你的风格…如果你看到最后的时候你不会修改variables,然后使用它。 如果你不喜欢看到它,那就离开它。

我个人喜欢尽可能小的冗长,所以我倾向于避免使用额外的关键字,这是不必要的。

我更喜欢dynamic语言,所以我可能不会奇怪,我喜欢避免冗长。

所以,我想说的就是select你正在倾向的方向,然后随它而行(无论如何,尽量保持一致)。


作为一个侧面说明,我曾经在使用和不使用这种模式的项目上工作过,而且我没有看到错误或错误的数量上的差异…我不认为这是一个巨大的模式提高你的bug数量或任何东西,但它又是风格,如果你想expression意图,你不会修改它,然后继续使用它。

如果你正在编写一个应用程序,那么在一年之后有人需要阅读代码,那么是的,对于不应该一直修改的variables使用final。 通过这样做,你的代码将会更加“自我logging”,同时你也可以减less其他开发者做一些愚蠢的事情,比如使用本地常量作为本地临时variables。

如果你正在写一些一次性的代码,那么,不要麻烦确定所有的常量,并把它们做成最终的结果。

在参数中避免意外更改参数值并引入一个细微的错误是很有用的。 我使用忽略这个build议,但花了大约4小时后。 在一个可怕的方法(数百行代码和多个fors,嵌套ifs和各种不好的做法),我会build议你这样做。

  public int processSomethingCritical( final int x, final int y ){ // hundreds of lines here // for loop here... int x2 = 0; x++; // bug aarrgg... // hundreds of lines there // if( x == 0 ) { ... } 

当然,在一个完美的世界里,这不会发生,但是..有时候你必须支持别人的代码。 🙁

我一直在使用final来使Java更加基于expression式。 看Java的条件( if,else,switch )不是基于expression式,我一直恨,特别是如果你习惯于函数式编程(即ML,Scala或Lisp)。

因此,您应该尝试始终(恕我直言)在使用条件时使用最终variables。

让我举一个例子:

  final String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; case JOB_POSTING_IMPORT: name = "Blah"; break; default: throw new IllegalStateException(); } 

现在,如果添加另一个case语句并且不设置name ,编译器将会失败。 编译器也将失败,如果你不打破每一个案件(你设置variables)。 这使得你可以使Java非常类似于Lisp的letexpression式,并且使得你的代码不会被大量缩进(因为词法范围variables)。

和@Recurse指出的(但显然是-1我)你可以做出前面的String name final得到编译器错误(我从来没有说过你不能),但你可以很容易地使编译器错误消失设置名称在switch语句抛出expression式语义或更糟糕的忘记break你不能导致错误(尽pipe@Recurse说)而不使用final

  String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; //break; whoops forgot break.. //this will cause a compile error for final ;P @Recurse case JOB_POSTING_IMPORT: name = "Blah"; break; } // code, code, code // Below is not possible with final name = "Whoops bug"; 

由于错误设置的名称(除了忘记break哪个也是另一个错误),我现在可以不小心做到这一点:

  String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; //should have handled all the cases for pluginType } // code, code, code // Below is not possible with final name = "Whoops bug"; 

最后的variables强制一个单一的评估应该是什么名字。 与具有返回值的函数必须始终返回一个值(忽略exception)类似,名称开关块必须parsing名称,并绑定到使代码重构更容易的开关块(即Eclipe重构:提取方法) 。

以上在OCaml中:

 type plugin = CandidateExport | JobPostingImport let p = CandidateExport let name = match p with | CandidateExport -> "Candidate Stuff" | JobPostingImport -> "Blah" ;; 

match ... with ...评估就像一个函数,即expression式。 注意它看起来像我们的switch语句。

scheme(球拍或鸡肉)中有一个例子:

 (define name (match b ['CandidateExport "Candidate Stuff"] ['JobPostingImport "Blah"])) 

我会尽可能地使用最终的。 这样做会标志,如果你无意中改变领域。 我也将方法参数设置为final。 这样做,我已经从我接pipe的代码中捕获了几个错误,当他们尝试“设置”一个忘记Java通过值的参数。

从这个问题是否显而易见,但是使方法参数最终只影响方法的主体还不清楚。 它没有向调用者传达任何有关该方法意图的有趣信息。 传入的对象仍然可以在方法内部进行变化(最终不是常量),variables的作用域在方法中。

为了回答你的确切问题,除非代码需要它(例如variables是从一个内部类引用的),否则我不会打扰做一个实例或局部variables(包括方法参数),或者澄清一些非常复杂的逻辑。

例如variables,如果他们是逻辑常量,我会让他们最终。

variablesfinal有很多用途。 这里仅仅是less数

最终常数

  public static class CircleToolsBetter { public final static double PI = 3.141; public double getCircleArea(final double radius) { return (Math.pow(radius, 2) * PI); } } 

这可以用于代码的其他部分,或者由其他类访问,这样,如果你改变了值,你将不必一一改变它们。

最终variables

 public static String someMethod(final String environmentKey) { final String key = "env." + environmentKey; System.out.println("Key is: " + key); return (System.getProperty(key)); } } 

在这个类中,你build立一个范围最终variables,为参数environmentKey添加一个前缀。 在这种情况下,最终variables只在执行范围内是最终的,这在每次执行方法时是不同的。 每次input方法,最后都被重build。 一旦build成,在方法执行的范围内不能改变。 这允许您在方法的持续时间内修复方法中的variables。 见下文:

 public class FinalVariables { public final static void main(final String[] args) { System.out.println("Note how the key variable is changed."); someMethod("JAVA_HOME"); someMethod("ANT_HOME"); } } 

最终常数

 public double equation2Better(final double inputValue) { final double K = 1.414; final double X = 45.0; double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M); double powInputValue = 0; if (result > 360) { powInputValue = X * Math.sin(result); } else { inputValue = K * Math.sin(result); // <= Compiler error } 

当你有很长的代码行时,这些特别有用,它会产生编译器错误,所以当有人不小心改变了不应该被改变的variables时,你不会遇到逻辑/业务错误。

最终collections

不同的情况下,当我们谈论集合,你需要把它们设置为一个不可修改的。

  public final static Set VALID_COLORS; static { Set temp = new HashSet( ); temp.add(Color.red); temp.add(Color.orange); temp.add(Color.yellow); temp.add(Color.green); temp.add(Color.blue); temp.add(Color.decode("#4B0082")); // indigo temp.add(Color.decode("#8A2BE2")); // violet VALID_COLORS = Collections.unmodifiableSet(temp); } 

否则,如果你不把它设置为不可修改的:

 Set colors = Rainbow.VALID_COLORS; colors.add(Color.black); // <= logic error but allowed by compiler 

最终类最终方法不能被分别扩展或覆盖。

编辑:解决有关封装的最终类问题:

有两种方法可以进行课堂决赛。 首先是在类声明中使用关键字final:

 public final class SomeClass { // . . . Class contents } 

做出类最后的第二种方法是将其所有构造函数声明为私有的:

 public class SomeClass { public final static SOME_INSTANCE = new SomeClass(5); private SomeClass(final int value) { } 

如果发现它实际上是最后一个,那么最后标记它可以节省你的麻烦,以便展示一下这个Test类。 乍一看是公开的。

 public class Test{ private Test(Class beanClass, Class stopClass, int flags) throws Exception{ // . . . snip . . . } } 

不幸的是,由于这个类的唯一构造函数是私有的,所以不可能扩展这个类。 在Test类的情况下,没有理由说这个类应该是final的。 Test类是隐式最终类如何导致问题的一个很好的例子。

所以你应该把它标记为final,当你隐式地做一个类的最终使它的构造函数是私有的。

你提到的有点权衡,但我更喜欢显式使用某些东西而不是隐式使用。 这将有助于消除未来代码维护者的一些模糊性 – 即使它只是你。

如果您有内部(匿名)类,并且该方法需要访问包含方法的variables,则需要将该variables作为最终的variables。

除此之外,你所说的是对的。

如果您将该variables设置为immutable ,请使用final关键字作为variables

通过将variables声明为final,它帮助开发人员在高度multithreading的环境中排除可能的variables修改问题。

随着Java 8的发布,我们有一个更多的概念被称为“ effectively final variable ”。 非最终variables可以作为最终variables。

从lambdaexpression式引用的局部variables必须是最终的或有效的最终的

如果在本地块中初始化后没有修改variables,则认为该variables是有效的 。 这意味着您现在可以在匿名类或lambdaexpression式中使用不带final关键字的本地variables,只要它们必须是有效的最终的。

在Java 7中,不能在匿名类中使用非最终本地variables,但是可以从Java 8中使用

看看这篇文章

一个非常简单的答案,我们有三个最终与variables,最终与方法&&最终与类..

如果uu不能多次赋值这个variables

最后用方法你不能重写这个方法。

最后一堂课不能延续最后一堂课

首先,最后的关键字用于使variables不变。 常量意味着它不会改变。 例如:

 final int CM_PER_INCH = 2.54; 

你会声明variablesfinal,因为厘米每英寸不会改变。

如果您尝试覆盖最终值,则该variables就是它首先被声明的内容。 例如:

 final String helloworld = "Hello World"; helloworld = "A String"; //helloworld still equals "Hello World" 

有一个编译错误是这样的:

 local variable is accessed from inner class, must be declared final 

如果你的variables不能被声明为final,或者你不想声明它,那么试试这个:

 final String[] helloworld = new String[1]; helloworld[0] = "Hello World!"; System.out.println(helloworld[0]); helloworld[0] = "A String"; System.out.println(helloworld[0]); 

这将打印:

 Hello World! A String