Java中的”最终类” “final class” 有什么意义?

What is the point of “final class” in Java?

我正在读一本关于Java的书,它说你可以把整个类声明为EDCOX1(0)。我想不出任何我会用这个的地方。

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

如果Java是面向对象的,并且声明一个类EDOCX1,0,那么它不会停止类具有对象特性的概念吗?


If they do, when do they use it so I can understand it better and know when to use it.

final类只是一个不能扩展的类。

(这并不意味着对该类对象的所有引用都将起到声明为final的作用。)

如果将某个班级宣布为期末班对本问题的答案有帮助:

  • 在Java中禁止继承的好理由?

If Java is object oriented, and you declare a class final, doesn't it stop the idea of class having the characteristics of objects?

在某种意义上是的。

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

相关文章:Java:何时创建最终类


在爪哇,具有EDCOX1×0修改符的项不能更改!

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

  • 最后一个类不能由任何其他类扩展
  • 无法重新分配最终变量的另一个值
  • 无法重写最终方法


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

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


最好的例子是

public final class String

它是不可变的类,不能扩展。当然,不仅仅是让类最终成为不可变的。


如果你把类层次结构想象成一棵树(就像它在Java中一样),抽象类只能是分支,而最终类只能是叶子。不属于这些类别的类既可以是分支也可以是叶。

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

在实践中,如果您希望对象是不可变的或者正在编写API,那么您需要使用final来向API的用户发出这样的信号:类只是不打算扩展。


相关阅读:鲍勃马丁的开闭原理。

重点引文:

Software Entities (Classes, Modules,
Functions, etc.) should be open for
Extension, but closed for
Modification.

EDCOX1×0 }关键字是在Java中强制执行的方法,无论是用于方法还是在类上。


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

  • 标准化:有些类执行标准函数,并不打算修改它们,例如执行与字符串操作或数学函数相关的各种函数的类等。
  • 安全原因:有时我们编写执行各种身份验证和密码相关功能的类,我们不希望其他人更改它们。
  • 我听说标记类final提高了效率,但坦率地说,我找不到这个论点有多重要。

    If Java is object oriented, and you declare a class final, doesn't it
    stop the idea of class having the characteristics of objects?

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

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


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

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


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

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

    1
    public class Base {}

    客户会:

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

    然后,如果在版本2中,您希望向Base添加一个method方法:

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

    它会破坏客户机代码。

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


    要解决最后一个类问题:

    有两种方法可以让一个班级进入决赛。第一种方法是在类声明中使用关键字final:

    1
    2
    3
    public final class SomeClass {
      //  . . . Class contents
    }

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

    1
    2
    3
    4
    public class SomeClass {
      public final static SOME_INSTANCE = new SomeClass(5);
      private SomeClass(final int value) {
      }

    把它标记为final,如果发现它是实际的final,就可以省去麻烦,来演示一下这个测试类。乍一看就公之于众。

    1
    2
    3
    4
    5
    6
    public class Test{
      private Test(Class beanClass, Class stopClass, int flags)
        throws Exception{
        //  . . . snip . . .
      }
    }

    不幸的是,由于类的唯一构造函数是私有的,所以无法扩展这个类。对于测试类,没有理由认为类应该是最终的。测试类是一个很好的例子,说明隐式最终类如何引起问题。

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


    当你上"期末"课时要小心。因为如果你想为最后一个类编写一个单元测试,你就不能将这个最后一个类子类化,以便使用迈克尔C.费瑟的书"有效地使用遗留代码"中描述的破坏依赖性的技术"子类和重写方法"。在这本书中,Feather说,"认真地说,很容易相信密封和最终是一个错误的错误,他们不应该被添加到编程语言中。但真正的错误在于我们。当我们直接依赖我们无法控制的图书馆时,我们只是在自找麻烦。"


    保持一个等级为最终等级的一个好处是:

    字符串类是最终的,因此没有人可以重写它的方法并更改其功能。例如,没有人可以更改length()方法的功能。它总是返回字符串的长度。

    这个类的开发人员不希望任何人更改这个类的功能,所以他将其作为最终版本保存。


    是的,但有时出于安全或速度的原因,您可能需要这样做。它也在C++中完成。它可能不适用于程序,但更适用于框架。网址:http://www.glenmccl.com/perfj_025.htm


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

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


    如上所述,如果您希望没有人可以更改方法的功能,那么您可以将其声明为final。

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


    把"最后一条线"看成是"线的尽头"——那家伙再也不能生产后代了。所以当你这样看的时候,你会遇到很多真实的场景,这些场景需要你在类中标记一个"行尾"标记。它是域驱动的设计——如果您的域要求给定的实体(类)不能创建子类,那么将其标记为final。

    我应该注意到,没有什么能阻止您继承"应该标记为最终"类。但这通常被归类为"滥用继承",之所以这样做,是因为大多数情况下,您希望从类中的基类继承一些函数。

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


    Android Looper类就是一个很好的例子。http://developer.android.com/reference/android/os/looper.html

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


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

    1
    2
    3
    4
    5
    6
    public class Employee {

        void greet() {
            System.out.println("Hello everyone!");
        }
    }

    现在,让GrumpyEmployee子类Employee并重写greet方法,如下所示。

    1
    2
    3
    4
    5
    6
    7
    public class GrumpyEmployee extends Employee {

        @Override
        void greet() {
            System.out.println("Get lost!");
        }
    }

    下面的代码介绍了sayHello方法。它以Employee实例为参数,调用greet方法,希望它可以说Hello everyone!,但我们得到的是Get lost!。这种行为的改变是由Employee grumpyEmployee = new GrumpyEmployee();引起的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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类,那么一个冒失的程序员会造成多大的混乱。


    无法扩展最终类。因此,如果您希望某个类以某种方式运行,并且没有人重写这些方法(可能使用效率较低、恶意代码),那么您可以将整个类声明为不希望更改的最终方法或特定方法。

    由于声明一个类并不能阻止类被实例化,这并不意味着它将阻止类具有对象的特性。只是你必须坚持这些方法,就像它们在类中声明的那样。


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

    如果我们只需要使类中的特定方法不被重写,我们只需将最后一个关键字放在它们前面。类仍然可以继承。


    很快,不能更改声明为final的类、变量或方法。

    更重要的是我的观点:

    老实说,我认为关键字final是错误的,因为它的存在允许用户定义一些不是final的东西。除了接口方法(按其定义),Java中的所有东西都应该默认为EDCOX1×5。让所有实例方法默认为virtual,这是一个不幸的选择(就c而言)。关键是要强迫用户首先思考,并明确地将一个方法定义为virtual,这会让他自问这个方法是否应该让其他人覆盖它,它是really的需要还是说服他重新设计他的代码。


    对象方向不是继承,而是封装。继承破坏了封装。

    在很多情况下,声明类final是完全有意义的。任何代表"价值"的物体,如颜色或金钱都可能是最终的。他们自己站着。

    如果您正在编写库,请使类成为最终类,除非您显式地将它们缩排为派生类。否则,人们可能会派生您的类并重写方法,从而破坏您的假设/不变量。这也可能会带来安全隐患。

    "有效Java"中的Joshua Bloch建议明确地设计继承或禁止它,他指出设计继承并不是那么容易。