Final关键字
在java类中,final可以用来修饰类,方法和变量。
- 修饰类:
当final修饰类时,表明该类不能被其他类所继承。
当我们需要让一个类永远不被继承,此时就可以用final修饰,但要注意:
final类中所有的成员方法都会隐式的定义为final方法。
- 修饰方法:
final修饰方法的目的有两个:
1.锁定方法,防止继承类对其进行更改,即方法不能被重写。
注意:若父类中final方法的访问权限为private,将导致子类中不能直接继承该方法,因此,此时可以在子类中定义相同方法名的函数,此时不会与重写final方法,而是在子类中重新地定义了新方法。
2.效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。此处不再赘述。
- 修饰变量
final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。
注意:final修饰一个成员变量(属性),必须要初始化。
不进行初始化将不能通过编译。
两种初始化方式:
1.在变量声明的时候初始化;
2.声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
当函数的参数类型声明为final时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。
【在java中,String被设计成final类,那为什么平时使用时,String的值可以被改变?】
字符串常量池是java堆内存中一个特殊的存储区域,当我们建立一个String对象时,假设常量池不存在该字符串,则创建一个,若存在则直接引用已经存在的字符串。当我们对String对象值改变的时候,例如 String str="A"; str="B" 。str是String对象的一个引用(我们这里所说的String对象其实是指字符串常量),当str=“B”执行时,并不是原本String对象("A")发生改变,而是创建一个新的对象("B"),令str引用它。
Finally
finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。(在try未执行或者虚拟机关闭时存在特例)
*但此处存在特例:
1.finally执行的前提是,只有与finally对应的try语句块得到执行的情况下,finally语句块才会执行。如果在执行try语句块之前已经返回或抛出异常,所以try对应的finally语句就不会被执行。
例如:
在try之前抛出异常 了,finally不会被执行。
2.使用System.exit (0) 语句,终止了 Java 虚拟机的运行,则finally不能被执行。
System.exit(0)用来结束当前正在运行中的java虚拟机。如果status是非零参数,那么表示是非正常退出。
System.exit(0)是正常退出,即将你的整个虚拟机里的内容都停掉。无论如何,JVM虚拟机关闭,意味内存释放。
即finally将无法被执行。(断电同理)
finally对于return的表达方式:
例如这段代码,返回的是什么呢?
返回结果是2,即finally‘替换’掉了trycase中的返回值。
- 以实际编译源码来说,一旦finally里有return,将把所有return全部替换为finally的return值。
举例来说:
- 但另一种情况则不同:如果finally不直接return,而是修改其变量,return将直接返回return时设置的值。
如果try语句里有return,根据jvm的官方说明,代码的行为如下:
1.如果有返回值,就把返回值保存到局部变量中
2.执行jsr指令跳到finally语句里执行
3.执行完finally语句后,返回之前保存在局部变量表里的值
例1:
例2:
到底返回值变不变可以简单的这么记忆:当finally调用的任何可变API,会修改返回值;当finally调用任何的不可变API,对返回值没有影响。
总结一下:其实return与finally并没有明显的谁强谁弱。在执行时,是return语句先把返回值写入但内存中,然后停下来等待finally语句块执行完,return再执行后面的一段。
Finalize
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
protected void finalize()
throws Throwable
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize
方法,以配置系统资源或执行其他清除。
finalize 的常规协定是:当 JavaTM 虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,将调用此方法,除非由于准备终止的其他某个对象或类的终结操作执行了某个操作。finalize 方法可以采取任何操作,其中包括再次使此对象对其他线程可用;不过,finalize 的主要目的是在不可撤消地丢弃对象之前执行清除操作。例如,表示输入/输出连接的对象的 finalize 方法可执行显式 I/O 事务,以便在永久丢弃对象之前中断连接。
Object 类的 finalize 方法执行非特殊性操作;它仅执行一些常规返回。Object 的子类可以重写此定义。
Java 编程语言不保证哪个线程将调用某个给定对象的 finalize 方法。但可以保证在调用 finalize 时,调用 finalize 的线程将不会持有任何用户可见的同步锁定。如果 finalize 方法抛出未捕获的异常,那么该异常将被忽略,并且该对象的终结操作将终止。
在启用某个对象的 finalize 方法后,将不会执行进一步操作,直到 Java 虚拟机再次确定尚未终止的任何线程无法再通过任何方法访问此对象,其中包括由准备终止的其他对象或类执行的可能操作,在执行该操作时,对象可能被丢弃。
对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。
finalize
方法抛出的任何异常都会导致此对象的终结操作停止,但可以通过其他方法忽略它。
- 终结方法(finalize)通常是不可预测的,也是很危险的,一般情况下是不必要的。会导致行为不稳定,降低性能以及可移植性问题,根据经验,应该避免使用终结方法。(EffectiveJava 创建销毁对象P24)
终结方法finalize()的缺点在于不能保证会被及时的执行,从一个对象变得不可到达开始到它的终结方法被执行,所花费的时间是任意长的。
及时地执行终结方法是垃圾回收算法的一个重要功能,这种算法在不同JVM的实现会大相径庭。一个程序在你的测试用JVM上运行的非常好,在最重要的客户JVM上根本无法运行,是完全有可能的。
JAVA语言规范不仅不保证终结方法会及时的执行,甚至根本不保证它们会被执行。总之,除非作为安全网,或者是为了终止非关键的本地资源,否则不要使用终结方法。
有关finalize()的执行,可以移步:javaGC与内存分配策略 ->对象的生存与死亡 查看为何finalize是不可预期的,尽量避免使用。