try,catch,finally,throw,throws
java将异常分为两种,Checked异常和Runtime异常.
IndexOutOfBoundsException
NumberFormatException
ArithmeticException
NullPointerException
捕获异常时,先捕获小异常,再捕获大异常,
从java7开始,一个catch块可以捕获多种类型的异常
public static void main(String[] args) { try { int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1]); int c = a / b; System.out.println("您输入的两个数相除的结果是:" + c ); } catch (IndexOutOfBoundsException|NumberFormatException |ArithmeticException ie) { System.out.println("程序发生了数组越界、数字格式异常、算术异常之一"); // 捕捉多异常时,异常变量默认有final修饰, // 所以下面代码有错: // ie = new ArithmeticException("test"); // ① } catch (Exception e) { System.out.println("未知异常"); // 捕捉一个类型的异常时,异常变量没有final修饰 // 所以下面代码完全正确。 e = new RuntimeException("test"); // ② } }
打印到控制台:
ioe.printStackTrace(System.out);
或者
ioe.printStackTrace();
10.2.5 使用finally回收资源
程序在try块里打开的某些物理资源(比如IO),必须显示的回收.
java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收堆内存中对象所占用的内存.
public static void main(String[] args) { FileInputStream fis = null; try { fis = new FileInputStream("a.txt"); } catch (IOException ioe) { System.out.println(ioe.getMessage()); // return语句强制方法返回,依然会执行finally // return ; // ① // 使用exit来退出虚拟机,不会执行finally System.exit(1); // ② } finally { // 关闭磁盘文件,回收资源 .这也是个异常处理嵌套
if (fis != null) { try { fis.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } System.out.println("执行finally块里的资源回收!"); } }
通常情况下,不要在finally快中使用return或throw等导致方法终止的语句,一旦在finally块中使用了return或throw语句,将会导致try块,catch块中的return.throw语句失效.
10.2.7 java7 的自动关闭资源的try语句
try后紧跟一对圆括号,里面声明或初始化一个或多个必须被显示关闭的资源.为了保证try语句可以正常的关闭资源,这些资源实现类必须实现AutoCloseable或Colseable接口,实现这两个接口就必须实现close()方法.
实现Closeable接口的close()方法只能抛出IOException,实现AutoCloseable接口的close()方法可以抛出任何异常.
public static void main(String[] args) throws IOException { try ( // 声明、初始化两个可关闭的资源 // try语句会自动关闭这两个资源。 BufferedReader br = new BufferedReader( new FileReader("src/learnJava/LearnException.java")); PrintStream ps = new PrintStream(new FileOutputStream("a.txt"))) { // 使用两个资源 System.out.println(br.readLine()); ps.println("庄生晓梦迷蝴蝶"); } }
Checked异常和Runtime异常体系
所有的RuntimeException类及其子类的示例被称为Runtime异常;其他异常被称为Checked异常.
10.3.1 使用throws声明抛出异常
public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("b.txt"); }
如果某段代码中调用了一个带throws声明的方法,该方法声明抛出了Checked异常,则表明该方法希望它的调用者来处理该异常.也就是说,调用该方法时,要么放在try块中显示捕获该异常,要么在另一个带throws声明抛出的方法中.
方法重写时,throws声明抛出的异常应该是父类异常的子类或同类.
大部分推荐使用Runtime异常,而不是使用Checked异常,
public static void main(String[] args){ try{ FileInputStream fis = new FileInputStream("b.txt"); }catch(IOException e){ throw new RuntimeException(e); } }
如果throw语句抛出是Checked异常,则改异常或者被调用者显示抛出,或者在try块里.
如果throw语句抛出的是Runtime异常,则该语句无需放在try块里,也无需放在带throws声明抛出的方法中:既可以用try来捕获该异常,也可以完全不理会.
10.4.2 自定义异常类.
用户自定义的异常都应该继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类,通常需要覆写有参和无参两个构造方法.
public class AuctionException extends Exception { // 无参数的构造器 public AuctionException(){} //① // 带一个字符串参数的构造器 public AuctionException(String msg) //② { super(msg); } }
throw和catch结合使用,能再次捕捉到bid方法中的异常,并对该异常进行处理
public class AuctionTest { private double initPrice = 30.0; // 因为该方法中显式抛出了AuctionException异常, // 所以此处需要声明抛出AuctionException异常 public void bid(String bidPrice) throws AuctionException { double d = 0.0; try { d = Double.parseDouble(bidPrice); } catch (Exception e) { // 此处完成本方法中可以对异常执行的修复处理, // 此处仅仅是在控制台打印异常跟踪栈信息。 e.printStackTrace(); // 再次抛出自定义异常 throw new AuctionException("竞拍价必须是数值," + "不能包含其他字符!"); } if (initPrice > d) { throw new AuctionException("竞拍价比起拍价低," + "不允许竞拍!"); } initPrice = d; } public static void main(String[] args) { AuctionTest at = new AuctionTest(); try { at.bid("df"); } catch (AuctionException ae) { // 再次捕捉到bid方法中的异常。并对该异常进行处理 System.err.println(ae.getMessage()); } } }
10.4.4 java7增强的throw语句
public static void main(String[] args) // Java 6认为①号代码可能抛出Exception, // 所以此处声明抛出Exception // throws Exception // Java 7会检查①号代码可能抛出异常的实际类型, // 因此此处只需声明抛出FileNotFoundException即可。 throws FileNotFoundException { try { FileInputStream fis = new FileInputStream("b.txt"); } catch (Exception ex) { ex.printStackTrace(); throw ex; // ① // throw new RuntimeException(ex); } }
或者
public static void main(String[] args) // Java 6认为①号代码可能抛出Exception, // 所以此处声明抛出Exception // throws Exception // Java 7会检查①号代码可能抛出异常的实际类型, // 因此此处只需声明抛出FileNotFoundException即可。 // throws FileNotFoundException { try { FileInputStream fis = new FileInputStream("b.txt"); } catch (Exception ex) { ex.printStackTrace(); // throw ex; // ① throw new RuntimeException(ex); } }
构造器如下
public class SalException extends Exception { public SalException(){} public SalException(String msg) { super(msg); } // 创建一个可以接受Throwable参数的构造器 public SalException(Throwable t) { super(t); } }
10.5 java的异常跟踪栈
public class LearnException { public static void main(String[] args) { firstMethod(); } public static void firstMethod() { secondMethod(); } public static void secondMethod() { thirdMethod(); } public static void thirdMethod() { throw new SelfException("自定义异常信息"); } } class SelfException extends RuntimeException { SelfException(){} SelfException(String msg) { super(msg); } }
Exception in thread "main" learnJava.SelfException: 自定义异常信息 at learnJava.LearnException.thirdMethod(LearnException.java:29) at learnJava.LearnException.secondMethod(LearnException.java:25) at learnJava.LearnException.firstMethod(LearnException.java:21) at learnJava.LearnException.main(LearnException.java:17)
1.不要过度使用异常,包括
1.不能用异常抛出代替所有错误处理
2.不要使用异常处理来代替流程控制.
2.不要使用过于庞大的try块
正确的做法是,把可能出现异常的程序分别放到单独的try块中处理.
3.尽量避免使用一个catch来捕获所有异常,特别是一些关键的代码
4.不要忽略捕获到的异常
如何处理捕获到的异常
1.checked异常,要尽可能修复
2.重新抛出新的异常.把当前运行环境下要做的事情尽量昨晚,然后把异常包装成当前层的异常,重新抛给上层调用者
3.在合适的层处理异常.如果当前层不知道如何处理异常,就不要在当前从用catch捕获异常,直接使用throws声明抛出异常,让上层调用者来负责处理该异常.