在 Java 中禁止继承的充分理由是什么,例如使用最终类或使用单个私有无参数构造函数的类?使方法成为最终方法的充分理由是什么?
11 回答
您最好的参考是 Joshua Bloch 的优秀著作“Effective Java”的第 19 项,名为“Design and document for inheritance or else improment it”。(这是第二版的第 17 项和第一版的第 15 项。)你真的应该阅读它,但我会总结一下。
如果祖先不是被设计为从其继承的,那么继承类与其父类的交互可能会令人惊讶且不可预测。
因此,类应该有两种:
设计为可扩展的类,并且有足够的文档来描述它应该如何完成
课程标记为最终
如果您正在编写纯粹的内部代码,这可能有点矫枉过正。然而,在类文件中添加五个字符所涉及的额外工作量非常小。如果您只是为内部消费而编写,那么未来的编码员总是可以删除“final”——您可以将其视为警告说“此类在设计时没有考虑到继承”。
您可能希望将方法设为最终方法,以便覆盖类无法更改其他方法中依赖的行为。构造函数中调用的方法通常被声明为 final,因此您在创建对象时不会遇到任何令人不快的意外。
将类设为 final 的一个原因是,如果您想强制组合而不是继承。这在避免类之间的紧密耦合方面通常是可取的。
您可以在 3 个用例中使用最终方法。
- 避免派生类覆盖特定的基类功能。
- 这是出于安全目的,其中基类提供了框架的一些重要核心功能,而派生类不应该更改它。
- 最终方法比实例方法更快,因为最终方法和私有方法没有使用虚拟表概念。因此,只要有可能,请尝试使用 final 方法。
使班级决赛的目的:
这样没有人可以扩展这些类并改变它们的行为。
例如:包装类 Integer 是最终类。如果该类不是最终类,那么任何人都可以将 Integer 扩展到他自己的类中并改变整数类的基本行为。为了避免这种情况,java 将所有包装类都作为最终类。
您可能想要制作不可变对象(http://en.wikipedia.org/wiki/Immutable_object),您可能想要创建单例(http://en.wikipedia.org/wiki/Singleton_pattern),或者您可能想要以防止有人出于效率、安全或安保的原因覆盖该方法。
继承就像一把电锯——非常强大,但在坏人手中却很糟糕。您要么设计一个要继承的类(这可能会限制灵活性并花费更长的时间),要么应该禁止它。
请参阅 Effective Java 第 2 版第 16 和 17 项,或我的博文“继承税”。
嗯...我可以想到两件事:
您可能有一个处理某些安全问题的类。通过对其进行子类化并将其子类化版本提供给您的系统,攻击者可以规避安全限制。例如,您的应用程序可能支持插件,如果插件可以子类化您的安全相关类,它可以使用此技巧以某种方式将其子类化版本走私到位。但是,这是 Sun 必须处理的有关 applet 之类的事情,也许不是这样一个现实的情况。
一个更现实的方法是避免对象变得可变。例如,由于字符串是不可变的,您的代码可以安全地保留对它的引用
String blah = someOtherString;
而不是先复制字符串。但是,如果您可以子类化 String,则可以向其添加允许修改字符串值的方法,现在没有代码可以再依赖于字符串将保持不变,如果它只是像上面那样复制字符串,而是必须复制细绳。
阻止人们做可能混淆自己和他人的事情。想象一个物理库,其中有一些已定义的常数或计算。在不使用 final 关键字的情况下,有人可能会出现并重新定义不应更改的基本计算或常量。
此外,如果您正在编写一个商业封闭源代码类,您可能不希望人们能够更改功能,特别是如果您需要支持它并且人们已经覆盖了您的方法并抱怨调用它会给意想不到的结果。
如果将类和方法标记为 final,您可能会注意到性能的小幅提升,因为运行时不必查找正确的类方法来调用给定对象。非最终方法被标记为虚拟,以便在需要时可以适当地扩展它们,最终方法可以直接链接或在类中内联编译。
You want to make a method final so that overriding classes does not change its behavior. When you want to be able to change the behavior make the method public. When you override a public method it can be changed.