异常机制
在某些情况下,由于编写的代码不适当,会发生编译异常或运行异常,有时甚至会发生错误,出现异常时,程序都会在异常处终止运行
Exception:叫做异常,代表程序可能出现的问题
类型
每一个异常都是一个类,并且都继承自 Exception 类(Exception类继承自 Throwable 类)
异常分为:运行时异常、编译时异常

运行时异常
如数组越界异常,空指针异常,算术异常
特点
编译阶段无法感知代码是否会出现问题,只有在运行的时候才知道会不会出错(正常情况下是不会出错的),这样的异常称为运行时异常,异常也是由类定义的,所有的运行时异常都继承自RuntimeException。
编译时异常
编译时异常明确指出可能会出现的异常,在编译阶段就需要进行处理(捕获异常)必须要考虑到出现异常的情况,如果不进行处理,将无法通过编译!默认继承自Exception类的异常都是编译时异常。
如使用Clone方法时,需要先使类实现 Clonable 接口,对Clone方法进行重写,才能使用clone方法,否则会抛出 CloneNotSupportException 异常
//Object类中的clone方法定义
protected native Object clone() throws CloneNotSupportedException;
错误
错误(Error)不属于异常(Exception),但二者都继承于Throwable
Error:代表系统级别错误(属于严重问题)
系统一旦出现问题,sun公司会把这些错误封装成Error对象,Error是给sun公司自己用的,不是给程序员用的,我们开发人员不用管它
作用
- 异常时用来查询bug的关键参考信息
- 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况(throw)
处理方式
分为三种:JVM默认的处理方式、自己处理、抛出异常
JVM默认的处理方式
- 把异常的名称,异常原因及异常出现的位置等信息输出在了控制台
- 程序停止执行,异常下面的代码不会再执行了
自己处理(捕获异常)
- 格式:
try{
//可能出现异常的代码;
} catch(//异常类名+变量名){
//异常的处理代码
}
- 好处:可以让程序继续往下执行,不会停止
- 细节:
catch中的代码只有在try中出现了对应的异常才会执行- 如果
try中可能会遇到多个问题,要写多个catch与之对应(但这些异常中如果存在父子关系的话,那么父类一定要写在下面)
在JDK 7 之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开,表示如果出现了A异常或者B异常的话,采取同一种处理方案 - 如果
try中遇到的问题没有被捕获,最终还是会交给虚拟机进行处理 - 如果
try中遇到了问题,那么try下面的其他代码就不会执行了,直接跳转到对应的catch中
抛出处理
需要用到 throws 和 throw 两个关键字
- throws:
- 写在方法定义处,表示声明一个异常,告诉调用者,使用本方法可能会有哪些异常
public void 方法名() throws 异常类名1,异常类名2{
...
}
- 编译时异常:必须要写
运行时异常:可以不写
- throw
写在方法内,结束方法。手动抛出异常对象,交给调用者,方法中下面的代码不再执行了
public void 方法名(){
throw new NullPointerException();
}
方法
使用的是定义在 Throwable 中的成员方法
| String getMessage() | 返回此throwable的详细消息字符串 |
|---|---|
| String toString() | 返回此可抛出的简短描述 |
| void printStackTrace() | 把异常的错误信息输出在控制台 |
常用的方法是第三个 printStackTrace ,其底层是利用 System.err.println 进行输出,把异常的错误信息以红色字体输出在控制台,并且不会停止程序运行
自定义异常
步骤
- 定义异常类
- 写继承关系
- 空参构造
- 带参构造
定义
如果想自定义一个编译时异常,可以新建一个类并继承 Exception 类
如果想自定义一个运行时异常,可以新建一个异常,此时idea会将其自动继承 RunTimeException 类
比如,新建一个运行时异常:
public class StudyException extends RunTimeException{
public StudyException(String message){
super(message); //调用父类的带参构造方法
}
}
使用
在定义一个方法时,在异常分支下,可以选择抛出异常
但是抛出运行时异常和编译时异常的情况略有不同
- 运行时异常
直接在异常分支下加 throw new +异常() ,小括号内可写抛出异常的原因
public static void main(String[] args){
int res = div(4,0);
sout(res);
}
public static int div(int a, int b){
if(b==0) throw new DivException("除数不能为0");
return a / b;
}
class DivException extends RunTimeException{
public DivException(String message){
super(message);
}
}
- 编译时异常
除了在异常分支下加 throw new +异常()外,还需要在方法后面加上 throw 异常
即如果在方法中抛出了一个非运行时异常,那么必须告知函数的调用方我们会抛出某个异常,函数调用方必须要对抛出的这个异常进行对应的处理才可以
public static void main(String[] args)throw Exception{
int res = div(4,0);
sout(res);
}
public static int div(int a, int b) throw Exception{
if(b==0) throw new DivException("除数不能为0");
return a / b;
}
class DivException extends Exception{
public DivException(String message){
super(message);
}
}
在方法的定义以及使用该方法的方法后面都需要加 throw ,如果方法中有多个异常,需要用逗号隔开,写上所有的异常
- 特殊情况
在重写方法时,如果父类中的方法表明了会抛出某个异常,只要重写的内容中不会抛出对应的异常我们可以直接省去:
@Override
protected Object clone() {
return new Object(); //此时子类中没有使用clone方法,所以不用写throw
}
意义
就是为了让控制台的报错信息更加地见名知意
出现异常时的打印信息
当出现异常时:

程序会终止,并且会打印栈追踪信息,这里简单介绍一下,实际上方法之间的调用是有层级关系的,而当异常发生时,方法调用的每一层都会在栈追踪信息中打印出来,比如这里有两个at,实际上就是在告诉我们程序运行到哪个位置时出现的异常,位于最上面的就是发生异常的最核心位置,我们代码的第15行。
并且这里会打印出当前抛出的异常类型和我们刚刚自定义异常信息。