这两天在学习java基础中的异常,可能是以前在学校的时候没弄清楚,而且参加工作了以后在asp.net的开发中也很少用到,所以这两天学习java基础的过程中,感觉有些困难,所以在这里记录总结一下
首先,异常的概念
异常:程序在运行时出现的不正常情况
由来:这种问题也是现实生活中一个具体的事物,可以通过java的类的形式进行描述,并封装成对象,其实就是java对不正常情况进行描述后的对象体现,即将问题封装成对象就是异常
异常的体系
Throwable
|----Error
|----Exception
|----RuntimeException
从上面可以看出,Throwable是所有异常的根类,即超类,如果object是所有类的超类一样。只有当对象是此类(或其子类之一)的实例时,才能通过jvm或者java throw语句抛出。类似的,只有此类或其子类之一才可以是catch子句中的参数类型
对于程序在运行时出现的不正常情况,可以划分成两种,一种是严重的问题,另一种是非严重的问题,分别对应异常体系中的Error和Exception
Error和Exception的区别
Error:严重的问题,java通过Error类来描述,一般不编写针对性的代码对其进行处理
Exception:对于非严重的问题,java通过Exception类来进行描述,可以使用针对性的处理方式进行处理
Errro:一般不可处理的,是由jvm抛出的严重性的问题,不作针对性处理,而是直接修改程序.
异常体系的特点
异常体系中的所有类以及建立的对象都具备可抛性。即可以被throw和throws 关键字所操作,也只有异常体系才有这个特点
throw和throws的用法和区别:
throw定义在函数内,用于抛出异常对象。后面跟的是异常对象。
throws定义在函数上,用于抛出异常类。后面跟的是异常类,可以跟多个,用逗号隔开
异常的分类一、编译时被检测异常(编译时异常)
该异常在编译时,如果没有处理(没有throws也没有try ),编译失败
该异常被标识,代表可以被处理。只要是Exception及其子类都是,除了特殊子类RuntimeException体系
二、运行时异常
在编译时,不需要处理,编译器不检查,该异常发生,建议不处理,让程序停止,需要对代码进行修正
就是Exception中的RuntimeException及其子类。
异常的处理
try
{
//这里是需要被检测的代码;
}
catch(异常类 变量) //该变量用于接收发生的异常对象
{
//处理异常的代码;(处理方式)
}
finally
{
//一定会被执行的语句</span>
//特殊情况,当catch中执行System.exit(0);表示系统退出,即jvm退出。不会执行finally中的代码
}
如下:
class Demo
{
int div(int x,int y)
{
int[] arr=new int[x];
System.out.println(arr[4]);
return x/y;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,0);
System.out.println("结果:"+x);
}
catch(Exception e)
{
System.out.println(e.getMessage());
System.out.println(e);
System.out.println(e.toString());
e.printStackTrace(); //打印堆栈中的输出信息,其实JVM默认的异常处理机制,就是在调用
printStackTrace()方法,打印异常在堆栈中的跟踪信息
}
System.out.println("over:");
}
}
其中
e.getMessage()的结果为4,e.toString()打印出的异常类为ArrayIndexOutOfBoundsException类,意思是角标越界,即在长度为4的数组中没有4这个角标
异常处理的原则:
1、函数内容如果抛出了需要检测的异常,那么函数上必须声明,如:
Demo
{
int div(int x,int y)throws Exception //在函数上通过throws的关键字声明了该功能有可能会出现问题
{
if(y==0)</span>
throw new Exception();
return x/y;
}
}
2、如果调用到了声明异常的函数(如上段代码↑):要么try catch 要么throws
如图:
上图为捕捉try catch
上图为抛出throws
注:上图的主函数抛出异常,因为是JVM在调用主函数,所以此异常抛给了JVM,编译成功,但运行时JVM会显示异常信息
3、什么时候用try catch,什么时候throws 呢
功能内容可以解决的,用catch。解决不了的,用throws告诉调用者,由调用者解决.
4、一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理
内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个
注:1、声明异常时,建议声明更为具体的异常,这样处理的可以更具体。2、如果多个catch块中的异常出现了继承关系,那么父类异常catch块放在最下面。3、不要定义多余的catch块
代码如下:
class Demo
{
//针对指定的异常:算术异常和数组角标超出异常</span>
int div(int x,int y)throws ArithmeticException,ArrayIndexOutOfBoundsException //多异常
{
int[] arr=new int[x];
System.out.println(arr[4]);
return x/y;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,0);
System.out.println("结果:"+x);
}
catch(ArithmeticException e) //针对算术异常(除数为0)
{
System.out.println(e.toString());
}
catch(ArrayIndexOutOfBoundsException e)//针对数组角标越界异常
{
System.out.println(e.toString());
}
/* catch(Exception e) //如果catch块中的异常出现继承关系,则父类异常catch块放在最下面
{
System.out.println(e.toString());
}
*/
<span style="white-space:pre"> System.out.println("over");}}</span>
当主函数分别调用d.div(4,0)和d.div(5,0)后的结果为:
和
针对上面的注意中的第2点,如图:
多个异常catch块中出现了继承关系,但是父类异常放在最上面的话,会直接用父类异常的catch块,显示捕捉到两个异常,而下面的两个子类
5、catch块内,需要定义针对性的处理方式。不要简单的定义printStackTrace输出语句,也不要不写。在实际项目中,当程序中出现异常时,通常会将异常记录到异常日志文件中。
6、当捕获到的异常,在本功能中处理不了时,可以继续在catch中抛出。
如果该异常处理不了,但并不属于该功能出现的异常,可以将异常转换后,再抛出和该功能相关的异常。或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,让调用者知道并处理。也可以将捕获的异常处理后,转换成新的异常
自定义异常
由来:因为项目中会出现特有的问题,而这些问题并未被java所描述并封装。所以对于这些特有的问题,可以按照java的对问题封装的思想,将特有的问题进行自定义的异常封装。
如何定义异常信息呢?
因为自定义异常会继承Exception,而其父类中已经将异常信息的操作都完成了,所以子类只要在构造时,将异常信息传递给父类,通过super语句,就可以直接通过getMessage方法获取自定义的异常信息。
自定义异常继承Exception的原因:异常体系其特点是,异常类和异常对象都可以被抛出,他们都具备可抛性,这个可抛性是Throwable这个体系中的独有特点,只有这个体系中的类和对象才可以被throws和throw操作
class FuShuException extends Exception
{
private int value;
FuShuException(String msg,int value) //构造函数
{
super(msg); //调用父类Exception的构造函数
this.value=value;
}
public int getValue()
{
return this.value;
}
}
class Demo1
{
int div(int a,int b)throws FuShuException //声明
{
if(b<0)
throw new FuShuException("异常:除数为负!/by fushu",b);//手动通过throw关键字抛出一个自定义异常对象
return a/b;
}
}
class OwerExceptionDemo
{
public static void main(String[] args)
{
Demo1 d=new Demo1();
try
{
int x=d.div(4,-1); //调用了声明异常的函数,那么需要调用者处理,throws或者try
System.out.println("结果:"+x);
}
catch(FuShuException f)
{
System.out.println(f.toString());
System.out.println("除数出现了负数:"+f.getValue());
}
}
}
Exception中有一个特殊的子类异常:RuntimeException 运行时异常
1、如果在函数内容中抛出该异常,函数上可以不用声明,编译一样通过。如下图
图1
图2
图1编译成功,而图2编译失败,其原因是:图1中抛出的ArithmeticException,其父类为RuntimeException
注:之所以不用在函数上函数上声明,是因为不需要让调用者处理。当该异常发生,希望程序停止,因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。
2、如果在函数上声明了该异常,调用中以不用进行处理,编译一样通过。 如下图:
3、自定义异常时,如果该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException,如图
finally代码块:定义一定执行的代码,通常用于关闭资源
例如向数据库中insert 数据时发生异常,finally代码块可用于断开数据库连接
注:catch 是用于处理异常。如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。
异常在子父类覆盖中的体现:
1、子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类
class AException extends Exception
{
//A 异常
}
class BException extends AException
{
//B 异常
}
class CException extends Exception
{
// C 异常
}
class Fu //父类
{
void show()throws AException //抛出A异常
{
}
}
class Zi extends Fu //继承父类
{
void show{} throws AException //此处只能抛出A异常或B异常,不能抛出C异常
{
}
}
2、如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集
3、如果父类或者接口的方法中,没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类方法发生了异常,就必须进行try处理,绝对不能抛。
以上即为根据这两天毕老师的异常讲解视频作出的归纳和总结。