异常
异常:指程序在运行过程中发生在正常情况以外的事件,如用户输入错误、除数为0、需要的文件不存在、文件不能打开、数组下标越界、类型不匹配等。
异常处理:指对程序运行过程中产生的异常情况进行恰当处理的技术。
作用:提供了完善的面向对象异常处理机制(Exception Handing),当不可预期的异常发生时,异常处理机制会尝试恢复异常发生前的状态或对这些结果做一些善后处理,以保证程序能正常结束而不崩溃。通过异常处理机制,可以预防错误的程序代码或系统错误造成的不可预期的后果。
【程序案例1】数组下标越界异常处理
package chapter10;
public class Demo1001{
public static void main(String[] args){
float a [] = {4,3,7,8,9},x;
int i = 10;
try{
x = a[i];
}catch(Exception e) {
System.out.println("数组下标越界!");
e.printStackTrace();
}
}
}
其中,第6行使Java处理异常的代码,第7行是可能产生异常的代码,第8~11行是出现异常后的处理措施。显然,程序正常代码(第7行)与异常处理代码(第8-11行)存在明显区别。
程序运行结果如图所示
异常处理机制
异常处理方式
Java语言有两种处理异常方式:①利用try…catch…finally语句处理异常,优点是分开了处理异常代码与程序正常代码,增强了程序可读性,减少中途终止程序运行可能带来的危害;②由Java异常处理机制预设方式处理,一旦程序发生异常,停止执行程序并显示一些错误信息给用户。如果没有处理异常措施,系统可能崩溃。
异常类结构
Java异常处理机制中,类java.lang.Throwable 是所有异常类的祖先类,Throwable类用两个直接子类。①Error子类,它包含Java系统或执行环境中发生的异常,用户无法捕捉它们,如CPU运行错误、内存溢出、I/O错误等;②Exception子类,它包含一般性的异常,如数组下标越界异常、除数为0异常、类型转换异常、I/O异常、SQL异常等,用户能用try…catch…finally语句捕捉它们,也能定义这些异常类的子类创建自己的异常处理。如果显示Java语言的异常类结构。
Java异常类型(Excpetion子类)分为受检查异常和不受检查异常。
(1)Error子类代表Java系统内部错误,如ThreadDeath类、IOError类等,Java系统不能处理这些异常,由虚拟机直接抛出。这些异常属于不受检查异常。
(2)RuntimeException子类代表运行时异常,是程序运行过程中产生的错误,程序员通过调试修复它们。例如,通过检查数组下标和数组边界避免数组越界异常、类型转换前通过instanceof运算符进行类型检查等。这些异常也属于不受检查异常。
(3)其他子类如IOException、SQLExceptio、FileNotFoundException,它们的特点是程序运行前Java编译器会检查它们,如果程序中可能出现这类异常,程序员需要用异常处理语句try…catch…finally捕获,或者用throws子句抛出,否则不能通过编译。
ArrayIndexOutOfBoundsException、数字化格式异常NumberFormatException、算术异常ArithmeticException等是不受检查异常,这些异常需要程序员啊主动排除;数据库访问异常SQLException、没有发现类异常ClassNotFoundException等是受检查异常,这些异常需要程序员主动捕获处理。
try…catch…finally语句
Java处理异常的利器是try…catch…finally语句,该语句主要功能是能明确捕捉某种类型的异常,并按要求进行处理,发挥了异常处理机制的最佳优势。
如下图显示try…catch…finally语句的执行流程,如果try语句块的代码产生异常,JVM抛出该异常对象,并在catch语句中寻找匹配的异常类型,如果有匹配的类型则执行对应的catch语句块,然后执行finally语句块,否则直接执行finally语句块。finally是异常处理的统一出口,不管程序是否异常,一定会执行finally语句块。如果省略finally,执行catch语句块后程序跳转到try…catch语句后继续执行。
package chapter10;
public class Demo1002{
public static void main(String[] args){
int[] a = {1,2,3,4};
int index = 10;
System.out.println("====程序开始====");
try{
System.out.println("第"+index+"个位置元素是"+a[index]);
System.out.println("正在执行try语句");
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("执行catch语句");
e.printStackTrace();
}finally{
System.out.println("====程序结束====");
}
}
}
该程序案例演示了try…catch…finally处理数组下标越界异常。包含了try…catch…finally语句三要素。数组下标越界异常ArrayIndexOutOfBoundsException是不受检查异常,程序员通过调试能改正该错误。
程序第7~15行使用try…catch…finally语句处理第8行可能产生的错误。执行第8行时,JVM抛出数组下标越界异常ArrayIndexOutOfBoundsException对象,第10行的catch语句匹配该异常类型,执行该catch语句块。不管try语句是否抛出异常,总会执行第14行finally块的代码。
程序运行结果如图所示
该程序案例结果显示,虽然第8行出现错误,但仍然保持了从程序开始到程序结束的完整过程。利用Java异常处理机制,能保证程序在出现异常情况下仍然能完成其他处理,程序没有崩溃。
Java异常处理机制能同时处理多个异常。
throws关键字
Java程序运行时频繁发生方法调用,形成方法调用栈。如果方法抛出了异常对象,有两种解决方式:①使用try…catch…finally语句;②把该异常对象上交给调用该方法的上级方法。
在Java程序中,如果不知道在方法中如何处理出现的异常对象,可在定义该方法时使用throws抛出该异常,把该异常提交给上级调用者处理。
throws能抛出方法可能产生的多个异常。方法之间的调用以及throws抛出异常,使异常的传播形成反向异常传播链。如果在抛出异常的方法中没有完全捕获异常(异常没被捕获,或异常被处理后重新抛出了新异常),那么异常将从发生异常的方法逐渐向外传播,首先传给该方法的调用者,该方法调用者再次传给其调用者…直至main方法,如果main方法依然没有处理该异常,JVM抛出该异常,打印异常跟踪栈信息,终止程序。
throw语句及自定义异常
throw语句
程序产生异常对象时,JVM自动抛出它们。除此之外,为了提高异常处理的灵活新,Java允许程序员自行抛出异常对象。
throw语句能明确抛出程序员主动创建的异常对象。
throw语句
throw new 异常类(参数); //抛出异常对象
下面代码块,程序没有产生任何异常,第6行throw语句抛出程序员主动创建的RuntimeException异常对象,第7行捕获第6行抛出的异常对象。
public static void show(){
int[] arr = {4,7,2,6};
int x;
try{
x = arr[2];
throw new RuntimeException("数组异常!"); //抛出创建的异常对象
}catch(RuntimeException e){ //捕获抛出的异常对象
System.out.println("捕获了创建的RuntimeException异常对象");
}finally{
}
}
如果throw仅能抛出Java提供的标准异常对象,则失去了throw的价值。throw的主要作用是抛出程序员自定义的异常对象。
自定义异常
从继承关系来说,自定义异常类可以是任何标准异常类的子类,如果自定义异常类继承标准异常类,那么使用自定义异常的程序员因无法直观分辨异常类型而感到莫名其妙。
标准异常类都是Exception类或RuntimeException类的子类,自定义异常类一般继承Exception类或RuntimeException类。如果自定义异常类继承了Exception类,该自定义异常类就是受检查异常;如果自定义异常类继承了RuntimeException类,该自定义异常类就是不受检查异常。
自定义异常的语法格式
class 异常类名 extends Exception (或 RuntimeException){ //自定义异常类
public 异常类名() { } //空构造方法
public 异常类名(String msg){ //带参数的构造方法
super(msg); //调用父类构造方法
}
}
习题
题目:高速公路某路段设有车速检测雷达,如果速度≥120km/h或者速度<60km/h,显示屏给出提示信息,具体要求如下。
(1)自定义速度异常类SpeedException抛出速度异常提示信息。
(2)定义雷达类Radar。该类中公共静态方法measureSpeed(double speed),如果速度≥120km/h或者速度<60km/h,该方法抛出SpeedException异常。
(3)在测试类Client中,随机产生若干50~150km/h的速度,调用measureSpeed(double speed)方法测试是否超速。一种运行结果如图所示。
由于能力有限,只能写出如下代码
class SpeedExcpetion extends Exception{
public SpeedExcpetion(){ }
public SpeedExcpetion(String msg){
super(msg);
}
}
class Radar{
public static double measureSpeed;
public static void overSpeed() throws SpeedExcpetion{
if (measureSpeed >= 120)
throw new SpeedExcpetion("限速120,你已经超速了!");
else if (measureSpeed < 60) {
throw new SpeedExcpetion("最低时速60,你要加速了!");
}else
System.out.println("时速正常!请保持!");
}
}
public class Client{
public static void main(String[] args) {
double min = 50;
double max = 150;
double a =(double) min + (Math.random()* (max - min ));
DecimalFormat fourteen = new DecimalFormat("#.00000000000000");
String str = fourteen.format(a);
Radar.measureSpeed = a;
try{
Radar.overSpeed();
}catch(SpeedExcpetion e){
System.out.println("速度是:"+Radar.measureSpeed);
System.out.println(e.getMessage());
}finally {
System.out.println("检查结束,谢谢配合,一路平安!");
}
}
}
运行结果大致有三种
如果代码有需要完善的地方,欢迎大家提供回答!!