我正在读一本关于Java的书,它说你可以将整个班级声明为final
。我想不出任何我会用它的地方。
我刚接触编程,我想知道程序员是否真的在他们的程序中使用它。如果他们这样做,他们何时使用它,以便我能更好地理解它并知道何时使用它。
如果Java是面向对象的,并且你声明了一个类final
,那么它是否会阻止具有对象特征的类的想法?
答案 0 :(得分:467)
如果他们这样做,他们何时使用它以便我能更好地理解它并知道何时使用它。
final
类只是无法扩展的类。
(这并不意味着对类对象的所有引用都会像它们被声明为final
一样。)
在这个问题的答案中涵盖了将一个类声明为final是有用的:
如果Java是面向对象的,并且您声明了一个类
final
,那么它是否会阻止具有对象特征的类的想法?
在某种意义上是的。
通过将类标记为final,您可以为该部分代码禁用该语言的强大而灵活的功能。但是,某些类不应该(并且在某些情况下可以不是)设计为以良好的方式考虑子类。在这些情况下,将类标记为final是有意义的,即使它限制了OOP。 (但请记住,最终的课程仍然可以扩展另一个非最终课程。)
答案 1 :(得分:172)
在Java中,无法更改带有final
修饰符的项目!
这包括最终类,最终变量和最终方法:
答案 2 :(得分:29)
出于安全原因,当您希望阻止继承类时,最终结果很重要的一种情况。这使您可以确保某人无法覆盖您正在运行的代码。
另一种情况是优化:我似乎记得Java编译器在最终类中内联了一些函数调用。因此,如果您调用a.x()
并且a声明为final
,我们在编译时知道代码将是什么并且可以内联到调用函数中。我不知道这是否真的已经完成,但最终有可能。
答案 3 :(得分:19)
最好的例子是
public final class String
这是一个不可变的类,无法扩展。 当然,不仅仅是让班级最终成为不可变的。
答案 4 :(得分:15)
如果将类层次结构想象为树(就像在Java中一样),抽象类只能是分支,而最终类只能是叶子。既不属于这些类别的类也可以是分支和叶子。
这里没有违反OO原则,最终只是提供了一个很好的对称性。
实际上,如果您希望对象是不可变的,或者您正在编写API,那么您希望使用final,以向API的用户发出该类不是用于扩展的信号。
答案 5 :(得分:15)
相关阅读:鲍勃·马丁的The Open-Closed Principle。
关键报价:
软件实体(类,模块, 功能等)应该是开放的 扩展,但关闭 修改
final
关键字是在Java中强制执行此操作的方法,无论是在方法上还是在类上使用。
答案 6 :(得分:10)
关键字final
本身意味着某些东西是最终的,不应该以任何方式进行修改。如果一个类如果标记为final
,则它不能被扩展或分类。但问题是为什么我们要标记一个班级final
? IMO有各种原因:
我听说标记班final
提高了效率,但坦率地说,我发现这个论点不太重要。
如果Java是面向对象的,并且你声明了一个类final,那不是 停止具有对象特征的类的想法?
也许是的,但有时这是预期的目的。有时我们通过牺牲这个类的扩展能力来实现更大的安全性等。但是如果需要的话,最终的类仍然可以扩展一个类。
另一方面,prefer composition over inheritance和final
关键字实际上有助于执行此原则。
答案 7 :(得分:5)
当你上课“最后”时要小心。因为如果你想为最后一个类编写单元测试,你不能继承这个最终类,以便使用Michael C. Feathers的书“有效地使用遗留代码”中描述的依赖性破坏技术“Subclass and Override Method”。 。在这本书中,Feathers说,“说真的,很容易相信密封和最终是一个错误的错误,他们应该永远不会被添加到编程语言中。但真正的错误在于我们。当我们直接依赖我们无法控制的图书馆,我们只是在寻找麻烦。“
答案 8 :(得分:5)
final class
可以避免在添加新方法时破坏公共API
假设您执行Base
课程的第1版:
public class Base {}
和客户做:
class Derived extends Base {
public int method() { return 1; }
}
然后,如果在版本2中,您想要将method
方法添加到Base
:
class Base {
public String method() { return null; }
}
它会破坏客户端代码。
如果我们使用final class Base
代替,则客户端无法继承,并且方法添加不会破坏API。
答案 9 :(得分:5)
解决最终类别问题:
有两种方法可以让课程最终成功。第一种是在类声明中使用关键字final:
public final class SomeClass {
// . . . Class contents
}
使类最终成功的第二种方法是将其所有构造函数声明为private:
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类如何导致问题的一个很好的例子。
因此,当您通过使其构造函数为私有来隐式地使类成为最终时,您应该将其标记为最终。
答案 10 :(得分:5)
如果类被标记为final
,则意味着类的结构不能被外部任何东西修改。这是最明显的,当你进行传统的多态继承时,基本上class B extends A
就行不通了。它基本上是一种保护代码某些部分的方法(范围)。
为了澄清,标记类final
不会将其字段标记为final
,因此不会保护对象属性,而是保护实际的类结构。
答案 11 :(得分:3)
将课程保持为最终的一个好处: -
String类保持最终,以便没有人可以覆盖其方法并更改功能。例如,没有人可以改变length()方法的功能。它将始终返回字符串的长度。
这个类的开发人员不希望任何人改变这个类的功能,所以他把它保留为最终版。
答案 12 :(得分:3)
最后一堂课是一个无法扩展的课程。此外,方法可以声明为final,表示子类不能覆盖它们。
如果您编写API或库并希望避免扩展以更改基本行为,则防止该类被子类化可能特别有用。
答案 13 :(得分:3)
是的,出于安全或速度原因,有时你可能会想要这个。它也是用C ++完成的。它可能不是 适用于程序,但更适用于框架。 http://www.glenmccl.com/perfj_025.htm
答案 14 :(得分:1)
如上所述,如果你想要没有人可以改变方法的功能,那么你可以将其声明为最终。
示例:用于下载/上传的应用程序服务器文件路径,基于偏移量拆分字符串,此类方法可以将其声明为Final,以便不会更改这些方法函数。如果你想在一个单独的类中使用这样的最终方法,那么将该类定义为Final类。所以Final类将拥有所有最终方法,其中Final方法可以在非final类中声明和定义。
答案 15 :(得分:1)
最终课程无法延期。因此,如果您希望某个类以某种方式运行并且没有人覆盖这些方法(可能效率较低且恶意代码较多),则可以将整个类声明为最终或特定方法,而您不希望这些方法改变。
由于声明一个类并不会阻止实例化类,因此并不意味着它会阻止类具有对象的特性。只是你必须坚持这些方法,就像它们在课堂上宣布的方式一样。
答案 16 :(得分:1)
将FINAL视为“行的终结” - 那个家伙再也不会产生后代了。因此,当您以这种方式看到它时,您会遇到许多真实场景,需要您在课程中标记“行尾”标记。它是域驱动设计 - 如果您的域要求给定的ENTITY(类)无法创建子类,则将其标记为FINAL。
我应该注意,没有什么可以阻止你继承“应该被标记为最终”类。但这通常被归类为“滥用继承”,因为大多数情况下你想从类中的基类继承一些函数。
最好的方法是查看域名并让它决定您的设计决策。
答案 17 :(得分:1)
Android Looper类就是一个很好的实例。 http://developer.android.com/reference/android/os/Looper.html
Looper类提供某些功能,不打算被任何其他类覆盖。因此,这里没有子类。
答案 18 :(得分:1)
假设您有一个Employee
类,该类具有方法greet
。调用greet
方法时,它只打印Hello everyone!
。这就是greet
方法的 预期行为
public class Employee {
void greet() {
System.out.println("Hello everyone!");
}
}
现在,让GrumpyEmployee
成为Employee
的子类,并覆盖greet
方法,如下所示。
public class GrumpyEmployee extends Employee {
@Override
void greet() {
System.out.println("Get lost!");
}
}
现在在下面的代码中查看sayHello
方法。它以Employee
实例为参数,并调用greet方法,希望它表示 Hello everyone!
,但是我们得到的是 Get lost!
。这种行为上的变化是由于Employee grumpyEmployee = new GrumpyEmployee();
public class TestFinal {
static Employee grumpyEmployee = new GrumpyEmployee();
public static void main(String[] args) {
TestFinal testFinal = new TestFinal();
testFinal.sayHello(grumpyEmployee);
}
private void sayHello(Employee employee) {
employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
}
}
如果将Employee
类设为final
,则可以 避免 这种情况。试想一下,如果String
类未声明为final
,那么厚脸皮的程序员可能会造成多大的混乱。
答案 19 :(得分:0)
很快,无法更改声明为final的类,变量或方法。
我的观点更重要的是:
老实说,我认为关键字mapTo
是一个错误,因为它的存在允许用户定义不 final
的内容。除了接口方法(按照其定义),Java中的所有内容都应该是final
。默认情况下,让所有实例方法都为final
是一个不幸的选择(就pipe
而言)。关键是强迫用户首先思考并明确地将方法明确地定义为virtual
- 这会让他问自己这种方法是否应该让别人覆盖它,无论它是virtual
a需要或说服他重新设计他的代码。
答案 20 :(得分:0)
最终课程无法进一步扩展。如果不需要使类在Java中可继承,则可以使用这种方法。
如果我们只需要在一类特定的方法没有被覆盖,我们就可以把最后的关键字在他们面前。有类仍然继承。
答案 21 :(得分:0)
在java final关键字用于以下场合。
在Java中,final变量无法重新分配,最终类不能扩展,并且final方法不能覆盖。
答案 22 :(得分:0)
我只知道一个实际用例:生成的类
在生成类的用例中,我知道一个:依赖注入,例如https://github.com/google/dagger
答案 23 :(得分:0)
其他答案集中在 final class
告诉编译器的内容:不允许另一个类声明它 extends
这个类,以及为什么这是可取的。
但编译器并不是短语 final class
的唯一读者。每个阅读源代码的程序员也会阅读源代码。它可以帮助快速理解程序。
一般来说,如果程序员看到 Thing thing = that.someMethod(...);
并且程序员想了解通过 thing
对象引用访问的对象的后续行为,那么程序员必须考虑 Thing
类层次结构:可能有很多类型,分散在许多包中。但是如果程序员知道或读到final class Thing
,他们立即知道他们不需要搜索和学习这么多Java文件,因为没有派生类:他们只需要学习Thing.java
和,也许是基类。
答案 24 :(得分:-2)
对象定位不是关于继承,而是关于封装。继承打破了封装。
在很多情况下宣布课程决赛很有意义。任何代表“价值”的对象,如颜色或金额,都可能是最终的。他们独立站立。
如果您正在编写库,请将您的类设为final,除非您明确地将它们缩进以进行派生。否则,人们可能会派生你的类并覆盖方法,打破你的假设/不变量。这也可能具有安全隐患。
“有效Java”中的Joshua Bloch建议明确设计继承或禁止继承,他指出设计继承并不容易。