1、异常基本概念
异常:就是程序在运行时出现不正常情况。
异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述。并封装成对象。其实就是java对不正常情况进行描述后的对象体现。
对于问题的划分:
两种:一种是严重的问题,一种非严重的问题。
对于严重的,java通过Error类进行描述。对于Error一般不编写针对性的代码对其进行处理。
对于非严重的,java通过Exception类进行描述。对于Exception可以使用针对性的处理方式进行处理。
2、java异常的处理
java 提供了特有的语句进行处理。
try
{
需要被检测的代码;
}
catch(异常类 变量)
{
处理异常的代码;(处理方式)
}
finally
{
一定会执行的语句;
}
//示例
class Demo
{
int div(int a,int b)throws Exception//在功能上通过throws的关键字声明了该功能有可能会出现问题。
{ //这样的话,如果调用这个方法者不处理,编译时就会出现异常
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)//当然我也可以在这边将异常再次抛出,就能正常编译了
{ //但是若调用方法时传入了会产生异常的参数,编译正常,但是运行会异常
//因为将异常抛给虚拟机了
Demo d = new Demo();
try
{
int x = d.div(4,1);
System.out.println("x="+x);//注意打印语句如果放在try外面,它就不知道x是谁了
//而且要知道,当异常发生在上面一句时,这句就不会运行了
}
catch (Exception e)//Exception e = new ArithmeticException(); 这个异常是除数为零异常
{
System.out.println("除零啦");
System.out.println(e.getMessage());// / by zero; 这就是获取的异常信息
System.out.println(e.toString());// 异常名称 : 异常信息。 打印的比上一句更全面
e.printStackTrace();//打印异常名称,异常信息,异常出现的位置。 内容最具体
//其实jvm默认的异常处理机制,就是在调用printStackTrace方法。
}
System.out.println("over");//异常处理完成后,这句就可以运行了
}
}
3、对多异常的处理
(1)声明异常时,建议声明更为具体的异常。这样处理的可以更具体。
(2)对方声明几个异常,就对应有几个catch块。不要定义多余的catch块。 如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。
建立在进行catch处理时,catch中一定要定义具体处理方式。不要简单定义一句 e.printStackTrace(),也不要简单的就书写一条输出语句。通常呢,会用日志的形式记录下来,作为异常日志
//这得要一个例子,还是上面那块代码,修改了下,注意看注释理解上文
class Demo
{
int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException//在功能上通过throws的关键字声明了该功能有可能会出现问题。
{
int[] arr = new int[a]; //而正常情况下,若是一旦这句异常了后面一句就不会执行了,直接输出这句的异常
System.out.println(arr[4]);
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(5,0); //输入这个参数会有两个异常
System.out.println("x="+x);
}
catch(Exception e) //但是这个是异常的老大,放在最前面的话,就会都被它处理掉了,所以这样写,是有问题的
{ //而且,尽量不要用这种大包大揽的异常类,没有针对性,自己没发现的异常也会就这样被他处理了
System.out.println("hahah:"+e.toString());
}
catch (ArithmeticException e)
{
System.out.println(e.toString());//这个toString方法其实自动调用getMessage方法
System.out.println("被零除了!!");
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());
System.out.println("角标越界啦!!");
}
System.out.println("over");
}
}
4、自定义异常(很有意思得多看看)仔细看注释
/*
需求:在本程序中,对于除数是-1,也视为是错误的是无法进行运算的。
那么就需要对这个问题进行自定义的描述。
*/
class FuShuException extends Exception //getMessage();
{
private int value;//为了存发生错误的那个数
FuShuException()
{
super();
}
FuShuException(String msg,int value)
{
super(msg);//直接调用父类的构造函数
this.value = value;
}
public int getValue()//获取发生错的数
{
return value;
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0)
throw new FuShuException("出现了除数是负数的情况------ / by fushu",b);//手动通过throw关键字抛出一个自定义异常对象。
/* //注意看给这个异常对象传的参数
当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。要么在内部try catch处理。
要么在函数上声明让调用者处理。这边在函数上面声明了
*/
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,-9);
System.out.println("x="+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());//其实是调用getMessage方法
System.out.println("错误的负数是:"+e.getValue());
}
System.out.println("over");
}
}
5、throws和throw的区别
(1)throws使用在函数上。throw使用在函数内。
(2)throws后面跟的异常类。可以跟多个。用逗号隔开。throw后跟的是异常对象。
6、RuntimeException
Exceptoin中有一个特殊的子类异常RuntimeException 运行时异常。
如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常。调用者可以不用进行处理。编译一样通过;
为什么不用在函数声明?是因为不需要让调用者处理。当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。
总结异常分两种:
(1)编译时被检测的异常。
(2)编译时不被检测的异常(运行时异常。RuntimeException以及其子类)
7、finally
//这段代码反应了finally的作用
class FuShuException extends Exception//这个异常在函数中被扔出时是要在函数上声明的
{
FuShuException(String msg)
{
super(msg);
}
}
class Demo
{
int div(int a,int b)throws FuShuException//表明这个异常还是可处理的
{
if(b<0)
throw new FuShuException("除数为负数");
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,-1);//发生异常
System.out.println("x="+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());
return;//表示退出主函数,但是finally中的代码还是会执行,over就打印不出来了。
//System.exit(0);//系统,退出。jvm结束。只有这种情况下读不到finally
}
finally
{
System.out.println("finally");//finally中存放的是一定会被执行的代码。
}
System.out.println("over");
}
}
那,关于finally通常用于的地方呢,下段伪代码可以展示
总结:finally代码块:定义一定执行的代码。通常用于关闭资源。这个代码反应的思想很重要,是一种模块化的思想,模块间抛异常,要抛对方能够处理的异常
class NoException extends Exception//定义没有数据异常
{
}
public void method()throws NoException
{
try
{
连接数据库;
数据操作;//throw new SQLException();
}
catch (SQLException e)
{
会对数据库进行异常处理;
//像这种问题是有关联性的,我自己处理了,还要让别人知道因此还得再抛一个异常
throw new NoException();//不能抛sql异常,这个提供数据者无关系,因此我们要抛对方能处理的异常
}
finally
{
关闭数据库;//该动作,无论数据操作是否成功,一定要关闭资源。
}
}
最后记住一点:catch是用于处理异常。如果没有catch就代表异常没有被处理过,如果该异常是检测时异常。那么必须声明。
8、异常在子父类覆盖中的体现
(1)子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
(2)如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
(3)如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
总之:这种情况下如果子类方法发生了异常。就必须要进行try处理。绝对不能抛。