认识final、finally、finalize

final 修饰类、属性、方法

final可以用来修饰类,final修饰的类不允许其他类继承也就是说,final修饰的类是独一无二的。

public final class FinalUsage{
	
}

class FinalUsageExtend extends FinalUsage{//报错,不允许继承FinalUsage

}

final可以修饰方法,final修饰的方法不允许被重写,我们先进行演示,不使用final关键字的情况

public class FinalUsage{
	public void writeArticle{
		System.out.println("writing···");
	}
}

class FinalUsageExtends extends FinalUsage{
	@Override
	public void writeArticle(){
		super.writeArticle();
	}
}

使用final修饰方法后,这个方法不允许被重写

public class FinalUsage{
	public final void writeArticle{
		System.out.println("writing···");
	}
}

class FinalUsageExtends extends FinalUsage{
	@Override
	public void writeArticle(){//报错,此方法不允许被继承重写
		super.writeArticle();
	}
}

final可以修饰变量,final修饰的变量一经定义后就不能被修改,如下面所示

public class FinalUsage{
	final String str="Jxh";
	public void writeArticle(){
		str="JXH is writing";//报错
	}
}

上面使用的字符串String,String默认就是final的,其实用不用final修饰意义不大,因为字符串本身就不能被改写。

java中不仅有基本数据类型,还有引用数据类型,那么应用数据类型被final修饰后会如何?

public class Person{
	int id;
	String name;
	get() and set()···
	toString()···
}

然后我们定义一个final的Person

static final Person person = new Person(25,"JXH");

public static void main(String[] args){
	System.out.println(person);
	person.setId(26);
	person.setName("jxh001");
	System.out.println(person);
}

输出一下,你会发现一个奇怪的现象,为什么我们明明改了person中的id和name,编译器却没有报错?

因为final修饰引用类型,只是保证对象的引用不会改变。对象内部的数据可以改变。

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

public class FinalTest{
	final Integer finalNum;
	public FinalTest(){
		finalNum = 11;
	}
	public FinalTest(int num){
		finalNum = num;
	}
	public static void main(Stringp[] args){
		new FinalTest();
		new FinalTest(25);
	}
}

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

finally保证程序一定被执行

finally是保证程序一定执行的机制,同样的它也是java中的一个关键字,一般来讲,finally一般不会单独使用,它一般和try···catch一起使用,例如:

try{
	lock.lock();
}catch(Exception e){
	e.printStackTreace();
}finally{
	lock.unlock();
}

上面我们大致聊了finally使用,其作用就是保证在try块中的代码执行完成后,必然会执行finally中的语句。不管try块中是否抛出异常。
接下来我们深入认识一下finally,以及finally的字节码是什么,以及finally究竟何时执行的本质。
首先我们要知道finally块只会在try块执行的情况下才执行,finally不会单独存在。
这个不用过多解释,finally必须和try块或try catch块一起使用。
其次,finally块在离开try块执行完成后或者try块未执行完成但是接下来是控制转移语句时(return/continue/break)在控制转移语句之前执行。
这一条其实是说明finally的执行时机的,我们以return为例来看一下是不是这么回事。

static int mayThrowException{
	try{
		return 1;
	}finally{
		System.out.println("finally");
	}
}
public static void main(String[] args){
		System.out.println(FinallyTest.mayThrowException());
}

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

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

static int mayThrowException{
	try{
		return 1;
	}finally{
		return 2;
	}
}
public static void main(String[] args){
		System.out.println(FinallyTest.mayThrowException());
}

在执行finally语句之前,控制转移语句会将返回值存储到本地变量中
看一下代码:

static int mayThrowException(){
  int i = 100;
  try {
    return i;
  }finally {
    ++i;
  }
}

public static void main(String[] args) {
  System.out.println(FinallyTest.mayThrowException());
}

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

finally本质(本段来自:https://mp.weixin.qq.com/s/RESORVcNf4CccEpGTAEOLw)
下面我们看一下代码

public static void main(String[] args) {

  int a1 = 0;
  try {
    a1 = 1;
  }catch (Exception e){
    a1 = 2;
  }finally {
    a1 = 3;
  }

  System.out.println(a1);
}

这段代码最后的结果是什么呢?是3,为啥呢?
在这里插入图片描述
字节码的中文注释我已经给你标出来了,这里需要注意一下下面的 Exception table,Exception table 是异常表,异常表中每一个条目代表一个异常发生器,异常发生器由 From 指针,To 指针,Target 指针和应该捕获的异常类型构成。

所以上面这段代码的执行路径有三种
如果 try 语句块中出现了属于 exception 及其子类的异常,则跳转到 catch 处理
如果 try 语句块中出现了不属于 exception 及其子类的异常,则跳转到 finally 处理
如果 catch 语句块中新出现了异常,则跳转到 finally 处理
聊到这里,我们还没说 finally 的本质到底是什么,仔细观察一下上面的字节码,你会发现其实 finally 会把 a1 = 3 的字节码 iconst_3 和 istore_1 放在 try 块和 catch 块的后面,所以上面这段代码就形同于

public static void main(String[] args) {

  int a1 = 0;
  try {
    a1 = 1;
  // finally a1 = 3
  }catch (Exception e){
    a1 = 2;
    // finally a1 = 3
  }finally {
    a1 = 3;
  }
  System.out.println(a1);
}

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

public static void main(String[] args) {
  int a1 = 1;
  System.out.println(a1);
}

比如上面我们使用了一段非常简单的程序来验证,编译后我们来看一下它的字节码
在这里插入图片描述
可以看到,果然没有异常表的存在。
在这里插入图片描述
1.调用 System.exit 方法
2.调用 Runtime.getRuntime().halt(exitStatus) 方法
3.JVM 宕机(搞笑脸)
4.如果 JVM 在 try 或 catch 块中达到了无限循环(或其他不间断,不终止的语句)
5.操作系统是否强行终止了 JVM 进程;例如,在 UNIX 上执行 kill -9 pid
6.如果主机系统死机;例如电源故障,硬件错误,操作系统死机等不会执行
7.如果 finally 块由守护程序线程执行,那么所有非守护线程在 finally 调用之前退出。

finalize 的作用

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值