异常与异常处理
本文属于自己学习记录,本人新人一枚,欢迎纠正错误和参与讨论
创建于2015年8月22日
一、什么是异常?
从字面上理解就是有异于常态,也就是说和正常情况不一样;在咱们的程序当中就可以理解为阻止当前方法或作用域的叫做异常,也就是说在程序开发过程使我们想要执行的代码没办法继续执行下去,这个时候异常信息就产生了。
二、异常的分类
JAVA中所有不正常的类都继承于Throwable类。
它又分为两大类,一个是Error(错误),一个是Exception(异常)
1、Error是指错误,比如虚拟机错误、线程死锁等,外号叫做程序终结者。比如一家流水线生产的工厂,本来是按照正常的程序在生产产品,但是,这个时候某个机器突然损坏,或者整个工厂停电了,这个时候就造成了错误。
2、Exception是指的异常,出现这种情况可能是你的应用程序的编码、环境、用户的操作输入出现了问题等。同时Exception还分为非检查异常和检查异常。
非检查异常:指的就是运行时异常RuntimeException,比如NullPointerException(空指针)异常,ClassCastException(类型转换)异常,ArrayIndexOutOfBoundsException(数组下表越界)异常等。
检查异常:除了RuntimeException异常的其他所有异常。比如IOException(文件)异常、SQLException(SQL)异常。
区别:最主要体现在他们的处理方式上,检查异常需要使用try,catch和finally关键字在编译期间进行处理,否则会出现编译器报错。而非检查异常(RuntimeException)则不需要这样做。
三、异常的处理
在JAVA中一般使用try-catch或try-catch-finally来捕获异常并进行处理。
try{
//一些会抛出异常的方法
}catch(Exception e){
//处理此异常的代码
}
当我们的try中的程序出现异常后,try中的程序会停止执行,然后程序的控制权会被移交给catch块中的异常处理程序处理。有如下一段程序:
try{
System.out.println("请输入一个数字:");
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
System.out.println("你输入的数字是" + num);
}catch(Exception e){
System.out.println(e);
}
此程序接受的数据类型必须是一个int类型,如果输入了一个字符串则会出现异常:
请输入一个数字:
abc
java.util.InputMismatchException
这个时候会抛出异常,此异常提示的是说输入类型不匹配。
在异常处理中还有一个关键字finally,它的作用是不管你的try是否会出现异常,最后finally都会被执行,但是这个地方finally一般用来做释放资源等操作。比如这样:
try{
System.out.println("请输入一个数字:");
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
System.out.println("你输入的数字是" + num);
}catch(Exception e){
System.out.println(e);
}finally{
System.out.println("这个地方你需要输入一个整数类型");
}
执行的结果:
请输入一个数字:
123
你输入的数字是123
这个地方你需要输入一个整数类型!
请输入一个数字:
abc
java.util.InputMismatchException
这个地方你需要输入一个整数类型!
看的出来,不管你的输入是否正确,try是否出现异常,最后finally中的程序都会被执行。
然后还有一个多重catch,看代码:
try{
System.out.println("请输入一个数字:");
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
System.out.println("你输入的数字是" + num);
int[] k = new int[3];
for (int i = 0;i <= 3; i++) {
k[i] = i;
System.out.println(k[i]);
}
}catch(InputMismatchException e){
System.out.println(e);
System.out.println("必须输入一个整数!");
}catch(ArrayIndexOutOfBoundsException e){
System.out.println(e);
System.out.println("数组下标越界!");
}catch(Exception e){
System.out.println(e);
}finally{
System.out.println("程序执行完毕!");
}
请输入一个数字:
abc
java.util.InputMismatchException
必须输入一个整数!
程序执行完毕!
请输入一个数字:
123
你输入的数字是123
java。lang.ArrayIndexOutOfBoundsException:3
数组下标越界!
程序执行完毕!
当我们使用多重catch语句时执行的顺序是自上而下,同时我们定义异常类型的时候必须得先是子类异常,再是父类异常,不然会报错,因为执行的顺序是自上而下,既然第一个父类里面包含了其他两个异常,这个时候下面定义的异常就是多余的,看图:
try{
System.out.println("请输入一个数字:");
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
System.out.println("你输入的数字是" + num);
int[] k = new int[3];
for (int i = 0;i <= 3; i++) {
k[i] = i;
System.out.println(k[i]);
}
}catch(Exception e){
System.out.println(e);
}catch(InputMismatchException e){
System.out.println(e);
System.out.println("必须输入一个整数!");
}catch(ArrayIndexOutOfBoundsException e){
System.out.println(e);
System.out.println("数组下标越界!");
}finally{
System.out.println("程序执行完毕!");
}
catch(InputMismatchException e)
catch(ArrayIndexOutOfBoundsException e)
会编译报错
其实说完了try-catch-finally之后并没有完,比如一旦我们的异常中没有自己需要的异常类型,这个时候我们就可以去自定义一个异常,需要用到throws(声明异常)和throw(抛出异常)。
四、自定义异常
首先介绍throws(声明异常)和throw(抛出异常)。
throws(声明异常):声明将要抛出什么异常(声明)。例如:
public class Other{
public void run()throws InputMismatchException,Exception{
System.out.println("奔跑");
}
}
在方法后面加上throws 异常,如果有多个异常则用“,”隔开。
throw(抛出异常):将产生的异常信息抛出(动作)。例如:
public class Other{
public void run(int num)throws InputMismatchException,Exception{
if(num == 0){
throw new Exception("当前除数不能为0!");
}
}
}
int num = 0;
Other o = new Other();
try {
o.run(num);
} catch (InputMismatchException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
java.lang.Exception: 当前数不能为0!!!
at com.exception.AboutException$Other.run(AboutException.java:62)
at com.exception.AboutException.main(AboutException.java:48)
自定义异常:也就是我们自己定义的异常,但是必须继承一个异常类型,可以是RuntimeException,可以是ClassNotFoundException等,同时也可以直接给多有异常的父类Exception。
1、创建一个异常类DividentIsNotZeroException,继承Exception类。
此异常表示被除数不能为0
public class DividendIsNotZeroException extends Exception{}
2、创建一个带有参数的构造方法,并且调用父类的一个带字符串message参数的构造方法,不要问为什么,因为你要传递一个信息进去
public class DividendIsNotZeroException extends Exception {
//无参构造器,防止系统默认的去给我们创建,还有后期可能会用到
public DividendIsNotZeroException(){}
//有参构造器,意在传递一个错误信息去父类
public DividendIsNotZeroException(String message){
//调用父类中的构造方法
super(message);
}
}
3、具体的实现的步骤,为了更好的体现,我们做一个链式异常
构建一个main方法
第一步:test1():抛出被除数不能为0的异常
//定义test1()方法 声明一个异常
public void test1()throws DividendIsNotZeroException{
if(divident == 0){
//抛出一个新的异常,并且带一个String类型参数
throw new DividendIsNotZeroException("被除数不能为0!");
}else{
System.out.println(divident);
}
}
第二步:test2()调用test1()抛出的异常,并且包装成运行时异常
public void test2(){
try {
test1();
//catch里面是捕获异常
} catch (DividendIsNotZeroException e) {
//new一个RuntimeException
RuntimeException runExc =
new RuntimeException("由运行所引发的一个异常");
//把捕获到的异常传进runtimeException的initCause()方法
runExc.initCause(e);
throw runExc;
}
}
第三步:main方法中,调用test2(),尝试捕获test2()抛出的异常
int divident = 1;
public static void main(String[] args) {
MainTest t = new MainTest();
try{
t.test2();
}catch(Exception e){
e.printStackTrace();
}
}
这个时候如果被除数divident为0,则抛出异常
java.lang.RuntimeException: 由运行所引发的一个异常
at com.myException.MainTest.test2(MainTest.java:38)
at com.myException.MainTest.main(MainTest.java:16)
Caused by: com.myException.DividendIsNotZeroException: 被除数不能为0!
at com.myException.MainTest.test1(MainTest.java:25)
at com.myException.MainTest.test2(MainTest.java:33)
... 1 more
看结果首先是一个RuntimeException(运行时异常),运行的时候由于被除数为0导致产生我们的自定义异常DividentIsNotZeroException
以上就是自定义异常的用法
附录(常见异常):
算数异常类:ArithmeticException
空指针异常类:NollPointerException
类型强制转换类型:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
等等。。。。。。。。。