一、回顾finally语法
很多人了解try……catch……finally语法,我们先简单回顾下语法。
1 执行顺序
public static void main(String[] args) {
try{
System.out.println("i'm a try");
System.out.println(1/0);
}catch (Exception e){
System.out.println("i'm a exception");
}finally {
System.out.println("i'm a finally");
}
}
执行结果如下,先执行try代码块,如果有异常再执行catch代码块,最后执行finally语句
i'm a try
i'm a exception
i'm a finally
2 finally不能改变之前return的返回值
下面,我们再看一个实例
public static void main(String[] args) {
System.out.println(exec());
}
private static String exec(){
String result = "";
try{
System.out.println("i'm a try");
result = "try";
return result;
}catch (Exception e){
System.out.println("i'm a exception");
result = "exception";
}finally {
System.out.println("i'm a finally");
result = "finally";
}
return result;
}
执行结果如下:
i'm a try
i'm a finally
try
请注意,return 只有发生在finally之前,finally中代码才不会改变最终return的数据,如果上述例子中将result = "try";
代码后面的return result;
注释掉,那么结果将会是输出“finally”
二、finally语句一定会执行吗?
1 使用System.exit方法
我们先看第一个例子
public static void main(String[] args) {
System.out.println(exec());
}
private static String exec(){
String result = "";
try{
System.out.println("i'm a try");
result = "try";
System.exit(0);
return result;
}catch (Exception e){
System.out.println("i'm a exception");
result = "exception";
}finally {
System.out.println("i'm a finally");
result = "fianlly";
}
return result;
}
运行结果如下:
i'm a try
本例,说明了之执行了,try部分,然后在System.exit(0);
直接退出程序,后续finally不再执行
2 主线程结束
我们主线程中启动一个daemon线程,如下
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try{
System.out.println("i'm a try");
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
System.out.println("i'm a exception");
}finally {
System.out.println("i'm a finally");
}
}
});
t.setDaemon(true);
t.start();
System.out.println("main end");
}
执行结果如下,可以看到daemon线程中finally并没有打印
main end
i'm a try
3 突然机器断电或者线程被kill
此处就大家应该都能理解,就不再说明。
很多人提出那内存溢出或者栈溢出呢?
我们简单的写个例子
public static void main(String[] args) {
System.out.println(exec());
}
private static String exec(){
String result = "";
try{
System.out.println("i'm a try");
result = "try";
byte[] temp = new byte[1024 * 1024 * 100];
return result;
}catch (Exception e){
System.out.println("i'm a exception");
result = "exception";
}finally {
System.out.println("i'm a finally");
result = "fianlly";
}
return result;
}
将jvm设置如下:
此时运行结果如下,说明内存溢出并不会导致finally不执行,同样栈溢出也不会导致
i'm a try
i'm a finally
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.example.demo.test.FinallyTest.exec(FinallyTest.java:13)
at com.example.demo.test.FinallyTest.main(FinallyTest.java:5)
三、finally的本质
正常来说,finally的本质是不管 try 语句块正常结束还是异常结束,finally 语句块是保证要执行的。那么finally是如何保证的呢?
下面我们继续开始一个例子
public static void main(String[] args) {
try{
int a = 1;
}catch (Exception e){
int a = 2;
}finally {
int a = 3;
}
}
我们通过javap
反编译一下:
发现,存在多个iconst_3(iconst指令将常量压入栈,iconst_3代表把常量3压入栈中)
根据字节码,我们翻译为java就类似这样
public static void main(String[] args) {
try{
int a = 1;
int c = 3;//冗余
}catch (Exception e){
int b = 2;
int c = 3;//冗余
}finally {
int c = 3;
}
}
此时我们发现finally通过冗余代码,保证其执行