推荐学习专栏:Java 编程进阶之路【从入门到精通】,从入门到就业精通,买不了吃亏,买不了上当!!
异常就是不正常的意思,是程序执行过程中出现的非正常情况,最终导致 JVM 非正常终止。异常是 Java 中提供的一种识别及响应错误情况的一致性机制。有效地异常处理能使程序更加健壮、易于调试。
在Java语言中,将程序执行中发生的不正常情况称为异常 , 开发过程中的语法错误和逻辑错误不是异常,此时无法通过编译,程序无法运行,异常是指程序运行过程中的问题。
示例:
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 0;
int c = a / b;
System.out.println(a + "/" + b + "=" + c);
}
}
当程序运行到int c = a / b
时,JVM 会 new 一个异常对象:
new ArithmeticException("/ by zero");
并且 JVM 将异常对象抛出,打印输出信息到控制台。
Java 中异常发生的原因有很多,例如:
Java 异常其实是为了帮助我们找到程序中的问题,其本质其实是一个类,产生异常就是创建异常对象并抛出这个异常对象。
异常的根类是 Java.lang.Trowable
,是所有异常和错误是超类,其有两个子类,分别是 Error 类和 Exception 类。平时我们常说的异常主要是这里的 Exception 类,这里也主要是对该类就行讲解。
Exception 类是指编译期异常类,即编译Java代码即写代码期出现的问题,其子类 RutimeException 类称为运行期异常,指 Java 运行中出现的问题,只要我们处理掉异常,程序就可以继续执行。Error 类是程序无法处理的错误,表示运行应用程序中较严重问题。
Java异常分为可查异常和不可查异常。正确的程序在运行过程中,很容易出现情理可容的异常状况。可查异常虽然是异常状况,但在一定程度上它的发生是可以预计的,而且一旦发生这种异常状况,就必须采取某种方式进行处理。
除了 RuntimeException 及其子类以外,其他的Exception类及其子类都属于可查异常,最典型的是IO类异常。
RuntimeException 与其子类和错误 Error 属于不可查异常,编译器不要求强制处置的异常。
Error 是指系统内部的错误,这类错误由系统进行处理,程序本身无需捕获处理。比如:内存溢出错误OOM,堆栈溢出错误StackOverflowError等,一般发生这种情况,JVM会选择终止程序。此时必须修改代码,程序才能执行。
示例:
//堆栈溢出错误
public class Test {
public static void recursionMethod() {
recursionMethod();// 无限递归下去
}
public static void main(String[] args) {
recursionMethod();
}
}
报错信息:
Exception in thread "main" java.lang.StackOverflowError
at Test.recursionMethod(Test.java:4)
at Test.recursionMethod(Test.java:4)
at Test.recursionMethod(Test.java:4)
at Test.recursionMethod(Test.java:4)
...
通过了解异常产生的过程我们可以深入学习如何处理异常,通过下面的例子来学习异常产生的过程,定义一个数组,定义一个获取数组指定索引位置元素的值,此时程序就有可能产生数组索引越界的异常:
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
int b=getElement(arr,3);
System.out.println(b);
}
public static int getElement(int[] arr,int index){
int a=arr[index];
return a;
}
}
异常信息:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at Test.getElement(Test.java:8)
at Test.main(Test.java:4)
程序在执行 getElement() 方法时,传入了 arr 和3两个参数,目的是访问数组 arr 索引为3的元素的值,但是由于数组 arr 的长度为3,此时 JVM就会检测到程序出现了异常,JVM执行一下操作:
第一步: JVM根据异常的原因创建了一个异常的对象,包含了异常产生的内容,原因和位置。
new ArrayIndexOutOfBoundsException("3");
第二步:因为 getElement() 方法中没有异常处理的逻辑(try…catch),所以 JVM 把异常对象抛给了该方法的调用者 main 方法来处理这个异常。
当 main 方法接收到这个异常对象后,由于它也没有异常处理的逻辑,所以继续把异常对象抛给了 main 方法的调用者 JVM 来处理。当 JVM 接收到这个异常对象后,首先把异常对象的内容输出打印在控制台,其次采用中断处理,结束这个 Java 程序。
我们可以使用 throw 在指定的方法中抛出异常,使用时必须写在方法的内部,且 throw 后创建的对象必须是 Exception 类的对象或者是其子类的对象。
throw 抛出异常后,我们必须处理这个异常,如果 throw 后创建的是 RuntimeException 类或者其子类的对象,我们可以不处理,默认交给 JVM 处理,即打印异常,中断程序。如果创建的是编译时(写代码时)异常对象,此时必须使用 throws 或者 try…catch 来处理异常。
示例:在定义一个方法时,我们要先对方法传递的参数进行合法性校验,如果传递的参数不合法,我们就要使用抛出异常的方法告知方法的调用者参数不合法。
public class Test {
public static void main(String[] args) {
int[] arr=null;
int b=getElement(arr,3);
System.out.println(b);
}
public static int getElement(int[] arr,int index){
if(arr==null) {
throw new NullPointerException("传递的数组为空");
}
if(index<0||index>=arr.length){
throw new ArrayIndexOutOfBoundsException("传递的参数超出数组下标") ;
}
int a=arr[index];
return a;
}
}
此时,由于传入的数组为空值,且new NullPointerException("传递的数组为空")
为运行时异常类的对象,所以异常信息会被打印在控制台,并且中断程序。
使用 throws 关键字处理异常是今天要谈论的异常处理的第一种方式。前面说到,在方法中使用 throw 抛出异常对象后,必须处理这个异常对象,此时我们就可以使用 throws 把这个异常对象抛给方法的调用者,最终抛给 JVM 做中断处理。
使用 throws 关键字有以下几个注意事项:
当我们调用了声明异常的方法后,就必须对这个异常进行处理,要么继续使用 throws 抛出异常对象交给方法的调用者处理,最终由 JVM 做中断处理。要么使用 try…catch 自己处理异常。
示例:定义一个方法,对方法输入的文件路径的参数做合法性判断。
import java.io.FileNotFoundException;
public class Test {
public static void main(String[] args) throws FileNotFoundException {
//定义一个方法,对传递的路径参数做合法性校验
readFile("c://b.txt");
}
public static void readFile(String fileName) throws FileNotFoundException {
if (!(fileName.equals("c://a.txt"))) {
throw new FileNotFoundException("传递的文件路径不是c://a.txt");
}
}
}
此时,如果 readFile() 方法传入的参数不是 "c://a.txt"
,程序运行到调用该方法的位置时,JVM 对程序做中断处理,打印异常内容到控制台。
现在要谈论的是异常处理的第二种方法,使用 try…catch 捕获异常。前面我们使用 throws 声明异常,显然是有局限性的,程序运行到调用声明异常的方法时,如果程序出现异常,则后序代码不会执行。而此方法可以对程序中出现的问题做指定方式的处理。
语法:
try{
//可能出现异常的代码
}catch(){//括号中定义异常变量,用来接收try中抛出的异常对象,try中抛出多个异常对象,可以使用多个catch处理异常对象
//异常处理逻辑
}
如果 try 中出现了异常,就会抛出异常对象并执行 catch 中的语句,执行完继续执行 try…catch 后的语句。否则不会执行 catch 中的语句,执行完 try 中的语句直接执行 try…catch 后的语句。
示例:定义一个方法,对方法输入的文件路径的参数做合法性判断,如果出现异常,则捕获异常。
import java.io.FileNotFoundException;
public class Test {
public static void main(String[] args) {
try {
//定义一个方法,对传递的路径参数做合法性校验
readFile("c://b.txt");
} catch (FileNotFoundException e) {
System.out.println("传递的文件路径不是c://a.txt");
}
System.out.println("后续代码");
}
public static void readFile(String fileName) throws FileNotFoundException {
if (!(fileName.equals("c://a.txt"))) {
throw new FileNotFoundException("传递的文件路径不是c://a.txt");
}
}
}
此时,如果 readFile() 方法中传入的参数不是 c://txt
,则捕获异常给 catch 中的语句块做异常处理,执行完以后继续执行 try…catch 后面的语句。
Throwable 中定义了三个方法用于获取异常信息,分别是:
示例1:使用 getMessage() 方法获取异常信息
import java.io.FileNotFoundException;
public class Test {
public static void main(String[] args) {
try {
//定义一个方法,对传递的路径参数做合法性校验
readFile("c://b.txt");
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
}
}
public static void readFile(String fileName) throws FileNotFoundException {
if (!(fileName.equals("c://a.txt"))) {
throw new FileNotFoundException("传递的文件路径不是c://a.txt");
}
}
}
此时,如果程序出现异常,异常信息显示为:
示例2:使用 toString() 方法获取异常信息
try {
//定义一个方法,对传递的路径参数做合法性校验
readFile("c://b.txt");
} catch (FileNotFoundException e) {
System.out.println(e.toString());
}
异常信息为:
示例3:使用 printStackTrace() 方法获取异常信息:
try {
//定义一个方法,对传递的路径参数做合法性校验
readFile("c://b.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
此时的异常信息为:
在使用 try…catch 捕获异常时,如果 try 中代码出现了异常,则创建异常对象抛给 catch 语句块中的异常处理逻辑代码处理,此时 try 中后续的代码将不会执行。有时我们需要这个位置的代码继续执行,例如需要释放资源,则出现了 fianlly 代码块。
无论程序是否出现异常,finally 代码块中的代码都会执行。且 finally 不可以单独使用,必须使用在 try 代码后,后序 IO 流中继续学习。
示例:
import java.io.FileNotFoundException;
public class Test {
public static void main(String[] args) {
try {
//定义一个方法,对传递的路径参数做合法性校验
readFile("c://b.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
System.out.println("资源释放");
}
}
public static void readFile(String fileName) throws FileNotFoundException {
if (!(fileName.equals("c://a.txt"))) {
throw new FileNotFoundException("传递的文件路径不是c://a.txt");
}
}
}
此时,程序出现了异常,但是 finally 中的语句继续执行。
java.io.FileNotFoundException: 传递的文件路径不是c://a.txt
at Test.readFile(Test.java:17)
at Test.main(Test.java:7)
资源释放
系统定义的异常主要用来处理系统可以预见的常见运行错误,对于某个应用所特有的运行错误,需要编程人员根据特殊逻辑来创建自己的异常类。
例如在我们输入成绩的时候,往往会有一个范围,而这个范围不是JVM能够识别的,此时就需要自己定义异常类。
语法格式:
public class 自定义异常类名 extends Exception{ … }
使用继承 Exception 类的类定义自定义异常逻辑类,而 Exception 中常用的构造方法也可以被子类用super调用。同样还可以使用继承自 runtimeException 类的类实现运行时的自定义异常类。
示例:
//栈操作异常:自定义异常!
public class StackOperationException extends Exception{ // 编译时异常!
public MyStackOperationException(){
}
public MyStackOperationException(String s){
super(s);
}
}
【Java编程进阶】封装继承多态详解
【Java编程进阶】抽象类和接口详解
【Java编程进阶】Object类及常用方法详解