异常机制


异常机制

在某些情况下,由于编写的代码不适当,会发生编译异常运行异常,有时甚至会发生错误,出现异常时,程序都会在异常处终止运行

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公司自己用的,不是给程序员用的,我们开发人员不用管它

作用

  1. 异常时用来查询bug的关键参考信息
  2. 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况(throw)

处理方式

分为三种:JVM默认的处理方式自己处理抛出异常

JVM默认的处理方式

  1. 把异常的名称,异常原因及异常出现的位置等信息输出在了控制台
  2. 程序停止执行,异常下面的代码不会再执行了

自己处理(捕获异常)

  1. 格式:
try{
		//可能出现异常的代码;
} catch(//异常类名+变量名){
		//异常的处理代码
}
  1. 好处:可以让程序继续往下执行,不会停止
  2. 细节:
  • catch中的代码只有在 try中出现了对应的异常才会执行
  • 如果 try 中可能会遇到多个问题,要写多个 catch 与之对应(但这些异常中如果存在父子关系的话,那么父类一定要写在下面
    在JDK 7 之后,我们可以在 catch同时捕获多个异常,中间用 | 进行隔开,表示如果出现了A异常或者B异常的话,采取同一种处理方案
  • 如果 try 中遇到的问题没有被捕获,最终还是会交给虚拟机进行处理
  • 如果 try 中遇到了问题,那么 try 下面的其他代码就不会执行了,直接跳转到对应的 catch

抛出处理

需要用到 throwsthrow 两个关键字

  • throws:
  1. 写在方法定义处,表示声明一个异常,告诉调用者,使用本方法可能会有哪些异常
public void 方法名() throws 异常类名1,异常类名2{
	...
}
  1. 编译时异常:必须要写
    运行时异常:可以不写
  • throw
    写在方法内,结束方法。手动抛出异常对象,交给调用者,方法中下面的代码不再执行了
public void 方法名(){
		throw new NullPointerException();
}

方法

使用的是定义在 Throwable 中的成员方法

String getMessage() 返回此throwable的详细消息字符串
String toString() 返回此可抛出的简短描述
void printStackTrace() 把异常的错误信息输出在控制台

常用的方法是第三个 printStackTrace ,其底层是利用 System.err.println 进行输出,把异常的错误信息以红色字体输出在控制台,并且不会停止程序运行

自定义异常

步骤

  1. 定义异常类
  2. 写继承关系
  3. 空参构造
  4. 带参构造

定义

如果想自定义一个编译时异常,可以新建一个类并继承 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行。

并且这里会打印出当前抛出的异常类型和我们刚刚自定义异常信息。


Author: havenochoice
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source havenochoice !
评论
  TOC