Java异常
程序在运行的过程中可能产生问题。对于问题的可以划分成两种:一种是严重的问题,一种是非严重的问题。
对于严重的,java通过Error类进行描述。对于Error一般不编写针对性的代码对其进行处理。
对于非严重的,java通过Exception类进行描述。对于Exeption可以使用针对性的处理方式进行处理。
无论Error或者Exception都具备一些共性内容。
比如:不正常情况的信息,引发原因等。这两个类就可以向上抽取出一个类,形成一个体系。
Throwable
|--Error
|--...
|--Exception
|...
异常和错误的情况注意区分。
异常:就是其中一种问题。它是程序在运行的时候出现的不正常情况。
异常的由来:问题也是现实生活中一个具体的事物个体。它们也有共性,因此可以进行描述抽取成一个类。这个类就叫做异常类。
异常可以分为:java能够识别的异常和用户自定义的异常。
1、java能够识别的异常。
比如ArithmeticException。除数不能为0。
看下面的代码:
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
int x=d.div(4,1);
System.out.println(“x=”+x);
System.out.println(“over”);
}
}
上述代码是没有问题的,正常运行出结果。但是当传入的是4,0;编译通过,但运行失败。因为运行的时候才回去算。运行时显示出一大堆信息。虚拟机默认的处理机制。问题丢给虚拟机,虚拟机就会这样处理。下面我们有java提供的特有语句进行处理。
(1)第一种格式:
try
{
需要被检测的代码;
}
Catch(Exception e)
{
处理的代码;
}
finaly
{
一定会执行的语句。
}
(2)第二种格式:
try
{
需要被检测的代码;
}
Catch(Exception e)
{
处理的代码;
}
(3)第三种格式:
try
{
需要被检测的代码;
}
finaly
{
一定会执行的语句。(即使是程序遇到return或者是throw等程序结束标志,也会执行。唯一一种情况就是程序遇到System.exit(0),不会执行。)
}
这里插入一下:final有什么用?
看一需求:客户端-程序访问数据库,而数据库在服务器(一台主机),访问数据就得连接数据库,连完后,过一段时间必须断开连接。因为服务器是一台主机,主机中就有cpu,cpu处理的程序是有限的,则必须连接之后,一段时间断开让其他客户端来连接访问数据库,所以,不敢有没有从数据库中取出数据都的断开连接。
例如:
public void method()
{
连接数据库;
数据操作;
关闭数据库;
}
增,删,改,查,可能会出现一些问题,如要删除的数据不存在。throw SQLException();抛完异常,程序就结束了,则关闭操作就没有被执行到。因此,没问题,能按顺序执行到,关闭数据库。出了问题就关闭不了,就在耗费资源。则:关闭数据库动作,无论数据操作是否成功,一定要关闭资源。
public void method()
{
try
{
连接数据库;
数据操作;
}
catch(SQLException e)
{
}
finally
{
关闭数据库;
}
}
/*
finally代码:定义一定执行的代码。
通常用于关闭资源。
*/
(4)第四种格式:
try
{
需要被检测的代码;
}
Catch(Exception e)
{
处理的代码;
}
Catch(Exception e)
{
处理的代码;
}
代码如下:
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,0);
System.out.println(“x=”+x);
}
Catch(Exception e)
{
System.out.println(“除0了”);
}
System.out.println(“over”);
}
}
看图进一步掌握:
可以对捕获到的异常对象进行常见方法操作:
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d=new Demo();
try
{
int x=d.div(4,0);
System.out.println(“x=”+x);
}
Catch(Exception e)
{
System.out.println(“除0了”);
//打印异常信息。Java能识别的异常有异常信息。
//如果是自定义异常,则需要自己定义异常信息。
// /by zero
System.out.println(e.getMessage());
//打印异常名、异常信息
//java.lang.ArithmeticException:/by zero
//如果是自定义类的异常信息,异常类名前面没有
//java.lang
//可知,toString()调用了getMessage()
System.out.println(e.toString());
//打印异常名、异常信息、异常位置。分行打印。
//java.lang.ArithmeticException:/by zero
//java.lang.ArithmeticException:/by zero
//at Demo.div(Exception.java.50)
//atExceptionDemo.main(ExceptionDemo.java:62)
//虚拟机默认的处理机制就是调用这个方法。
e.printStackTrace();
}
System.out.println(“over”);
}
}
通常功能可能有问题,我们必须要在函数上声明。利用关键字throws,功能内可能会发生什么异常就声明什么什么异常,当然你也可以声明Exception,但是这样调用者不知道具体会发生什么问题,处理的也会没有针对性,因此,建议声明的具体一些,这样处理的也就具体一些。当我们在函数这样声明了,就表示告诉调用者这个功能是有可能发生问题的。强制调用者必须在调用的时候进行处理,否则会编译失败。处理有两种方式:一种是通过try{}catch(){},一种是继续在它的函数头上声明向它的上一级抛。告诉它的上一级他可能会发生异常。异常可以逐级向上抛,抛到java虚拟机就到头了。||对于多异常,一个功能可能会发生多个问题。也是类似的解决办法。唯一要注意的就是,对方声明几个异常,(这几个异常用逗号隔开)就对应有几个catch块,不要多定义。如果你非要再加个catch(Exception e){},这个catch块只能放在最后,否则只要发生异常,不管什么异常都由它处理了,其他若干个catch等于废话,永远执行不到,编译会失败。
温馨提示:建议在进行catch处理的时候,catch中一定要定义具体的处理方式。不要简单的定义一句e.printStackTrace(),或者简单的书写一条输出语句。那样的话还不如不解决(声明+处理),发生问题直接让虚拟机启动它的默认处理机制。而且,你这样用户也看懂。真发生了问题,我们会用一个硬盘文件记录下来,称之为异常日志文件。管理员/网站维护人员回去看,会去解决,调试。我们在学习异常的时候,打印是为了方便。
2、开发员自定义的异常。
比如:FushuException.功能中我们不允许发生的事(如除数不能为负数)、和实际情况不符合的事(如求面积不能为负)可能发生。就会产生一个问题。我们就自己定义一个异常类。在异常类出现之前,我们对此的解决办法就是通过if-else语句进行判断,但是这将会导致问题处理代码(if(){})和正常流程代码(else{})联系紧密,聚集在一起阅读起来很痛苦,因此异常的出现能让问题处理代码和正常流程代码相分离。
自定义异常和java识别的异常唯一区别就是:问题发生时需要手动抛出异常。利用关键字throw,如:
throw new FushuException();
(这里注意:throw和return一样也是程序结束的标志,后面不能再有语句。否则编译失败。)
唯一的特殊之处:手动抛出的异常如果在内部解决掉,函数上就不要再声明,向调用者抛。如:
class Demo
{
public void method()
{
try
{
throw new Exception();
}
catch(Exception e)
{
try
{
throw e;
}
catch()
{
}
}
}
}
其他的地方和java能够识别的异常使用相同。
自定义异常类必须继承Exception。Throwable这个体系有一个特点:异常类和异常对象都具备可抛性。
实例:
class FushuException extends Exception
{
}
class Demo
{
int div(int a,int b)
{
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());
System.out.println(“除数出现负数”);
}
System.out.println(“over”);
}
}
运行结果:
FushuException
除数出现负数了
Over
如何定义异常信息?
我们知道:
class Throwable
{
private String message;
Throwable()
{
}
Throwable(String message)
{
this.message=message;
}
public String getMessage()
{
return message;
}
}
class Exception extends Throwable
{
private String message;
Exception()
{
}
Exception(String message)
{
//this.message=message;父类已做,自类没必要再做
super();
}
}
因此,对于自定义异常类,可以这么定义异常信息:
class FushuException extends Exception
{
//自定义异常也可以没有异常信息
FushuException()
{
}
FushuException(String message)
{
This.message=message;
}
}
class Demo
{
int div(int a,int b)
{
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());
System.out.println(“除数出现负数 /by fushu”);
}
System.out.println(“over”);
}
}
运行结果:
FushuException:除数出现了负数 /by fushu
Over
当然我们还可以在自定义异常类中定义其他的功能。
3、RuntimeException
(1)对于继承了RuntimeException的java能够识别的异常类:
如果在函数上声明了,调用者可以不用解决,照样编译通过。这样的类有:ArithmeticException,ArrayIndexOutofBoundException、NullPointerException等。
(2)对于继承了RuntimeException的开发员自定义的异常类:
① 如果在函数内抛出了异常,函数上可以不用声明,编译一样通过。
② 如果在函数上声明了,调用者可以不用解决,照样编译通过。
对于(1)和(2)中的第二条,我们干脆就不声明了,因为你声明了,调用这就会习惯性去解决,虽说是解决了,但可能没有实质性的解决掉。这样等于将问题隐藏了。因此:自定义异常类时,如果该异常发生,程序无法再继续进行,我们想让程序停掉。那么我们就让自定义异常类继承RuntimeException。这样问题最后抛向了虚拟机。启动默认处理机制。
说到这:
对于异常又可以分为:
1、编译时被检测的异常。(可处理,需处理)
2、编译时不被检测的异常。(运行时异常)(不可处理)
4、异常经典示例
(1)毕老师用电脑上课
class Computer
{
public void run()
{
System.out.println(“电脑运行”);
}
public void reset()
{
System.out.println(“电脑重启”);
}
}
class Teacher
{
private String name;
private Computer cmpt;
Teacher(String name)
{
this.name=name;
cmpt=new Computer();
}
public void prelect()
{
cmpt.run();
System.out.println(“讲课”);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Teacher t=new Teacher(“毕老师”);
T.prelect();
}
}
编译运行:
电脑运行
上课
/*
开始思考上课中出现的问题。应该是电脑出现了问题,而不是老师。
比如:
电脑蓝屏
电脑冒烟
要对问题进行描述,封装成对象。
*/
class LanPingException extends Exception
{
LangPinException(String message)
{
super(message);
}
}
class MaoYanException extends Exception
{
MaoYanException(String message)
{
super(message);
}
}
class Computer
{
private int state=1;
public void run()
throws LanPinException,MaoYanException
{
if(state==2)
throw new LanPinException(“蓝屏”);
if(state==3)
throw new MaoYanException(“冒烟”);
}
public void reset()
{
state=1;
System.out.println(“电脑重启”);
}
}
class Teacher
{
private String name;
private Computer cmpt;
Teacher(String name)
{
this.name=name;
cmpt=new Computer();
}
public void prelect()throws MaoYanException
{
try
{
cmpt.run()
}
catch(LanPinException e)
{
cmpt.reset();
}
catch(MaoYanException e)
{
throw e;
}
System.out.println(“讲课”);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Teacher t=new Teacher(“毕老师”);
Try
{
T.prelect();
}
catch(Exception e)
{
}
}
}
那么问题来了,这个冒烟的问题抛给了main(),它也不能处理。两者没有直接的关系。因此,在prelect()中的第二个catch就不能抛出e。那怎么办呢?把冒烟的异常转化成授课老师的异常在抛给main()去处理。具体为:电脑冒烟,老师就不能讲课,因此老师就产生了异常。
/*
当冒烟发生后,出现讲课进度无法继续。
出现了讲师问题:课时无法完成。
*/
class LanPingException extends Exception
{
LangPinException(String message)
{
super(message);
}
}
class MaoYanException extends Exception
{
MaoYanException(String message)
{
super(message);
}
}
//定义新异常类
class NoPlanException extends Exception
{
NoPlanException(String message)
{
super(message);
}
}
class Computer
{
private int state=1;
public void run()
throws LanPinException,MaoYanException
{
if(state==2)
throw new LanPinException(“蓝屏”);
if(state==3)
throw new MaoYanException(“冒烟”);
}
public void reset()
{
state=1;
System.out.println(“电脑重启”);
}
}
class Teacher
{
private String name;
private Computer cmpt;
Teacher(String name)
{
this.name=name;
cmpt=new Computer();
}
public void prelect()throws MaoYanException
{
try
{
cmpt.run()
}
catch(LanPinException e)
{
cmpt.reset();
}
catch(MaoYanException e)
{
test();
//解决不了,继续抛。
//解决得了,继续抛。抛和该功能相关的异常。
throw new NoPlanException(“课时无法继续”);
}
System.out.println(“讲课”);
}
public void test()
{
System.out.println(“lianxi”);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Teacher t=new Teacher(“毕老师”);
try
{
t.prelect();
}
catch(NoPlanException e)
{
System.out.println(e.toString());
System.out.println(“换电脑或者换老师,不然就放假”);
}
}
}
(2)求图形面积
interface Shape
{
void getArea();
}
class Rec implements Shape
{
private int len,wid;
Res(int len,int wid)
{
this.len=len;
this.wid=wid;
}
public void getArea()
{
System.out.println(len*wid);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Rec r=new Rec(3,4);
r.getArea();
}
}
运行结果:
12
传入(3,-4)
运行结果:
-12
问题:这与现实生活中的情况不符合。于是我们用以前常用的方法来解决:
interface Shape
{
void getArea();
}
class Rec implements Shape
{
private int len,wid;
Res(int len,int wid)
{
if(len<=0||wid<=0)
System.out.println(“非法”);
else
{
this.len=len;
this.wid=wid;
}
}
public void getArea()
{
System.out.println(len*wid);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Rec r=new Rec(3,4);
r.getArea();
}
}
这种解决方案使得正常流程代码和问题处理代码聚集在一起。阅读性很差。因此定义异常类:
class NoValueException extends Exception
{
NoValueException(String message)
{
super(message);
}
}
interface Shape
{
void getArea();
}
class Rec implements Shape
{
private int len,wid;
Res(int len,int wid)throws NoValueException
{
if(len<=0||wid<=0)
Throw new NoValueException(“出现非法值”);
else
{
this.len=len;
this.wid=wid;
}
}
public void getArea()
{
System.out.println(len*wid);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
try
{
Rec r=new Rec(3,4);
r.getArea();
}
catch(NoValueException e)
{
System.out.println(e.toString());
}
System.out.println(“over”);
}
}
但是这个问题其实我们不应该去处理,处理不当等于将问题隐藏,我们应该根据异常信息去改动程序。因此应该让NoValueException继承RuntimeException。如下所示:
class NoValueException extends RuntimeException
{
NoValueException(String message)
{
super(message);
}
}
interface Shape
{
void getArea();
}
class Rec implements Shape
{
private int len,wid;
Res(int len,int wid)throws NoValueException
{
if(len<=0||wid<=0)
Throw new NoValueException(“出现非法值”);
else
{
this.len=len;
this.wid=wid;
}
}
public void getArea()
{
System.out.println(len*wid);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Rec r=new Rec(3,4);
r.getArea();
}
}
5、异常—覆盖时的异常特点
(1)子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。或者不抛。若在子类中抛出其他异常,只能在内部解决,不能throws声明出去。
(2)如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子类。或者不抛。若在子类中抛出其他异常,只能在内部解决。不能throws声明出去。
(3)如果父类或者接口的方法中没有异常抛出,那么子类在覆盖该方法时,也不可以抛。如果子类方法可能发生异常,只能在内部解决,不能throws声明出去。
其实:这些规定都是由于,声明时父类声明了具体的异常,如果声明的是根异常类Exception,则没有这些规定。
示例:看图
2015-11-22至2015-11-24著