0003_Java笔记 final、finally、 finalize;final 并不等同于 immutable;final 修饰变量值是否能修改?

学习目标:

  1. final、finally、finalize
  2. immutable

学习结论:

## final、finally、finalize

final(最终的) 可以用来修饰类,方法,变量,有不同的意义

  • 修饰类:类不能被继承。
  • 修饰方法:方法不能被重写(override)。
  • 修饰变量:变量不能被修改。
    思考题:final 修饰变量时,真的无法修改变量的值吗?为什么?final 并不等同于 immutable(不可变得)

java.lang包下面很多类,都被声明为final class;平时使用的一些第三方库也会使用这种方法来确保一定的安全性
final有性能优势,有助于JVM将方法内联。这一点有很多贴子和书籍上都有说。随着JVM的性能提升,判断内联未必依赖 final 的提示,要相信 JVM 还是非常智能的。如果你试图依靠这种小技巧来提升性能,需要考虑一下成本效益。


finally

finally 则是 Java 保证重点代码一定要被执行的一种机制。
finally作为异常处理的一部分,语句块中代码一定会被执行,经常被用来释放资源。可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC 连接、保证 unlock 等动作。

锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如 ReadWriteLock 的读取锁。

有没有什么情况是不会执行finally代码块的代码呢? 可以尝试执行一下如下代码:(这是个特例,不必纠结)

//System.out.println("Print from finally"); 不会被执行。
try {
  // do something
  System.exit(1);
} finally{
  System.out.println("Print from finally");
}

finalize

finalize 是基础类 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。

在Java中,由于GC的自动回收机制,理论上在对象不再被引用时,GC就会收回其内存。实际上GC不能准时的回收。因而并不能保证finalize方法会被及时地执行,垃圾对象的回收时机具有不确定性(例如:GC负责打扫你家别墅中的垃圾,它需要一个房间一个房间巡逻,你在A房间让的垃圾,它可能要很久才能来打扫A房间),也不能保证它们会被执行(程序由始至终都未触发垃圾回收)。

public class Test {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("GC,请收了我吧");
    }
    public static void main(String[] args) {
        Test test = new Test();
        test = null;//对象不再被引用
        //如果不手动执行GC,可能finalize永远不会执行
        System.gc();
    }
}

finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被标记为 deprecated
利用上面的提到的 try-with-resources 或者 try-finally 机制,是非常好的回收资源的办法。

JDK中的又引入了Cleaner机制

  • 首先它的使用方法有点繁琐,和try-with-resource机制一样,需要实现AutoCloseable接口,这样在重写的close()方法中释放资源会被自动调用回收。
  • cleaner机制需要创建单独的线程去执行逻辑,这与finalize机制不同。
  • 执行finalize机制的线程不可控所以cleaner机制不存在类似于先执行finalize逻辑在回收对象的问题,即只要执行了cleaner机制就不会降低垃圾回收效率。
  • 但是前提是执行了cleaner机制,因为它的clean()方法还是写在重写的close()方法中等待被自动调用,所以无法保证保证被及时执行。

知识点拓展:

思考题:final 修饰变量时,真的无法修改变量的“值”吗?为什么? final 并不等同于 immutable(不可变得)

到底能不能修改呢!翠花:上酸菜。看示例:

public class Test {
    public static void main(String[] args) {
        final StringBuffer strBuf =new StringBuffer("0");
        System.out.println(strBuf.toString());
        for (int i = 1; i <= 5; i++) {
            strBuf.append(i);
        }
        System.out.println(strBuf.toString());
    }
}

执行结果:
final修改变量
哇!修改了。为什么呢?在Java中:

  • final修饰基本类型变量时,变量变为常量,只能赋值一次。可以尝试修改试试,编译报错。
  • final修改引用类型变量时,变量的引用不能修改,但是值是可以修改。上面例子已经充分说明一切了。你可以试试修改Map等其他类型。
	public static void main(String[] args) {
        final List strList = new ArrayList<>();
        strList.add("Hello");
        strList.add("world");
        List unmodifiableStrList;
        unmodifiableStrList = List.of("hello", "world");
        unmodifiableStrList.add("again");
    }

final 只能约束 strList 这个引用不可以被赋值,但是 strList 对象行为不被 final 影响,添加元素等操作是完全正常的。
如果希望对象本身是不可变的,那么需要相应的类支持不可变的行为。在上面这个例子中,List.of 方法创建的本身就是不可变 List,最后那句 add 是会在运行时抛出异常的。


Immutable不可变的

Java 语言目前并没有原生的不可变支持.实现 immutable 的类.

  • 将 class 自身声明为 final,这样别人就不能扩展来绕过限制了。
  • 将所有成员变量定义为 private 和 final,并且不要实现 setter 方法。
  • 通常构造对象时,成员变量使用深度拷贝来初始化,而不是直接赋值,这是一种防御措施,因为你无法确定输入对象不被其他人修改。(浅拷贝:引用类型copy的引用;深拷贝:引用类型也copy值)
  • 如果确实需要实现 getter 方法,或者其他可能会返回内部状态的方法,使用 copy-on-write 原则,创建私有的 copy。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EngineerForSoul

你的鼓励是我孜孜不倦的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值