让人纠结的finally

2024-02-18 02:58
文章标签 finally 纠结

本文主要是介绍让人纠结的finally,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近分析某一apk,被其中的各种try-catch-finally块虐得死去活来,有木有。。。
由于喜欢刨根,各种谷歌都没有给出解释。 一番研究之后,发现确实有些蹊跷。(本人水平有限,不喜勿喷,谢谢)

切入正题:
try{}catch反编译后,格式:    
    eg: .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0
加上finally,那就多一行声明:
   eg: .catchall {:try_start_0 .. :try_end_0} :catchall_0
大家都知道,finally块中的代码始终都要执行,那么是不是类似goto到finally块中去执行代码呢? 可惜不是,是直接复制了3份。
比如:
  try {
    FileInputStream stream = new FileInputStream(new File("/mnt/sdcard/test.txt"));
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  }finally{
    System.out.println("Fin");
  }
smali:
:try_start_0
    new-instance v1, Ljava/io/FileInputStream;

    new-instance v2, Ljava/io/File;

    const-string v3, "/mnt/sdcard/test.txt"

    invoke-direct {v2, v3}, Ljava/io/File;-><init>(Ljava/lang/String;)V

    invoke-direct {v1, v2}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V
    :try_end_0
    .catchall {:try_start_0 .. :try_end_0} :catchall_0
    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0

    .line 28
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v2, "Fin"

    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V


    .line 30
    :goto_0
    return-void

    .line 24
    :catch_0
    move-exception v0

    .line 26
    .local v0, e:Ljava/io/FileNotFoundException;
    :try_start_1
    invoke-virtual {v0}, Ljava/io/FileNotFoundException;->printStackTrace()V
    :try_end_1
    .catchall {:try_start_1 .. :try_end_1} :catchall_0

    .line 28
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v2, "Fin"

    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    goto :goto_0

    .line 27
    .end local v0           #e:Ljava/io/FileNotFoundException;

    :catchall_0
    move-exception v1

    .line 28
    sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v3, "Fin"

    invoke-virtual {v2, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 29
    throw v1

(由于黄色看不清,这里修改为红色)

不难发现,正常执行流程和catch块中增加了finally中的代码,而且二者是同一个出口。(goto_0)
那黄色(第三块红色)那块是什么???
经过多次测试,只要catch块中调用了函数,就会隐式生成一个try,且只有.catchall 
:try_start_1
    invoke-virtual {v0}, Ljava/io/FileNotFoundException;->printStackTrace()V
 :try_end_1
 .catchall {:try_start_1 .. :try_end_1} :catchall_0

catchall_n一般结构是:
move-exception vm
......
throw vm

另外,经过多次动态调试smali,都没有出现程序进入.catchall_n模块。估计是没有构造出相应的触发条件。由于运行时异常不需要显式声明,猜测可能运行时异常触发。故构造一下代码:
  public void fun(int flag) throws IllegalArgumentException
  {
    switch(flag){
      case 1: throw new IllegalArgumentException();
      case 2: throw new RuntimeException();
      default: break;
    }
  }
  public void Test(int flag)
  {
    try {
      fun(flag);    
      foo();
    } catch (Exception e) {
      foo();
    }finally{
      Log.i("Finally", "Exit");
    }
  }
  public void foo(){
    throw new RuntimeException();
  }
由于Log.i("Finally", "Exit");会被拷贝三份,故修改.catall中的输出,进行区别。具体如下:
    :catchall_1a
    move-exception v1

    .line 39
    const-string v2, "Finally"

    const-string v3, "Exit_Finally"
修改"Exit"为"Exit_Finally"

    invoke-static {v2, v3}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

    .line 40
    throw v1
onCreate分别测试调用Test(0)和Test(1),均打印出"Exit_Finally",并未打印出"Exit"。
点击图片以查看大图图片名称:	1.png查看次数:	3文件大小:	11.4 KB文件 ID :	90388

这下终于明白了.catchall的作用,确实保证了finally块中的代码一定会被执行。

PS:要是多重try-catch-finally嵌套,而且finally块中有很多代码,那就。。。 万恶的finally块,虐了我千百遍。。。

这篇关于让人纠结的finally的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/719834

相关文章

try -catch-finally的理解,同时在try-catch-finally中含有return和throws的理解

在没有try-catch或try-catch-finally的情况下,程序正常执行到某行,在这行报错后,这行后面的代码就不执行了,程序就停止了,中断了。 例如   在有try-catch或try-catch-finally 情况上,在某行执行错误,在try中这行下的代码不执行,try外的代码执行。当然是catch在没有做处理的情况下。如果catch中做了处理,在不影响当前程序下,try

Java中finally关键字的使用

与其他语言的模型相比,finally 关键字是对 Java 异常处理模型的最佳补充。finally 结构使代码总会执行,而不管有无异常发生。使用 finally 可以维护对象的内部状态,并可以清理非内存资源。 如果没有 finally,您的代码就会很费解。例如,下面的代码说明,在不使用 finally 的情况下您必须如何编写代码来释放非内存资源: import java.net.*; impo

C++异常处理: try,catch,throw,finally的用法

写在前面 所谓异常处理,即让一个程序运行时遇到自己无法处理的错误时抛出一个异常,希望调用者可以发现处理问题. 异常处理的基本思想是简化程序的错误代码,为程序键壮性提供一个标准检测机制. 也许我们已经使用过异常,但是你习惯使用异常了吗? 现在很多软件都是n*365*24小时运行,软件的健壮性至关重要.  内容导读本文包括2个大的异常实现概念:C++的标准异常和SEH异常. C++标

[置顶]C++异常处理:try,catch,throw,finally的用法

写在前面 所谓异常处理,即让一个程序运行时遇到自己无法处理的错误时抛出一个异常,希望调用者可以发现处理问题. 异常处理的基本思想是简化程序的错误代码,为程序键壮性提供一个标准检测机制. 也许我们已经使用过异常,但是你习惯使用异常了吗? 现在很多软件都是n*365*24小时运行,软件的健壮性至关重要.  内容导读本文包括2个大的异常实现概念:C++的标准异常和SEH异常. C++标

try-catch-finally中finally的使用

1.finally时可选的 2.finally中声明的时一定会被执行的代码,即使catch中又出现异常了,try中有return语句,catch中有return语句的情况 3.像数据库连接,输入输出流,网络编程Socket等资源,JVM时不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。

try里面放return,finally还会执行吗?

引言 喜欢请点赞,支持点在看。 关注牛马圈,干货不间断。 趣聊 今天,在与同事讨论编程语言特性时,我们谈到了一个有趣的话题:在JavaScript中,finally块中的return语句是否会覆盖try块中的return。我首先通过网络搜索,发现关于这一问题的讨论颇多,其中一篇关于JavaScript的文章尤为引人关注。以下是该文章的部分内容截图: ❝ 文章指出,在JavaScript中

爱奇艺的数据库选型大法,实用不纠结!

点击上方“朱小厮的博客”,选择“设为星标” 后台回复”加群“加入公众号专属技术群 来源:rrd.me/fgGsG 我们进行数据库选型的时候要考虑哪些问题?有哪些需求?待选用的数据库是否和需求对得上?是不是直接可以拿来用?需不需要一些额外的开发?这些都会在本文的分享中提及。 一、数据库技术选型的思考维度 我们做选型的时候首先要问: 谁选型?是负责采购的同学、 DBA 还是业务研发? 如果选型的是

【Java学习】理解try{ return } finally {} 中执行顺序

先看一段代码: public class testFinally {public int test() {int x = 1;try{return ++x;}catch(Exception e){}finally{++x;}return x;}public static void main(String[] args) {testFinally t = new testFinally();in

java的finally用法

java的finally用法 finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。 之前在写爬虫的时候数据库连接的频率很高,有时候数据处理的不好,sql报错后,抛出异常但后边的数据库连接没

Java 进阶篇之try-catch-finally语句与自定义异常

引言 在前面的文章中,我们介绍了创建、使用和导入包(Java进阶篇之异常处理的概念和种类),在 Java 编程中,异常处理是确保程序健壮性和稳定性的重要机制。通过异常处理,程序可以应对运行时错误,避免崩溃并提供友好的错误信息。本篇文章将详细介绍 Java 中的 try-catch-finally 语句及其使用方式,并深入探讨如何创建和使用自定义异常。通过本文,你将掌握异常处理的基本原则与高级技巧