final的理解(final 是什么意思中文翻译是什么意思啊了),本文通过数据整理汇集了final的理解(final 是什么意思中文翻译是什么意思啊了)相关信息,下面一起看看。

我把之前的文章总结到Github里了,欢迎各位大明星。

此文章已提交。

Final是Java中的一个关键词,也是Java中非常重要的一个关键词。final修改的类、方法和变量有不同的含义。Finally也是一个关键字,但是我们可以用finally和其他关键字结合起来做一些组合运算;Finalize是人们不愿意看到的方法。它是对象祖先中的一个方法,不再推荐finalize机制。在本文中,cxuan将带你从这三个关键词入手,带你从用法、应用、原理等角度去了解这三个关键词。

final、finally和finalize我相信在座的各位都是经验丰富的程序员,基本的关键词final就不用多说了。然而,我们应该照顾小白的读者。毕竟,我们都来自小白。

final修饰类、属性和方法 final可以用来修饰一个类,final修饰的类不允许被其他类继承,也就是final修饰的类是唯一的。如下

首先,我们定义一个FinalUsage类,它使用final decoration。同时我们定义了一个FinalUsage类,要扩展)final usage。当我们如上继承后,编译器不会让我们这样玩,这提示我们继承不能从FinalUsage类。为什么?没关系,这是Java的约定俗成,有一些不必要的理由,遵循就好。

Final可以用来修改方法,final修改的方法不允许重写。让我们先演示一下没有最终关键字修改的情况。

如上图所示,我们使用FinalUsageExtend类来继承FinalUsage类,并提供writeArticle方法的重写。这个编译没问题,重写的重点是@Override注释和方法修饰符、名称和返回值的一致性。

注意:很多程序员在重写方法时会忽略@Override,这无疑增加了代码阅读的难度。不建议这样做。

当我们用final修改方法时,这个方法是不能重写的,如下所示。

当我们将writeArticle方法声明为void时,重写的方法将报告一个错误,并且不能重写writeArticle方法。

Final可以修改变量。一旦定义,由final修改的变量就不能修改,如下所示。

编译器提示的错误是不能继承final修饰的类。

我们使用上面的字符串String,默认情况下是final。其实用final decoration也没多大意义,因为字符串不能重写,说明不了问题。

让我们重写它,并使用基本数据类型来演示它。

同样,可以看出编译器还是给出了年龄不能重写的提示,证明final修饰的变量是不能重写的。

在Java中,不仅有基本数据类型,还有引用数据类型。引用类型被final修饰后会怎么样?让我们看看下面的代码。

首先,构造一个Person类。

publicclassPerson { intidStringnameGet()和set().toString().}然后我们定义一个最终的Person变量。

staticfinalperson person=new person(25,' cxuan ');publistaticvoidmain(String[]args){ system . out . println(person);person . setid(26);person . set name(' c Xuan 001 ');system . out . println(person);}输出,你会发现一个奇怪的现象,为什么我们明明当面改了id和名字,编译器却没有报错?

这是因为最终装修的参照类型只是保证对象的参照不会改变。对象内部的数据可以更改。这涉及到内存中对象的分配,我们后面会讲到。

finally保证程序一定被执行最后是一种确保程序必须被执行的机制。同样,它也是Java中的一个关键字。一般来说,最后是不单独使用的。它通常与try块一起使用。例如,这里有一个尝试.最后是代码块。

请尝试{ lock . lock();}最后{ lock . unlock();}这是一个锁定/解锁的代码示例。锁被锁定后,最终在中执行解锁操作。因为finally可以保证代码必须执行,所以一些重要的代码通常放在finally,比如解锁操作、关闭流操作、释放连接操作。

当lock.lock()生成异常时,它也可以与try一起使用.接住。

..finally一起使用

try{lock.lock();}catch(Exceptione){e.printStackTrace();}finally{lock.unlock();}

try...finally这种写法适用于JDK1.7之前,在JDK1.7中引入了一种新的关闭流的操作,那就是try...with...resources,Java引入了try-with-resources声明,将try-catch-finally简化为try-catch,这其实是一种语法糖,并不是多了一种语法。try...with...resources在编译时还是会进行转化为try-catch-finally语句。

语法糖(Syntacticsugar),也译为糖衣语法,是指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

在Java中,有一些为了简化程序员使用的语法糖,后面有机会我们再谈。

finalize的作用

finalize是祖宗类Object类的一个方法,它的设计目的是保证对象在垃圾收集前完成特定资源的回收。finalize现在已经不再推荐使用,在JDK1.9中已经明确的被标记为deprecated。

深入理解final、finally和finalizefinal设计

许多编程语言都会有某种方法来告知编译器,某一块数据是恒定不变的。有时候恒定不变的数据很有用,比如

一个永不改变的编译期常量。例如staticfinalintnum=1024一个运行时被初始化的值,而且你不希望改变它

final的设计会和abstract的设计产生冲突,因为abstract关键字主要修饰抽象类,而抽象类需要被具体的类所实现。final表示禁止继承,也就不会存在被实现的问题。因为只有继承后,子类才可以实现父类的方法。

类中的所有private都隐式的指定为final的,在private修饰的代码中使用final并没有额外的意义。

空白final

Java是允许空白final的,空白final指的是声明为final,但是却没有对其赋值使其初始化。但是无论如何,编译器都需要初始化final,所以这个初始化的任务就交给了构造器来完成,空白final给final提供了更大的灵活性。如下代码

publicclassFinalTest{finalIntegerfinalNum;publicFinalTest(){finalNum=11;}publicFinalTest(intnum){finalNum=num;}publicstaticvoidmain(String[]args){newFinalTest();newFinalTest(25);}}

在不同的构造器中对不同的final进行初始化,使finalNum的使用更加灵活。

使用final的方法主要有两个:不可变和效率

不可变:不可变说的是把方法锁定(注意不是加锁),重在防止其他方法重写。效率:这个主要是针对Java早期版本来说的,在Java早期实现中,如果将方法声明为final的,就是同意编译器将对此方法的调用改为内嵌调用,但是却没有带来显著的性能优化。这种调用就比较鸡肋,在Java5/6中,hotspot虚拟机会自动探测到内嵌调用,并把它们优化掉,所以使用final修饰的方法就主要有一个:不可变。

注意:final不是Immutable的,Immutable才是真正的不可变。

final不是真正的Immutable,因为final关键字引用的对象是可以改变的。如果我们真的希望对象不可变,通常需要相应的类支持不可变行为,比如下面这段代码

finalList<String>fList=newArrayList();fList.add("Hello");fList.add("World");ListunmodfiableList=List.of("hello","world");unmodfiableList.add("again");

List.of方法创建的就是不可变的List。不可变Immutable在很多情况下是很好的选择,一般来说,实现Immutable需要注意如下几点

将类声明为final,防止其他类进行扩展。将类内部的成员变量(包括实例变量和类变量)声明为private或final的,不要提供可以修改成员变量的方法,也就是setter方法。在构造对象时,通常使用deep-clone,这样有助于防止在直接对对象赋值时,其他人对输入对象的修改。坚持copy-on-write原则,创建私有的拷贝。final能提高性能吗?

final能否提高性能一直是业界争论的点,很多书籍中都介绍了可以在特定场景提高性能,例如final可能用于帮助JVM将方法进行内联,可以改造编译器进行编译的能力等等,但这些结论很多都是基于假设作出的。

或许R大这篇回答会给我们一些结论

大致说的就是无论局部变量声明时带不带final关键字修饰,对其访问的效率都一样。

比如下面这段代码(不带final的版本)

staticintfoo(){inta=someValueA();intb=someValueB();returna+b;//这里访问局部变量}

带final的版本

staticintfoo(){finalinta=someValueA();finalintb=someValueB();returna+b;//这里访问局部变量}

使用javac编译后得出来的结果一摸一样。

invokestaticsomeValueA:()Iistore_0//设置a的值invokestaticsomeValueB:()Iistore_1//设置b的值iload_0//读取a的值iload_1//读取b的值iaddireturn

因为上面是使用引用类型,所以字节码相同。

如果是常量类型,我们看一下

//带finalstaticintfoo(){finalinta=11;finalintb=12;returna+b;}//不带finalstaticintfoo(){inta=11;intb=12;returna+b;}

我们分别编译一下两个foo方法,会发现如下字节码

左边是非final关键字修饰的代码,右边是有final关键字修饰的代码,对比这两个字节码,可以得出如下结论。

不管有没有final修饰,inta=11或者inta=12都当作常量看待。在return返回处,不加final的a+b会当作变量来处理;加final修饰的a+b会直接当作常量处理。

其实这种层面上的差异只对比较简易的JVM影响较大,因为这样的VM对解释器的依赖较大,原本Class文件里的字节码是怎样的它就怎么执行;对高性能的JVM(例如HotSpot、J9等)则没啥影响。

所以,大部分final对性能优化的影响,可以直接忽略,我们使用final更多的考量在于其不可变性。

深入理解finally

我们上面大致聊到了finally的使用,其作用就是保证在try块中的代码执行完成之后,必然会执行finally中的语句。不管try块中是否抛出异常。

那么下面我们就来深入认识一下finally,以及finally的字节码是什么,以及finally究竟何时执行的本质。

首先我们知道finally块只会在try块执行的情况下才执行,finally不会单独存在。

这个不用再过多解释,这是大家都知道的一条规则。finally必须和try块或者trycatch块一起使用。

其次,finally块在离开try块执行完成后或者try块未执行完成但是接下来是控制转移语句时(return/continue/break)在控制转移语句之前执行

这一条其实是说明finally的执行时机的,我们以return为例来看一下是不是这么回事。

如下这段代码

staticintmayThrowException(){try{return1;}finally{System.out.println("finally");}}publicstaticvoidmain(String[]args){System.out.println(FinallyTest.mayThrowException());}

从执行结果可以证明是finally要先于return执行的。

当finally有返回值时,会直接返回。不会再去返回try或者catch中的返回值。

staticintmayThrowException(){try{return1;}finally{return2;}}publicstaticvoidmain(String[]args){System.out.println(FinallyTest.mayThrowException());}在执行finally语句之前,控制转移语句会将返回值存在本地变量中

看下面这段代码

staticintmayThrowException(){inti=100;try{returni;}finally{++i;}}publicstaticvoidmain(String[]args){System.out.println(FinallyTest.mayThrowException());}

上面这段代码能够说明returni是先于++i执行的,而且returni会把i的值暂存,和finally一起返回。

finally的本质

下面我们来看一段代码

publicstaticvoidmain(String[]args){inta1=0;try{a1=1;}catch(Exceptione){a1=2;}finally{a1=3;}System.out.println(a1);}

这段代码输出的结果是什么呢?答案是3,为啥呢?

抱着疑问,我们先来看一下这段代码的字节码

字节码的中文注释我已经给你标出来了,这里需要注意一下下面的Exceptiontable,Exceptiontable是异常表,异常表中每一个条目代表一个异常发生器,异常发生器由From指针,To指针,Target指针和应该捕获的异常类型构成。

所以上面这段代码的执行路径有三种

如果try语句块中出现了属于exception及其子类的异常,则跳转到catch处理如果try语句块中出现了不属于exception及其子类的异常,则跳转到finally处理如果catch语句块中新出现了异常,则跳转到finally处理

聊到这里,我们还没说finally的本质到底是什么,仔细观察一下上面的字节码,你会发现其实finally会把a1=3的字节码iconst_3和istore_1放在try块和catch块的后面,所以上面这段代码就形同于

publicstaticvoidmain(String[]args){inta1=0;try{a1=1;//finallya1=3}catch(Exceptione){a1=2;//finallya1=3}finally{a1=3;}System.out.println(a1);}

上面中的Exceptiontable是只有Throwable的子类exception和error才会执行异常走查的异常表,正常情况下没有try块是没有异常表的,下面来验证一下

publicstaticvoidmain(String[]args){inta1=1;System.out.println(a1);}

比如上面我们使用了一段非常简单的程序来验证,编译后我们来看一下它的字节码

可以看到,果然没有异常表的存在。

finally一定会执行吗

上面我们讨论的都是finally一定会执行的情况,那么finally一定会被执行吗?恐怕不是。

除了机房断电、机房爆炸、机房进水、机房被雷劈、强制关机、拔电源之外,还有几种情况能够使finally不会执行。

调用System.exit方法调用Runtime.getRuntime().halt(exitStatus)方法JVM宕机(搞笑脸)如果JVM在try或catch块中达到了无限循环(或其他不间断,不终止的语句)操作系统是否强行终止了JVM进程;例如,在UNIX上执行kill-9pid如果主机系统死机;例如电源故障,硬件错误,操作系统死机等不会执行如果finally块由守护程序线程执行,那么所有非守护线程在finally调用之前退出。finalize真的没用吗

我们上面简单介绍了一下finalize方法,并说明了它是一种不好的实践。那么finalize调用的时机是什么?为什么说finalize没用呢?

我们知道,Java与C++一个显著的区别在于Java能够自动管理内存,在Java中,由于GC的自动回收机制,因而并不能保证finalize方法会被及时地执行(垃圾对象的回收时机具有不确定性),也不能保证它们会被执行。

也就是说,finalize的执行时期不确定,我们并不能依赖于finalize方法帮我们进行垃圾回收,可能出现的情况是在我们耗尽资源之前,gc却仍未触发,所以推荐使用资源用完即显示释放的方式,比如close方法。除此之外,finalize方法也会生吞异常。

finalize的工作方式是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将会首先调用finalize方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。垃圾回收只与内存有关。

我们在日常开发中并不提倡使用finalize方法,能用finalize方法的地方,使用try...finally会处理的更好。

你好,我是cxuan,一枚技术人。我一共写了六本PDF

《Java核心技术总结》《HTTP核心总结》《程序员必知的基础知识》《操作系统核心总结》《Java核心基础2.0》《Java面试题总结》

现在我把百度链接给大家放出来了,大家可以点击下方的链接领取

链接:密码:p9rs

更多final的理解(final 是什么意思中文翻译是什么意思啊了)相关信息请关注本站。