这篇速读文章来自面试的常客final、finally、finalize。说实话不大很理解为啥把这三者放在一起。这完全不相干啊!不过既来之则安之吧。
今天让我们聊一聊final、finally、finalize各自的场景。
小A:MDove,我最近发现很多文章会把final、finally、finalize放在一起比较。恕我愚钝,我实在不知道它们有什么关系。
MDove:它们的关系你竟然看不出来?这不就是:这三者就是卡巴斯基、巴基斯坦和小丑巴基的关系,有个基巴关系!
小A:啊?......
MDove:它们三者的确没有什么关系,估计大家是看它们长得像,所以弄到一起去比较吧。今天咱们也别谈三者的关系了,咱们就一个个聊一聊它们的特点。先从最常用的final开始:
MDove:对于我们来说final是很基础的关键字。final可以用来修饰 类、方法、变量 。
MDove:咱们重点提一提第3点,这里所谓的不可修改对于基本类型来来,的确是不可以修改。而对于引用类型来说,只能说不能重新赋值。也就是不能改变引用地址。但是作为引用类型,它内部所包含的内容如果不是final则可以随意修改咯,就像:
final List<String> arryList = new ArrayList<>(); arryList.add("AAA"); arryList.add("BBB"); List<String> unmodifiableArrayList = List.of("AAA", "BBB"); unmodifiableArrayList.add("CCC"); 复制代码
MDove:final只能约束arryList这个引用不可以被重新赋值,但是arryList对象行为不被final影响,也就是说add是没问题的。如果我们希望对象行为不可变,我们就需要使用List.of(),这个方法创建的对象本身就是不可变的,因此最后那句add是会在运行时抛出异常的。
MDove:我们都知道final声明的变量需要显示的给它赋初始值的。这里考考你,如果不想直接给它赋值,那应该怎么做?
小A:我猜...需要在构造方法里边吧?
MDove:没错的确是这样:
fianl int num; final int num2 = 666; public Test(){ num = 666; } 复制代码
MDove:前面的文章,我们有提到其实这俩种写法,对于编译的class文件是等价的。
MDove:关于final,还有个有趣的地方。在很多文章中,会提到在特定场景下final能够提高性能。比如:利用final可能有助于JVM将方法进行内联,可以改善编译器进行条件编译的能力等等。说实话这种假设完全没有考虑的必要。
MDove:接下来让我们来聊一聊finally。提到finally,那么try-catch就逃不掉了。finally 则是Java保证重点代码一定要被执行的一种机制。最常用的地方:通过try-catch-finally来进行类似资源释放、保证解锁等动作。比如:
FileInputStream inputStream = null; try { inputStream = new FileInputStream(new File("test")); System.out.println(inputStream.read()); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } } 复制代码
MDove:这里提一点,try-finally也是可以的。不过这里个人不建议省略掉catch。因为前一段时间我们就踩到了这个坑。我们项目里捕获的异常,一般都会在catch里通过Error的CallBack传出去,打Log。那次我们在追一个Bug的时候,发现竟然什么Log都没有。后来才发现,出Bug的地方,try异常后没有做任何处理直接finally,导致没有办法看到Log日志,浪费了很多时间。
MDove:当然关于finally中释放资源的操作,更推荐使用Java7中添加的try-with-resources语句。简单,高效~比如:
try (FileInputStream inputStream = new FileInputStream(new File("test"))) { System.out.println(inputStream.read()); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } 复制代码
这二者的写法是等价的。当然这只是一种语法糖~
MDove:当然有些丧心病狂的题目,会问你什么情况下finally不执行。这种情况就不执行了:
try { System.exit(1); } finally{ System.out.println(“程序都死了,我还执行个毛线~”); } 复制代码
MDove:可以看出,能导致程序停止的操作,finally都没办法执行了。说实话这真的没有啥意义...
MDove:最后我们来聊一聊finalize。
MDove:说实话,我们日常开发中finalize用的并不多,而且也不被推荐使用。甚至在Java9中,明确将Object.finalize()标记为deprecated!
MDove:关于finalize说白了,它设计之初的作用就是:在CG要回收某个对象时,让这个对象有底气的大喊一声:“报告,我还能再抢救一下!”。但是也正是因为如此,JVM要对它进行额外处理。finalize也就成为了CG回收的阻碍者,也就会导致这个对象经过多个垃圾收集周期才能被回收。
MDove:因为我自己对finalize了解也不是很深,所以关于finalize的内容就暂时让我们止步于此。如果你还是感兴趣,可以搜一搜相关的分析文章。
MDove:不知道一路捋到这,你是不是多少对这三者有了一些了解。实话实说,他们三者了解了解就完OJBK了,知道它们设计的初衷,了解它们的用法。可以灵活多变的应用到业务中就哦了。过分的追求一些“奇技淫巧”,反而失去了设计本身的意义。
小A:真是学无止境,我觉得我好想选错了行业。