第七章 异常处理
7.1 异常概述与异常体系结构
7.1.1 异常的简述
在使用计算机语言进行项目开发的过程中,即使程序员把代码写的尽善尽美 ,在系统的运行过程中仍然会出现一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持畅通等等。
异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
7.1.2 异常事件的分类
Java程序在执行过程中所发生的异常事件可为两类:
①.Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误,资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。
②.Exception:其它因编程错误或偶然的外界因素导致的一般性问题,可以使用针对性的代码进行处理。例如:空指针访问,试图读取不存在的文件,网络连接中断,数组角标越界。
7.1.3 异常事件的解决办法:
对于这些错误,一般有两种解决办法:一是遇到错误就终止程序的运行。另一种方法是由程序员在编写程序时,就考虑到错误的检测,错误信息的提示,以及错误的处理。
捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。比如:除数为0,数组下标越界等
7.1.4 异常体系结构
分类:编译时异常和运行时异常
7.1.5 常见异常
算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
异常情况
public class Demo2 {
public static void main(String[] args) {
/* 抛出数组下标越界异常
Exception in thread "main"
java.lang.ArrayIndexOutOfBoundsException:
Index 5 out of bounds for length 5*/
int[] arr = new int[]{1, 2, 3, 4, 5};
System.out.println(arr[5]);
/* 抛出算术异常
Exception in thread "main"
java.lang.ArithmeticException: / by zero
*/
System.out.println(2/0);
}
}
7.2 异常处理机制一:try-catch-finally
7.2.1 异常处理机制的简述
在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长,臃肿,可读性差。因此采用异常处理机制。
Java异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使程序简洁,优雅,并易于维护。
7.2.2 Java异常处理的抓抛模型
Java程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
过程一:“抛”: 异常对象的生成
虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常的实例对象并抛出——自动抛出
开发人员手动创建:Exception exception = new ClassCastException( );创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样。
过程二:“抓”:可以理解成异常的处理方式:①.try-catch-finally ②.throws
7.2.3 try-catch-finally 异常处理机制的使用
//try-catch-finally的异常处理格式:
try {
//可能出现的异常
} catch (异常类型1 变量名1) {
//处理异常方式1
}catch (异常类型1 变量名1) {
//处理异常方式1
}catch (异常类型1 变量名1) {
//处理异常方式1
}
....
finally{
//一定会执行的代码
}
①.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
②.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前try-catch结构(在没有finally的情况),继续执行其后的代码。
③.catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓;catch中的异常处理类型如果满足子父类关系,则要求子类一定声明在父类的上面,否则,报错。
④.常用的异常类对象处理的方式:String getMessage( )/printStackTrace( )
⑤.在try结构中声明的变量,再出了try结构以后,就不能再被调用了
使用try-catch-finally处理编译时异常,程序不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
7.2.4 finally的使用
finally是可选的;finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况;像数据库连接,输入输出流,网络编程Socket等资源,JVM是不可能自动的回收的,我们需要自己动手的进行资源的释放。此时的资源释放,就需要声明在finally中。
public class Demo2 {
public static void main(String[] args) {
try {
int[] arr = new int[]{1, 2, 3, 4, 5};
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e);
} finally {
System.out.println("结束");
}
/* 运行结果
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
结束
*/
}
}
7.3 异常处理机制二:throws
7.3.1 throws异常处理机制的简述
“throws + 异常类型”写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会抛出。异常代码后续的代码,就不再执行!
try-catch-finally:真正的将异常给处理了;throws的方式只是将异常抛给了方法的调用者,并没有真正地将异常处理掉。
public class Demo2 {
public static void main(String[] args) {
s();
}
static void s() throws ArrayIndexOutOfBoundsException {
int[] arr = new int[]{1, 2, 3, 4, 5};
System.out.println(arr[5]);
}
}
7.3.2 throws异常处理机制中方法重写的规则
子类重写的方法抛出的异常类型不能大于父类被重写的方法抛出的异常
7.3.3 开发中如何选择使用try-catch-finally 还是throws?
①.如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法里有异常,必须使用try-catch-finally方式处理。
②.执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑try-catch-finally方式进行处理。
7.4 手动抛出异常:throw
7.4.1 throw的使用简述
在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。
①.创建一个异常对象。封装一些提示信息(信息可以自己编写)。
②.需要将这个异常对象告知给调用者。throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
使用格式:
throw new 异常类名(参数);
7.4.2 throw的使用实例
public class Demo2 {
public static void main(String[] args) throws Exception {
play(-5);
eat(5);
}
//手动抛出异常:方式一 运行时抛出
public static void play(int a) {
int[] arr = new int[]{1, 2, 3, 4, 5};
if (a>0){
System.out.println(arr[a]);
}else {
throw new RuntimeException("数据大小范围异常");
}
}
//手动抛出异常:方式二 编译时抛出
public static void eat(int a) throws Exception {
int[] arr = new int[]{1, 2, 3, 4, 5};
if (a<4){
System.out.println(arr[a]);
}else {
throw new Exception("角标越界");
}
}
}
7.5 用户自定义异常
7.5.1 用户自定义异常的简述
一般情况下,我们使用系统内部提供的异常就足够了,但是有时为了特殊的目的,必须使用用户自定义异常。
7.5.2 自定义异常类的方法
①.继承于现有的异常结构:RuntimeException,Exception
②提供全局常量:serialVersionUID
③.提供重载构造器
//调用抛出异常类
public class Demo2 {
public static void main(String[] args) throws MyException {
play(-5);
}
public static void play(int a) throws MyException {
int[] arr = new int[]{1, 2, 3, 4, 5};
if (a>0){
System.out.println(arr[a]);
}else {
throw new MyException("数据大小范围异常");
}
}
}
//自定义抛出异常类 —— MyException
class MyException extends Exception{
static final long serialVersionUID = -3377516993124229948L;
public MyException() {
}
public MyException(String message) {
super("异常信息:"+message);
}
}
7.5.3 自定义异常类的实例练习
输出结果
import java.util.Scanner;
public class Demo2 {
public static void main(String[] args) throws EcDef {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入被除数:");
int a = scanner.nextInt();
System.out.print("请输入除数:");
int b = scanner.nextInt();
if (a < 0 | b < 0 ) {
throw new EcDef("除数或被除数不能为负数");
} else {
//try-catch-finally 异常机制
try {
String ecm = ecm(a, b);
System.out.println(ecm);
} catch (ArithmeticException msg) {
System.out.println("除数不能为0");
} finally {
System.out.println("期待下次使用!");
}
}
}
public static String ecm(int a, int b) {
return a + "÷" + b + "=" + (a / b);
}
}
//自定义 EcDef 输入负数异常类 :
class EcDef extends Exception {
static final long serialVersionUID = -3377516993124229948L;
public EcDef() {
}
public EcDef(String message) {
super("异常信息:" + message);
}
}