前言
介绍Java的异常处理机制,包括异常的处理和自定义异常。
目录
2.public void printStackTrace( )
一、异常概述
Java的异常处理机制:
程序中出现的不正常的情况,像数组越界,找不到文件等,就称之为异常(Exception)。Java的异常处理机制就会将不正常情况信息输出到控制台,供程序员进行参考,以便修改。
注意,异常和错误是两个不一样的概念,异常通常是因为编程错误或者外部环境不支持而导致的程序无法顺利进行,可以处理排查。而错误一般无法处理,可能是因为虚拟机出现异常或者系统空间不够。再Java中异常和错误都以对象的形式存在,都继承了Throwable类。
异常再Java中以对象的形式存在,可以实例化一个异常对象:
//Java中的异常以对象的形式存在,可以直接创建一个异常对象
ArrayIndexOutOfBoundsException ae = new ArrayIndexOutOfBoundsException();
事实上,在发生异常时,Java也是new的异常对象。
二、异常的分类
Java中的异常分为两类:
分别是编译时异常和运行时异常(RuntimeException)。它们都继承自Exception,运行时异常独成一类,就是RuntimeException。该类及该类派生类之外的,其它的继承自Exception的类就都是编译时异常类。
1.编译时异常
编译时异常:以在编译阶段就能被Java检测到而得名。也称为未受检或未受控异常。编译时异常需要程序员在编译阶段就必须要发现和修改,否则编译无法通过。
2.运行时异常(RuntimeException)
运行时异常:在运行阶段被发现的异常,也称为受控或受检异常。该类异常程序员可以修改,也可以不处理。
编译时异常和运行时异常的区别:
1.异常产生原因:
编译时异常通常由于外部环境或者是业务逻辑上的问题而导致的异常,如文件未找到异常(FileNotFoundException),数据格式异常(DataFormatException)等。运行时异常主要是由于编程错误,或运行环境造成。如空指针异常(NullPointerException),数组越界异常(ArrayIndexOutOfBoundsExceptio)等。
2.异常处理要求:
编译时异常要求在编程的时候必须显式的处理(捕捉或者上抛),运行时异常可以处理也可以不处理。
3.异常被检测的时机:
编译时异常在编译阶段就能被编译器检测出来,而运行时异常只能在运行阶段被检测到。
三、异常的两种处理方式
在Java中有两种异常处理方式,分别是异常上抛(throws)和异常捕捉(try...catch)。
1.异常上抛
异常上抛就是将异常抛给上一级,也就是它的直接调用者处理。它的直接调用者也可以选择将异常上抛,一直选择异常上抛的终点是JVM虚拟机,JVN处理该异常的方式就是终止程序运行。
异常上抛使用throws关键字:
import java.io.FileNotFoundException;
public class Test01 {
//上抛异常时,该方法的直接调用者必须对异常进行处理
//该方法的直接调用者可上抛该异常,也可上抛它的基类
public static void main(String[] args) throws Exception{
fun();
}
//使用throws关键字上抛异常
//可上抛多个异常,异常之间用逗号隔开
public static void fun() throws ClassNotFoundException, FileNotFoundException {
}
}
选择上抛时,该方法的直接调用者可上抛该异常,也可上抛它的基类。
可上抛多个异常,异常之间用逗号隔开。
注意抛出异常(throw)和异常上抛(throws)的区别:
throw是手动抛出异常,也就是人为的制造异常。throws是对异常进行处理,二者的原理恰好是对立的。
import java.io.FileNotFoundException;
public class Test01 {
//throw抛出(制造)异常,throws上抛(处理)异常
public static void main(String[] args) throws Exception{
fun();
}
//抛出异常时,也要对异常进行处理,一般是上抛。
public static void fun() throws FileNotFoundException {
//异常以对象的方式存在
FileNotFoundException f = new FileNotFoundException();
throw f;
}
}
2.异常捕捉
异常捕捉是真正的对异常进行处理,它使用try...catch语句将异常信息反馈出来,不会终止程序的运行,增强了程序的健壮性。
格式:
try{
这里放可能发生的异常信息;
}catch(接收异常对象的变量){
异常信息的反馈
}
import java.io.FileNotFoundException;
public class Test02 {
public static void main(String[] args) {
//使用try...catch语句直接捕捉到异常,并反馈。
try {
//这里放可能发生异常的语句
doSome();
} catch (FileNotFoundException e) {
//在这里对异常信息输出反馈
}
}
public static void doSome() throws FileNotFoundException {
}
}
可以写多个catch捕捉多个异常,也可以直接写一个他们的基类(Exception)。但建议异常捕捉越具体越好,这样便于调试。
import java.io.FileNotFoundException;
public class Test02 {
public static void main(String[] args) {
//多个catch语句捕捉多个异常
//捕捉多个异常时必须遵循由小到大的形式,否则后面捕捉的异常无用,编译器会报错
try {
doSome();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}
//使用try...catch语句捕捉异常,当异常发生后程序继续运行
System.out.println("haha");
}
public static void doSome() throws FileNotFoundException,ClassNotFoundException {
}
}
当使用多个catch语句捕捉多个异常时,会按由上至下的方式捕捉。因此当多个异常之间有继承关系时,必须按照派生类在前基类在后的由小到大的形式,否则后面有的捕捉无效,编译不会通过。
另外,JDK8之后可以使用或运算符 | 来选择异常,如:catch(FileNotFoundException |ClassNotFoundException e)。是在异常类型之间使用 | ,异常对象还是只有一个。
注意:
异常处理都是针对方法而言的。
当选择异常上抛时,发生异常后,直接上报,该程序结束运行,发生异常位置后的代码都无法执行。
选择异常捕捉时,当try语句中的异常发生后,直接转到catch语句中执行。try中异常后的语句会被越过。但该程序还没有结束,catch后的语句会继续执行。
四、异常对象的常用方法
Java异常的常用方法有两个,分别是getMessage( )和printStackTrace( )。这两个方法都是获取异常信息,都可以用来对异常进行反馈。
1.public String getMessage( )
获取关于异常的简单描述,这个简单描述就是创建异常对象时得到的关于异常的描述。
import java.io.FileNotFoundException;
public class Test03 {
public static void main(String[] args) {
FileNotFoundException fne = new FileNotFoundException("文件未找到");
//getMessage()方法得到的是创建异常对象时得到的异常信息
String s = fne.getMessage();
System.out.println(s);//文件未找到
}
}
2.public void printStackTrace( )
该方法会直接输出关于异常追踪的堆栈信息。JVM会使用另一个线程去追踪关于这个异常的堆栈信息,因此它和前后语句的执行顺序不定。该方法最常用。
import java.io.FileNotFoundException;
public class Test03 {
public static void main(String[] args) {
FileNotFoundException fne = new FileNotFoundException("文件未找到");
try {
throw fne;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
该异常的输出信息是:
java.io.FileNotFoundException: 文件未找到
at Test03.main(Test03.java:5)
显示该异常是FileNotFoundException类型的,异常的描述信息是文件未找到。该异常发生在Test03这个类的main方法中,在Test03.java文件中的第五行。
五、finally子句
finally子句必须与try一起出现,可以没有catch。放在finally子句中的语句一定会执行,且一定是在try...catch语句块的最后执行。是保证安全的一道保障。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Test04 {
public static void main(String[] args) {
//Java的变量的作用域以花括号为界限,在一个花括号内有效
//为了能在finally子句中将fis关闭,要把它定义在try的花括号之外
FileInputStream fis = null;
try {
fis = new FileInputStream("bbb");
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
//使用IO流读或写时一定要记得关闭,否则会造成内存泄露
if(fis != null){
try{
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
finally的强制性在于哪怕在try语句中有return,在finally中的语句也一定会执行。
public class Test05 {
public static void main(String[] args) {
try{
System.out.println("try");
return;
}finally{
System.out.println("finally");
}
}
}
以上程序的输出结果是:
try
finally
finally语句在return被执行后,返回前执行的。也是在break和continue之前执行的。
当然,如果使用System.exit(0)退出JVM,finally子句才不会被执行。
六、自定义异常
在Java中自定义异常很简单,只需两步:
1.定义编译时异常,就继承Exception;定义运行时异常,就继承RuntimeException。
2.提供一个无参构造方法和一个有参(参数为String类型)的构造方法,在有参构造方法中使用super()将该参数传进去。
public class Test06 {
public static void main(String[] args) {
try {
fun();
} catch (MyException e) {
e.printStackTrace();
}
}
public static void fun() throws MyException{
throw new MyException("自定义异常");
}
}
//自定义一个编译时异常
class MyException extends Exception{
//提供一个无参构造和一个参数为String类型的构造方法
public MyException() {
}
public MyException(String message) {
super(message);
}
}
如有错误,欢迎批评指正,谢谢。