Java基础知识--try-catch-finally

转载:http://www.cnblogs.com/aigongsi/archive/2012/04/19/2457735.html

写的很好:

看了一位博友的一片文章,讲解的是关于java中关于try、catch、finally中一些问题
下面看一个例子(例1),来讲解java里面中try、catch、finally的处理流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public   class   TryCatchFinally {
 
     @SuppressWarnings ( "finally" )
     public   static   final   String test() {
         String t =  "" ;
 
         try   {
             t =  "try" ;
             return   t;
         catch   (Exception e) {
             // result = "catch";
             t =  "catch" ;
             return   t;
         finally   {
             t =  "finally" ;
         }
     }
 
     public   static   void   main(String[] args) {
         System.out.print(TryCatchFinally.test());
     }
 
}
  首先程序执行try语句块,把变量t赋值为try,由于没有发现异常,接下来执行finally语句块,把变量t赋值为finally,然后return t,则t的值是finally,最后t的值就是finally,程序结果应该显示finally,但是实际结果为try。为什么会这样,我们不妨先看看这段代码编译出来的class对应的字节码,看虚拟机内部是如何执行的。
我们用javap -verbose TryCatchFinally 来显示目标文件(.class文件)字节码信息
系统运行环境:mac os lion系统 64bit
jdk信息:Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-11M3527) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)
编译出来的字节码部分信息,我们只看test方法,其他的先忽略掉
public static final java.lang.String test() ;
  Code:
   Stack= 1, Locals= 4, Args_size= 0
   0:    ldc    # 16 ; //String
   2 :    astore_0
   3:    ldc    # 18 ; //String try
   5 :    astore_0
   6 :    aload_0
   7 :    astore_3
   8:    ldc    # 20 ; //String finally
   10 :    astore_0
   11 :    aload_3
   12 :    areturn
   13 :    astore_1
   14:    ldc    # 22 ; //String catch
   16 :    astore_0
   17 :    aload_0
   18 :    astore_3
   19:    ldc    # 20 ; //String finally
   21 :    astore_0
   22 :    aload_3
   23 :    areturn
   24 :    astore_2
   25:    ldc    # 20 ; //String finally
   27 :    astore_0
   28 :    aload_2
   29 :    athrow
  Exception table:
   from   to  target type
     3     8    13    Class java/lang/Exception

     3     8    24    any
    13    19    24    any
  LineNumberTable:
   line 5: 0
   line 8: 3
   line 9: 6
   line 15: 8
   line 9: 11
   line 10: 13
   line 12: 14
   line 13: 17
   line 15: 19
   line 13: 22
   line 14: 24
   line 15: 25
   line 16: 28

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   3      27      0    t       Ljava/lang/String ;
   14      10      1    e       Ljava/lang/Exception ;

  StackMapTable: number_of_entries = 2
   frame_type = 255 /* full_frame */
     offset_delta = 13
     locals = [ class java/lang/String ]
     stack = [ class java/lang/Exception ]
   frame_type = 74 /* same_locals_1_stack_item */
     stack = [ class java/lang/Throwable ]
首先看LocalVariableTable信息,这里面定义了两个变量 一个是t String类型,一个是e Exception 类型
接下来看Code部分
第[0-2]行,给第0个变量赋值“”,也就是String t="";
第[3-6]行,也就是执行try语句块 赋值语句 ,也就是 t = "try";
第7行,重点是第7行,把第s对应的值"try"付给第三个变量,但是这里面第三个变量并没有定义,这个比较奇怪
第[8-10] 行,对第0个变量进行赋值操作,也就是t="finally"
第[11-12]行,把第三个变量对应的值返回
通过字节码,我们发现,在try语句的return块中,return 返回的引用变量(t 是引用类型)并不是try语句外定义的引用变量t,而是系统重新定义了一个局部引用t’,这个引用指向了引用t对应的值,也就是try ,即使在finally语句中把引用t指向了值finally,因为return的返回引用已经不是t ,所以引用t的对应的值和try语句中的返回值无关了。
 
下面在看一个例子:(例2)
1 public class TryCatchFinally {
2
3     @SuppressWarnings("finally" )
4     public static final String test() {
5         String t = "" ;
6
7         try {
8             t = "try" ;
9             return t;
10         } catch (Exception e) {
11             // result = "catch";
12             t = "catch" ;
13             return t;
14         } finally {
15             t = "finally" ;
16             return t;
17         }
18     }
19
20     public static void main(String[] args) {
21         System.out.print(TryCatchFinally.test());
22     }
23
24 }
这里稍微修改了 第一段代码,只是在finally语句块里面加入了 一个 return t 的表达式。
按照第一段代码的解释,先进行try{}语句,然后在return之前把当前的t的值try保存到一个变量t',然后执行finally语句块,修改了变量t的值,在返回变量t。
这里面有两个return语句,但是程序到底返回的是try 还是 finally。接下来我们还是看字节码信息
public static final java.lang.String test() ;
  Code:
   Stack= 1, Locals= 2, Args_size= 0
   0:    ldc    # 16 ; //String
   2 :    astore_0
   3:    ldc    # 18 ; //String try
   5 :    astore_0
   6:    goto    17
   9 :    astore_1
   10:    ldc    # 20 ; //String catch
   12 :    astore_0
   13:    goto    17
   16:    pop
   17:    ldc    # 22 ; //String finally
   19 :    astore_0
   20 :    aload_0
   21 :    areturn
  Exception table:
   from   to  target type
     3     9     9    Class java/lang/Exception

     3    16    16    any
  LineNumberTable:
   line 5: 0
   line 8: 3
   line 9: 6
   line 10: 9
   line 12: 10
   line 13: 13
   line 14: 16
   line 15: 17
   line 16: 20

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   3      19      0    t       Ljava/lang/String ;
   10      6      1    e       Ljava/lang/Exception ;

  StackMapTable: number_of_entries = 3
   frame_type = 255 /* full_frame */
     offset_delta = 9
     locals = [ class java/lang/String ]
     stack = [ class java/lang/Exception ]
   frame_type = 70 /* same_locals_1_stack_item */
     stack = [ class java/lang/Throwable ]
   frame_type = 0 /* same */
这段代码翻译出来的字节码和第一段代码完全不同,还是继续看code属性
第[0-2]行、[3-5]行第一段代码逻辑类似,就是初始化t,把try中的t进行赋值try
第6行,这里面跳转到第17行,[17-19]就是执行finally里面的赋值语句,把变量t赋值为finally,然后返回t对应的值
我们发现try语句中的return语句给忽略。可能jvm认为一个方法里面有两个return语句并没有太大的意义,所以try中的return语句给忽略了,直接起作用的是finally中的return语句,所以这次返回的是finally。
 
接下来在看看复杂一点的例子:(例3)
public class TryCatchFinally {

    @SuppressWarnings("finally" )
    public static final String test() {
        String t = "" ;

        try {
            t = "try" ;
            Integer.parseInt( null );
            return t;
        } catch (Exception e) {
            t = "catch" ;
            return t;
        } finally {
            t = "finally" ;
            // System.out.println(t);
            // return t;
        }
    }

    public static void main(String[] args) {
        System.out.print(TryCatchFinally.test());
    }

}
 
这里面try语句里面会抛出 java.lang.NumberFormatException,所以程序会先执行catch语句中的逻辑,t赋值为catch,在执行return之前,会把返回值保存到一个临时变量里面t ',执行finally的逻辑,t赋值为finally,但是返回值和t',所以变量t的值和返回值已经没有关系了,返回的是catch
 
例4:
public class TryCatchFinally {

    @SuppressWarnings("finally" )
    public static final String test() {
        String t = "" ;

        try {
            t = "try" ;
            Integer.parseInt( null );
            return t;
        } catch (Exception e) {
            t = "catch" ;
            return t;
        } finally {
            t = "finally" ;
            return t;
        }
    }

    public static void main(String[] args) {
        System.out.print(TryCatchFinally.test());
    }

}
这个和例2有点类似,由于try语句里面抛出异常,程序转入catch语句块,catch语句在执行return语句之前执行finally,而finally语句有return,则直接执行finally的语句值,返回finally
 
例5:
public class TryCatchFinally {

    @SuppressWarnings("finally" )
    public static final String test() {
        String t = "" ;

        try {
            t = "try" ;
            Integer.parseInt( null );
            return t;
        } catch (Exception e) {
            t = "catch" ;
            Integer.parseInt( null );
            return t;
        } finally {
            t = "finally" ;
            // return t;
        }
    }

    public static void main(String[] args) {
        System.out.print(TryCatchFinally.test());
    }

}
这个例子在catch语句块添加了Integer.parser(null)语句,强制抛出了一个异常。然后finally语句块里面没有return语句。继续分析一下,由于try语句抛出异常,程序进入catch语句块,catch语句块又抛出一个异常,说明catch语句要退出,则执行finally语句块,对t进行赋值。然后catch语句块里面抛出异常。结果是抛出java.lang.NumberFormatException异常
例子6:
public class TryCatchFinally {

    @SuppressWarnings("finally" )
    public static final String test() {
        String t = "" ;

        try {
            t = "try" ;
            Integer.parseInt( null );
            return t;
        } catch (Exception e) {
            t = "catch" ;
            Integer.parseInt( null );
            return t;
        } finally {
            t = "finally" ;
            return t;
        }
    }

    public static void main(String[] args) {
        System.out.print(TryCatchFinally.test());
    }

}
这个例子和上面例子中唯一不同的是,这个例子里面finally 语句里面有return语句块。try catch中运行的逻辑和上面例子一样,当catch语句块里面抛出异常之后,进入finally语句快,然后返回t。则程序忽略catch语句块里面抛出的异常信息,直接返回t对应的值 也就是finally。方法不会抛出异常
例子7:
public class TryCatchFinally {

    @SuppressWarnings("finally" )
    public static final String test() {
        String t = "" ;

        try {
            t = "try" ;
            Integer.parseInt( null );
            return t;
        } catch (NullPointerException e) {
            t = "catch" ;
            return t;
        } finally {
            t = "finally" ;
        }
    }

    public static void main(String[] args) {
        System.out.print(TryCatchFinally.test());
    }

}
这个例子里面catch语句里面catch的是NPE异常,而不是java.lang.NumberFormatException异常,所以不会进入catch语句块,直接进入finally语句块,finally对s赋值之后,由try语句抛出java.lang.NumberFormatException异常。
例子8
public class TryCatchFinally {

    @SuppressWarnings("finally" )
    public static final String test() {
        String t = "" ;

        try {
            t = "try" ;
            Integer.parseInt( null );
            return t;
        } catch (NullPointerException e) {
            t = "catch" ;
            return t;
        } finally {
            t = "finally" ;
            return t;
        }
    }

    public static void main(String[] args) {
        System.out.print(TryCatchFinally.test());
    }

}
和上面的例子中try catch的逻辑相同,try语句执行完成执行finally语句,finally赋值s 并且返回s ,最后程序结果返回finally
 
例子9:
public class TryCatchFinally {

    @SuppressWarnings("finally" )
    public static final String test() {
        String t = "" ;

        try {
            t = "try" ; return t;
        } catch (Exception e) {
            t = "catch" ;
            return t;
        } finally {
            t = "finally" ;
            String.valueOf( null );
            return t;
        }
    }

    public static void main(String[] args) {
        System.out.print(TryCatchFinally.test());
    }

}
这个例子中,对finally语句中添加了String.valueOf(null), 强制抛出NPE异常。首先程序执行try语句,在返回执行,执行finally语句块,finally语句抛出NPE异常,整个结果返回NPE异常。
 
对以上所有的例子进行总结
1 try、catch、finally语句中,在如果try语句有return语句,则返回的之后当前try中变量此时对应的值,此后对变量做任何的修改,都不影响try中return的返回值
2 如果finally块中有return 语句,则返回try或catch中的返回语句忽略。
3 如果finally块中抛出异常,则整个try、catch、finally块中抛出异常
 
所以使用try、catch、finally语句块中需要注意的是
1 尽量在try或者catch中使用return语句。通过finally块中达到对try或者catch返回值修改是不可行的。
2 finally块中避免使用return语句,因为finally块中如果使用return语句,会显示的消化掉try、catch块中的异常信息,屏蔽了错误的发生
3 finally块中避免再次抛出异常,否则整个包含try语句块的方法回抛出异常,并且会消化掉try、catch块中的异常
 
 
 
 
 
 
 
 
GodIsCoder 

博客园blog地址: http://www.cnblogs.com/aigongsi/ 

独立Blog:  God Is Coder 

个人网站:  iphone发码网 

本人版权归作者和博客园所有,欢迎转载,转载请注明出处
分类:  java语言
标签:  java try catch
好文要顶   关注我   收藏该文    
7
0
posted @  2012-04-19 19:34   God Is Coder  阅读( 57974 ) 评论( 9 )   编辑   收藏
评论列表
  
#1楼  2012-04-19 21:01  JustYong   
这个好,我自以为对Try,Catch这块还是掌握得蛮好的,但是这上面所说的还是有我所不知道的地方。
支持(0) 反对(0)
  
#2楼  2012-04-20 05:53  cndavy   
好,

直接看结论了
支持(0) 反对(0)
  
#3楼  2012-04-20 08:20  houjinxin   
LocalVariableTable: 

Start Length Slot Name Signature

3 27 0 t Ljava/lang/String;

14 10 1 e Ljava/lang/Exception;

我用javap -verbose TryCatchFinally命令之后怎么没有出现上面的本地变量表
支持(1) 反对(0)
  
#4楼  2012-04-20 08:35  houjinxin   
我知道了 在编译时用javac -g TryCatchFinally.java
支持(0) 反对(0)
  
#5楼  2012-04-20 09:00  houjinxin   
这个在java虚拟机规范的哪一章啊
支持(0) 反对(0)
  
#6楼  2015-09-02 10:23  BlueFish0323   
总结的非常到位!之前面试碰到过类似的问题,一直没找到啥原因,看了上面文章之后,豁然开朗啊!
支持(0) 反对(0)
  
#7楼  2015-09-19 15:26  起个po名真费劲   
真不错
支持(0) 反对(0)
  
#8楼  2017-01-22 22:42  |浅お唱|≮   
总结的第一点:

引用1 try、catch、finally语句中,在如果try语句有return语句,则返回的之后当前try中变量此时对应的值,此后对变量做任何的修改,都不影响try中return的返回值



这个描述的还是有点问题的。

如果返回的是一个可变类型的引用变量,在finally中改变了其内容,还是会影响返回的结果。

比如HashMap。

作者选的引用变量是String,而String是不可变类型的,所以不会引发这种问题。
支持(1) 反对(0)
  
#9楼  2017-04-14 13:51  贝拉巴拉   
@ |浅お唱|≮

引用总结的第一点:

引用引用1 try、catch、finally语句中,在如果try语句有return语句,则返回的之后当前try中变量此时对应的值,此后对变量做任何的修改,都不影响try中return的返回值



这个描述的还是有点问题的。

如果返回的是一个可变类型的引用变量,在finally中改变了其内容,还是会影响返回的结果。

比如HashMap。

作者选的引用变量是String,而String是不可变类型的,所以不会引发这种问题。



这个例子不是特别好,换成StringBuffer会更好点

猜你喜欢

转载自blog.csdn.net/ljxbbss/article/details/78212488
今日推荐