一、什么是异常?
异常时指程序在执行期间发生的事件,是导致程序中断运行的正常指令流。
举一个简单的例子:
public class ExceptionDemo01 {
public static void main(String[] args) {
int x=10;
int y=0 ;
int m=x/y;//除数为0出现异常
System.out.println("m="+m);
int n=x+y;
System.out.println("n="+n);
}
}
上面的程序在编译的时候没有任何错误,但是运行时就会产生异常中断的执行,并立即退出运行,后面的语句都不会再执行,由此可见,出现异常会改变原先的执行顺序,导致一系列的严重后果。异常的本质是在一个程序执行期间发生的一个事件,该事件中断了正常的执行流程。在Java发放内部发生错误时,该方法会创建一个Exception类型的对象返回给jvm,JVM会找一个合适的方法类来处理这个异常。
Java中所有的异常类都是继承自Throwable类,就是说所有的异常类都是Throwable的子类;Throwable类有两个子类,一个是Error,另一个是Exception类,他们是异常的两个分支。
Error描述的是Java运行系统中的内部错误以及资源耗尽情况下的错误,应用程序不能跑出这个错误。Exception类下面同样有两个分支,一个是运行时异常(非必检异常),一个是非运行是异常(必检异常)。
常见的几种异常:
1.1算术异常:
int x=10;
int y=0 ;
int m=x/y;//除数为0出现异常
1.2数组越界异常:
int arr[] ={1,2,3};
System.out.println(arr[3]);
1.3数组存储异常:
boolean b[]={false,false,false};
int arr[]={1,2,3};
System.out.println(arr,0,b,0,1);
1.4对象转化异常:
Object obj = new Object();
int in[]=(int [])obj;
1.5空指针异常:
String str = null;
System.out.println(str.length());
二、异常的两种处理机制(要么进行抛出throws,要么进行捕获try ...catch...finally)
2.1try...catch处理异常的格式
try {
//可能出现异常的语句
}catch(异常类型1 异常信息对象1){
//对异常 信息的一种处理
}catch(异常类型2 异常信息对象2){
//对异常信息的处理语句
}finally {
//一定会运行的语句
}
其实在一个try中如果有多个异常,可以采用多个catch来进行捕获异常。一般而言,try不能单独的存在,它一般可以和try...catch...finally, try...catch..以及try...finally组合try,catch,finally这三个关键字都不能单独的存在。
举个简单的数据越界的例子:
public class ExceptionDemo {
public static void main(String[] args) {
int arr[] = {1,2,3};
try {
System.out.println("arr[0]="+arr[0]);
System.out.println("arr[3]="+arr[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("异常信息是:"+e);
}finally {
System.out.println("无论是否有异常,都会执行finally语句");
}
}
}
运行结果截图:
一般大家都会认为finally里面是必须能够执行到的语句,但是有两点需要注意,finally语句是执行不到的
在finally语句执行之前执行了System.exit(0),退出运行,以及在执行finally之前所有的线程都死亡,则finally也不会再执行
public class ExceptionDemo {
public static void main(String[] args) {
int arr[] = {1,2,3};
try {
System.out.println("arr[0]="+arr[0]);
System.out.println("arr[3]="+arr[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.exit(0);//测试finally是否会依然执行
System.out.println("异常信息是:"+e);
}finally {
System.out.println("无论是否有异常,都会执行finally语句");
}
}
}
运行结果:
可以看出finally里面的语句不会再执行。
当有多个catch的时候就会出现一个问题,catch里面所捕获的异常的范围大小的顺序,我们知道Exception的范围是最大的,那么多个catch里面的异常顺序该如何来进行排列呢,下面让我们来看一个实例:
import java.util.Scanner;
public class ExceptionDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int arr[] = new int[5];
try {
System.out.println("请输入数组元素:");
for(int i=0;i<arr.length;i++) {
arr[i] = scan.nextInt();
}
System.out.println("请输入需要取出的数组的下标:");
int n = scan.nextInt();
System.out.println("arr["+n+"]="+arr[n]);
int m = arr[3]/0;
System.out.println("m="+m);
} catch (ArrayIndexOutOfBoundsException e1) {
e1.printStackTrace();
System.out.println("数组越界异常信息:");
}catch (IndexOutOfBoundsException e2) {
e2.printStackTrace();
System.out.println("越界异常信息:");
}catch (Exception e) {
e.printStackTrace();
System.out.println("Exception异常信息");
}
System.out.println("***程序结束***");
}
}
对于有多个异常信息需要去捕获,那么就应该去在catch中对需要进行捕获的异常信息进行排序,范围小的在前面,范围大的在后面。
注意:在重写方法时遇到了异常,应该注意:
重写方法异常时,可以抛出与被重写方法相同的异常或者被重写方法异常的子类异常。
如果父类的方法没有声明抛出异常,那么子类重写方法不可以声明抛出检查异常,但是可以抛出不检查异常。
如果父类的方法声明抛出异常,包括自定义的异常,那么子类覆盖方法可以不声明抛出异常。但是,如果子类重写方法要抛出异常的话,只能抛出与被重写方法相同的异常或者被重写方法异常的子类,不能是其他检查异常。
三、throws声明异常
如果一个方法中抛出的异常没有在catch中找到合适的进行异常处理的程序,Java就会退出该方法。如果不想捕获,就可以进行抛出。下列代码中,主方法main()仅仅使用throws关键字,,没有使用try...catch语句。所以方法拥有的异常都会一直向上抛,抛给主方法,而主方法依然不做任何处理,那么此时就会抛给JVM,可是JVM就会调用默认异常处理程序处理,即中断程序的执行并退出。
public class ExceptionDemo02 {
public static void arrException() throws FileNotFoundException {
FileInputStream file = new FileInputStream("不存在的文档.txt");
}
public static void main(String[] args) throws Exception {
arrException();
}
}
另外,java还允许开发人员自定义异常类,自定义的异常类至少应继承Throwable类,才能够作为异常类,一般情况下,多是继承Exception类或其子类。