Java的异常
在计算机程序运行的过程中,总是会出现各种各样的错误。有一些是用户造成的,还有一些错误是随机出现,并且无法避免的,比如:
- 网络突然断了,连接不到服务器
- 内存耗尽,程序崩溃
- 用户点“打印”,但没有连接打印机
因此,一个健壮的程序必须处理各种错误,错误就是在程序运行期间,调用某个函数的时候,如果失败了,就代表出错
调用方获知调用失败的信息的方式有两种:
1、约定返回错误码。例如处理一个文件,返回0代表成功,返回其他数表示约定的错误码。
2、在语言层面上提供一个异常处理机制。Java内置了一套异常处理机制,总是使用异常来表示错误。异常是一种class,因此它本身带有类型信息。异常可以在任何地方抛出,但只需要在上次捕获,这样就和方法调用分离了。
因为Java的异常体系是基于class面向对象思想设计的,所以它的继承关系如下图:
从继承关系可知:Throwable是异常体系的根,它继承自Object。Throwable有两个体系:Error和Exception。
Error表示严重的错误,程序对此一般无能为力,例如:
- OutOfMemoryError:内存耗尽
- NoClassDefFoundError:无法加载某个class
- StackOverflowError:虚拟机栈溢出
Exception则是运行时的错误,它可以被捕获并处理。某些异常是应用程序逻辑处理的一部分,应该捕获并处理。例如:
- NumberFormatExpection:数据类型的格式错误
- FileNotFoundException:未找到文件
- SocketException:读取网络失败
还有一些异常是程序逻辑编写不对造成的,应该修复程序本身。例如:
- NullPointerException:对某个null的对象调用方法或字段
- IndexOutodBoundsException:数组索引越界
Exception又分为两大类:
- RuntimeException:以及它的子类;
- RuntimeException(包括IOException、ReflectiveOperationException等等)
Java规定:
- 必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常称为Checked Exception。
- 不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。
捕获异常
在Java中,凡是可能抛出异常的语句,都可以用try……catch捕获。把可能发生的异常的语句放在try{......}中,然后使用catch捕获对应的Exception及其子类。
多catch语句
try: 包围可能产生异常的代码块如果产生异常,系统会根据异常产生的原因,创建一个异常对象。
catch: 捕获异常,处理异常。
try(Scanner in=new Scanner(System.in)){
//获取输入时,可能产生InputMismatchException
//输入非法内容:a
//JVM创建一个InputMismactchException类型的异常对象,并抛出
//程序会从try块退出
int n1=in.nextInt();//输入a
int n2=in.nextInt();
//计算除法时,可能产生ArithmeticException
System.out.printf("%d÷%d=%d",n1,n2,n1/n2);
}catch(NullPointerException ex) {
System.err.println("空指针");
}catch(InputMismatchException ex) {
System.err.println("输入有误");
}catch(ArithmeticException ex) {
System.err.println("算数逻辑有误");
}
输入:
a
输出结果为:
输入有误
需要注意的是,存在多个catch时,catch包裹的子类必须写在前面。
finally语句
如果有无论是否有异常的发生,我们都需要执行的语句,那么Java的try……catch机制还提供了finally语句,finally语句块保证有无错误都会执行。
public static void main(String[] args) {
System.out.println("输出结果res="+todo());
}
public static int todo() {
int n=8;
int res=0;
try {
n=n/0;//出现异常
res=n*10;//res=80;
//return前,缓存返回值,再去执行finally
return res;
}catch(Exception ex) {
System.out.println("出现异常!");
res=n+10;
return res;
}finally {
res=res+100000;
System.out.println("执行fianlly");
}
}
输出结果为:
出现异常!
执行fianlly
输出结果res=18
finally有几个特点:
- finally语句不是必须的,可以不写。
- finally总是最后执行。
抛出异常
当发生错误时,例如,用户输入了非法的字符,我们就可以抛出异常。
抛出异常分两步:
1、创建某个Exception的实例
2、用throw语句输出;
public static void main(String[] args) {
String str="唧唧复唧唧木兰当户织不闻机杼声";
String res=copy(str,20,9);//注意这里的第二个参数有误
System.out.println(res);
}
//复制字符串
public static String copy(String s,int startIndex,int endIndex) {
//输入判断
//方式1:不常用
if(s==null) {
NullPointerException NPE=new NullPointerException();//无参构造方法
throw NPE;
}
//方式2:直接抛出新对象不必命名
if(startIndex>endIndex) {
throw new IllegalArgumentException("开始下标不能大于结束下标");//有参构造方法,返回值为String
}
if(startIndex<0||endIndex>=s.length()) {
throw new StringIndexOutOfBoundsException("下标超出字符串的范围");
}
//复制字符串
char[] arr=s.toCharArray();
StringBuilder sb=new StringBuilder();
for (int i = startIndex; i <= endIndex; i++) {
char c=arr[i];
sb.append(c);
}
return sb.toString();
}
输出结果为:
Exception in thread "main" java.lang.IllegalArgumentException: 开始下标不能大于结束下标
at Throw.throw01.copy(throw01.java:22)
at Throw.throw01.main(throw01.java:6)
自定义异常
我们也可以根据自己的需求来自定义新的异常类型,但是保持一个合理的异常继承体系是非常重要的。一个常见的做法就是自定义一个BaseException作为"根异常",然后派生出各种业务类型的异常。BaseException需要从一个适合的Exception派生,通常建议从RuntimeException派生。
public static void main(String[] args) throws NotEnoughException {
work();
}
public static void work() throws NotEnoughException {
System.out.println("初始库存"+Reposistory.stock);
//入库
Reposistory.in(20);
System.out.println("库存"+Reposistory.stock);
//出库
//调用方法,如果声明抛出检查异常时
//该方法必须使用try...catch处理
//或者使用throws声明抛出
Reposistory.out(1000);
System.out.println("库存"+Reposistory.stock);
}
}
//仓库类
class Reposistory{
static int stock=100;//库存数量
//入库
public static void in(int count) {
stock=stock+count;
}
//出库
public static void out(int count) throws NotEnoughException {
if(count>stock) {
//如果抛出RuntimeException运行时异常,
//可以省略异常类型的方法声明 throws RuntimeException
//throw new RuntimeException("库存不足");
//如果抛出检查异常(编译器异常)
//必须完成异常类型的方法声明 throws NotEnoughException
throw new NotEnoughException();
}
stock=stock-count;
}
}
//自定义异常
//库存不足异常
class NotEnoughException extends Exception{
public NotEnoughException() {
super("库存不足!");
}
}
输出结果为:
初始库存100
库存120
Exception in thread "main" NewExpection.NotEnoughException: 库存不足!
at NewExpection.Reposistory.out(NewException01.java:49)
at NewExpection.NewException01.work(NewException01.java:19)
at NewExpection.NewException01.main(NewException01.java:5)
上面的结果中NotEnoughException为自定义的异常类型。
Exception异常处理思维导图
以上是我个人对Exception异常处理的一些总结,如有错漏请务必联系,非常感谢