“最后一节课”的意义是什么?在Java中?

我正在读一本关于Java的书,它说你可以将整个类声明为final。我想不出有什么地方可以用它。

我只是一个编程新手,我想知道如果程序员真的在他们的程序中使用这个。如果他们使用,他们什么时候使用,这样我就能更好地理解它,知道什么时候使用它。

如果Java是面向对象的,并且您声明了一个类final,难道它不会阻止类具有对象的特征吗?

558407 次浏览

最好的例子是

公共最终类

,这是一个不可变的类,不能扩展。 当然,不仅仅是使类final为不可变

是的,有时你可能想要这个,无论是出于安全性还是速度原因。在c++中也可以做到。可能不适用于程序,但更适用于框架。 # EYZ0 < / p >

如果类被标记为final,这意味着类的结构不能被任何外部的东西修改。最明显的是当你在做传统的多态继承时,基本上class B extends A不能工作。它基本上是一种保护代码(程度)的某些部分的方法。

为了澄清,标记类final并没有将它的字段标记为final,因此不保护对象属性,而是保护实际的类结构。

在一个场景中,final很重要,当您出于安全原因想要使用类的防止继承时。这允许您确保代码由某人运行不能被覆盖

另一个场景是为了优化:我似乎记得Java编译器内联了final类中的一些函数调用。所以,如果你调用a.x()并且a被声明为final,我们在编译时就知道代码是什么,并且可以内联到调用函数中。我不知道这是否真的做到了,但最终是有可能的。

如果您将类层次结构想象成一棵树(就像在Java中那样),抽象类只能是分支,最终类只能是叶。不属于这两个类别的类可以同时是分支和叶。

这里并没有违反OO原则,final只是提供了一个漂亮的对称性。

在实践中,如果你想让你的对象是不可变的,或者如果你正在编写一个API,你想要使用final来告诉API的用户这个类不是用于扩展的。

final类是不能扩展的类。此外,方法可以声明为final,以表明不能被子类覆盖。

如果您编写api或库,并希望避免被扩展以改变基本行为,则防止类被子类化可能特别有用。

相关阅读:Bob Martin的开闭原理

主要引用:

软件实体(类,模块, 函数等)应该是开放的 分机,但已关闭 修改。< / p >

final关键字是在Java中强制执行这一点的方法,无论是用于方法还是用于类。

在Java中,不能更改带有final修饰符的项!

这包括final类、final变量和final方法:

  • final类不能被任何其他类扩展
  • 最终变量不能被重新赋值
  • final方法不能被覆盖

Final类不能扩展。因此,如果你想要一个类以某种方式运行,并且不希望有人重写方法(可能效率较低,恶意代码更多),你可以将整个类声明为final或你不想被更改的特定方法。

由于声明一个类并不会阻止一个类被实例化,这并不意味着它会阻止类具有对象的特征。只是您必须坚持使用类中声明方法的方式。

将一个类保留为最终类的一个好处是:-

String类保持为final,这样任何人都不能重写其方法并更改功能。例如,没有人可以改变length()方法的功能。它总是返回一个字符串的长度。

这个类的开发人员不希望任何人改变这个类的功能,所以他把它作为最终的。

如上所述,如果你想让任何人都不能改变方法的功能,那么你可以将它声明为final。

示例:用于下载/上传的应用服务器文件路径,基于偏移量拆分字符串,这样的方法你可以将其声明为Final,这样这些方法函数就不会被改变。如果你想要这样的final方法在一个单独的类中,那么将这个类定义为final类。所以Final类将拥有所有Final方法,而Final方法可以在非Final类中声明和定义。

当你把一门课设定为“final”时要小心。因为如果你想为final类编写单元测试,你不能为了使用Michael C. Feathers的书《Working effective with Legacy Code》中描述的“子类和重写方法”而子类化这个final类。在这本书中,Feathers说:“说真的,人们很容易相信sealed和final是一个错误的错误,它们不应该被添加到编程语言中。但真正的错误在于我们。当我们直接依赖不受我们控制的图书馆时,我们只是在自找麻烦。”

要解决最后一个类问题:

有两种方法可以让一门课成为期末考试。第一种是在类声明中使用关键字final:

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

使类成为final的第二种方法是将其所有构造函数声明为private:

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

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

所以当你隐式地将一个类的构造函数设为private时,你应该将它标记为final。

把FINAL看作是“线的尽头”——那个家伙再也不能生育后代了。所以当你这样看的时候,有很多现实世界的场景,你会遇到,需要你标记一个'结束行'标记类。这是领域驱动设计——如果你的领域要求一个给定的实体(类)不能创建子类,那么把它标记为FINAL。

我应该指出,没有什么可以阻止您继承一个“应该标记为final”的类。但这通常被归类为“滥用继承”,这样做是因为大多数情况下你想从你的类的基类继承一些函数。

最好的方法是查看领域,并让它决定您的设计决策。

面向对象不是关于继承,而是关于封装。继承会破坏封装。

在很多情况下,声明类final是非常有意义的。任何代表“价值”的对象,如颜色或金额,都可能是最终的。他们是独立的。

如果您正在编写类库,请将类设为final,除非您显式地将它们缩进以派生。否则,人们可能会派生你的类并重写方法,破坏你的假设/不变量。这也可能有安全隐患。

Joshua Bloch在《Effective Java》一书中建议明确地为继承而设计,或者禁止为继承而设计,他指出为继承而设计并不是那么容易。

final class可以避免在添加新方法时破坏公共API

假设在你的Base类的版本1中,你做:

public class Base {}

客户会这样做:

class Derived extends Base {
public int method() { return 1; }
}

然后,如果在版本2中,你想在Base中添加method方法:

class Base {
public String method() { return null; }
}

这将破坏客户端代码。

如果我们使用final class Base代替,客户端就不能继承,方法的添加也不会破坏API。

Android Looper类就是一个很好的例子。 # EYZ0 < / p >

Looper类提供了某些不打算被任何其他类覆盖的功能。因此,这里没有子类。

关键字final本身意味着某些东西是最终的,不应该以任何方式修改。如果一个类标记为final,那么它就不能被扩展或子类化。但问题是,为什么要将类标记为final?在我看来,原因有很多:

  1. 一些类执行标准函数,它们是不需要修改的,例如,类执行与字符串操作或数学函数相关的各种函数等。
  2. 安全原因:有时我们编写了执行各种身份验证和密码相关功能的类,并且我们不希望其他人更改它们。

我听说标记类final可以提高效率,但坦率地说,我找不到这个论点有多大分量。

如果Java是面向对象的,并且你声明了一个final类,不是吗 停止类具有对象特征的想法?< / p >

也许是的,但有时这就是预期的目的。有时我们这样做是为了获得更大的安全性等好处,牺牲了这个类的扩展能力。但是final类仍然可以在需要时扩展一个类。

另一方面,更喜欢组合而不是继承final关键字实际上有助于执行这一原则。

假设您有一个Employee类,它有一个方法greet。当调用greet方法时,它只打印Hello everyone!。这是greet方法的< em > < / em >预期行为

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,这种情况可以是< em > < /避免em >。想象一下,如果String类没有声明为final,厚脸皮的程序员会造成多大的混乱。

Final类不能进一步扩展。如果不需要在java中使类可继承,可以使用这种方法。

如果我们只需要让一个类中的特定方法不被覆盖,我们可以把final关键字放在它们前面。在那里类仍然是可继承的。

在java中,final关键字用于以下场合。

  1. 最后一个变量
  2. 最后的方法
  3. 最后的课程

在java中final变量不能重赋,最后的课程不能扩展,final方法不能覆盖。

我只知道一个实际用例:生成类

在生成类的用例中,我知道一个:依赖注入,例如https://github.com/google/dagger

其他的答案集中在final class告诉编译器什么:不允许另一个类声明它extends这个类,以及为什么这是可取的。

但是编译器并不是短语final class的唯一读者。每个阅读源代码的程序员也会阅读该代码。它可以帮助快速理解程序。

一般来说,如果程序员看到了Thing thing = that.someMethod(...);,并且希望了解通过thing对象引用访问的对象的后续行为,那么程序员必须考虑Thing类层次结构:可能有许多类型,分散在许多包中。但是如果程序员知道或者读过final class Thing,他们马上就知道他们不需要搜索和研究那么多Java文件,因为没有派生类:他们只需要研究Thing.java,也许,它是基类。