当前位置: 首页 > Linux

final关键字和安全发布的一般理解

时间:2023-04-06 03:08:18 Linux

final在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。大家应该都知道,final是指最终、最后的意思。即不能继续修改类表示不能继承(String-所有方法隐含final);修改后的方法表示不能覆盖(privatefinalmethod()---有重载,无覆盖);被修改的变量表示不能修改(显示初始化--声明时的构造方法和初始化);final相关知识架构图当一个类被final修饰时,说明这个类不能被继承。也就是说,如果你永远不会让一个类被继承,你可以用final来修饰它;注意:final类中的所有成员方法都会被隐式指定为final方法(也可以认为不能被继承,因为你不能继承所有的方法,所以所有的方法都是隐式final的)当final修饰一个方法时,表示this方法不能被重写,即不能被子类重写(但不影响多个重载的final方法,重载和重写不是一个概念)注意:如果是final且私有的方法,子类看不到private,所以如果子类写了一个看似“重写”的新方法,其实是属于子类方法的新方法,这不是重写final方法。当final修饰一个变量时,相当于一个只读变量,只能读取,不能设置。如果是成员变量,则需要赋值或构造。设置。——当必须定义局部变量时,参数列表就是传递参数的时候,其他时候不能改变。综上所述,对final的一般认知就是这些,意思是final、final、unchangeable,可用于Define类、方法、变量;securitypublishing其实final还有另外一个作用,就是安全发布对象的方法。什么是安全发布?--两个关键字“publish”和“safety”所谓publishing通俗理解就是创建一个对象,让当前作用域外的代码可以使用,比如Objecto=newObject();,然后接下来使用Objecto但是对于普通变量的创建,之前已经分析过了,大致可以分为三步:分配内存空间,将o指向分配的内存空间,调用构造函数初始化对象,这些三步不是原子的,如果执行到第二步,还没有初始化,此时对象不为null。如果被其他代码访问,会得到错误的结果。也就是说,对象在完全创建之前就被使用了,其他线程看到的结果可能不一致。这是不安全发布的根本原因。JVM创建对象的过程包括空间分配、指针设置、数据初始化等步骤。同步,涉及主存和缓存,处理器和寄存器等,可见性无法保证;securerelease那么,什么是securerelease,简单的理解就是可以保证一个对象的创建在被其他人使用之前就已经完成了数据的构造设置,或者说一个对象在被使用的时候已经被初始化了。不幸的是,Java不保证这一点,你需要自己保证,比如synchronized关键字,原子性和排他性就可以做到这一点;如何确保安全放行?有几种方法:一种是刚才说的锁机制,可以通过加锁的方式保证中间状态不会被读取;通过volatile或AtomicReference声明对象;使用final关键字;在静态初始化块中初始化(JVM会保证);显然,对于锁机制,ConcurrentMap那些线程安全的容器也满足这个要求,所以释放final安全释放也是安全的。对于final,当你创建一个对象时,使用final关键字来让另一个线程不会访问这个“部分创建”的对象,因为:当构造函数退出时,保证final字段的值对其他访问的线程可见构造的对象。如果一个成员是final,JVM规范有如下明确的保证:一旦对象引用对其他线程可见,其final成员也必须被正确赋值;所以有了final的帮助,就好像给对象的创建和访问加了锁,自然就保证了对象的安全释放。如果不想以后被继承、改写、改变,就尽可能声明为final。一篇很好的文章:https://www.javamex.com/tutorials/synchronization_final.shtml对于普通变量,Object内存空间分配、指针设置、数据初始化、将这个变量的引用赋值给另一个引用都可能重新排序,所以其他线程可能会读取不一致的中间状态。但是对于final修饰的变量,JVM会保证顺序;它不会在对final变量的写操作完成之前对其他变量的变量引用重新排序,即final变量的设置总是在读取之前完成总结一下,除了不可变的定义,final也和线程安全释放密切相关;借助final,可以实现对象安全释放的保证,只需要借助final,不需要额外的努力,并且可以保证在多线程环境下,总能读取到正确的初始化值,所以如果你不想后面修改变量,你应该总是使用final关键字,显然,在某些场景下,final也可以解决某些安全问题;