一、概述
在程序运行过程中出现的错误,称为异常。Java中,把各种异常现象进行了抽象形成了异常类。
异常处理的目的是为了提高程序的安全性与健壮性。
二、异常分类
异常都继承自Throwable类。
分为Error、Exception两大类:
1、Error(错误)
此类错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等。是程序中无法处理的错误,非代码性错误,表示出现了严重的错误,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复;
2、Exception
程序本身可以捕获并且可以处理的异常。
该类异常又分为两大类:
运行时异常
(不受检查异常):是RuntimeException类及其子类表示的异常,指的是JVM在运行期间可能出现的错误。
特点:
♦此类异常编译器不会去检查,所以也称不受检查异常;
♦此类异常一般由程序逻辑错误引起的,可以捕获处理,也可以不处理;
编译时异常
(受检查异常):Exception中除RuntimeException极其子类之外的异常。
特点:
♦编译器会检查此类异常,所以也称受检查异常;
♦出现此类异常时,必须要处理,否则编译不通过;
三、Java常见异常有哪些
常见的运行时异常:
- NullPointerException:空指针异常,调用了未经初始化的对象或者是不存在的对象;
- ClassCastException:类型转换异常;
- IllegalArgumentException:非法的参数异常;
- IndexOutOfBoundsException:数组下标越界异常;
- ArithmeticException:算术异常;
- SQLException:SQL异常;
常见的非运行时异常:
- IOException:IO流异常;
- ClassNotFoundException:没找到指定类异常;
- NoSuchFieldException:没找到指定字段异常;
- NoSuchMetodException:没找到指定方法异常;
- InterruptedException:中断异常;
四、异常的处理
运行时异常不需要预处理,通过规范的代码可以避免产生这种异常;
而编译时异常必须预处理,否则编译报错,有两种预处理方式 :捕获处理
和抛出处理
;
Ⅰ、异常捕获处理
在代码中使用try
、catch
、finally
将异常捕获
格式1:
try {
//写可能产生异常的代码
} catch (异常类型1 e1) {
//捕获异常类型1的异常, 进行处理
} catch (异常类型2 e1) {
//捕获异常类型2的异常, 进行处理
} finally {
//一般情况下, 这里写释放系统资源的语句
}
注意点:
- 如果try代码块的某条语句产生了异常, 就立即跳转到相应catch子句执行, try代码块该语句后面的代码不再执行
- try块里可能会产生多个异常,所以后面的catch可以有多个,以捕获多种异常
- 在开发时, 异常的处理方式一般是打印异常栈跟踪信息(e.printStackTrace())
- 在部署后, 一般是把异常信息打印到日志文件中(logger.error(e.getMessage()))
- 如果捕获的异常类型有继承关系, 应该先捕获子异常再捕获父异常,如果没有继承关系, catch子句没有先后顺序
- 若try里面有return,则先运行finally里的语句再执行return
- 正常来说finally里的语句都会执行,但以下情况不会
- 若try里面有System.exit(0)强制退出,则不会运行finally块
- 如果在进入try块之前就出现异常,则也不会运行finally块
格式2:
try (/*资源变量的声明、赋值*/){
//数据操作
} catch (异常类型 e) {
//捕获异常并处理
}
在1.7 JDK后,java提供了try–with–resource语法糖
,资源对象需要实现AutoCloseable
接口(几乎所有资源类都实现了该接口),重写其中的close方法,那么在try()
里打开资源,相关资源就会自动关闭,不再需要手动执行,不管是正常退出或异常退出,这意味着可以不用在finally块中关闭资源;
示例:
File file = new File("file1.txt");
try (FileInputStream in =new FileInputStream(file)){
byte[] b=new byte[1024];
int length=0;
while((length=in.read(b))!=-1){
System.out.println(new String(b,0,length));
}
} catch (IOException e) {
e.printStackTrace();
}
Ⅱ、异常抛出处理
在方法声明中使用throws
抛出异常
格式:
public void method( ) throws 异常1,异常2{
//方法体
}
注意点:
- 抛出该异常的方法不处理异常,而是谁调用这个方法,谁负责处理该异常
- catch块中通过throw语句抛出了一个异常,所在的方法应该使用throws抛出该异常
若最终在main方法上抛出异常,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。
Ⅲ、如何选择处理异常的方式
一般情况下,如果方法体中有编译时异常需要预处理,可以选择捕获 ,也可以选择抛出处理。
在调用其他方法时,如果被调用的方法有抛出编译时异常时,选择捕获处理,因为你调用了方法, 你负责处理该异常。
Ⅳ、异常处理相关方法
String getMessage()
:获取异常描述信息void printStackTrace()
:获取异常的跟踪栈信息
Ⅴ、throw 和 throws 的区别
throw
- 用在方法内部,表示抛出一个异常实例
- 只能用于抛出一种异常,throw一般用于抛出自定义异常。
throws
- 用在方法声明上,表示可能会发生这几种异常
- 可以抛出多种异常;
五、自定义异常
自定义异常通常继承于Exception
或RuntimeException
自定义异常类可以有自己的变量和方法来传递异常相关信息。
定义异常类
//运行时异常
class myRuntimeException extends RuntimeException{
public myRuntimeException(String message){//构造函数,其中参数message为异常对象的描述信息
super(message);
}
}
//编译时异常
class myCheckedException extends Exception{
public myCheckedException(String message){
super(message);
}
}
使用
class myThrowTest{
public static int divide1(Integer x, Integer y){
if(y==0){
throw new myRuntimeException("除数y不能为0!");//抛出自定义的运行时异常
}else {
return x/y;
}
}
public static int divide2(Integer x, Integer y) throws myCheckedException {
if(x==null||y==null){
throw new myCheckedException("数据为空!");//抛出自定义的编译时异常
}else {
return x/y;
}
}
public static void count1(Integer x,Integer y){
try {
x=2;y=0;
int result=divide1(x,y);
System.out.println(result);
} catch (myRuntimeException e) {//可能会出现myRuntimeException 异常,可以处理,也可以不处理
e.printStackTrace();
}
}
public static void count2(Integer x,Integer y){
try {
int result=divide2(x,y);
System.out.println(result);
} catch (myCheckedException e) {//检测出myCheckedException 异常,必须要处理
e.printStackTrace();
}
}
public static void main(String[] args){
Integer x=null;
Integer y=null;
count1(x,y);
count2(x,y);
}
}
运行结果: