异常介绍
Java程序员在项目开发中,不可能将代码写得尽善尽美,即使写的尽善尽美也难以避免一些问题,如输入数据类型或者格式不匹配,读取的文件不存在,客户端与服务器通信网络堵塞等,这些问题不是代码所产生的。将这些不正常的现象统称为异常。
异常类的继承结构
所有的异常都是由Throwable类继承而来,Throwable下边有两个子类分别是Error和Exception。
- Error类:描述Java运行时内部发生错误或资源耗尽等问题,此时程序不抛出异常,不进行处理。
Error举例:
public class Test {
public static void main(String[] args) {
main(args);
}
}
运行结果:
- Exception类:
- RuntimeException类:运行时异常。
- IOException类:I/O错误导致的异常
Exception举例
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
System.out.println("输入:");
Scanner input = new Scanner(System.in);
int num = 0;
num = input.nextInt();
System.out.println(num);
}
}
运行结果:
受查异常(编译时异常)与非受查异常(运行时异常)
- 非受查异常:RuntimeException及其直接子类。不强制要求用户进行处理。当发生该类异常时,若没有显示处理,则会一直向上抛,最终结果是线程中止(线程抛出),或者程序终止(main方法抛出)。
- 受查异常:除非受查异常之外的异常。要求用户进行强制处理,不处理编译不能通过。
常见的运行时异常
- 数组下标越界异常:ArrayIndexOutOfBoundsException(左右两端都存在越界异常问题)
- 算术异常:ArithmeticException(如分母为0时)
- 类型转换异常:ClassCastException(未发生向上转型前的向上转型)
- 空指针异常:.NullPointerException
- 数字格式转换异常:NumberFormatException。(非数字类型的i字符串转换为数字格式)
- 并发修改异常 ConcurrentModifyException
异常的影响
异常是导致程序中断执行的一种指令流,程序之中如果出现异常且没有合理的处理的话,就会导致程序终止运行。即异常语句之后的程序不再执行。
正常举例:
package www.java.util;
public class Test {
public static void main(String[] args) {
System.out.println("1.计算开始:");
System.out.println("2.计算中" + (10 / 2));
System.out.println("3.计算结束");
}
}
运行结果:
异常举例:
public class Test {
public static void main(String[] args) {
System.out.println("1.计算开始:");
System.out.println("2.计算中" + (10 / 0));
System.out.println("3.计算结束");
}
}
运行结果:
可以看到发生异常之后 ,异常之后的语句是不再执行的,异常之前的语句可以正常执行,为了保证出现异常之后的程序还可以正常执行,有必要进行异常处理。
异常的处理
方式一:抓(可以直接解决)
try{
可能出现异常的语句
}catch(异常类 对象){
处理方式1
} catch(异常类 对象){
处理方式2
}finally{
异常出口,一定执行的代码
}
对于try,catch,finally,这三个关键字组合方式有这么几种:try…catch , try…finally,try…catch…finally。其中每一种组合内的catch都可以有多个。
异常处理举例:
public class Test {
public static void main(String[] args) {
System.out.println("1.计算前:");
try{
//可能出现异常的语句
System.out.println("2.计算中:" + (10/0));
}catch(ArithmeticException e){
System.out.println("异常处理");
}
System.out.println("3.计算结束");
}
}
运行结果:
经过异常的处理之后,异常语句之后语句仍然可以正常进行。为了能清楚的获取异常信息,可以直接调用异常类中的printSTacktrace
(),getMesseg()
方法.
打印错误堆栈信息:
public class Test {
public static void main(String[] args) {
System.out.println("1.计算前:");
try{
//可能出现异常的语句
System.out.println("2.计算中:" + (10/0));
}catch(ArithmeticException e){
e.printStackTrace();
}
System.out.println("3.计算结束");
}
}
输出结果:
方式二:抛(不能直接解决或者不想解决,抛给调用者)
一个方法中的某些语句可能存在异常,该方法不确定如何处理该异常,因此将这种异常显示的抛出,抛给调用该方法的方法,最高至Java虚拟机。由调用者负责进行处理该异常。
throw关键字
throw用在方法内部,表示人为进行异常类对象的抛出。经常搭配 if … else等条件控制语句使用。
throw用在方法中:
public class Test {
public static void main(String[] args) {
String id = "0006";
if (id.length() == 4) {
System.out.println("正确");
} else {
try {
throw new IllegalAccessException("长度不符合"); //手动抛出异常
}catch (IllegalAccessException e){ //自抛自接,异常处理
e.printStackTrace();
}
}
}
}
throws关键字
throws关键字用在方法声明出,明确告诉调用者本方法可能产生异常,同时我并没有对该异常进行处理,该异常由调用者进行处理,调用者无法处理时也可以继续抛出,由更高级的调用者进行处理。
throws举例:
package www.java.util;;
public class Test {
public static void main(String[] args)throws NullPointerException,ArrayIndexOutOfBoundsException {
int[] arr = new int[0];
System.out.println(getElement(arr));
}
public static int getElement(int[] arr) throws NullPointerException,ArrayIndexOutOfBoundsException{
if(arr == null){
throw new NullPointerException("空引用!!!");
}
if(arr.length < 4){
throw new ArrayIndexOutOfBoundsException("数组元素个数过少!!!");
}
return arr[3];
}
}
运行结果:
多catch处理
当异常方法抛出多个异常时,为了捕获每一个抛出的异常就需要多个catch进行处理。
package www.java.util;;
public class Test {
public static void main(String[] args){
int[] arr = new int[0];
try {
System.out.println(getElement(arr));
}catch (NullPointerException e){
System.out.println(e.toString()); //调用toString方法
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}
System.out.println("异常处理完毕");
}
public static int getElement(int[] arr) throws NullPointerException,ArrayIndexOutOfBoundsException {
if (arr == null) {
throw new NullPointerException("空引用!!!");
}
if (arr.length < 4) {
throw new ArrayIndexOutOfBoundsException("数组元素个数过少!!!");
}
return arr[3];
}
}
多catch处理细节
当抛出的多个异常类对象之间为并列关系的时候,无论先捕获哪个异常类对象都无所谓,但当多个异常类之间有继承关系时,则必须先捕获子类的异常类对象,因为子类的异常类对象可以通过向上转型自动被父类所接受,后面的catch语句将不会再执行。
package www.java.util;;
public class Test {
public static void main(String[] args){
int[] arr = new int[0];
try {
System.out.println(getElement(arr));
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e); //默认调用toString方法
}catch (Exception e){
System.out.println(e);
}
System.out.println("异常处理完毕");
}
public static int getElement(int[] arr) throws Exception,ArrayIndexOutOfBoundsException {
if (arr == null) {
throw new Exception("空引用!!!");
}
if (arr.length < 4) {
throw new ArrayIndexOutOfBoundsException("数组元素个数过少!!!");
}
return arr[3];
}
}
运行结果:
注:两个catch块不能调换顺序。
总结: 对异常类对象不断进行抛出,不进行处理,发生异常之后的语句是不会再执行的,最终由jvm虚拟机终止程序的运行,但通过try…catch…finally处理过的异常,异常后的语句会正常执行。运行时异常发生时,不建议使用try…catch进行异常处理,比如数组问题,函数参数部分传进来null后面的其他语句没有再执行的意义,应该让程序立即结束,由程序员修改代码。
方法重写时的异常处理
- 父类的方法抛异常,子类重写后的方法可以抛出异常,也可以不抛出异常,但子类要抛出异常只能是父类的异常及其子类。
- 父类方法不抛出异常,子类重写以后也不能抛出异常(编译时异常)。
- 若子类调用了有异常的方法,若父类没有抛出异常,子类只能使用try…catch进行异常处理,不能进行异常抛出。
- (接口也是如此)