目录
一、异常概述
- 异常:在Java语言中,将程序执行中发生的不正常情况称为异常,(开发过程中的语法错误和逻辑错误不是异常)
- Java的异常是
class
- Java程序在执行过程中所发生的异常事件可分为两类:
Error
:Java虚拟机无法解决的严重问题,如JVM系统内部错误、资源耗尽等严重情况,如:- 栈溢出
java.lang.StackOverflowError
- 堆溢出
java.lang.OutOfMemoryError
- 栈溢出
Exception
: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理,如:- 空指针访问
- 试图读取不存在的文件
- 网络连接中断
- 数组角标越界
- 两种解决办法:
- 遇到错误就终止程序的运行
- 由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理
二、异常体系结构
Exception
中:- 蓝色:非受检(unchecked)异常 、运行时异常
- 红色:受检(checked)异常、编译时异常
2.1 编译时异常 \ 运行时异常
- 编译时异常:执行javac.exe命名时,出现的异常
- 编译器要求必须处置的异常
- 即程序在运行时由于外界因素造成的一般性异常
- 编译器要求Java程序必须捕获或声明所有编译时异常
- 运行时异常:执行java.exe命名时,出现的异常
- 编译器不要求强制处置的异常
- 一般是指编程时的逻辑错误,是程序 员应该积极避免其出现的异常
- 对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对 程序的可读性和运行效率产生影响
2.2 常见异常类型
2.2.1 运行时异常
-
NullPointerException
空指针异常int[] arr = null; System.out.println(arr[3]);
String str = "abc"; str = null; System.out.println(str.charAt(0));
-
IndexOutOfBoundsException
下标越界异常
ArrayIndexOutOfBoundsException
数组下标越界异常int[] arr = new int[10]; System.out.println(arr[10]);
StringIndexOutOfBoundsException
字符串下标越界异常String str = "abc"; System.out.println(str.charAt(3));
-
ClassCastException
类型转换异常Object obj = new Date(); String str = (String)obj;
-
NumberFormatException
数值类型的格式异常String str = "123"; str = "abc"; int num = Integer.parseInt(str);
-
InputMismatchException
输入不匹配异常Scanner scanner = new Scanner(System.in); int score = scanner.nextInt(); System.out.println(score); scanner.close();
-
ArithmeticException
算数异常int a = 10; int b = 0; System.out.println(a / b);
2.2.2 编译时异常
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file); // FileNotFoundException
int data = fis.read(); // IOException
while(data != -1){
System.out.print((char)data);
data = fis.read(); // IOException
}
fis.close(); // IOException
三、异常的处理
3.1 异常处理机制 \ 抓抛模型
- Java异常处理机制:将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护
- Java异常处理的抓抛模型:
- 抛出
throw
异常:Java程序的执行过程中如出现异常,会生成一个异常类对象, 该异常对象将被提交给Java运行时系统- 一旦抛出对象以后,其后的代码就不再执行
- 异常对象的生成:
- 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当 前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并自动抛出
- 由开发人员手动创建:如
Exception exception = new ClassCastException();
创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样
- 捕获
catch
异常:如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处 理,如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法,这个过程将一直继续下去,直到异常被处理
- 如果一个异常回到
main()
方法,并且main()
也不处理,则程序运行终止 - 程序员通常只能处理
Exception
,而对Error
无能为力 - 异常的处理方式:
try-catch-finally
throws
- 如果一个异常回到
- 抛出
3.2 try-catch-finally
try{
可能产生异常的代码;
}
catch( ExceptionName1 e ){
当产生ExceptionName1型异常时的处置措施;
}
catch( ExceptionName2 e ){
当产生ExceptionName2型异常时的处置措施;
}
[ finally{
无论是否发生异常,都无条件执行的语句;
} ]
- 说明:
- 快速生成
try-catch
finally
:finally
是可选的- 通过
finally
语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理 - 不论在
try
代码块中是否发生了异常事件,catch
语句是否执行,catch
语句是否有异常,try
和catch
语句中是否有return
,finally
块中的语句都会被执行 - 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放,此时的资源释放,就需要声明在finally中
- 使用
try
将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch
中进行匹配 - 一旦try中的异常对象匹配到某一个
catch
时,就进入catch
中进行异常的处理,一旦处理完成,就跳出当前的try-catch
结构
catch
中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面,否则报错- 常用的异常对象处理的方式:
String getMessage();
获取异常信息,返回字符串printStackTrace();
获取异常类名和异常信息,以及异常出现在程序中的位置,返回值void
try
结构中声明的变量,出了try
结构以后不能再被调用try-catch-finally
结构可以嵌套
- 快速生成
- 如何看待代码中的编译时异常和运行时异常:
- 使用
try-catch-finally
处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错,相当于我们使用try-catch-finally
将一个编译时可能出现的异常,延迟到运行时出现 - 开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写
try-catch-finally
了;针对于编译时异常,一定要考虑异常的处理
- 使用
3.3 throws
-
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这 种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,由该方法的调用者负责处理
-
throws + 异常类型
写在方法的声明处,指明此方法执行时,可能会抛出的异常类型 -
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足
throws
后异常类型时,就会被抛出。异常代码后续的代码,就不再执行! -
throws后面的异常类型可以是方法中产生的异常类型,也可以是该异常类型的父类,
method2()
向上throws IOException
即可,因为是FileNotFoundException
的父类
-
重写方法声明抛出异常的原则:
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
- 在多态的情况下,其他方法中对
methodA()
方法的调用时,异常的捕获按父类声明的异常IOException
处理 - 父类中被重写的方法没
throws
方式处理异常,则子类重写的方法也不能使用throws
public class A { public void methodA() throws IOException { …… } } public class B1 extends A { public void methodA() throws FileNotFoundException { …… } } public class B2 extends A { public void methodA() throws Exception { // 报错! …… } }
3.4 对比两种处理方式
try-catch-finally
:真正的将异常给处理掉了throws
的方式只是将异常抛给了方法的调用者,并没真正将异常处理掉- 如何选择两种处理方式:
- 如果父类中被重写的方法没
throws
方式处理异常,则子类重写的方法也不能使用throws
,意味着如果子类重写的方法中异常,必须使用try-catch-finally
方式处理 - 执行的
方法a
中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws
的方式进行处理,而执行的方法a
可以考虑使用try-catch-finally
方式进行处理
- 如果父类中被重写的方法没
四、手动抛出异常
throw
和throws
区别:throw
表示抛出一个异常类的对象,生成异常对象的过程,声明在方法体内throws
属于异常处理的一种方式,声明在方法的声明处
- 可以抛出的异常必须是
Throwable
或其子类的实例
五、用户自定义异常类
如何自定义异常类:
- 继承于现的异常结构:
RuntimeException
、Exception
- 提供全局常量:
serialVersionUID
- 通常需要编写几个重载的构造器
- 异常类的名字设置要见名知意,可以根据名字判断异常类型
- 还可以添加方法
getID()
,返回错误位置