Java中“最后一课”的意义何在?

时间:2011-03-03 13:52:23

标签: java final

我正在读一本关于Java的书,它说你可以将整个班级声明为final。我想不出任何我会用它的地方。

我刚接触编程,我想知道程序员是否真的在他们的程序中使用它。如果他们这样做,他们何时使用它,以便我能更好地理解它并知道何时使用它。

如果Java是面向对象的,并且你声明了一个类final,那么它是否会阻止具有对象特征的类的想法?

25 个答案:

答案 0 :(得分:467)

  

如果他们这样做,他们何时使用它以便我能更好地理解它并知道何时使用它。

final类只是无法扩展的类。

(这并不意味着对类对象的所有引用都会像它们被声明为final一样。)

在这个问题的答案中涵盖了将一个类声明为final是有用的:

  

如果Java是面向对象的,并且您声明了一个类final,那么它是否会阻止具有对象特征的类的想法?

在某种意义上是的。

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


相关文章:Java: When to create a final class

答案 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有各种原因:

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

      

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

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

    另一方面,prefer composition over inheritancefinal关键字实际上有助于执行此原则。

答案 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关键字用于以下场合。

  1. 最终变量
  2. 最终方法
  3. 最终班
  

在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建议明确设计继承或禁止继承,他指出设计继承并不容易。