[短文速读 -6] final?finally?finalize?

1,137 阅读5分钟

前言

这篇速读文章来自面试的常客final、finally、finalize。说实话不大很理解为啥把这三者放在一起。这完全不相干啊!不过既来之则安之吧。

今天让我们聊一聊final、finally、finalize各自的场景。

正题

引子

小A:MDove,我最近发现很多文章会把final、finally、finalize放在一起比较。恕我愚钝,我实在不知道它们有什么关系。

MDove:它们的关系你竟然看不出来?这不就是:这三者就是卡巴斯基、巴基斯坦和小丑巴基的关系,有个基巴关系!

小A:啊?......

MDove:它们三者的确没有什么关系,估计大家是看它们长得像,所以弄到一起去比较吧。今天咱们也别谈三者的关系了,咱们就一个个聊一聊它们的特点。先从最常用的final开始:

final

MDove:对于我们来说final是很基础的关键字。final可以用来修饰类、方法、变量

  • 1、final修饰的class,代表不可以继承扩展。
  • 2、final的方法也是不可以重写的。
  • 3、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将方法进行内联,可以改善编译器进行条件编译的能力等等。说实话这种假设完全没有考虑的必要。

finally

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。

finalize

MDove:说实话,我们日常开发中finalize用的并不多,而且也不被推荐使用。甚至在Java9中,明确将Object.finalize()标记为deprecated!

MDove:关于finalize说白了,它设计之初的作用就是:在CG要回收某个对象时,让这个对象有底气的大喊一声:“报告,我还能再抢救一下!”。但是也正是因为如此,JVM要对它进行额外处理。finalize也就成为了CG回收的阻碍者,也就会导致这个对象经过多个垃圾收集周期才能被回收。

MDove:因为我自己对finalize了解也不是很深,所以关于finalize的内容就暂时让我们止步于此。如果你还是感兴趣,可以搜一搜相关的分析文章。

MDove:不知道一路捋到这,你是不是多少对这三者有了一些了解。实话实说,他们三者了解了解就完OJBK了,知道它们设计的初衷,了解它们的用法。可以灵活多变的应用到业务中就哦了。过分的追求一些“奇技淫巧”,反而失去了设计本身的意义。

小A:真是学无止境,我觉得我好想选错了行业。

剧终

我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,以及我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

个人公众号:IT面试填坑小分队