糖果派对官方网站_可以赌钱的糖果游戏_手机版
bb电子糖果派对第十章.异常处理

bb电子糖果派对第十章.异常处理

作者:Web前端    来源:未知    发布时间:2020-01-29 05:06    浏览量:

作者Yegor Bugayenko是Teamed.io的软件架构师,热衷于软件质量研究和有效的项目管理方法探索。在本文中,Yegor 就「异常被捕获但并未重新抛出」这个问题进行了深入讨论,并分享了一些建议。对异常只捕获但并未重新抛出究竟是 anti-pattern,还是个普通而且非常流行的错误确实无从考究。但毫无疑问的是,在所有异常捕获代码中,它基本无处不在,正如下面这段代码:

Java的异常机制主要依赖于try、catch、finally、throw和throws五个关键字。

try { stream.write(data);} catch (IOException ex) { ex.printStackTrace();}

Java将异常分为两种,Checked异常和Runtime异常,Java认为Checked异常都是可以在运行期间得到解决的异常,所以它强制要求程序处理所有的Checked异常;而Runtime异常则无须处理。

注意:下面的代码并没有反对。

异常处理机制:

try { stream.write('X');} catch (IOException ex) { throw new IllegalStateException(ex);}

  Java异常处理机制可以让程序具有极好的容错性,让程序更加健壮。当程序运行出现意外情形时,系统会自动生成一个Exception对象来通知程序,从而实现将“业务功能实现代码”

这叫做Exception Chaining,是一个非常有效的构造。那么,捕获异常并记录究竟存在什么样的问题?首先,从宏观着手,这里正在谈论的是面向对象编程——意味着需要处理的是对象。一个对象应该是这个样子:

   和“错误处理代码”分离,提供更好的可读性。

final class Wire { private final OutputStream stream; Wire(final OutputStream stm) { this.stream = stm; } public void send(final int data) { try { this.stream.write(x); } catch (IOException ex) { ex.printStackTrace(); } }}

  使用try...catch捕获异常:

这里这样来应用这个类的:

bb电子糖果派对 1bb电子糖果派对 2

new Wire(stream).send(1);
1 //Java异常处理机制的语法结构
2 try{
3     //业务实现
4     ...
5 }catch (Exception e){
6     alert 输入不合法
7     goto retry
8 }

看起来不错,对么?当调用 send(1) 时,并不需要担心出现 IOException ,它将在方法内部处理。同时,如果出现异常,异常信息会被记录。但是这么做的理念是完全错误的,它传承自没有异常处理的语言,比如 C。异常的发明是为了将整个错误处理代码从主要逻辑中移除,以此来简化设计。同时,它们不仅仅是被移走,而且被集中在一个地方——在 main() 方法中,即整个应用的入口。一个异常的主要目的是搜集尽可能多的错误信息并将它抛到最上层,在这里用户能够针对它做一些处理。Exception chaining 则可以帮助更多,它允许在异常抛至上层的过程中扩充错误信息。在这个过程中,实际上非常类似于每次捕获到泡泡后,所做的只是将它添加到一个更大的泡泡中然后重新抛出。当它到达最高层的时候,那里就有许多泡泡,像一个 Russian Doll,一个嵌套着另外一个。最原始的异常就是最小的那个泡泡。当捕获一个异常而并没有重新抛出时,等同于你捏碎了这个泡泡。其中包含的大量信息,包括最原始的异常和所有其它带有信息的泡泡,都被你牢牢的抓在手中。你杜绝为上层呈现它,同时你如何处理和使用上层也毫无察觉。这一切都像是暗箱操作,一些潜在的重要信息被隐藏。因此,在这里直接导致的是 send() 方法无法得到信任,同样基于 send() 方法的操作也无法得到信任,对象之间的信任链被破坏殆尽。这里的建议是尽可能少捕获异常,同时一旦捕获则必须抛出。不幸的是,Java 在很多地方的设计违背了这个原则。例如,Java 有需检查和不需检查的异常两类,但是在我看来,只应该有需检查的异常。而且,Java 允许在一个方法中将多个异常类型声明为 throwable ——这是另一个错误;坚持只声明一种类型。在层次结构的顶部有一个通用的 Exception 类,在我看来也是错误的。除此之外,一些内置的类不允许抛出任何需检查的异常,比如说Runnable.run()。Java 还有许多关于异常的问题。但是尝试记住这些原则,你的代码将会更加整洁:catch 除非你别无选择。P.S.这个类应该是这个样子:

View Code

final class Wire { private final OutputStream stream; Wire(final OutputStream stm) { this.stream = stm; } public void send(final int data) throws IOException { this.stream.write(x); }}

    若执行try块中的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象提交给Java运行时环境,这个过程被称为抛出(throw)异常。

原文链接:Catch Me If You ... Can't Do Otherwise本文系OneAPM工程师编译整理。OneAPM是应用性能管理领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客。

    当Java运行时环境收到异常对象时,会寻找能处理该异常对象的catch块,若找到合适的catch块,则把该异常对象交给该catch块处理,这个过程被称为捕获(catch)异常;

    若Java运行时环境找不到捕获异常的catch块,则运行时环境终止,Java程序也将退出。

bb电子糖果派对 3bb电子糖果派对 4

 1 try{
 2                 //将用户输入的字符串以逗号(,)作为分隔符,分割成2个字符串
 3                 String[] posStrArr = inputStr.split(",");
 4                 //将2个字符串转换成用户下棋的坐标
 5                 int xPos = Integer.parseInt(posStrArr[0]);
 6                 int yPos = Integer.parseInt(posStrArr[1]);
 7                 //把对应的数组元素赋为“O”
 8                 if(!gb.board[xPos - 1][yPos - 1].equals("+")){
 9                     System.out.println("您输入的坐标点已有棋子了," + "请重新输入");
10                     continue;
11                 }
12                 gb.board[yPos - 1][xPos - 1] = "O";
13             }catch (Exception e){
14                 System.out.println("您输入的坐标不合法,请重新输入," + "下棋坐标应以x,y的格式");
15                 continue;
16             }

View Code

    上面代码把处理用户输入字符串的代码都放在try块里进行,只要用户输入的字符串不是有效坐标值(包括字母不能正确解析,没有逗号不能正确解析,解析出来的坐标引

     起数组越界...),系统都将抛出异常对象,并把这个异常交个对应的catch块。

  异常类的继承体系:

    当Java运行时环境接收到异常对象时,如何为该异常对象寻找catch块?上面程序中catch关键字的形式:(Exception e),这意味着每个catch块都是专门用于处理该异

     常类及其子类的异常实例。

    当Java运行时环境接收到异常对象后,会依次判断该异常对象是否是catch块后异常类或其子类的实例,若是,Java运行时环境将调用该catch块来处理该异常;否则再次

     拿该异常对象和下一个catch块里的异常类进行比较。

    try块后面的花括号{ }不可省略,即使try块中只有一条代码,也不可省略这个花括号。catch块后的花括号也不可被省略。在try块中声明的变量是代码块内局部变量,只在try

     块内有效,在catch块中不能访问该变量。

  Java常见异常类之间的继承关系图:

bb电子糖果派对 5

  从图中可看出,Java把所有的非正常情况分成两种:异常(Exception)和错误(Error),它们都继承Throwable父类。

  Error错误一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,动态链接失败等,这种错误无法恢复或不可捕获,将导致应用程序中断。通常应用程序无法处理这些错误,

   因此应用程序不应该试图使用catch块来捕获Error对象,也无需在其throws子句中声明该方法可能抛出Error及其任何子类。

bb电子糖果派对 6bb电子糖果派对 7

 1 public class DivTest{
 2     public static void main(String[] args){
 3         try{
 4             int a = Integer.parseInt(args[0]);
 5             int b = Integer.parseInt(args[1]);
 6             int c = a / b;
 7             System.out.println("您输入的两个数相除的结果是:" + c);
 8         }catch(IndexOutOfBoundsException ie){
 9             System.out.println("数组越界:运行程序时输入的参数个数不够");
10         }catch(NumberFormatException ne){
11             System.out.println("数字格式异常:程序只能接收整数参数");
12         }catch(ArithmeticException ae){
13             System.out.println("算术异常");
14         }catch(Exception e){
15             System.out.println("未知异常");
16         }
17     }
18 }

View Code

bb电子糖果派对 8

  IndexOutOfBoundsException:索引越界异常

  NumberFormatException:数字格式异常

  ArihmeticException:算术异常,计算的表达式无意义

bb电子糖果派对 9bb电子糖果派对 10

 1 import java.util.Date;
 2 
 3 public class NullTest{
 4     public static void main(String[] args){
 5         Date d = null;
 6         try{
 7             System.out.println(d.after(new Date()));
 8         }catch(NullPointerException ne){
 9             System.out.println("空指针异常");
10         }catch(Exception e){
11             System.out.println("未知异常");
12         }
13     }
14 }

View Code

bb电子糖果派对 11

bb电子糖果派对第十章.异常处理。  NullPointerException:当试图调用一个null对象的实例方法或实例变量时,就会引发NullPointerException异常

   捕获异常时注意,一定先捕获小异常,再捕获大异常。

  Java7 提供的多异常捕获:

    Java7以前,每个catch块只能捕获一种类型的异常;但从Java7开始,一个catch块可以捕获多种类型的异常。

    使用一个catch块捕获多种类型的异常时需要注意如下两个地方:

      1.捕获多种类型的异常时,多种异常类型之间需要用(|)竖线隔开

      2.捕获多种类型的异常时,异常变量有隐式的final修饰,因此程序不能对异常变量重新赋值。

bb电子糖果派对 12bb电子糖果派对 13

 1 public class MultiExceptionTest{
 2     public static void main(String[] args){
 3         try{
 4             int a = Integer.parseInt(args[0]);
 5             int b = Integer.parseInt(args[1]);
 6             int c = a / b;
 7             System.out.println("您输入的两个数相除的结果是:" + c);
 8         }catch(IndexOutOfBoundsException | NumberFormatException | ArithmeticException ie){
 9             System.out.println("程序放生了索引越界,数字格式异常、算术异常之一");
10             //捕获多异常时,异常变量默认有final修饰
11             //所以下面代码有错
12             ie = new ArithmeticException("test");
13         }catch(Exception e){
14             System.out.println("未知异常");
15             //捕获一种类型的异常时,异常变量没有final修饰
16             //所以下面代码完全正确
17             e = new RuntimeException("test");
18         }
19     }
20 }

View Code

bb电子糖果派对 14

      捕获多种类型异常时,异常变量使用隐式的final修饰,因此产生编译错误;捕获一种异常时,异常变量没有final修饰,因此不会出错。

  访问异常信息:

    若程序需要在catch块中访问异常对象的相关信息,可通过访问catch块后面的异常形参来获得。

    当Java运行时决定调用某个catch块来处理该异常对象时,会将异常对象赋给catch块后的异常参数,程序即可通过该参数来获得异常的相关信息:

      1.getMessage():返回该异常的详细描述字符串

      2.printStackTrace():将该异常的跟踪栈信息输出到标准错误输出

      3.printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流

      4.getStackTrace():返回该异常的跟踪栈信息

bb电子糖果派对 15bb电子糖果派对 16

 1 import java.io.FileInputStream;
 2 import java.io.IOException;
 3 
 4 public class AccessExceptionMsg{
 5     public static void main(String[] args){
 6         try{
 7             FileInputStream fis = new FileInputStream("a.txt");
 8         }catch(IOException ioe){
 9             System.out.println(ioe.getMessage());
10             ioe.printStackTrace();
11         }
12     }
13 }

View Code

bb电子糖果派对 17

      “a.txt”(系统找不到指定的文件):是调用异常的getMessage()方法返回的字符串。下面更详细的信息是该异常的跟踪栈信息。

  使用finally回收资源:

    有些时候,程序在try块中打开了一些物理资源(如:数据库连接,网络连接,磁盘文件等),这些物理资源都必须显示回收。

    Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收堆内存中对象所占用的内存。

    为保证一定能回收try块中打开的物理资源,异常处理机制提供了finally块。不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了

     return语句,finally块总是被执行。完整的Java异常处理语法结构如下:

bb电子糖果派对 18bb电子糖果派对 19

 1 //完整的Java异常处理语法结构
 2 try{
 3     //业务实现
 4     ...
 5 }catch (SubException e){
 6     //异常处理块1
 7     ...
 8 }catch(SubException2 e){
 9     //异常处理块2
10     ...
11 }
12 ...
13 finally{
14     //资源回收
15     ...
16 }

View Code

    异常处理语法结构中只有try块是必须的,即若没有try块,则不能有后面的catch块和finally块;

    catch块和finally块都是可选的,但catch块和finally块至少出现其中之一,也可以同时出现;

    可以有多个catch块,捕获父类异常的catch块必须位于捕获子类异常的后面;

    但不能只有try块,既没有catch块,也没有finally块;

    多个catch块必须位于try块之后,finally块必须位于所有的catch块之后。

bb电子糖果派对 20bb电子糖果派对 21

 1 import java.io.FileInputStream;
 2 import java.io.IOException;
 3 
 4 public class FinallyTest{
 5     public static void main(String[] args){
 6         FileInputStream fis = null;
 7         try{
 8             fis = new FileInputStream("a.text");
 9         }catch(IOException ioe){
10             System.out.println(ioe.getMessage());
11             //return语句强制方法返回
12             return ;
13             //使用exit退出虚拟机
14             //System.exit(1);
15         }finally{
16             //关闭磁盘文件,回收资源
17             if(fis != null){
18                 try{
19                     fis.close();
20                 }catch(IOException ioe){
21                     ioe.printStackTrace();
22                 }
23             }
24             System.out.println("执行finally块里的资源回收!");
25         }
26     }
27 }

View Code

bb电子糖果派对 22

    finally块被执行了,将上面代码的return ;注释掉,将System.exit(1);打开:

bb电子糖果派对 23

    上面结果表明finally块没有被执行。若在异常处理代码中使用System.exit(1)语句退出虚拟机,则finally块将失去执行的机会。

    除非在try块、catch块中调用了退出虚拟机的方法,否则不管在try块,catch块中执行了怎样的代码,出现怎样的情况,异常处理的finally块总会被执行。

    通常情况下,不要再finally块中使用return或throw等导致方法终止的语句,(throws语句将在后面介绍),一旦在finally块中使用了return或throw语句,将会导致try块,

     catch块中的return、throw语句失效:

bb电子糖果派对 24bb电子糖果派对 25

 1 public class FinallyFlowTest{
 2     public static void main(String[] args) throws Exception {
 3         boolean a = test();
 4         System.out.println(a);
 5     }
 6     
 7     public static boolean test(){
 8         try{
 9             //因为finally块中包含了return语句
10             //所以下面的return语句失去了作用
11             return true;
12         }finally{
13             return false;
14         }
15     }
16 }

View Code

bb电子糖果派对 26

    上面程序中,try块中的return true;语句失去了作用。

    当Java程序执行try块、catch块时遇到了return或throw语句,这两个语句都会导致该方法立即结束,但是系统执行这两个语句并不会结束该方法,而是去寻找该异常处理流

     程中是否包含finally块,若没有finally块,程序立即执行return或throw语句,该方法终止;若有finally块,系统立即开始执行finally——只有当finally块执行完成后,系统

     才会再次跳回来执行try块、catch块里的return或throw语句;若finally块里也使用了return或throw等导致方法终止语句,finally块已经终止了方法,系统将不会跳回去执行

     try块、catch块中的任何代码。

    尽量避免在finally块中使用return或throw等导致方法终止的语句。

  异常处理的嵌套:

bb电子糖果派对 27bb电子糖果派对 28

 1 import java.io.FileInputStream;
 2 import java.io.IOException;
 3 
 4 public class FinallyTest{
 5     public static void main(String[] args){
 6         FileInputStream fis = null;
 7         try{
 8             fis = new FileInputStream("a.text");
 9         }catch(IOException ioe){
10             System.out.println(ioe.getMessage());
11             //return语句强制方法返回
12             //return ;
13             //使用exit退出虚拟机
14             System.exit(1);
15         }finally{
16             //关闭磁盘文件,回收资源
17             if(fis != null){
18                 try{
19                     fis.close();
20                 }catch(IOException ioe){
21                     ioe.printStackTrace();
22                 }
23             }
24             System.out.println("执行finally块里的资源回收!");
25         }
26     }
27 }

View Code

    正如上面程序所示,finally块中也包含了一个完整的异常处理流程,这种在try块、catch块或finally块中包含完整的异常处理流程的情况被称为异常处理的嵌套。

    异常处理嵌套的深度没有明确限制,通常,没有必要使用超过两层的嵌套异常处理。

  Java7的自动关闭资源的try语句:

    Java7增强了try语句的功能——它允许在try关键字后紧跟一对圆括号,圆括号可以声明、初始化一个或多个资源,此处的资源指的是那些必须在程序结束时显示关闭的资源

     (比如:数据库连接、网络连接等),try语句在该语句结束时自动关闭这些资源。

    需要指出的是:为保证try语句可以正常关闭资源,这些资源实现类必须实现AutoCloseable或Closeable接口,实现这两个接口就必须实现close()方法。

    Closeable是AutoCloseable的子接口,可以被自动关闭的资源类要么实现AutoCloseable接口,要么实现Closeable接口。

    Closeable接口里的close方法声明抛出了IOException,,因此它的实现类在实现close()方法时只能声明抛出IOException或其子类;

    AutoCloseable接口里的close()方法声明抛出了Exception,因此它的实现类在实现close()方法时可以声明抛出任何异常。

bb电子糖果派对 29bb电子糖果派对 30

 1 import java.io.BufferedReader;
 2 import java.io.FileReader;
 3 import java.io.FileOutputStream;
 4 import java.io.PrintStream;
 5 import java.io.IOException;
 6 
 7 public class AutoCloseTest{
 8     public static void main(String[] args) throws IOException{
 9         try(
10             //声明、初始化两个可关闭的资源
11             //try语句会自动关闭这两个资源
12             BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
13             PrintStream ps = new PrintStream(new FileOutputStream("a.txt"));
14             ){
15                 //使用两个资源
16                 System.out.println(br.readLine());
17                 ps.println("庄生晓梦迷蝴蝶");
18             }
19     }
20 }

View Code

bb电子糖果派对 31

bb电子糖果派对 32

    这里提醒一句,try()圆括号中的最后一条语句的分号,可要可不要。

    上面程序中try语句中声明、初始化了BufferedReader、PrintStream,所以try语句会自动关闭它们。

    自动关闭资源的try语句相当于包含了隐式的finally块,因此这个try语句可以既没有catch块,也没有finally块。

    Java7几乎把所有的“资源类”(包括文件IO的各种类,JDBC编程的Connection、Statement等接口)进行了改写,改写后的资源类都实现了AutoCloseable或Closeable接口

    若程序需要,自动关闭资源的try语句后也可带多个catch块和一个finally块。

  Checked异常和Runtime异常体系:

    Java异常分为两大类:Checked异常和Runtime异常(运行时异常)。所有的RuntimeException类及其子类的实例被称为Runtime异常;不是RuntimeException类及其子类

     的异常实例被称为Checked异常。

    Java认为Checked异常都是可以被处理(修复)的异常,所以Java程序必须显示处理Checked异常,若程序没有处理Checked异常,改程序会在编译时报错,无法通过编译

  使用throws声明抛出异常:

    当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理;若main方法也不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常交给

bb电子糖果派对 ,     JVM处理。JVM对异常的处理方法是,打印异常的跟踪栈信息,并终止程序运行,这就是前面程序在遇到异常后自动结束的原因。

    throws声明抛出只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类间以逗号隔开:

      throws ExceptionClass1, ExceptionClass2...

      上面throws声明抛出的语法格式仅跟在方法签名之后,一旦使用throws语句声明抛出该异常,程序就无需使用try...catch块来捕获该异常了。

bb电子糖果派对 33bb电子糖果派对 34

1 import java.io.FileInputStream;
2 import java.io.IOException;
3 
4 public class ThrowsTest{
5     public static void main(String[] args) throws IOException{
6         FileInputStream fis = new FileInputStream("a.txt");
7     }
8 }

View Code

bb电子糖果派对 35

      上面程序声明不处理IOException异常,将该异常交给JVM处理,所以一旦程序遇到该异常,JVM就会打印该异常的跟踪栈信息,并结束程序。

      若某段代码中调用了一个带throws声明的方法,该方法声明抛出了Checked异常,则表明该方法希望它的调用者来处理该异常。

      处理异常的方法有两种,一是调用该方法是放在try块中显示捕获该异常,二是在另一个带throws声明抛出的方法中,交给上一级处理:

bb电子糖果派对 36bb电子糖果派对 37

 1 import java.io.FileInputStream;
 2 import java.io.IOException;
 3 
 4 public class ThrowsTest2{
 5     public static void main(String[] args) throws Exception{
 6         //因为test()方法声明抛出IOException异常
 7         //所以调用该方法的代码要么处于try...catch块中
 8         //要么处于另一个带throws声明抛出的方法中
 9         test();
10     }
11     
12     public static void test() throws IOException{
13         //因为FileInputStream的构造器声明抛出IOException异常
14         //所以调用FileInputStream的代码要么处于try...catch块中
15         //要么处于另一个带throws声明抛出的方法中
16         FileInputStream fis = new FileInputStream("a.txt");
17     }
18 }

友情链接: 网站地图
Copyright © 2015-2019 http://www.tk-web.com. bb电子糖果派对有限公司 版权所有