精华内容
下载资源
问答
  • 很多人对 try-catch 有一定的误解,比如我们经常会把它(try-catch)“低性能”直接画上等号,但对 try-catch 的本质(是什么)却缺少着最基础的了解,因此我们也会在本篇中对 try-catch 的本质进行相关的探索。...

    作者:Java中文社群

    今天给大家带来的是关于 try-catch 应该放在循环体外,还是放在循环体内的文章,我们将从性能业务场景分析这两个方面来回答此问题。

    很多人对 try-catch 有一定的误解,比如我们经常会把它(try-catch)和“低性能”直接画上等号,但对 try-catch 的本质(是什么)却缺少着最基础的了解,因此我们也会在本篇中对 try-catch 的本质进行相关的探索

    4c76ae613ad83105216f646a7f4e43b5.png

    小贴士:我会尽量用代码和评测结果来证明问题,但由于本身认知的局限,如有不当之处,请读者朋友们在评论区指出。

    性能评测

    话不多说,我们直接来开始今天的测试,本文我们依旧使用 Oracle 官方提供的 JMH(Java Microbenchmark Harness,JAVA 微基准测试套件)来进行测试。

    首先在 pom.xml 文件中添加 JMH 框架,配置如下:

    org.openjdk.jmh   jmh-core   {version}

    完整测试代码如下:

    import org.openjdk.jmh.annotations.*;import org.openjdk.jmh.runner.Runner;import org.openjdk.jmh.runner.RunnerException;import org.openjdk.jmh.runner.options.Options;import org.openjdk.jmh.runner.options.OptionsBuilder;import java.util.concurrent.TimeUnit;/** * try - catch 性能测试 */@BenchmarkMode(Mode.AverageTime) // 测试完成时间@OutputTimeUnit(TimeUnit.NANOSECONDS)@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 1 轮,每次 1s@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s@Fork(1) // fork 1 个线程@State(Scope.Benchmark)@Threads(100)public class TryCatchPerformanceTest {    private static final int forSize = 1000; // 循环次数    public static void main(String[] args) throws RunnerException {        // 启动基准测试        Options opt = new OptionsBuilder()                .include(TryCatchPerformanceTest.class.getSimpleName()) // 要导入的测试类                .build();        new Runner(opt).run(); // 执行测试    }    @Benchmark    public int innerForeach() {        int count = 0;        for (int i = 0; i < forSize; i++) {            try {                if (i == forSize) {                    throw new Exception("new Exception");                }                count++;            } catch (Exception e) {                e.printStackTrace();            }        }        return count;    }    @Benchmark    public int outerForeach() {        int count = 0;        try {            for (int i = 0; i < forSize; i++) {                if (i == forSize) {                    throw new Exception("new Exception");                }                count++;            }        } catch (Exception e) {            e.printStackTrace();        }        return count;    }}

    以上代码的测试结果为:

    96ca1e1575b53493799debd242b31b29.png

    从以上结果可以看出,程序在循环 1000 次的情况下,单次平均执行时间为:

    • 循环内包含 try-catch 的平均执行时间是 635 纳秒 ±75 纳秒,也就是 635 纳秒上下误差是 75 纳秒;
    • 循环外包含 try-catch 的平均执行时间是 630 纳秒,上下误差 38 纳秒。

    也就是说,在没有发生异常的情况下,除去误差值,我们得到的结论是:try-catch 无论是在 for 循环内还是 for 循环外,它们的性能相同,几乎没有任何差别

    83078a5ba45ddec8ddb9ec2b4fa803ec.png

    try-catch的本质

    要理解 try-catch 的性能问题,必须从它的字节码开始分析,只有这样我能才能知道 try-catch 的本质到底是什么,以及它是如何执行的。

    此时我们写一个最简单的 try-catch 代码:

    public class AppTest {    public static void main(String[] args) {        try {            int count = 0;            throw new Exception("new Exception");        } catch (Exception e) {            e.printStackTrace();        }    }}

    然后使用 javac 生成字节码之后,再使用 javap -c AppTest 的命令来查看字节码文件:

    ➜ javap -c AppTest 警告: 二进制文件AppTest包含com.example.AppTestCompiled from "AppTest.java"public class com.example.AppTest {  public com.example.AppTest();    Code:       0: aload_0       1: invokespecial #1                  // Method java/lang/Object."":()V       4: return  public static void main(java.lang.String[]);    Code:       0: iconst_0       1: istore_1       2: new           #2                  // class java/lang/Exception       5: dup       6: ldc           #3                  // String new Exception       8: invokespecial #4                  // Method java/lang/Exception."":(Ljava/lang/String;)V      11: athrow      12: astore_1      13: aload_1      14: invokevirtual #5                  // Method java/lang/Exception.printStackTrace:()V      17: return    Exception table:       from    to  target type           0    12    12   Class java/lang/Exception}

    从以上字节码中可以看到有一个异常表:

    Exception table:       from    to  target type          0    12    12   Class java/lang/Exception

    参数说明:

    • from:表示 try-catch 的开始地址;
    • to:表示 try-catch 的结束地址;
    • target:表示异常的处理起始位;
    • type:表示异常类名称。

    从字节码指令可以看出,当代码运行时出错时,会先判断出错数据是否在 from 到 to 的范围内,如果是则从 target 标志位往下执行,如果没有出错,直接 goto 到 return。也就是说,如果代码不出错的话,性能几乎是不受影响的,和正常的代码的执行逻辑是一样的。

    ddf4de7c5f4a2bfa193c6aa118a90051.png

    业务情况分析

    虽然 try-catch 在循环体内还是循环体外的性能是类似的,但是它们所代码的业务含义却完全不同,例如以下代码:

    public class AppTest {    public static void main(String[] args) {        System.out.println("循环内的执行结果:" + innerForeach());        System.out.println("循环外的执行结果:" + outerForeach());    }        // 方法一    public static int innerForeach() {        int count = 0;        for (int i = 0; i < 6; i++) {            try {                if (i == 3) {                    throw new Exception("new Exception");                }                count++;            } catch (Exception e) {                e.printStackTrace();            }        }        return count;    }    // 方法二    public static int outerForeach() {        int count = 0;        try {            for (int i = 0; i < 6; i++) {                if (i == 3) {                    throw new Exception("new Exception");                }                count++;            }        } catch (Exception e) {            e.printStackTrace();        }        return count;    }}

    以上程序的执行结果为:

    java.lang.Exception: new Exception

    at com.example.AppTest.innerForeach(AppTest.java:15)

    at com.example.AppTest.main(AppTest.java:5)

    java.lang.Exception: new Exception

    at com.example.AppTest.outerForeach(AppTest.java:31)

    at com.example.AppTest.main(AppTest.java:6)

    循环内的执行结果:5

    循环外的执行结果:3

    可以看出在循环体内的 try-catch 在发生异常之后,可以继续执行循环;而循环外的 try-catch 在发生异常之后会终止循环。

    因此我们在决定 try-catch 究竟是应该放在循环内还是循环外,不取决于性能(因为性能几乎相同),而是应该取决于具体的业务场景

    例如我们需要处理一批数据,而无论这组数据中有哪一个数据有问题,都不能影响其他组的正常执行,此时我们可以把 try-catch 放置在循环体内;而当我们需要计算一组数据的合计值时,只要有一组数据有误,我们就需要终止执行,并抛出异常,此时我们需要将 try-catch 放置在循环体外来执行。

    5f4aea3cde18880e44ba1ac02adc0870.png

    总结

    本文我们测试了 try-catch 放在循环体内和循环体外的性能,发现二者在循环很多次的情况下性能几乎是一致的。然后我们通过字节码分析,发现只有当发生异常时,才会对比异常表进行异常处理,而正常情况下则可以忽略 try-catch 的执行。但在循环体内还是循环体外使用 try-catch,对于程序的执行结果来说是完全不同的,因此我们应该从实际的业务出发,来决定到 try-catch 应该存放的位置,而非性能考虑

    来源:掘金 链接:https://juejin.im/post/5ed5b998f265da76bd1ad012

    展开全文
  • 关于trycatch和finally

    2019-04-24 20:30:02
    try-catch-finally 规则( 异常处理语句的语法规则 ) ...必须遵循块顺序:若代码同时使用 catch finally 块,则必须catch 块放在 try 块之后。 catch 块与相应的异常类的类型相关。 一个 try 块...

    try-catch-finally 规则( 异常处理语句的语法规则 )

    1. 必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。

    2. 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。

    3. catch 块与相应的异常类的类型相关。

    4. 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或 其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块

    5. 可嵌套 try-catch-finally 结构。

    6. 在 try-catch-finally 结构中,可重新抛出异常。

    7. 除了下列情况,总将执行 finally 做为结束: JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击
      由此可以看出,catch只会匹配一个,因为只要匹配了一个,虚拟机就会使整个语句退出

    展开全文
  • try{ throw "test"; } catch (ex) { console.log(ex); //test } finally { console.log('finally') } ...try 后面必须接一个 catch 或者 finally 因此 js 中的 try catch 语句有三种形式 1. tr
    try{
    	throw "test";
    } catch (ex) {
    	console.log(ex); //test
    } finally {
    	console.log('finally')
    }
    
    它的执行流程:
    首先执行 try 块中的代码, 
    如果抛出异常由 catch 捕获并执行, 如果正常的话, catch 中的代码被忽略.
    不管有没有异常最后都执行 finally 的代码.
    
    try 后面必须接一个 catch 或者 finally
    因此 js 中的 try catch 语句有三种形式
    1. try catch
    2. try finally
    3. try catch finally
    
    
    展开全文
  • try catch和finally

    2017-03-26 00:05:00
    然后catch和finally可以不用但是要注意遵循原则。 存在一个或多个catch的时可以不用finally,也可以用。 不存在catch时,必须要用finally try预见可能发生异常的代码 catch如果发生异常,则转入catch的执行 ...

    在C#中这三个关键字用于处理异常。

    这三个关键字try是必定要用的,要不然就失去了意义。然后catch和finally可以不用但是要注意遵循原则。

    存在一个或多个catch的时可以不用finally,也可以用。

    不存在catch时,必须要用finally

    • try预见可能发生异常的代码
    • catch如果发生异常,则转入catch的执行

    catch的几种写法:

         catch

         这将捕获任何发生的异常

         catch(exception ex)

         这将捕获任何发生的异常.另外,还提供ex参数,你可以在处理异常时使用ex参数来获得有关异常信息     

         catch(exception 的派生类 ex)

         这将捕获派生类定义的异常,例如,我想捕获一个无效操作的异常InvalidOperationException,这样,如果try语句中抛出的异常是InvalidOperationException将转入该处执行,其他异常不处理

         catch可以有多个,也可以没有,每个catch处理一个特定的异常。.net按照你catch的顺序查找异常处理块,如果找到,则进行处理,如果找不到,则向上一层抛出。如果你在调试程序将中断执行,如果是部署的程序,将会终止。

         如过没有catch块,异常总是向上层(如果有)抛出,或者中断程序执行。    

    • finally 可以没有,也可以只有一个。无论有没有发生异常。它总会在这个异常处理结构的最后运行。即使你在try块内使用return返回了,在返回之前,finally总是要执行,这以便让你有机会能在异常处理最后做一些清理工作。如关闭数据库连接等等。
    • 注意:如果没有catch块,那么finally块是必须的。
    • 如果你不希望在这里处理异常。而当异常发生时提交到上层处理,但在这个地方无论是否发生异常,都必须执行一些操作,就可以使用try finally。顺便说明一下,return是可以放在try语句块中的。但不管在什么时候返回,在返回前finally将会执行

       try  

          {  

              //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容 

         }  

       catch //(Exception e)  括号里面的参数是表示的这个catch语句里面要对应处理的错误类型及其参数

       {          

            //throw new Exception("转化失败");  

            //除非try里面执行代码发生了异常,否则这里的代码不会执行

        }  

       finally  

      {        

           //不管什么情况都会执行,包括try catch里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally

       }  

    try-catch语句的两种书写形式

    第一种:try{ 可能出错的程序 }  catch{ 出错后的处理方法}

       

         有运行结果可以看出 当输入的数字为整数比如“3”时程序没有出错所以执行Console.WriteLine("你输入的是" + i);这句话 输出了“你输入的是3”。 但当输入的是小数或是字母比如“3.2”“h”时 出现了错误 那么程序就跳转到catch中执行Console.WriteLine("你输入的字符格式有误"); 输出 “你输入的字符有误”。

     第二种:try{ 可能出现错误的程序 }  catch(可能出错的地方){出错的处理方法}

          由上面的结果可以看出:当输入整数“3”时 程序正常执行,但当输入“3.2”“h”时程序不能把他们转化成整数 那么(Exception e)就捕获错误信息并在catch“{}”中进行错误的处理。

     

    关于throw的用法可以用这么个例子来简单解释一下:

         A类里面有一个a方法,B类里面有一个b方法,那么我们现在在a方法中调用B类的b方法,但是由于某些原因,b方法有时候会出现一些错误,但是我们不想在b里面处理这个错误,那么我们再b里面可以写一个判断语句

         if(错误的标识出现了)

         {

             throw new IndexOutOfRangeException();//这里的错误类型有很多在后面列出来了,

         }

         然后再程序a中我们可以写一个catch语句来截获这个错误,并作出相应的处理

         Catch(IndexOutOfRangeExceptione)

        {

             MessageBox.Show(“发现IndexOutOfRangeException的错误”);//也可以有其他的处理方式,处理这种提醒式的处理,也可以继续抛出错误,让上一层来处理

         }

    错误的种类:

    算术异常类:ArithmeticExecption
    空指针异常类:NullPointerException
    类型强制转换异常:ClassCastException
    数组负下标异常:NegativeArrayException
    数组下标越界异常:ArrayIndexOutOfBoundsException
    违背安全原则异常:SecturityException
    文件已结束异常:EOFException
    文件未找到异常:FileNotFoundException
    字符串转换为数字异常:NumberFormatException
    操作数据库异常:SQLException
    输入输出异常:IOException
    方法未找到异常:NoSuchMethodException

    关于throw和throws

    throw 是语句抛出一个异常;throws是方法抛出一个异常;

    throw语法:throw <异常对象>

    throws语法:[<修饰符>]<返回值类型><方法名>([<参数列表>])[throws<异常类>]
    其中:异常类可以声明多个,用逗号分割。

     

    • 只要try开始执行了,finally一定会执行.
    • 捕捉到的异常可以重新抛出 ,  re-throwable
    • Try后不一定要有catch块,但是一旦用了try关键字,后边必须有catch或者finally块,或者二者都有

    有return的情况下try catch finally的执行顺序

    1.不管有没有出现异常,finally块中代码都会执行

    2.当try和从catch中有return时,finally仍会执行

    3.finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;

    4.finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

    举例:
    情况1:try{} catch(){}finally{} return;
                显然程序按顺序执行。
    情况2:try{ return; }catch(){} finally{} return;
              程序执行try块中return之前(包括return语句中的表达式运算)代码;
             再执行finally块,最后执行try中return;
             finally块之后的语句return,因为程序在try中已经return所以不再执行。
    情况3:try{ } catch(){return;} finally{} return;
             程序先执行try,如果遇到异常执行catch块,
             有异常:则执行catch中return之前(包括return语句中的表达式运算)代码,再执行finally语句中全部代码,
                         最后执行catch块中return. finally之后也就是4处的代码不再执行。
             无异常:执行完try再finally再return.
    情况4:try{ return; }catch(){} finally{return;}
              程序执行try块中return之前(包括return语句中的表达式运算)代码;
              再执行finally块,因为finally块中有return所以提前退出。
    情况5:try{} catch(){return;}finally{return;}
              程序执行catch块中return之前(包括return语句中的表达式运算)代码;
              再执行finally块,因为finally块中有return所以提前退出。
    情况6:try{ return;}catch(){return;} finally{return;}
              程序执行try块中return之前(包括return语句中的表达式运算)代码;
              有异常:执行catch块中return之前(包括return语句中的表达式运算)代码;
                           则再执行finally块,因为finally块中有return所以提前退出。
              无异常:则再执行finally块,因为finally块中有return所以提前退出。
      结论1:任何执行try 或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话。
                      如果finally中有return语句,那么程序就return了,所以finally中的return是一定会被return的,
                      编译器把finally中的return实现为一个warning。

    结果2:
    	在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。
    在转去之前,try中先把要返回的结果存放到不同于x的局部变量中去,执行完finally之后,在从中取出返回结果,
    因此,即使finally中对变量x进行了改变,但是不会影响返回结果。
    它应该使用栈保存返回值。

     实例:

     

                            try
                            {
                                throw new Exception("不存在合法数据,请重新导入!");
                            }
                            catch (Exception ex)
                            {
                                throw;
                            }

     

     

     

        更多的关于try----catch的用法请转:http://msdn.microsoft.com/zh-cn/library/0yd65esw 

    转载于:https://www.cnblogs.com/llljpf/p/6619092.html

    展开全文
  • Try Finally和Try Catch之间的差异可能重复@MP每个人都应该做代码审查,向他们提问是如何学习改进的。如果try块中的任何代码都可以引发选中的异常,则它必须出现在方法签名的throws子句中。如果抛出未经检查的异常...
  • PHP的Try, throw catch

    2019-09-15 19:36:04
    Try, throw catch Try - 使用异常的函数应该位于 "try" 代码块内。如果没有触发异常,则代码将照常继续执行。但是如果异常被触发,会抛出一个异常。 Throw - 这里规定如何触发异常。每一个 "throw" 必须对应...
  • if else和try catch

    2017-01-04 20:43:53
    if-else语句中,else紧跟在if语句后面else if也是一样...try和catch块作为一个整体出现,catch块是try-catch结构中的一部分,必须紧跟在try块后,不能单独使用,在二者间也不能插入其他语句——《C++程序设计》444页
  • Java中try和catch的故事

    2017-04-05 20:51:31
    1.一个try和它的catch语句形成了一个单元。catch子句的范围限制于try语句前面所定义的语句。一个catch语句不能捕获另一个try声明所引发的异常(除非是嵌套的try语句情况)。  被try保护的语句声明必须在一个大...
  • 哈喽,亲爱的小伙伴们,技术学磊哥,进步没得说!...很多人对 try-catch 有一定的误解,比如我们经常会把它(try-catch“低性能”直接画上等号,但对 try-catch 的本质(是什么)却缺少着最基础的了解,因此...
  • Try, throw和catch用法

    2019-04-03 12:15:00
    使用思路如下: 1、Try - 使用异常的函数应该位于 "try" 代码块内。...每一个 "throw" 必须对应至少一个 "catch"3、Catch - "catch" 代码块会捕获异常,并创建一个包含异常信息的对象 亲测过...
  • 异常 trycatch方法 throwthrows 自定义异常 这部分的笔记几乎是没做任何整理,直接把课上的笔记教材照搬进来,可做的练习也很少。只要会trycatch方法,明白异常异常抛出是个怎么回事就行… 异常 在java...
  • try和catch中的一个出现,另一个必须出现吗,为什么?
  • try finally没有catch的用法

    千次阅读 2016-12-12 12:01:15
    在Java的trycatch、finally中,try必须的,catch和finally都不是必须的。 因为捕获异常是必须要有的,而捕获到异常之后,如果不做处理,那就不用写catch,如果需要对捕获到异常之后做处理才需要catch,finally也...
  • try{ } catch{ } finally{ }

    2018-10-05 23:11:00
    如果函数带返回值,try和catch语句块必须都要有return; try或catch的return后面的表达式计算完毕,暂存该值;然后进入finally,等待finally执行结束,回到try或catch中返回。 finally影响不了返回值。 try和...
  • 本文简单记录一下关于php中的 Try, throw catch 的用法,后面有时间再做一个详细的解析。 Try - 使用异常的函数应该位于 "try" 代码块内。如果没有触发异常,则代码将照常继续执行。但是如果异常被触发,会抛...
  • 之前学习了java异常的相关知识,突然想起来里面的代码执行顺序不太清楚,翻阅一下笔记,做出如下总结:...(要求程序员在编写程序阶段必须预先处理这些异常进行处理,如果不处理,就报错)编译时异常的另外两个名字:受
  • 小编典典在Java中, 可以 使用多个catch块。它不一定意味着您必须这样做。...一种 不好的做法是将单个处理程序用于一般Exception(或更糟糕的是Throwable,也将捕获RuntimeExceptionsErrors):try...
  • 本文简单记录一下关于php中的 Try, throw catch 的用法,后面有时间再做一个详细的解析。Try - 使用异常的函数应该位于 "try" 代码块内。如果没有触发异常,则代码将照常继续执行。但是如果异常被触发,会抛出一...
  • 2.finally子句必须和try一起出现使用,不能单独编写。 一个try,可以有多个catch,但只能有一个finally,且顺序固定为:trycatch(可以多个)…finally。 一个try只跟一个finally也是允许的,可以没有catch。 一个...
  • C#中的try catch finally

    2017-04-06 17:35:23
    然后catch和finally可以不用但是要注意遵循原则 一个或多个catch的时间可以不用finally但是也可以用。 如果没有catch的时间必须要用finally。 其中每个关键字都对应的有自己的代码块 如这样的形式 try { //...
  • 1. 粉红色的是受检查的异常(checked exceptions),其必须try{}catch语句块所捕获,或者在方法签名里通过throws子句声明.受检查的异常必须在编译时被捕捉处理,命名为 Checked Exception 是因为Java编译器要进行检查,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,210
精华内容 484
关键字:

try必须和catch