在适用的Java中使用“final”修饰符[关闭]

问题描述 投票:184回答:25

在Java中,有一种做法是声明每个变量(本地或类),如果确实是参数final。

虽然这使得代码更加冗长,但这有助于轻松阅读/掌握代码,并且还可以防止错误,因为意图已明确标记。

您对此有何看法,您对此有何看法?

java oop
25个回答
172
投票

我认为这一切都与良好的编码风格有关。当然,你可以编写好的,强大的程序而不需要在任何地方使用大量的final修饰符,但是当你想到它时......

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

首先,在你的代码中看到很多final关键字看起来很尴尬,但很快你就会停止注意到这个词本身,并且只会认为,这个东西永远不会从这一点改变 - (你可以从我这里拿走;-)

我认为这是一个很好的做法。我并不是一直都在使用它,但是当我能够并且标记一些final它是有意义的时候我会这样做。


4
投票

我必须为我的工作阅读很多代码。在实例变量上缺少最终结果是让我烦恼的最重要的事情之一,并使得理解代码变得不必要。对于我的钱,最终的局部变量导致比清晰度更混乱。该语言应该被设计为默认,但我们必须忍受错误。有时它特别适用于循环和if-else树的明确赋值,但大多数情况下它往往表明你的方法太复杂了。


4
投票

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

如果我们承认这里许多伟大帖子所指出的“最终”的许多好处,虽然承认它需要更多的打字和空间,我认为Java应该默认变量“最终”,并要求标记“可变的“如果编码员希望它是。


3
投票

显然,final应该用于常量,并强制实现不变性,但是方法还有另一个重要用途。

有效的Java有一个完整的项目(项目15)指出了非预期继承的缺陷。实际上,如果您没有为继承设计和记录您的类,继承它可能会产生意想不到的问题(该项提供了一个很好的示例)。因此,建议您对任何不打算从中继承的类和/或方法使用final。

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


3
投票

另一个警告是,许多人混淆final意味着实例变量的内容不能改变,而不是引用不能改变。


2
投票

即使对于局部变量,知道它被声明为final也意味着我不必担心稍后会更改引用。这意味着在调试时我会在稍后看到该变量,我相信它指的是同一个对象。在寻找错误时,我不必担心这一点。一个好处是,如果99%的变量被宣布为最终变量,那么真正变量的少数变量会更好。此外,最终让编译器找到一些可能会被忽视的愚蠢错误。


2
投票

选择为每个方法中的每个参数键入final将对编码员和代码阅读器产生如此大的刺激。

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

或者,您始终可以使用自动为您执行此操作的代码样式工具。所有IDE都实现了它们或作为插件。


1
投票

与Java中的变量一起使用时的最终结果在C ++中提供了常量的替代。因此,当final和static用于变量时,它变得不可变。同时使迁移的C ++程序员非常高兴;-)

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

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

一旦使用非常清楚,应小心使用。它主要取决于设计,因为使用final方法不会有助于多态性。

只有当你确定变量的值将/永远不会改变时,才应该将它用于变量。还要确保遵循SUN鼓励的编码约定。例如:final int COLOR_RED = 1; (大写由下划线分隔)

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

关于可读性部分,随后在使用final修饰符时注释起着非常重要的作用。


1
投票

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

几乎同样适用于成员变量,因为它们提供的好处很少,除了常量的情况。

它也不影响不变性,因为不可变的东西的最佳指标是它被记录为这样和/或没有可以改变对象的方法(这一点,同时使类最终是保证这一点的唯一方法)它是不可改变的)。

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


1
投票

我设置Eclipse以在所有未修改的字段和属性上添加final。这可以很好地使用Eclipse“保存动作”,它在保存文件时添加这些最终修饰符(以及其他内容)。

强烈推荐。

查看Eclipse Save Actions的my blog post


1
投票

我几乎不使用final方法或类,因为我喜欢让人们覆盖它们。

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


180
投票

痴迷于:

  • 最终字段 - 将字段标记为最终强制它们在构造结束时设置,使该字段引用不可变。这样可以安全地发布字段,并且可以避免在以后的读取时进行同步。 (请注意,对于对象引用,只有字段引用是不可变的 - 对象引用引用的内容仍然可以更改,并且会影响不变性。)
  • 最终的静态字段 - 虽然我现在使用枚举用于我曾经使用静态最终字段的许多情况。

考虑但明智地使用:

  • 最终类 - 框架/ API设计是我认为的唯一案例。
  • 最终方法 - 与最终类基本相同。如果你正在使用疯狂的模板方法模式和标记最终的东西,你可能过分依赖继承而不是委托。

忽略除非感觉肛门:

  • 方法参数和局部变量 - 我很可能这样做很大程度上是因为我很懒,我发现它使代码混乱。我将完全承认,我不打算修改的标记参数和局部变量是“更严格”。我希望这是默认的。但事实并非如此,而且我发现整个决赛的代码更难以理解。如果我在别人的代码中,我不会把它们拉出去,但是如果我正在编写新的代码,我就不会把它们放进去。一个例外是你需要标记最终的东西以便你可以访问它来自一个匿名的内部类。

1
投票

对于论据,我认为他们不需要。 Mostley他们只是伤害了可读性。重新分配参数变量是非常愚蠢的,我应该非常确信它们无论如何都可以被视为常量。

Eclipse最终变成红色这一事实使得更容易在代码中发现变量声明,我认为这些声明在大多数情况下会提高读取性。

我试图强制执行任何和所有变量都应该是最终的规则,因为没有极端的有效理由。回答“这个变量是什么?”要容易得多。问题,如果你只需要找到启动并确信这就是它。

实际上,我现在对非最终变量感到相当紧张。这就像把刀挂在头顶上的一根线上,或者只是把它放在你的厨房抽屉之间的区别......

最终变量只是标记值的好方法。

非final变量绑定到一些容易出错的算法的一部分。

一个很好的特性是,当大多数时候选择在算法中使用变量时,解决方法是编写一个方法,这通常可以显着改善代码。


1
投票

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

话虽如此,如果我要设计一种语言,我会将每个变量设为final,除非被其他关键字修改。

想到,我不会在课程和方法上使用最终版本。这是一个或多或少复杂的设计选择,除非您的类是实用程序类(在这种情况下,您应该只有一个私有构造函数)。

我还使用Collections.unmodifiable ...在需要时创建不可修改的列表。


0
投票

对事件侦听器使用匿名本地类等是Java中的常见模式。 final关键字的最常见用法是确保even监听器可以访问范围中的变量。

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

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

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

0
投票

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

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

至于类,仅针对某些基础结构类,我使用过final类。

如果在函数内部写入函数参数,IntelliJ IDEA会发出警告。所以,我已经停止使用final作为函数参数。我也没有在java Runtime库中看到它们。


0
投票

标记类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();
    }
}

-1
投票

强烈建议使用final作为常量。但是,我不会将它用于方法或类(或至少考虑它一段时间),因为它使测试更难,如果不是不可能的话。如果你绝对必须让一个类或方法最终,请确保这个类实现了一些接口,所以你可以让一个mock实现相同的接口。


32
投票

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

我建议查看下面链接的文章了解更多详情。

Final Word On the final Keyword


29
投票

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

一些链接:


20
投票

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

  1. 声明意图:通过声明一个最终变量,我声明这个变量只能写入一次。这是对其他开发人员的一个微妙暗示,也是编译器的一个重要提示。
  2. 强制执行一次性变量:我相信每个变量在生活中应该只有一个目的。通过给每个变量只有一个目的,可以减少在调试时理解该特定变量的目的所需的时间。
  3. 允许优化:我知道编译器曾经有过性能增强技巧,它特别依赖于变量引用的不变性。我喜欢认为编译器会使用这些旧的性能技巧(或新的)。

但是,我确实认为最终的类和方法几乎没有最终变量引用那么有用。与这些声明一起使用时,final关键字只是为自动化测试和以前所未有的方式使用代码提供了障碍。


17
投票

Effective Java有一个项目“赞成不可变对象”。将字段声明为final可以帮助您对此采取一些小步骤,但对于真正不可变的对象,当然还有更多。

如果您知道对象是不可变的,则可以共享它们以便在没有同步担忧的情况下在许多线程/客户端之间进行读取,并且更容易推断出程序的运行方式。


10
投票

我从来没有遇到过变量上的最终关键字阻止我犯错误的情况,所以目前我认为这是浪费时间。

除非有这样做的真正原因(因为你想要对该变量做出最终的具体观点),我宁愿不这样做,因为我发现它使代码的可读性降低。

但是,如果你没有发现它会使代码更难以阅读或更长时间地写入,那么一定要去寻找它。

编辑:作为一个澄清(并尝试赢回选票),我不是说不要将常数标记为最终,我说不要做的事情如下:

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

  return first+second;
}

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

值得记住的是,所有最终的做法都是阻止你重新分配一个变量,它不会让它变成不可变的或类似的东西。


8
投票

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;

5
投票

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

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

不可变对象不仅仅是线程安全。它们还可以更容易地推断出程序中的状态转换,因为可以改变的空间是故意的,并且如果一致地使用,则仅限于应该改变的事物。

我有时也会把方法做成最终,但不是经常。我很少把课程定稿。我一般这样做是因为我没有必要这样做。我通常不会使用很多继承。我更喜欢使用接口和对象组合 - 这也适用于我发现通常更容易测试的设计。当您对接口而不是具体类进行编码时,在使用jMock等框架进行测试时,不需要使用继承,使用接口创建模拟对象比使用具体类更容易。

我想我应该让我的大部分班级最终完成,但我还没有进入habbit。

© www.soinside.com 2019 - 2024. All rights reserved.