Java是面向对象的语言,当然也对异常包装成了对象(Throwable),方便了我们的使用。
异常:就是程序在运行时出现不正常情况。
异常由来:
问题也是现实生活中一个具体的事物,也可以通过Java的类的形式进行描述。并封装成对象。 其实就是Java对不正常情况进行描述后的对象的体现。描述不正常的情况的类,就称为异常类。
不同的问题用不同的类进行具体的描述。比如角标越界异常,空指针异常等等。
问题很多,意味着描述的类也很多,将其共性进行向上抽取,形成了异常体系。
异常的好处:
(1)、将问题进行封装。
(2)、将正常流程代码和问题处理代码相分离,方便于阅读。
异常体系的特点:子类的后缀名都是用父类名作为后缀,阅读性很强。
最终问题(不正常情况)就分成了两大类。
Throwable:
无论是Error,还是Exception问题,问题发生就应该可以抛出,让调用者知道并处理。
该体系的特点就在于Throwable及其所有的子类都具有可抛性。
可抛性到底指的是什么呢?怎么体现可抛性呢?
throws throw 凡是可以被这两个关键字所操作的类和对象都具备可抛性。
1, 一般不可处理的。Error
特点:是同Jvm抛出的严重性的问题。
通常出现重大问题如:运行的类不在在或者内存溢出等。
这种问题发生一般不针对性处理。直接修改程序。
错误:通常是不编写代码对其进行针对处理。
2,可以处理的。Exception
处理Exception异常有俩种方法:
一是:在运行时运行出现的一些情况,可以通过 try catch finally
二是:通过throws在函数上声明。(如果函数内throw异常,那么函数就必须声明)
异常:是在运行时期发生的不正常情况..
处理异常(Exception)的几种格式:
格式一:
try{
可能发生异常的代码;
}
catch(接收捕获异常类的对象 异常类引用变量){
处理代码;
}
格式二:
try{
可能发生异常的代码;
}
fianlly{
一定要执行的语句;(通常用于关闭资源)
}
格式三:
try{
可能发生异常的代码;
}
catch(捕获异常类的对象 异常类引用变量){
处理代码;
}
finally{
一定要执行的语句;(通常用于关闭资源)
}
异常示例:
class Test {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9};
try{
System.out.println(arr[5]);
}
catch (Exception e){
System.out.println("数组角标越界");
}
finally{
System.out.println("return 之前必须执行");
return ;
}
}
}
throw和throws的用法
(1)、throw定义在函数内,用于抛出异常对象。
(2)、throws定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开
异常的选用:
当抛出的异常是调用者误操作完成的,那么继承RuntimeException类,如果该异常的发生,就让该程序停掉。
一般情况下继承的都是Exception异常。
异常的分类:
1,编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。这样的问题都可以针对性的处理。
2,编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。
所以自定义异常时,要么继承Exception。要么继承RuntimeException。
注意:
当函数内有throw抛出异常对象,并未进行try处理。必须要在函数上声明,否则编译失败。
Exception有一个特殊的子类,RuntiimeException。函数内如果抛出的RuntimeException异常,函数上可以不用声明。
例子:
class MyRutimeException extends RuntimeException{
MyRutimeException(String msg){
super(msg);
}
}
class Test{
public static void main (String[] args) {
try{
show(true);
}
catch(MyRuntimeException e){
System.out.println(e.toString());
//这里写成e可不可以,当然是可以的,因为默认就是toString()
}
}
public static void show(boolean b){
if(b)
throw new MyRutimeException("值为true了");
}
}
注意:
如果Exception在内部被解决,比如内部catch(捕获)并对异常进行了处理,则该方法不用声明异常。
记住一点,catch是用于处理异常,如果没有catch就代表异常没有被处理。
如果该异常是检测,可以没有catch语句。
当然:如果抛出的是多个异常,就要有几个catch,也有简单的做法,直接抛父类异常Exception
异常处理的原则:
1,函数内容如果抛出需要检测的异常,那么函数上必须要声明。否则必须在函数内用try catch捕捉,否则编译失败。
2,如果调用到了声明异常的函数,要么try catch要么throws,否则编译失败。
(如果调用的函数抛出异常,那么调用者可以try catch也可以throws)
(如果调用的是复写父类的函数抛出异常,那么调用者只能抛出父类同样的异常,或者父类异常的子集)
3,什么时候catch,什么时候throws 呢?
功能内容可以解决,用catch。
解决不了,用throws把异常告诉调用者,由调用者解决 。
4,一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。
内部有几个需要检测的异常,就抛几个异常,抛出几个,就对应有几个catch。
5,抛到最后抛给谁才是个头呢?答案是抛到虚拟机才是头。
fianlly 特殊之处:
finally语句中定义的是一定要执行的语句,通常用于关闭资源。(因为资源必须释放)
finally除非遇到System.exit(0);也是就虚拟机退出才不会执行。
子类覆盖父类时对异常处理的注意事项:
1、如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2、如果父类方法抛出多个异常,那么子类再覆盖该方法时,只能抛出父类异常的子集。
3、如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try。
自定义异常
定义类继承Exception或者RuntimeException
(其实我们这样也是用面向对象思考问题,把我们程序设计中可能出现的问题进行了封装。)
(1)、为了让该自定义类具备可抛性
(2)、让该类具备操作异常的共性方法
(3)、当要自定义异常的信息时,可以使用父类已经定义好的功能,异常信息传递给父类的构造函数。
class FuShuIndexException extends Exception
{
FuShuIndexException()
{}
FuShuIndexException(String msg)
{
super(msg);
}
}
class Demo
{
public int method(int[] arr,int index)throws NullPointerException,FuShuIndexException
{
if(arr==null)
throw new NullPointerException("没有任何数组实体");
if(index<0)
throw new FuShuIndexException("负数角标异常!!!!");
return arr[index];
}
}
class ExceptionDemo4
{
public static void main(String[] args)
{
int[] arr = new int[3];
Demo d = new Demo();
try
{
//int num = d.method(null,-1);
int num = d.method(arr,-1);
System.out.println("num="+num);
}
catch(NullPointerException e)
{
System.out.println(e.toString());
e.printStackTrace();
}
catch (FuShuIndexException f)
{
System.out.println("message:"+f.getMessage());
System.out.println("string:"+f);
f.printStackTrace();//jvm默认的异常处理机制就是调用异常对象的这个方法。
}
/*
catch(Exception e)//多catch父类的catch放在最下面。
{
}
*/
System.out.println("over");
}
}
/*
毕老师用电脑上课。
问题领域中涉及两个对象。
毕老师,电脑。
分析其中的问题。
比如电脑蓝屏啦。冒烟啦。
*/
class LanPingException extends Exception
{
LanPingException(String msg)
{
super(msg);
}
}
class MaoYanException extends Exception
{
MaoYanException(String msg)
{
super(msg);
}
}
class NoPlanException extends Exception
{
NoPlanException(String msg)
{
super(msg);
}
}
class Computer
{
private int state = 2;
public void run()throws LanPingException,MaoYanException
{
if(state==1)
throw new LanPingException("电脑蓝屏啦!!");
if(state==2)
throw new MaoYanException("电脑冒烟啦!!");
System.out.println("电脑运行");
}
public void reset()
{
state = 0;
System.out.println("电脑重启");
}
}
对教师进行描述
教师属性 姓名(name),电脑(comp)
教师行为 讲课(prelect()) 如果出现异常,教师还可以布置练习方法(Test())
class Teacher
{
private String name;
private Computer comp;
Teacher(String name)
{
this.name = name;
comp = new Computer();
}
public void prelect()throws NoPlanException
{
try
{
comp.run();
System.out.println(name+"讲课");
}
catch (LanPingException e)
{
System.out.println(e.toString());
comp.reset();
prelect();
}
catch (MaoYanException e)
{
System.out.println(e.toString());
test();
//可以对电脑进行维修。
// throw e;
对异常进行了转换,本人是电脑冒烟故障,但是会影响到教师的课程,所以又对异常进行了封装,把电脑的问题转换成教材课程无法继续的问题,这就是异常的异常转换。
throw new NoPlanException("课时进度无法完成,原因:"+e.getMessage());
}
}
public void test()
{
System.out.println("大家练习");
}
}
创建教师对象,调用教师的讲课方法。
教师讲课可能出现问题,所以对发生的问题进行了处理
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("换人");
}
}
}
异常转换:
如果该异常处理不了,但并不属于该功能出现的异常,可以将异常转换后,再抛出和该功能相关的异常
或者异常可以在内部处理,但需要将异常缠上本功能相关的内容提供出去,调用者知道,并处理,也可以将捕获的异常处理后,转换为新的异常。
异常——练习
/*
有一个圆形和长方形,
都可以获取面积,对于面积如果出现非法的数值,视为是获取面积出现问题
问题通过异常通过异常来表示
现有对这个程序进行基本设计
*/
/*
由于面向对象的分析,分析出对圆和长方形求出对应的面积。
由于都是求面积,所以把它们的共性向上抽取,抽成一个接口;
方便让程序员们一看就知道这个体系是在求面积。
*/
interface Shape{
/*
分析返回结果,为了求面积,我们只要知道面积,需不需要给我返回结果。
单纯需要知道面积,而不需要面积再去做运算,是不需要返回结果的。void
*/
double getArea();
}
class NoValueRuntimeException implements RuntimeException{
NoValueException(String msg){
super (msg);
}
}
class Circle implements Shape{
private double radius;
private static final double PI = 3.14;
Circle(double radius){
if(radius <=0)
throw new NoValueRuntimeException("出现非法值");
this.radius=radius;
}
/*
复写父接口的方法,定义子接口,特定内容。父类接口方法的默认权限就是public
*/
public double getArea(){
//圆形的面积计算公式:π*r2
return (PI*radius*radius);
}
}
class Rec implements Shape{
private double len;
private double wid;
Rec(double len,double wid){
if(len<=0 || wid<=0)
throw new NoValueRuntimeException ("出现非法值");
this.len=len;
this.wid=wid;
}
public double getArea(){
//长方形的面积计算公式:len(长)*wid(宽)
return (len*wid);
}
}
class Test {
public static void main (String[] args){
try {
Rec r = new Rec (-3,4);
r.getArea();
}
catch (NoValueRuntimeException ,e){
e.printStackTrace();
}
System.out.println("over");
}
}