一切都结束了
Posted
技术标签:
【中文标题】一切都结束了【英文标题】:Everything's Final 【发布时间】:2011-04-03 07:33:06 【问题描述】:我一直在使用 PMD 来帮助发现我的 Java 代码中的潜在问题,并且我发现它的建议是在有用的、特殊的和“WTF?!”之间进行区分。
它一直告诉我要做的一件事是对我可以附加到的每个变量(包括输入参数)使用 final
关键字。对于实际的常量,这似乎是明智的,但对于其他东西,我觉得它很奇怪,甚至可能适得其反。
在每个变量声明上挂上final
是否有具体的优点/缺点?
【问题讨论】:
见***.com/questions/137868/… 【参考方案1】:以下是为什么将几乎所有内容标记为 final
最终常数
public static class CircleToolsBetter
public final static double PI = 3.141;
public double getCircleArea(final double radius)
return (Math.pow(radius, 2) * PI);
这可以用于代码的其他部分或由其他类访问,这样,如果您要更改值,就不必一一更改。
最终变量
public static String someMethod(final String environmentKey)
final String key = "env." + environmentKey;
System.out.println("Key is: " + key);
return (System.getProperty(key));
在此类中,您构建了一个作用域 final 变量,该变量将前缀添加到参数 environmentKey。在这种情况下,最终变量仅在执行范围内是最终的,在方法的每次执行时都是不同的。每次输入方法时,都会重新构建最终的方法。一经构造,在方法执行范围内无法更改。这允许您在方法的持续时间内修复方法中的变量。见下文:
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
当您有很长的代码行时,这些特别有用,并且它会产生编译器错误,因此当有人意外更改不应更改的变量时,您不会遇到逻辑/业务错误。
最终合集
当我们谈论集合时的不同情况,您需要将它们设置为不可修改的。
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 Classes 和 Final Methods 不能分别扩展或覆盖。
编辑:解决关于封装的最后一类问题:
有两种方法可以使类最终化。第一种是在类声明中使用关键字final:
public final class SomeClass
// . . . Class contents
使类成为final的第二种方法是将其所有构造函数声明为私有:
public class SomeClass
public final static SOME_INSTANCE = new SomeClass(5);
private SomeClass(final int value)
如果发现它实际上是一个final,那么将它标记为final可以为您省去麻烦,以演示查看这个Test类。乍一看似乎是公开的。
public class Test
private Test(Class beanClass, Class stopClass, int flags)
throws Exception
// . . . snip . . .
不幸的是,由于该类的唯一构造函数是私有的,因此无法扩展该类。在 Test 类的情况下,没有理由认为该类应该是最终的。测试类是隐式 final 类如何导致问题的一个很好的例子。
因此,当您通过将其构造函数设为私有来隐式将类设为 final 时,应将其标记为 final。
【讨论】:
【参考方案2】:“你可以做的每一个变量声明”听起来有点极端,但final
实际上在很多方面都是有益的。有时我希望final
是默认行为,并且不需要关键字,但真正的“变量”需要variable
修饰符。 Scala 通过 val
和 var
关键字采用了类似的方法——强烈建议使用 val
(final
-like 关键字)。
仔细考虑每个成员变量是final
,volatile
,还是两者都不是,这一点尤为重要,因为类的线程安全取决于是否正确。分配给final
和volatile
变量的值始终对其他线程可见,无需使用synchronized
块。
对于局部变量,它并不那么重要,但是使用final
可以帮助您更清楚地推理您的代码并避免一些错误。如果您不希望在方法中更改值,请使用final
说明,并让编译器发现未注意到的违反此期望的违规行为。我目前不知道有什么,但很容易想象 JIT 编译器也可以使用这个提示来提高性能。
在实践中,我不会尽可能声明局部变量final
。我不喜欢视觉上的混乱,而且看起来很麻烦。但是,这并不意味着这不是我应该做的事情。
已提出将var
关键字添加到Java 以支持类型推断的建议。但作为该提案的一部分,已经有许多关于指定局部变量不变性的其他方法的建议。例如,一个建议是还添加关键字val
来声明具有推断类型的不可变变量。或者,有些人主张同时使用final
和var
。
【讨论】:
事后看来,这本来是最好的。但是,它会破坏 C 语义,而 C 语义是明确寻求为 C++ 程序员提供轻松过渡的。 JIT 编译器根本不需要在局部变量上使用final
,而 从不 需要;它可以查看每个变量是否实际被修改。当然,它看到的和程序员看到的可能完全不同……
final 在不同步的情况下不保证线程安全。如果在其他线程迭代列表时更改列表的大小,最终列表可能会抛出 ConcurrentModificationException。复杂的对象也是如此,比如 DAO。即使 DAO 本身是 final 的,某些线程可能会在其他线程读取属性时弄乱属性,并产生不一致的状态,因此您需要一个线程安全的 DAO / List 来确保 final 保证线程安全
@DGoiko 我没有说是这样,但感谢您的澄清。
哦,对不起,英语不是我妈妈的语言,我可能听起来很粗鲁。我只是想做一个旁注【参考方案3】:
final 告诉读者,首先分配的值或引用在以后的任何时候都是相同的。
由于在这种情况下所有可以是最终的都是最终的,所以 缺少 最终会告诉读者值 稍后会发生变化,并考虑到这一点。
【讨论】:
这对于原语或像字符串这样的不可变对象是有意义的。但这会鼓励对可变对象产生虚假的安全感吗? 一个缺失的final只会告诉你它可以在以后改变,当然不会。假设是完全错误的。如果一个类未定义为 final,则它可能是子类,但这并不意味着它是。 final 对所有变量的使用是有争议的(经常在这里),因此它的缺失可以很容易地归因于编码器的偏好。 @Robin,请注意 OP 说“一切都结束了”。这就是我正在讨论的情况。请在否决答案之前正确阅读问题。 @BlairHippo,仅当您不了解引用本身和被引用对象之间的区别时。你得到保证的是你拥有的东西是一样的——东西里面是什么是另一回事(但在这种特殊情况下,很有可能还有很多决赛)【参考方案4】:PMD 还有一些选项规则,你可以打开它来抱怨final
;这是一个任意规则。
如果我正在执行一个项目,其中 API 被导出到另一个团队 - 或世界 - 保留 PMD 规则不变。如果您只是在开发将永远是封闭 API 的东西,请禁用该规则并节省一些时间。
【讨论】:
【参考方案5】:这是 PMD 等工具的常见习语。例如,下面是 Checkstyle 中对应的规则。这真的是风格/偏好的问题,你可以为双方争论。
在我看来,对方法参数和局部变量(如果适用)使用 final 是一种很好的风格。 “为扩展而设计”成语值得商榷。
http://checkstyle.sourceforge.net/config_misc.html#FinalParameters http://checkstyle.sourceforge.net/config_design.html#DesignForExtension http://checkstyle.sourceforge.net/config_coding.html#FinalLocalVariable【讨论】:
以上是关于一切都结束了的主要内容,如果未能解决你的问题,请参考以下文章
奔波真是辛苦啊,然而生命终将逝去,只希望当一切都结束的时候,能够没有遗憾吧。