《疯狂Java讲义》读书笔记6
数据结构,对循环队列,双端队列的总结:
http://t.csdn.cn/kgZcI
刷题:
http://t.csdn.cn/YMc3M
异常处理
对于构造大型、健壮、可维护的应用,错误处理是整个应用要考虑的重要方面。
在Java中,Exception类是所有异常的基类,它有很多子类,主要包括以下几种:
- Checked Exception:编译时异常,也称为受检异常。这种异常在代码编译期间就必须被处理或声明抛出,否则无法通过编译。常见的 Checked Exception 有 IOException、ClassNotFoundException 等。
- Unchecked Exception:运行时异常,也称为非受检异常。这种异常是在代码运行期间才会被检测到,并且不需要明确地声明抛出。常见的 Unchecked Exception 有 NullPointerException、ArraylndexOutOfBoundsException 等。
- Error:表示严重的、不可恢复的错误,通常是JVM内部问题导致的异常,例如OutOfMemoryError、StackOverflowError 等。与 Exception 不同,Error不应该被程序员捕获和处理,而是应该尽量避免出现这些错误。
- RuntimeException:运行时异常,是 Unchecked Exception 的子类,这种异常通常是由程序逻辑错误引起的,例如除数为0、数组下标越界等。
- 自定义异常:根据需要,还可以自定义异常类,继承于Exception 或 RuntimeException,并针对具体的业务场景来进行异常类型的定义和扩展,以便更好地进行异常处理和调试。
处理机制
Java 的异常处理机制可以让程序具有极好的容错性,当程序运行出现意外情形时,系统会自动生成一个 Exception 对象来通知程序,从而实现将“业务功能实现代码”和“错误处理代码分离”。
使用 try...catch 捕获异常
一个 catch 可以捕获多种类型的异常,需要注意下面两个地方:
- 捕获多种类型的异常时,多种异常类型之间用竖线(|)隔开
- 捕获多种类型的异常时,异常变量有隐式的 final 修饰,因此程序不能对异常变量重新赋值
package com.ittht.day10;public class MultiException {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("程序发生了数组越界、数学格式异常、算术异常之一");}catch (Exception e){System.out.println("未知异常");//捕获一种类型的异常时,异常变量没有final修饰,所以可以赋值e=new RuntimeException("test");}}
}
其中的 args[0] 代表的是传入的第一个参数,args[1] 是传入的第二个参数。
访问异常信息
常用方法:
- getMessage():返回该异常的详细字符串
- printStackTrace():将该异常的跟踪栈信息输出到指定输出流
- printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流
- getStackTrace():返回该异常的跟踪栈
package com.ittht.day10;import java.io.FileInputStream;
import java.io.IOException;public class AccessExceptionMsg {public static void main(String[] args) {try{FileInputStream fit=new FileInputStream("a.txt");}catch (IOException ioe){//调用了Exception对象的getMessage()方法来得到异常对象的详细信息System.out.println(ioe.getMessage());//使用printStackTrace()方法来打印该异常的跟踪信息ioe.printStackTrace();}}
}
FileInputStream 是 Java IO 体系中的一个文件输入列,用于读取磁盘中的内容。
使用 finally 回收资源
有时程序在 try 中打开了一些物理资源(数据库连接,网络连接和磁盘文件),这些物理资源都必须显示回收。Java中的垃圾回收机制不会回收物理资源,只能回收堆内存中对象占用的内存。
如果 try 块中的某条语句发生了异常,该语句后面的其他语句通常不会获得执行的机会,位于该语句之后的资源回收局域不能执行;
如果在 catch 块中进行资源回收,但 catch 语句完全有可能不能执行,这也会导致不能回收这些物理资源。
所以异常处理机制提供了 finally 块,finally 块总会被执行。
try{//业务实现代码 } catch(SubException e){//异常处理1 } catch(SubException2 2){//异常处理2 } finally{//资源回收块 }
异常处理语句中 try 语句是必须的,但 catch 和 finally 块至少出现一个。
多个 catch 块必须位于 try 块之后。
finally 块必须位于所有的 catch 块之后。
package com.ittht.day10;import java.io.FileInputStream;
import java.io.IOException;public class FinallyTest {public static void main(String[] args) {FileInputStream fis=null;try{fis=new FileInputStream("a.txt");}catch(IOException ioe){System.out.println(ioe.getMessage());//return语句强制方法返回return;//使用exit退出虚拟机/*注释掉return后,使用该条语句的时候不会执行finally块*///System.exit(1);}finally {if(fis!=null){try{fis.close();}catch(IOException ioe){ioe.printStackTrace();}}System.out.println("执行finally块里面的资源回收");}}
}
除非在 catch 语句中调用了退出虚拟机的方法,否则不管在 try 块、catch 块中执行了什么样的代码,异常处理的 finally 块总会被执行。
不要在 finally 块中使用 return 或 throw 语句,将会导致 try 块、catch 块中的 return 、throw 语句失效。
package com.ittht.day10;public class FinallyFlow {public static void main(String[] args)throws Exception{boolean a = test();System.out.println(a);}public static boolean test(){try{//finally块中包含了return语句//所以下面的return语句失去作用return true;}finally{return false;}}
}
异常处理的嵌套
Closeable 是 AutoCloseable 的子接口,可以被自动关闭的资源类要么实现 Closeable接口,要么实现 AutoCloseable 接口。
Closeable 接口里的 close() 方法声明抛出来 IOException,所以它的实现类在实现 close 方法是只能声明抛出 IOException 或其子类。
AutoCloseable 接口的 close() 方法声明抛出来了 Exception ,所以它的实现类在实现 close()方法是可以声明抛出任何异常。
下面的代码是使用自用关闭资源的 try 语句:
package com.ittht.day10;import java.io.*;public class AutoCloseTest {public static void main(String[] args)throws IOException{try(//声明、初始化两个可关闭的资源//try语句会自动关闭这两个资源BufferedReader br=new BufferedReader(new FileReader("AutoCloseTest.java"));PrintStream ps=new PrintStream(new FileOutputStream("a.txt"))){//使用两个资源System.out.println(br.readLine());ps.println("java");}}
}
BufferedReader 和 PrintStream 都实现类 Closeable 接口,而且放在 try 语句中声明和初始化,所以 try 语句会自动关闭它们。
自动关闭资源的 try 语句后也可以带多个 catch 块和一个 finally 块。
Checked 异常和 Runtime 异常体系(运行时异常)
所有的 RuntimeException 类及其子类的实力被称为 Runtime 异常;其他的被称为 Checked 异常。
Checked 异常的处理方式——没有完善错误的代码不会被执行。
- 知道异常时,用 try...catch 块来捕获异常并在 catch 块中修复。
- 不知道如何处理该异常时,定义方法时声明抛出异常
Runtime 异常可以不用显式声明抛出,如果要捕获的话,也可以用 try...catch 块实现。
用 throw 声明抛出异常
throw 声明抛出的语法格式跟在方法签名之后(只能在方法签名中使用):
throws ExceptionClass1,ExceptionClass2...
如果某段代码调用量一个带 throws 声明的方法,该方法声明抛出了 Checked 异常,说明该方法希望它的调用者来处理。调用该方法要么放在 try 块中显式捕获该异常,要么放在另一个带 throws 声明抛出的方法中。
package com.ittht.day10;import java.io.FileInputStream;
import java.io.IOException;public class ThrowTest2 {public static void main(String[] args)throws Exception {test();}public static void test()throws IOException{FileInputStream fis=new FileInputStream("a.txt");}
}
方法重写时声明抛出异常的限制
使用 throws 声明抛出异常时有一个限制:
子类方法声明抛出的异常类型,不应该比父类方法声明抛出的异常类型多。
使用 Throw 抛出异常
语法格式为:
trows ExceptionInstance;
package com.ittht.day10;public class ThrowTest {public static void main(String[] args) {try{//调用声明抛出Checked异常的方法,//要么显式捕获,要么在main方法中再次声明抛出throwChecked(-3);}catch (Exception e){System.out.println(e.getMessage());}throwRuntime(3);}public static void throwChecked(int a)throws Exception{if(a>0){//自行抛出Exception异常//该代码不许处于try块里或者处于带throws声明的方法里throw new Exception("a的值大于0,不符合要求");}}public static void throwRuntime(int a){if(a>0){//自行抛出Exception异常//也可不理会该异常,把异常交给该方法调用者处理throw new RuntimeException("a的值大于0,不符合要求");}}
}
自定义异常类
自定义异常类都应该继承 Exception 基类。
如果希望定义一个 Runtime 异常,贼一个继承 RuntimeException 基类。
第一异常类时要提供两个构造器:一个无参数的构造器,另一个是带字符串参数的构造器。
package com.ittht.day10;public class AuctionException extends Exception{public AuctionException(){}public AuctionException(String msg){//通过super来调用父类的构造器,这行代码可以将此字符串参数传给异常对象的message属性//该message属性就是异常对象的详细描述super(msg);}
}
catch 和 throw 同时使用
异常的处理方法有两种:
- 在出现异常的方法内捕获并处理异常,方法的调用者不能再次捕获该异常
- 在方法签名中什么抛出该异常,将该异常完全交给方法调用者处理
所以有异常时,单靠某个方法不能完全处理该异常:比如在异常出现时,程序值对该异常进行部分处理,还有些处理需要再该方法的调用者中才能完成,大于一个再次抛出异常,让该方法的调用者也可以捕获到该异常。
package com.ittht.day10;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());}}
}
throw 抛出异常时是应用后台(控制台)记录了异常发生的详细情况
catch 抛出异常时是想应用使用者(用户)传达某种信息
异常跟踪栈
可以用对象的 printStackTrace() 方法用于打印到异常的跟踪栈信息,根据 printStackTrace() 方法的输出结果,可以找到异常的源头。
package com.ittht.day10;
class SelfException extends RuntimeException{SelfException(){}SelfException(String msg){super(msg);}
}
public class PrintStackTraceTest {public static void main(String[] args) {firstMethod();}public static void firstMethod(){secondMethod();}public static void secondMethod(){thirdMethod();}public static void thirdMethod(){throw new SelfException("自定义异常信息");}
}