深入理解final关键字以及一些建议

引子:一说到final关键字,相信大家都会立刻想起一些基本的作用,那么我们先稍微用寥寥数行来回顾一下。

一、final关键字的含义

final是Java中的一个保留关键字,它可以标记在成员变量、方法、类以及本地变量上。一旦我们将某个对象声明为了final的,那么我们将不能再改变这个对象的引用了。如果我们尝试将被修饰为final的对象重新赋值,编译器就会报错。

二、用法

1.修饰变量

final修饰在成员变量或者局部变量上,那么我们可以称这个变量是final变量,这可能使我们用到最多的地方,举个栗子:常量(虽然现在建议使用枚举类来代替常量)。

如果我们将被final修饰的变量重新赋值,编译器就会报出如图:cannot assign a value to final variable.(不能给final变量赋值)

2.修饰方法

被final所修饰的方法将无法被子类重写。

使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。” -- 摘自《Java编程思想》

因此如果你认为一个方法的功能已经足够完整了,子类中不需要改变的话,你可以声明此方法为final。final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定(正如编程思想中所提到的,在现在几版较新的JDK中,已经几乎没有性能差别了)。

(当我们尝试重写的时候编译器就会报错)。

注:类的private方法会隐式地被指定为final方法。

3.修饰类

如果某个类被final所修饰,那么表明这个的功能通常是完整的;该类将不能被继承。并且final类的所有方法都会被隐式的修饰成final。

4.ps:匿名类中的所有变量都必须是final的。

三、关键字final的好处小结

  1. final关键字提高了性能。JVM和Java应用都会缓存final变量。
  2. final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
  3. 使用final关键字,JVM会对方法、变量及类进行优化。
  4. 对于不可变类,它的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销。

四、来自《Effective Java》中的一些建议

该书的第17条:要么为了继承而设计,并提供文档说明,要么就禁止继承

该条目提醒我们,如果类不是被设计用来继承的,那么这个类就应该被禁止继承(听起来有点绕,但细想下来的设计思想是很好的),否则就应该提供足够的文档及注释(具体可参考java.util.AbstractCollection这个骨架实现里的注释文档规范)。

而禁止类被子类化的方法通常有两个:

1.将所有的构造器设为私有的(private)或者包级私有的(default),并使用静态工厂方法来代替构造器;

2.将类标记为final。

五、思考

1.一些思考回头再来审视我们日常中的程序,我们可能已经习惯了不去那么刻意的使用final,顶多在写常量的时候用一用,但实际上我们很多的类,方法或者变量是不需要被改变的,或者说不会被继承的。比如我在刚读到《Effective Java》中的这个条目后,回首自己正在做的一个项目中审视了一下,我首先将自己的domain层中的一些类标为了final,因为我觉得这些类是不可能被继承的,如果继承了是不太符合设计的,并且程序运行没有异常,同时修改的还有我的依赖注入方式(参考我的上一篇博客:Spring注解依赖注入的三种方式的优缺点以及优先选择)

我重新纠正了一下自己在设计类的时候的思想顺序:之前自己在准备写一个类的时候(虽然通常我是不给类加final的= =),可能觉得这个类(变量或者方法)不能被改变,有很强烈的这种想法时才会加上final,但现在是:这个类需不需要使他可以被子类化?如果在以后的项目更新,迭代中,并不需要,那么我会毫不犹豫的给他加上final。

2."final关键字能提升性能"?

当时发现这一点之后,我可能是中毒了,给能加上final的地方都加上了,自以为改善了性能心里还美滋滋呢。其实对这个“提升性能”一点一直还有一丝的疑问,于是我回头就去了Stack Overflow上转了一圈,找到了我想要的答案:Does use of final keyword in Java improve the performance?

大佬指出,通常是不会的,对于方法,HotPot会跟踪看它是否真的被重写了,并且能够优化没有被重写的内敛方法,直到它加载到了一个类复写了这个方法,这时它可以撤销(或部分撤销)这些优化。(当然,这是假设您使用的是HotPot,但到目前为止这是最常见的JVM,所以…)

之后大佬指出了我们不应该为了这么丝许的性能而绞尽脑汁,建议我们应该明确设计,写出好的结构的代码以及可读性优良的代码。(在此又应证了《Effective Java》中的第55条:谨慎地进行优化中所指出的核心:优化的格言就是:不要进行优化) (也验证了上面《Java编程思想》中最后的那句话)

3.关于局部变量以及参数中的final

接着我尝试将我的局部变量以及方法中的参数都标记为final的,同2一样,已经中毒颇深了。但是我对此同时也存在着同样的疑问,然后在Stack Overflow中得到了经验证的又一个结论:局部变量以及参数中的final,同样不能提升我们的性能,它甚至不会被写进字节码中。于是我操起了键盘啪啪啪一顿敲了几行代码编译了一下,并用反编译工具(如JD-GUI)打开:

先来看我们的源码:

public class FinalTest {
    private static void say(final int number) {
        System.out.println("number: " + number);
    }

    public static void main(String[] args) {
        final int num = 0;
        say(num);
    }
}

再来看看编译后的.class文件:

public class FinalTest {
    public FinalTest() {
    }

    private static void say(int number) {
        System.out.println("number: " + number);
    }

    public static void main(String[] args) {
        int num = false;
        say(0);
    }
}

可以看到在写入字节码的时候就被优化掉了,final只是编译时静态限制我们不能再赋值(改变引用)。

原文地址:https://www.cnblogs.com/chansblogs/p/8387801.html

时间: 2024-08-29 13:21:06

深入理解final关键字以及一些建议的相关文章

深入理解final关键字

在了解了final关键字的基本用法之后,这一节我们来看一下final关键字容易混淆的地方. 1.类的final变量和普通变量有什么区别? 当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了. 那么final变量和普通变量到底有何区别呢?下面请看一个例子: public class Test { public static void main

java基础4:深入理解final关键字

本文主要介绍了final关键字的使用方法及原理 具体代码在我的GitHub中可以找到 https://github.com/h2pl/MyTech 文章首发于我的个人博客: https://h2pl.github.io/2018/04/23/javase4 final关键字可以修饰类.方法和引用. 修饰类,该类不能被继承.并且这个类的对象在堆中分配内存后地址不可变. 修饰方法,方法不能被子类重写. 修饰引用,引用无法改变,对于基本类型,无法修改值,对于引用,虽然不能修改地址值,但是可以对指向对象

浅析Java中的final关键字

原文出处: 海子 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下面是本文的目录大纲: 一.final关键字的基本用法 二.深入理解final关键字 若有不正之处,请多多谅解并欢迎指正. 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字

转载:浅析Java中的final关键字

文章转自:http://www.cnblogs.com/dolphin0520/p/3736238.html 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下面是本文的目录大纲: 一.final关键字的基本用法 二.深入理解final关键字 若有不正之处,请多多谅解并欢迎指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cn

浅析Java中的final关键字(转载)

自http://www.cnblogs.com/dolphin0520/p/3736238.html转载 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能被继承.也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰.final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方

浅析Java中的final关键字(转)

浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下面是本文的目录大纲: 一.final关键字的基本用法 二.深入理解final关键字 若有不正之处,请多多谅解并欢迎指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3736238.html 一

浅谈Java中的final关键字

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类   当用final修饰一个类时,表明这个类不能被继承.也就是说,如果一个类你永远不会让他被继承,就可以用final

【Java学习笔记之二十】final关键字在Java继承中的用法小结

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类   当用final修饰一个类时,表明这个类不能被继承.也就是说,如果一个类你永远不会让他被继承,就可以用final

【转】浅析Java中的final关键字

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能被继承.也就是说,如果一个类你永远不会让它被继承,就可以用final进行