final关键字是一个常用的关键字,可以修饰变量、方法、类,用来表示它修饰的类、方法和变量不可改变,下面就聊一下使用 final 关键字的一些小细节。

细节一、final 修饰类成员变量和实例成员变量的赋值时机

对于类变量:

  1. 声明变量的时候直接赋初始值

  2. 在静态代码块中给类变量赋初始值

如下代码所示:

public class FinalTest {    //a变量直接赋值private final static  int a = 1;private final static  int b;//b变量通过静态代码块赋值static {b=2;}
}

对于实例变量:

  1. 在声明变量的时候直接赋值

  2. 在非静态代码块中赋值

  3. 在构造器中赋初始化值

如下代码所示:

public class FinalTest {//c变量在在声明时直接赋值private final  int c =1;private final  int d;private final  int e;//d变量在非静态代码块中赋值{d=2;}//e变量在构造器中赋值FinalTest(){e=3;}
}

细节二、当 final 修饰的成员变量未对它进行初始化时,会出现错误吗?

答:会出现错误。因为 java 语法规定,final 修饰的成员变量必须由程序员显示的初始化,系统不会对变量进行隐式的初始化。

如下图所示,未初始化变量就会出现编译错误:

细节三、final 修饰基本类型变量和引用类型变量的区别

如果 fianl 修饰的是一个基本数据类型的数据,一旦赋值后就不能再次更改。

那么 final 修饰的是引用数据类型呢?这个引用的变量能够改变吗?

看下面的代码:

public class FinalTest {//在声明final实例成员变量时进行赋值private final static Student student = new Student(50, "Java");public static void main(String[] args) {//对final引用数据类型student进行更改student.age = 100;System.out.println(student.toString());}static class Student {private int age;private String name;public Student(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "Student{" +"age=" + age +", name='" + name + '\'' +'}';}}
}//下面是打印结果
Student{age=100, name='Java'}

从打印结果可以看到:引用数据类型变量 student 的 age 属性修改成 100,是可以修改成功的。

结论:

  1. 当 final 修饰基本数据类型变量时,不能对基本数据类型变量重新赋值,因此基本数据类型变量不能被改变。

  2. 对于引用类型变量而言,它仅仅保存的是一个引用,final 只保证这个引用类型变量所引用的地址不会发生改变,即一直引用这个对象,但这个对象里面的属性是可以改变的。

细节四、final 修饰局部变量的场景

fianl 局部变量由程序员进行显示的初始化,如果 final 局部变量进行初始化之后就不能再次进行更改。

如果 final 变量未进行初始化,可以进行赋值,并且只能进行一次赋值,一旦赋值之后再次赋值就会出错。

下面的代码演示 final 修饰局部变量的情况:

细节五、final 修饰方法会对重载有影响吗?重写呢?

对于重载:final 修饰方法后是可以重载的

如下代码:

public class FinalTest {public final void test(){}//重载方法不会出现问题public final void test(String test){}
}

对于重写:当父类的方法被 final 修饰的时候,子类不能重写父类的该方法

如上代码所示,可以看到会出现 cannot override ,overridden method is final 的编译错误提示

细节六、final 修饰类的场景

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用 final 进行修饰。

final 类中的成员变量可以根据需要设为 final,但是要注意 final 类中的所有成员方法都会被隐式地指定为 final 方法。

细节七、写 final 域的重排序规则,你知道吗?

这个规则是指禁止对 final 域的写重排序到构造函数之外,这个规则的实现主要包含了两个方面:

  1. JMM 禁止编译器把 final 域的写重排序 到 构造函数 之外

  2. 编译器会在 final 域写之后,构造函数 return 之前,插入一个 StoreStore 屏障。这个屏障可以禁止处理器把 final 域的写重排序到构造函数之外

给举个例子,要不太抽象了,先看一段代码

public class FinalTest{private int a;  //普通域private final int b; //final域private static FinalTest finalTest;public FinalTest() {a = 1; // 1. 写普通域b = 2; // 2. 写final域}public static void writer() {finalTest = new FinalTest();}public static void reader() {FinalTest demo = finalTest; // 3.读对象引用int a = demo.a;    //4.读普通域int b = demo.b;    //5.读final域}
}

假设线程 A 在执行 writer()方法,线程 B 执行 reader()方法。

由于变量 a 和变量 b 之间没有依赖性,所以就有可能会出现下图所示的重排序

由于普通变量 a 可能会被重排序到构造函数之外,所以线程 B 就有可能读到的是普通变量 a 初始化之前的值(零值),这样就可能出现错误。

而 final 域变量 b,根据重排序规则,会禁止 final 修饰的变量 b 重排序到构造函数之外,从而 b 能够正确赋值,线程 B 就能够读到 final 域变量 b初始化后的值。

结论:写 final 域的重排序规则可以确保在对象引用为任意线程可见之前,对象的 final 域已经被正确初始化过了,而普通域就不具有这个保障。

细节八:读 final 域的重排序规则,你知道吗?

这个规则是指在一个线程中,初次读对象引用和初次读该对象包含的 final 域,JMM 会禁止这两个操作的重排序。

还是上面那段代码

public class FinalTest{private int a;  //普通域private final int b; //final域private static FinalTest finalTest;public FinalTest() {a = 1; // 1. 写普通域b = 2; // 2. 写final域}public static void writer() {finalTest = new FinalTest();}public static void reader() {FinalTest demo = finalTest; // 3.读对象引用int a = demo.a;    //4.读普通域int b = demo.b;    //5.读final域}
}

假设线程 A 在执行 writer()方法,线程 B 执行 reader()方法。

线程 B 可能就会出现下图所示的重排序

可以看到,由于读对象的普通域被重排序到了读对象引用的前面,就会出现线程 B 还未读到对象引用就在读取该对象的普通域变量,这显然是错误的操作。而 final 域的读操作就“限定”了在读 final 域变量前已经读到了该对象的引用,从而就可以避免这种情况。

结论:读 final 域的重排序规则可以确保在读一个对象的 final 域之前,一定会先读包含这个 final 域的对象的引用。

结束

今天给大家总结了一下使用 final 关键字容易忽视的一些小细节,看完希望你能有所收获。


往期推荐

Java中的Switch都支持String了,为什么不支持long?

对象复制的7种方法,还是Spring的最好用!

分布式ID生成的9种方法,特好用!

关注我,每天陪你进步一点点!

final的8个小细节,听说只有高手才知道!你知道几个?相关推荐

  1. final关键字的这8个小细节,你get到几个?

    前言 大家好,我是狂聊君. 今天来聊 final 关键字,因为最近在看的几本书都讲到了 final 关键字,发现好多小细节自己都忽视了,抽空总结了一下,分享给大家. 正文 final关键字是一个常用的 ...

  2. final 关键字的 8 个小细节

    作者 | 狂聊君   责编 | 张文 头图 | CSDN 下载自视觉中国 今天来聊 final 关键字,因为最近在看的几本书都讲到了 final 关键字,发现好多小细节自己都忽视了,抽空总结了一下,分 ...

  3. java怎么让多个方法循环运行下去_35 个小细节,提升 Java 代码的运行效率!你知道几个?...

    原标题:35 个小细节,提升 Java 代码的运行效率!你知道几个? 前言 代码优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个 ...

  4. 为什么MacBook风扇噪音这么大?快来注意这些小细节!

    我们的Mac需要做一些繁重的工作.例如,当涉及到Web开发,照片修饰和视频编辑时.听到我们的MacBook Pro风扇控制器弹起并且MacBook Pro风扇的噪音增大,因为我们知道它工作很艰辛,所以 ...

  5. 长假多图-生活中产品的小细节

    再长的休假也有结束的时候,今天第一天上班.假期中跑了一些地方,拍了一些照片,晚上整理起来,发现还是挺职业病的,特地拍摄了一些生活中产品的小细节,分享给大家. 先是在南京,国庆节前几天,都是街头随意拍摄 ...

  6. python123监考系统_来自监考老师的叮嘱,这些考试小细节千万不能忽视,决定成绩分数...

    回想起作为老师的第一次监考,还是自己当实习老师的时候,那个时候学生看到实习老师来监考,简直是欢呼雀跃得很,当时觉得匪夷所思,内心想:这有什么好开心的?但是当时拿着考卷走进教室的那一刹那,很多学生是发自 ...

  7. 如何用C语言改变宏定义的大小,C语言中宏定义使用的小细节

    C语言中宏定义使用的小细节 #pragma#pragma 预处理指令详解 在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#p ...

  8. mysql被拖垮_说几个拖垮系统的小细节!

    本文首发于个人微信公众号<andyqian>,期待你的关注! 前言 有好几天没有更新了,期间确实比较忙些,以至于周末也没休息.在这期间,感触还是蛮深的.现在碎片化的想法等整理好后,再以文章 ...

  9. 网站优化有什么值得注意的小细节吗?

    网站优化的目的就是让网站关键词达到首页,从而为网站带来更多的流量,促使网站达到更好的营销效果,那么对于关键词上首页有什么技巧,需要完善哪些细节呢?下面就跟大家一起分享一下. 一.挖掘更精准的关键词 对 ...

最新文章

  1. 可信工业数据空间系统架构1.0白皮书
  2. 《Git in Practice》作者访谈:关于Git的八个问题
  3. MATLAB报错“Integers can only be raised to positive integral powers.“
  4. splite和map的结合使用
  5. C++实现自动发邮件代码,这个是用网易邮箱的。
  6. 写一个类Army,代表一支军队,这个类有一个属性Weapon数组w(用来存储该军队所拥有的所有武器), 该类还提供一个构造方法,在构造方法里通过传一个int类型的参数来限定该类所能拥有
  7. elementui 嵌套表单验证_elementUI 表单嵌套表格验证,日期选择器联动限制等写法
  8. 微会动微营销引擎:SEM效果提升的2大方向+5种能力+7个策略
  9. android framework之priv-app,系统特权app权限那些坑
  10. scala当中的文件操作、网络请求和隐式转换
  11. Origin 导入数据画图使用经验总结
  12. android 本地图片模糊,Android端图片模糊的实现原理及方案
  13. 百万调音师—Audition多轨编辑
  14. Bugku_隐写 [Study notes][MISC]
  15. POJ 3669(优先队列BFS)(对地图进行优化)
  16. PHPstudy之PHP探针的查找
  17. 游戏编程中的人工智能技术--神经网络
  18. python多线程好还是多协程好_深入浅析python中的多进程、多线程、协程
  19. 想成为3D建模师,只学习3dsmax就够了吗?
  20. 商铺选址“风水”是门学问 大数据解读一步差三市主因

热门文章

  1. mysql 尝试读取超过流末尾的_MySql异常:尝试读取超出流末尾的内容
  2. LDAP命令介绍---ldappasswordmodify口令修改操作
  3. Zookeeper3.6.1常用的Shell命令
  4. HDFS常用Shell命令
  5. 爬虫scrapy模块
  6. react 16.6 懒加载 Lazy 尝鲜
  7. 移动端 | Vue.js对比微信小程序基础语法
  8. pycharm下载与安装
  9. 【译】NoClassDefFoundError和ClassNotFoundException的不同
  10. EL与JSTL注意事项汇总