异常处理
1. 使用try……catch捕获异常
格式
try{
…
}
catch (异常类型 变量名) {
…
}
catch (异常类型 变量名) {
…
}
…
package 异常处理;
public class DivTest {
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 ie) {
System.out.println("数组越界,运行程序是输入的参数不够");
}
catch (NumberFormatException ne) {
System.out.println("数字格式异常,程序只接收整数型参数");
}
catch (ArithmeticException as) {
System.out.println("算数异常");
}
catch (Exception e) {
System.out.println("未知异常");
}
}
}
对于try…catch块,如果try中出现了异常,程序会把异常抛出与后面的catch块中异常类型做比较,如果比较成功,则创建该异常对象,catch块可以有多个,如果都没有比较成功,则安系统默认的方式处理。对于运行时异常,系统处理的方案是输出异常类型并结束程序。
需要注意的几点:
1. try中的代码越少越好,这样可以减少资源消耗。
1. 如果try块中有好几行可能出现异常的代码,但是当抛出第一个异常时,后面的代码将不再执行,也就是说,当try块中有出现多个运行时异常时,系统将第一个异常抛出后,停止try块后续代码的执行。
2. 对于try后catch块,一定要把子类异常写在最前面,把父类异常写在后面。如果将父类写在最前,在比较时会直接进入该catch块,而不是在后面找它的子类。(简称:先处理小异常,在处理大异常)
一些常见的异常:
1. IndexOutOfBoundsException 原意是索引超过了界限。也就是访问了没有权限的内存。
2. NumberFormatException 原意是数字格式异常。 程序试图把字符串转化成数字类型时,当该字符串不能转换成适当的格式而引发的异常。
3. ArithmeticException 原意为算数异常。当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
4. NullPointerException 空指针异常。
public class NullTest {
public static void main(String[] args) {
List list = null;
try {
System.out.println(list.get(1));
}
catch (NullPointerException ne) {
System.out.println("空指针异常");
}
catch (Exception e) {
System.out.println("未知异常");
}
}
}
2. 异常处理继承树
Error错误一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败等,这些错误通常是程序不能处理的。
3. Java7提供的多异常捕获
在Java7之前一个catch块中只能捕获一个种类型的异常;但从Java7开始,一个catch块可以捕获多种类型的异常。
需要注意的几点:
1. 捕获多种类型的异常时,多种类型的异常之间用“|”隔开。
2. 捕获多种类型的异常时,异常变量有隐式的final修饰,因此程序不能对异常变量再赋值。
下面的程序示范了这个新特性:
public class ExceptionDemo {
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("a除以b的结果是:" + c);
}
catch (IndexOutOfBoundsException | NumberFormatException |
ArithmeticException ie) {
System.out.println("程序发生了数组越界、数字格式异常、算数异常之一");
// 捕获异常时,异常变量默认有final修饰
// 所以下面的代码有错
ie = new ArithmeticException("test");
}
catch (Exception e) {
System.out.println("未知异常");
e = new RuntimeException("test");
}
}
}
4. 访问异常信息
所有的异常对象都包含了如下的集中常用的方法
getMessage(): 返回异常的详细描述的字符串
printStackTrace(): 返回异常的跟踪栈信息输出到标准错误输出。
printStackTrace(PrintStream s) : 将该跟踪信息输出到指定输出流。
getStackTrace() : 返回该异常的跟踪信息。
public class AccessExceptionDemo {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("a.txt");
}
catch (IOException ioe) {
System.out.println(ioe.getMessage());
ioe.printStackTrace();
}
}
}
5. 使用finally回收资源
完整的Java异常处理语法结构:
try {
// 业务实现代码
...
}
catch (SubException e) {
// 异常处理块1
...
}
catch (SubException2 e) {
// 异常处理块2
...
}
finally {
// 资源回收
...
}
需要注意的几点:
1. catch块和finally都不是必须的,但是catch和finally中至少要有一个。
2. 不管代码中的try中的代码是否有异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了return语句,finaly块总会被执行。但有一种可能使用了exit退出了虚拟机。
public class FinallyTrestDemo {
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退出虚拟机
// System.exit(1);
}
finally {
// 关闭磁盘文件,回收资源
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("执行finally块里的资源回收!");
}
}
}
- 通常情况下,不要在finally中使用return或throw语句,将会导致try块、catch块中的return、throw语句失效。
public class FinallyFlowTest {
public static void main(String[] args)
throws Exception{
boolean a = test();
System.out.println(a); // 输出false
}
public static boolean test() {
try {
// 因为finally块中包含了return语句
// 所以下面的return将失去作用
return true;
}
finally {
return false;
}
}
}
当Java程序执行try块,catch块时遇到了一个return或throw语句,这两个语句都会导致程序该方法立即结束,但是系统执行这两个语句时并不会立即结束该方法,而是去寻找该异常处理流程是否包含finally块,如果没有finally块,程序立即执行return或throw语句,方法终止;如果程序有finally块——只有当finally块执行完成后,系统才会再次跳回来执行try块、catch块里的return或throw语句;如果finally块里也是用了return或throw等导致方法终止语句,finally块已经终止了方法,系统将不会跳回去执行try块,catch块里的代码。
值得注意的是:Java异常处理的try块、catch块、或finally块中可以进行嵌套。
6. Checked 异常和Runtime异常体系
Java 中的异常被分成两大类:
1. Check异常,不是RuntimeException类及其子类的异常被称为Checked异常。
2. Runtime异常,所有的RuntimeException类及其子类的异常被称为Runtime异常。
Checked异常也被称为编译时异常。也就是说代码可能会出现异常并且明确的知道如何处理这些异常,程序应该使用try…catch 块来捕捉这种异常。
7. Java7的自动关闭资源的try语句
Java7 增强与try语句的功能——它允许在try关键字后跟一对圆括号,圆括号可以声明、初始化一个或多个资源,此处的资源是那些必须显示的关闭的资源(比如数据库连接、网络连接等),try语句在该语句结束时自动关闭这些资源。
需要指出的是,为了保证try语句的正常关闭资源,这些资源必须显示的实现AutoCloseable或Closeable 接口,实现这两个接口就必须是实现close()方法。
注意:Closeable是AutoCloseable的子接口,可以被关闭的类有么实现AutoCloseable接口,要么实现Closeable接口。Closeable接口。Closeable接口里的close()方法声明抛出了IOException,因此它的实现类在实现close()方法时只能声明抛出了Exception,因此它的实现类在实现close()方法时可以声明抛出任何异常。
8. 使用throws声明抛出异常
throws的使用是当前方法不知道处理这种类型的异常,然后将这个异常抛出,让它的调用者处理,如果调用者处理不了,可以将其继续抛出。最后main方法也处理不了,则将其交给JVM处理。
JVM的处理方法:打印异常跟踪信息,并终止程序运行。
throws的语法格式如下: throws Exception1, Exception2...
上面的throws声明抛出的语法格式仅跟在方法签名之后,并且throws可以抛出多个异常,异常与异常之间用逗号隔开。
public class ExceptionDemo2 {
public static void main(String[] args) throws ArithmeticException {
int a = 10;
int b = 0;
System.out.println(a/b);
}
}
如果某段代码中调用了一个带throws声明的方法,则表明该方法想要他的调用者来处理这个异常。调用者可以使用try…catch块去解决这个异常,或者继续抛出。
public class ExceptionDemo2 {
public static void main(String[] args) {
try {
test();
}catch(ArithmeticException a) {
System.out.println("除数不能为0");
}
}
public static void test() throws ArithmeticException{
int a = 10;
int b = 0;
System.out.println(a/b);
}
}
使用throws声明抛出异常是:
1. 子类方法抛出的异常类型应该是父类方法抛出异常的子类或这相同的类型。
2. 子类方法不允许比父类方法抛出的异常多。
9. 使用throw抛出异常
如果在程序中自行抛出异常,则应使用throw语句,throw语句可单独使用,throw语句抛出的不是一异常类,而是异常类的实例,而且每次只能抛出一个异常类实例。
throw语句的语法格式如下: throw ExceptionInstance
如果throw语句抛出的是Check异常,则该throw与语句要么处于try块中,显示捕获,要么放在一个带throw声明抛出的方法中,几把该throw语句交给它的调用者处理;如果throw语句抛出的RuntimeException异常,则无需放在try块中,也无需放在throw语句声明抛出的方法中:程序既可以显示的使用try…catch块来捕获异常,也可以完全不理会异常,把该异常交给方法的调用者处理。
如下:
public class ThrowsTest2 {
public static void main(String[] args) throws Exception {
// 因为test()方法声明抛出IOException异常
// 所以调用该方法的代码要么处于try...catch块中,
// 要么处于另一个throws声明抛出的方法中
test();
}
public static void test() throws IOException {
// 因为FileInputStream的构造器声明抛出IOException异常
// 所以调用FileInputStream的代码要么处于try...catch块中
// 要么处于throws声明抛出的方法中
FileInputStream fis = new FileInputStream("a.txt");
}
}
10. 自定义异常类
用户自定义的异常类都应该继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。
定义异常类时通常需要提供两个构造器:
1. 一个是无参的构造器
2. 带有一个字符串的构造器。
下面程序创建了一个自定义异常长类:
public class AuctionException extends Exception {
// 无参的构造器
public AuctionException(){}
// 带一个字符串参数的构造器
public AuctionException(String msg) {
super(msg);
}
}