在编写代码时总是不可避免的会遇到这样或那样的错误,如:数组遍历时,自以为下标是在数组范围内的,但是一不小心索引就是超出范围的;或者是在进行强制类型转换时,输入的内容是字符串的,但是我们却误以为它是数字的,进行强制类型转换时就会报如下错误:ClassCastException;亦或者是我们在对数值型的数据进行除法运算时,不小心将除数设置成了0,也会报错..................列出来的种种错误(异常)在编译时是不会有爆红(报错)的提示,只会在运行时出现错误的显示。为了对这样的异常进行处理,java提供了异常处理机制。java秉承面向对象的基本思想,让所有的异常以类的形式存在。对于可能出现的异常,都需要事先预知进行处理,以保证程序的有效运行,否则程序会报错。在处理异常前,我们要先了解java内置异常类有哪两种分类:
- Error异常类及其子类:致命性异常,仅靠修改程序本身是不能使程序恢复执行的,通常用来描述java运行系统中的内部错误及资源耗尽的错误,该类异常比较严重,如果发生该类异常应终止程序。
- Exception异常类及其子类:非致命性异常,该异常可以通过捕获处理后正常执行
上面两种异常类都继承自java.lang.Throwable,用来描述经常容易发生的错误。
目录
数值下标越界异常-ArrayIndexOutOfBoundsException
赋值数组类型不兼容异常-ArrayStoreException
建立元素个数为负数的数组异常-NegativeArraySizeException
字符串转换为数字异常-NumberFormatException
Exception类
我们一般讨论最多的是Exception类异常,因为Error异常是致命性的异常,直接导致程序不能执行,我们就先不去了解Error。先来认识认识与我们息息相关的Exception类异常,我们可以根据数据发生的原因将Exception异常分为:
- RunTimeException异常
- 除了RunTimeException外的异常
RunTimeException异常
我们可以翻译成中文“运行时异常”顾名思义,就是在运行时出现的异常,该类异常就类似于我们在过马路时,有人(异常)骑车闯了红灯(没有被处理),还好死不死的撞到了我们(代码中有这个异常),造成了我们一天的工作学习受阻(程序出错),没法正常生活(程序没法有效运行),但是我们却还是可以通过抢救活过来的(非致命性的)。在我们的代码程序中存在许多的RunTimeException异常,如:
- 算术异常-ArithmeticException
- 数值下标越界异常-ArrayIndexOutOfBoundsException
- 赋值数组类型不兼容异常-ArrayStoreException
- 类型强制转换异常-ClassCastException
- 建立元素个数为负数的数组异常-NegativeArraySizeException
- 空指针异常-NullPointerException
- 字符串转换为数字异常-NumberFormatException
- 操作数据库异常
- 字符串索引超出范围异常
内置异常类的使用
算术异常-ArithmeticException
数值下标越界异常-ArrayIndexOutOfBoundsException
赋值数组类型不兼容异常-ArrayStoreException
类型强制转换异常-ClassCastException
建立元素个数为负数的数组异常-NegativeArraySizeException
空指针异常-NullPointerException
字符串转换为数字异常-NumberFormatException
从上面的代码我们可以看到,在我们编写代码时这些异常是没有任何提醒的,但是在运行时却会有异常,但是这个异常是不需要程序员手动进行捕获处理或者是声明抛出的,这种直接由java的虚拟机(JVM)自动进行捕获处理的异常叫做“不受检查异常”。“受检查异常”一般来说,基本上是我们自定义的异常(用户自定义异常类只需要继承自Exception类即可),即我们的异常不是内置的异常类里面的,是我们自己定义的,比方说:我们在进行算法运算时,如果想要进行运算的数字不能大于或者是小于某个数,就可以自定义异常:条件为当要进行运算的数字大于或者是小于某个数时就直接抛出异常,程序立即终止,不再运行。
这里就需要用到throw和throws关键字来进行抛异常
- throw:一般用在声明的方法体里面(中),程序在执行到throw语句时立即终止,throw后面的语句都不会去执行。
- throws:在声明方法时使用,直接在方法名后面throws异常类名,如果有多个的话就用逗号隔开。
在抛出异常后我们来捕获异常:异常处理器大致分为try-catch语句块和finally语句块。java语言的异常捕获结构由try,catch,finally3部分组成。
- try:存放可能发生异常的java语句
- catch:激发被捕获的异常
- finally:异常处理结构的最后执行部分,不管try语句如何退出,都会执行finally语句块。
自定义异常使用
创建一个Operation类,里面进行除法运算,有一个division方法,当分母(除数)小于零时(分母只要不为零都可以取,在这里我们只是想要自己定义一个异常类),则抛出自定义的异常(MyException异常类),最后在main方法中捕捉异常
首先我们先创建异常类,指定我们division方法可能抛出的异常
接着我们去创建一个Operation类及类里面的division方法,有两个int类型的参数(局部变量),返回值可以看自己需要写,如果不写返回值,改写void即可。
接着我们在main方法中使用try-catch语句块进行捕获处理和声明抛出异常
最后运行即可,大家也看到了我们在创建子类的构造方法时使用了super(参数)方法,super可以理解是子类指向自己父(超)类对象的一个指针,而这个父(超)类指得是离子类最近的一个父类,构造方法也叫构造器,是负责类中成员变量域的初始化。在java继承中构造方法的访问有一个很大的特点:子类中所有的构造方法默认都会访问父类中无参的构造方法。如果我们写super(参数)方法,而只写一个打印输出语句的话结果就会是下面这样--子类(MyException类)的输出语句不在前面而到了后面:
public class NeedChecked {
public static void main(String[] args) {
try { //可能(但不一定)发生异常的语句
Operation operation=new Operation();
double result=operation.division(22, -4);
System.out.println(result);
}catch(MyException myException) { //try语句发生异常,程序跳转到catch代码块中执行
System.out.println(myException); //直接打印出这个异常类,如果有包的话,包名也会一起打印
}finally { //程序结束语句的输出
System.out.println("自定义异常受检查结束");
}
}
}
class MyException extends Exception{
public MyException(String s) { //创建子类( MyException)的构造方法
// super(s); //如果不写的话,默认返回的是Exception的无参构造方法,因为每一个子类都会默认有父类的无参构造方法即--super();
System.out.println(s);
}
}
class Operation{
public static double division(int i,int j) throws MyException { //使用throws关键字在声明方法时抛异常
if(j<0) {
throw new MyException("私人订制:除数不能小于零。"); //使用throw关键字在方法体里面抛异常
}
return i/j;
}
}
受检查异常还有文件的处理,有兴趣的可以去看看Day014--java中的I/O流
在文件的处理时都需要我们手动的去进行捕获异常或声明抛出。
【结语:】java异常强制我们考虑程序的强健性和安全性,异常处理不应该仅仅是用来控制程序的正常运行。