Java中的异常处理
异常概述与异常体系结构
为什么会出现异常
在使用计算机语言进行项目开发的过程中,即使代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通常等等。
异常的定义
在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不属于异常)
异常的分类
- Error:**Java虚拟机无法解决的严重问题。**如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。
- Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理,如:
- 空指针访问
- 试图读取不存在的文件
- 网络连接中断
- 数组角标越界
异常的体系结构
异常的解决
- 遇到错误就终止程序的运行
- 在编写程序时,就考虑到错误的检测,错误消息的提示,以及错误的处理
- 捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。
- 分类:编译时异常、运行时异常(RuntimeException)
编译时异常 | 运行时异常 |
---|---|
编译器要求必须处置的异常 | 编译器不要求强制处置的异常 |
程序在运行时由于外界因素造成的一般性异常 | 一般指编程时的逻辑错误,应该积极避免出现 |
编译器要求Java程序必须捕获或声明所有编译时异常 | java.lang.RuntimeException类及它的子 类都是运行时异常。 |
如果程序不处理,可能会带来意想不到的结果 | 对于这类异常,可以不作处理 |
常见异常
//1.ArrayIndexOutOfBoundsException 数组角标越界
public static void main(String[] args) {
String friends[] = { "lisa", "bily", "kessy" };
for (int i = 0; i < 5; i++) {
System.out.println(friends[i]); // friends[4]?
}
System.out.println("\nthis is the end");
}
//2.NullPointerException 空指针异常
public class NullRef {
int i = 1;
public static void main(String[] args) {
NullRef t = new NullRef();
t = null;
System.out.println(t.i);
}
}
//3.ArithmeticException 被除数为整型时不可为零
public class DivideZero {
int x;
public static void main(String[] args) {
int y;
DivideZero c=new DivideZero();
y=3/c.x;
System.out.println("program ends ok!");
}
}
//4.ClassCastException java强制类型转换异常
public class Order {
public static void main(String[] args) {
Object obj = new Date();
Order order;
order = (Order) obj;
System.out.println(order);
}
}
//5.待补充
异常处理机制
一:异常的处理
异常的处理:抓抛模型
- 过程一:“抛”— 程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出,一旦抛出对象后,其后的代码就不再执行。
- 过程二:“抓”— 可以理解为异常的处理方式
- try-catch-finally
- throws
二:try-catch-finally
/*
* 1.try{}:选定捕获异常的范围,将可能出现异常的代码放在try语句块中
* 2.catch(){}:对异常对象进行处理的代码
* :每个try语句块可以伴随一个或多个catch语句,用于处理产生的不同类型的对象
* 3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的try-catch结构(没有finally{}的情况下)
* 4.如果可以明确产生的是何种异常,可以用该异常作为catch的参数;也可以用其父类作为参数
* 5.catch中的异常类型如果满足字父类关系,要求子类一定声明在父类的上面
* 6.在try结构中声明的变量,再出了try结构以后,就不能在被调用
* 7.try-catch-finally 结构可以嵌套
*/
try{
... //可能产生异常的代码
}catch(ExceptionName1 e){
... //当产生ExceptionName1型异常时的处置措施
String message = e.getMessage(); //获取异常信息,返回字符串
e.printStackTrace; //获取异常类名和异常信息,以及异常出现在程序中的位置
}catch(ExceptionName2 e){
... //当产生ExceptionName2型异常时的处置措施
}[finally{
... //无论是否发生异常,都无条件执行的语句
//不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句是否有return,finally块中的语句都会被执行
//finally语句和catch语句是任选的
}]
三:throws
/*
* 1.如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
* 2.在方法声明中用throws语句可以声明抛出异常的列表
* 3.throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
* 4.重写方法(范围小)不能抛出比被重写方法范围更大的异常类型
*/
// 声明抛出异常类型示例
public static void main(String[] args) throws FileNotFoundException {
//读文件的操作可能产生FileNotFoundException类型的异常
FileInputStream inputStream = new FileInputStream("hello.txt");
}
/*
* 方法重写的规则之一
* 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
*/
// 父类
class SuperClass{
public void method() throws IOException{}
}
//子类
class SubClass extends SuperClass{
public void method() throws FileNotFoundException{}
}
//测试类
public class ExceptionTest {
public static void main(String[] args) {
/*
* 1.在java多态机制中,对象引用superClass在编译时期是属于父类类型,但是在运行时属于子类类型。
* 2.在编译的时候,编译器发现catch中的IOException完全能将父类方法中抛出的异常捕获,因此编译通过;
* 但是在运行时期,由于superClass变成了子类类型,子类重写的方法抛出的异常是Exception,
* 显然IOException不能捕获这个比它更大的异常,因此在运行时期也就出现失败
* */
SuperClass superClass = new SubClass();
try {
superClass.method();
} catch (Exception e) {//报错:编译失败
e.printStackTrace();
}
}
}
处理方式的选择
- 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
- 在执行的方法中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,建议:这几个方法使用throws的方式进行处理。而执行的方法可以考虑使用try-catch-finally方式进行处理。
手动抛出异常
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。
- 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。
IOException e = new IOException(); throw e;
- 可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:
throw new String("want to throw");
用户自定义异常类
- 一般地,用户自定义异常类都是RuntimeException/Exception的子类。
- 自定义异常类通常需要编写几个重载的构造器。
- 自定义异常需要提供serialVersionUID
- 自定义的异常通过throw抛出。
- 自定义异常最重要的是异常类的名字,当异常出现时,可以根据
名字判断异常类型。
//用户自定义异常类
class MyException extends RuntimeException{
static final long serialVersionUID = 13465653435L;
private int idnumber;
public MyException(String message,int id){
super(message);
this.idnumber = id;
}
public int getIdnumber() {
return idnumber;
}
}
//测试类
public class ExceptionTest {
public void regist(int num) throws MyException{
if (num >= 0) {
System.out.println("输出一个正数");
} else {
throw new MyException("输出的数不是一个正数",num);
}
}
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
ExceptionTest test = new ExceptionTest();
test.regist(num);
}
}
总结
异常处理中的五个关键字
- try:执行可能产生异常的代码
- catch:捕获异常
- finally:无论是否发生异常,代码总被执行
- throw:异常的生成阶段:手动抛出异常对象
- throws:异常的处理方式:声明方法可能要抛出的各种异常类