Java中“最终类”的意义是什么?

Java中“最终类”的意义是什么?,java,final,Java,Final,我正在读一本关于Java的书,书中说可以将整个类声明为final。我想不出在什么地方我会用这个 我刚刚开始编程,我想知道程序员是否真的在他们的程序中使用这个。如果他们这样做了,他们什么时候使用它,这样我才能更好地理解它,知道什么时候使用它 如果Java是面向对象的,并且您声明了一个类final,那么类具有对象特性的想法就不会停止了吗?首先,我推荐这篇文章: 如果他们这样做了,他们什么时候使用它,这样我才能更好地理解它,知道什么时候使用它 最后一个类只是一个不能扩展的类 这并不意味着对类的对象的所

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

我刚刚开始编程,我想知道程序员是否真的在他们的程序中使用这个。如果他们这样做了,他们什么时候使用它,这样我才能更好地理解它,知道什么时候使用它


如果Java是面向对象的,并且您声明了一个类final,那么类具有对象特性的想法就不会停止了吗?

首先,我推荐这篇文章:

如果他们这样做了,他们什么时候使用它,这样我才能更好地理解它,知道什么时候使用它

最后一个类只是一个不能扩展的类

这并不意味着对类的对象的所有引用都会被声明为final

如果将一个类声明为最终类很有用,请参见此问题的答案:

如果Java是面向对象的,并且您声明了一个类final,那么类具有对象特性的想法就不会停止了吗

从某种意义上说是的

通过将一个类标记为final,您可以为该部分代码禁用该语言强大而灵活的功能。然而,有些类不应该,并且在某些情况下不能设计为以一种好的方式考虑子类化。在这些情况下,将类标记为final是有意义的,尽管它限制了OOP。但是请记住,最终类仍然可以扩展另一个非最终类。

最好的例子是

公共最终类字符串

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

是的,有时出于安全或速度原因,您可能需要这样做。它也在C++中完成。它可能不适用于程序,但更适用于框架。

如果类被标记为final,则表示该类的结构不能被任何外部内容修改。这一点最明显的地方是,当您执行传统的多态继承时,基本上B类扩展了A类,但它不起作用。这基本上是一种在一定程度上保护代码某些部分的方法


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

出于安全原因,当您想要阻止类的继承时,final非常重要。这允许您确保正在运行的代码不会被其他人覆盖


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

如果你把类的层次结构想象成Java中的树,抽象类只能是分支,而最终类只能是叶子。不属于这两个类别的类既可以是分支也可以是叶

这里没有违反OO原则,final只是提供了一个很好的对称性


在实践中,如果您希望对象不可变,或者如果您正在编写API,则需要使用final来向API的用户发出信号,表明该类只是不用于扩展。

final类是无法扩展的类。也可以将方法声明为final,以指示不能被子类重写的方法

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

相关阅读:Bob Martin

关键报价:

软件实体类、模块、, 功能等应向用户开放 分机,但因故障关闭 修改

final关键字是在Java中实现这一点的手段,无论它是在方法上还是在类上使用。

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

这包括最终类、最终变量和最终方法:

最终类不能由任何其他类扩展 无法为最终变量重新分配另一个值 无法重写最终方法
不能扩展最终类。因此,如果您希望一个类以某种方式运行,并且不希望有人用可能效率更低、恶意更大的代码重写这些方法,那么您可以将整个类声明为您不希望更改的最终或特定方法


由于声明类不会阻止类被实例化,因此并不意味着它将阻止类具有对象的特征。只是您必须按照类中声明方法的方式来坚持这些方法

将类保留为最终类的一个优点:-

String类保持为final,这样就没有人可以重写它的方法 并更改功能。e、 g任何人都不能更改长度方法的功能。它将始终返回字符串的长度


该类的开发人员不希望任何人更改该类的功能,因此他将其保留为最终版本。

如上所述,如果您希望没有人可以更改该方法的功能,则可以将其声明为最终版本


示例:用于下载/上载的应用程序服务器文件路径,基于偏移量拆分字符串,这样的方法可以声明为Final,这样这些方法函数就不会被更改。如果您希望在单独的类中使用这样的final方法,那么将该类定义为final类。所以Final类将拥有所有Final方法,其中as Final方法可以在非Final类中声明和定义。

将类设为Final时要小心。因为如果你想为一个最终类编写一个单元测试,你不能为了使用MichaelC.Feathers的书《有效地处理遗留代码》中描述的打破依赖的技术子类和重写方法而对这个最终类进行子类化。在这本书中,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可以省去您的麻烦,以演示如何查看这个测试类。乍一看,它是公开的

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}
不幸的是,由于该类的唯一构造函数是私有的,因此不可能扩展该类。对于测试类,没有理由认为该类是最终类。测试类是隐式final类如何导致问题的一个很好的例子


因此,当您通过使类的构造函数私有化来隐式地使类成为final时,您应该将其标记为final。

将final视为该行的结尾-这家伙再也不能产生后代了。因此,当您以这种方式看待它时,您将遇到大量实际场景,这些场景要求您向类标记一个“行结束”标记。它是域驱动的设计-如果您的域要求给定的实体类不能创建子类,那么将其标记为FINAL

我应该注意到,没有什么可以阻止您继承一个应该标记为final类的类。但这通常被归类为滥用继承,因为您通常希望从类中的基类继承一些函数


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

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

在很多情况下,声明一个类的最终版本是非常有意义的。任何代表“价值”的物体,如颜色或金钱,都可以是最终的。他们自立

如果您正在编写库,请将类设为final,除非您显式缩进它们以进行派生。否则,人们可能会派生您的类并重写方法,从而破坏您的假设/不变量。这也可能涉及到安全问题

Joshua Bloch在“Effective Java”中建议显式设计继承或禁止继承,他指出为继承设计并不是那么容易。

当您添加新方法时,final类可以避免破坏公共API

假设在基类的版本1上执行以下操作:

public class Base {}
客户会:

class Derived extends Base {
    public int method() { return 1; }
}
然后,如果在版本2中,您希望向基础添加方法:

它会破坏客户端代码


如果改用final类Base,客户端将无法继承,方法添加也不会破坏API。

Android Looper类就是一个很好的实例。

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

关键字final本身意味着某些东西是final的,不应该以任何方式修改。如果一个类被标记为final,那么它就不能被扩展或子类。但问题是我们为什么要在期末考试中打分?国际海事组织有各种原因:

标准化:一些类执行标准函数,它们不需要修改,例如执行与字符串操作或数学函数等相关的各种函数的类。 安全原因:有时我们编写执行各种身份验证和密码相关功能的类,我们不希望其他人更改它们。 我听说期末考试评分可以提高效率,但坦率地说,我觉得这个论点没有多大分量

如果Java是面向对象的,并且您声明了一个类final,不是吗 停止类具有对象特性的想法

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

另一方面,我们应该注意,final关键字实际上有助于实现这一原则。

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

现在,让GrumpyEmployee子类Employee并覆盖greet方法,如下所示

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}
现在在下面的代码中,看看sayHello方法。它将Employee实例作为参数,并调用greet方法,希望它能向所有人打招呼!但我们得到的是迷路!。这种行为的改变是因为Employee grumpyEmployee=新的grumpyEmployee


如果将Employee类设置为final,则可以避免这种情况。试想一下,如果字符串类没有声明为final,一个厚颜无耻的程序员会造成多大的混乱

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


如果我们只需要使类中的特定方法不被重写,我们只需要将final关键字放在它们前面。在java中,final关键字用于以下情况

最终变量 最终方法 期末班 在java中,final变量不能重新分配,final类不能扩展,final方法不能重写


呵呵,有时候它会保护Rube-Goldergian开发人员不受自己的影响。另外,有效Java的原则之一是支持组合而不是继承。final关键字的使用也有助于强化这一原则。您这样做主要是为了提高效率和安全性。我经常听到这句话,甚至维基百科也这么说,但我仍然不理解这一论点背后的原因。有人想解释一下,比如说,一个非最终的java.lang.String是如何导致效率低下或不安全的吗?@MRA如果我创建了一个接受字符串作为参数的方法,我假设它是不可变的,因为字符串是不可变的。因此,我知道我可以安全地调用String对象上的任何方法,而不会更改传递的字符串。如果我要扩展String,并更改substring的实现以更改实际的String,那么您希望不可变的String对象就不再是不可变的了。@Sortofabeginner,只要您说希望所有String方法和字段都是final,就可以创建具有附加功能的类。。。在这一点上,您还可以创建一个具有字符串的类,并创建对该字符串进行操作的方法。@Shay final等用于使对象不可变,所以我不会说它们彼此无关。请看这里@Sean:声明它为final不会使类关闭以进行扩展而不是打开吗?还是我把它看得太字面了?@Goran,是的。关键是在不需要修改的地方有选择地应用final,当然还要为扩展提供良好的挂钩在OCP中,修改指修改源代码,扩展指实现继承。因此,如果您希望实现代码关闭以进行修改,但通过继承打开以进行扩展,则在类/方法声明中使用final是没有意义的。@Rogerio我从中借用了引用和解释。我觉得这比原来的版本更有意义。扩展已经死了。无用的大量死亡。摧毁。我不在乎OCP。扩展类从来没有借口。内联通常只由实时编译器在运行时完成。它在没有最终版本的情况下也有效,但是JIT编译器还有更多的工作要做,以确保没有扩展类,或者这些扩展类不涉及此方法。关于内联和优化问题的一篇好文章可以在这里找到:>不能被谁所覆盖?我认为术语安全性在学术意义上几乎让人困惑意味着安全。安全意味着当您使用一个类时,您知道另一个类没有扩展它,并且依赖于基类的内部实现。我认为我们都遇到过这样的情况:类被以各种恶劣的方式扩展,不花一整天的时间就无法对代码进行推理。真正的问题是为什么,而不是什么。在Java中,带有最终修饰符的语句是不能更改的!,太过直截了当,事实上并不完全正确。正如Grady Booch所说,对象具有状态、行为和身份。虽然我们不能一次改变一个物体的身份
它的引用被标记为final,我们确实有机会通过为它的非final字段指定新值来更改它的状态,当然,它有这些值。任何计划获得Oracle Java认证(如1Z0-808等)的人都应该记住这一点,因为考试中可能会有关于这方面的问题……对象属性意味着什么?这是否意味着如果类被声明为final,我可以修改该类的成员变量?所以final类的唯一目的是防止继承。
public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}
public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}
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!"
    }
}