异常概述
我们在写程序的时候是为了描述现实生活中的事物,那么现实生活中的事物也会存在一些问题,问题本身也是现实生活中的一个具体事物,也可以通过java类的形式封装成对象进行描述,java对此称为——异常,其实就是java对不正常情况描述后的对象体现
对于问题的划分,分为两种:
严重问题:Error
非严重问题:Exception
对比现实生活,把人体疾病就可以封装成一个对象进行描述,分为可治愈和不可治愈,不可治愈的呢就是Error,可治愈的就是Exception,对于Error异常一般不编写针对性的代码进行处理,对于Exception可以编写一些针对性的代码进行处理,本篇文章也会围绕着Exception进行编写
无论是Error还是Exception都具有一些共性内容,例如不正常情况的信息,引发原因提示,向上抽取后就完成了一个基本的体系:
点此查看官方API文档
Throwable
-Error
-Exception
作为初学者如果看到这两张图片或许已经开始怀疑人生了,不要怕,我们并不会涉及到那么多异常,本文中也只挑选一下比较常见的进行讲解;
举个栗子:可治愈疾病下的N多种疾病,这些就是N多种疾病的具体体现,java将显示生活的很多的问题都进行了描述封装,异常就是用面对对象的思想进行描述,然后进行了封装
try catch
首先看下面这个栗子
class ErrorDemo
{
public static void main(String args[])
{
Demo d = new Demo();
d.div(5,0);
}
}
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
执行报错如下:
代码里面算式除数不为0才能想出相除
我们这段程序引发了java虚拟机定义的异常,java虚拟机内部的异常处理机制,直接提前停止程序,我们现在需要做的是把异常处理掉
java提供了特有的语句进行处理:
//处理异常的最基本的代码块格式
//由三部分组成,是一个整体
try
{
//需要被检测的代码
}
catch(异常类 变量)
{
//处理异常的代码,也就是处理方式
}
finally
{
//一定会执行的语句
}
了解了上面的异常处理语句,就可以针对上面的报错进行分析处理了:
class ErrorDemo
{
public static void main(String args[])
{
Demo d = new Demo();
try
{
d.div(5,0);
}
catch(Exception e)
{
System.out.println("除数为0!");
}
}
}
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
问题处理细节请看下面代码块:
class ErrorDemo
{
public static void main(String args[])
{
//1.创建对象
Demo d = new Demo();
try//7.try检测语句是否存在异常对象,无此try语句则使用虚拟机默认处理方式
{
//2.当d对象调用div函数的时候把5和0传给了a和b
d.div(5,0);//6.new AritchmeticException()
//错误抛出后接下来的语句都不会被执行
}
catch(Exception e)//8.try检测到来了这个对象后抛给了catch,参数用来接收传递过来的异常对象 ==Exception e = new ArithmeticException();
{
System.out.println("除数为0!");//9.执行此语句,问题被处理
}
finally
{
System.out.print("以上两个部分执行完毕后执行此内容");//10.一定会执行的语句
}
}
}
class Demo
{
//3.main函数讲5和0传入
int div(int a,int b)
{
//4.此处开始运算,引发java虚拟机识别的算术问题
return a/b; // 5.将问题变成new AritchmeticException()对象,并且抛给了调用这个功能的调用者
}
}
这里还有一个问题,catch(Exception e)这里的e有什么作用呢?
更多内容可以查看下官方API,本文不多做描述了
throws
这里介绍一下throws,首先回到上面的栗子
int div(int a,int b)
{
return a/b;
}
这个除法方法,对方传值的参数是不确定的,可能传的是正常的参数,也可能传0或者其他内容,这个时候编写功能的人就会在这个功能上添加一个标识——throws Exception
int div(int a,int b)throws Exception //在功能上通过throws声明了该功能有可能会出现问题
{
return a/b;
}
编译时提示:
加上throws后编译会提示必须将div功能的问题进行处理,处理方式有两种:分别是上面提示的捕捉、抛出
之前的例子我们已经使用过了捕获,咱们这里演示一下抛出:
class ErrorDemo
{
public static void main(String args[])throws Exception //将问题抛给java虚拟机
{
Demo d = new Demo();
d.div(5,8);
}
}
class Demo
{
int div(int a,int b)throws Exception //在功能上通过throws声明了该功能有可能会出现问题
{
return a/b;
}
}
这里的流程就是我们将问题抛给调用此内容的对象,然后又抛给了java虚拟机,然后虚拟机使用了默认的处理方式(报错),所以我们正常的处理方式就应该进行捕捉:
class ErrorDemo
{
public static void main(String args[])//throws Exception
{
Demo d = new Demo();
try//处理问题
{
d.div(5,0);
}
catch(Exception e)
{
System.out.println(e.toString());
}
finally
{
System.out.print("以上两个部分执行完毕后执行此内容");
}
}
}
class Demo
{
int div(int a,int b)throws Exception //抛出问题
{
return a/b;
}
}
多异常处理
我们在定义功能的时候有可能会发生不止一个问题,这就要用到我们的多异常处理,声明异常时可以声明更为具体的异常,这样处理可以更具体
请看下面的栗子:
int div(int a,int b)//ArithmeticException,ArrayIndexOutOfBoundsException
{
//参数a=4;b=0;
int arr[] = new arr[a];
arr[4]=1;//角标越界
return a/b;
}
这个代码块就出现了两个问题,一个数组越界,一个除数为0,所以这里我们抛出了两个异常对象,同样,在处理时也需要针对性的处理:
lass ErrorDemo
{
public static void main(String args[])//throws Exception
{
Demo d = new Demo();
try//处理问题
{
d.div(5,0);
}
catch(ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("除数为0异常处理");
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());
System.out.println("下标越界异常处理");
}
finally
{
System.out.print("以上两个部分执行完毕后执行此内容");
}
}
}
class Demo
{
int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException
{
int arr[] = new arr[a];
arr[4]=1;//角标越界
return a/b;
}
}
这里进行多异常处理的时候流程也跟之前抓捕一样,只不过catch抓捕到了内容之后会做一个选择,查看下面的catch语句是否是当前出现的异常对象,是的话则进入代码块处理异常,因为多态性的关系,catch(Exception e)可以抓捕所有异常,多异常处理的话,对方声明几个异常就要有几个catch块,注意,父类异常catch块要向下放,否则之前定义的catch块都是无效的,不要定义多余的catch块,在实际运用中不要像上面的案例一样只用一句print处理异常,一定要定义具体的处理方式
自定义异常
java本身对一些常见的异常做了一些封装,那么如果在我们自己做项目的过程中出现了一些特有的,java没有描述过的异常,既然java可以根据问题进行对象封装,那么我们自己也可以对一些特有的问题做一些封装,这就是——自定义异常
int div(int a,int b)
{
return a/b;
}
没错,又双叒叕是这个栗子,对于除数是-1的情况,我们来自定义一个异常来描述,定义一个异常类:
class MinusException extends Exception
{
}
既然是异常,肯定要融入异常体系当中,所以继承了Exception类,因为java无法识别我们自己定义的异常,所以只能手动抛出:
int div(int a,int b)
{
if(b<0)
{
throw new MinusException();//通过throw关键字抛出自定义异常对象
}
return a/b;
}
编译报错如下:
当在函数内部出现了throw抛出异常对象,就必须要给出对应的处理动作:
要么在内部try-catch处理,要么在函数上声明,让调用者处理
一般情况下,函数内出现异常,函数上需要声明:
int div(int a,int b)throws MinusException
{
if(b<0)
{
throw new MinusException();//通过throw关键字抛出自定义异常对象
}
return a/b;
}
编译报错:
异常已经抛出,现在调用方需要处理了
class ErrorDemo
{
public static void main(String args[])
{
Demo d = new Demo();
try
{
d.div(4,-2);
}
catch(MinusException e)
{
System.out.println("除数出现了负数了!");
}
}
}
class Demo
{
int div(int a,int b)throws MinusException
{
if(b<0)
{
throw new MinusException();//通过throw关键字抛出自定义异常对象
}
return a/b;
}
}
class MinusException extends Exception
{
}
现在我们的自定义异常就编写完成了,但是会发现,我们的自定义异常没有定义所属信息,也就是说我们打印的没有任何的异常信息,只有一个异常的名字,下面我们开始定义异常的所属信息,因为父类信息以及将异常信息的操作都完成了,所以子类只要在构造的时候直接将异常信息通过super传递给父类,就可以通过getMessage();获取到异常信息了,除此之外我们也可以手动来定义一些方法来输出一些异常信息
注意:自定义异常必须要继承Exception,因为异常类和异常对象都需要被抛出,都具备可抛性,这个性质是Throwable这个体系中独有的特点,只有这个体系中的类和对象才可以被throws和throw操作
throws和throw的区别
throws
- thorws使用在函数上
- throws后面跟的是异常类,可以多个,用“,”隔开
- 表示抛出异常,由该方法的调用者来处理
- throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
- throw使用在函数内
- 表示抛出异常,由方法体内的语句处理
- 只能抛出一个异常对象名
- throw跟的是异常对象
- throw一定抛出了某种异常
总结
最后总结一些常见的异常类吧
算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException