day09(下)_异常(上)
异常概述:
/* 异常: 就是程序在运行时出现不正常情况 异常由来: 问题也是现实生活中一个具体的事物, 也可以通过java类的形式进行描述. 并封装成对象.其实就是java对不正常情况 进行描述后的对象体现 对于问题的划分: 两种:一种是严重的问题,一种是非严重的问题 严重的:java通过Error类进行描述 对于Error一般不编写针对性的代码对其进行处理 非严重:java通过Exception类进行描述 对于Exception可以使用针对性的处理方式进行处理 无论Error或者Exception都具有一些共性内容 比如: 不正常情况的信息,引发原因等. Error类和Exception类向上抽取-->Throwable类(可查Java API文档)(可抛出/异常) java 提供特有的语句进行处理 try { 需要被检测的代码({ }之间的代码抛出异常,均可用下面catch接收) } catch(异常类 变量) { 处理异常的代码;(处理方式) } finally { 一定会执行的语句; } 3.对捕获到的异常对象进行常见方法操作 String getMessage():返回throwable的详细消息的字符串 *///以 除零 为例 class Arithmetic { public int div(int a,int b) { return a/b;//产生一个JVM识别的ArithmeticException问题
//把该问题封装成一个new ArithmeticException(); //然后把该问题抛给调用该功能(div)的调用者} } class ExceptionDemo1 { public static void main(String[] args) { try//检测问题 //不写try的话,主函数(被JVM调用)没办法处理该问题-->给了JVM-->JVM进行默认处理-->调用默认异常处理机制-->停止程序 //try检测到该问题对象把问题对象丢给catch(捕获) { int x= new Arithmetic().div(4,0); System.out.println("x="+x);//上面的代码已发生问题将不再执行,无任何意义 } catch (java.lang.Exception e)//Exception e=new ArithmeticException(); { System.out.println("除零了"); System.out.println(“e.getMessage()\t”+e.getMessage());// / by zero System.out.println(“e.toString()\t”+e.toString());//异常名称:异常信息 e.printStackTrace();//异常名称,异常信息,异常出现的位置 } //其实JVM默认的异常处理机制,就是在调用printStatckTrace()方法 //打印异常堆栈的跟踪信息 //以上问题处理完try...catch System.out.println("over"); } } /* 关于命名问题:(运行图二) 如果我的源文件名用了关键字Exception.java 命名这里如果不加java.lang将无法通过编译 在src.zip的lang文件夹下也有一个Exception.java JVM并没有编译lang下的Exception.java因此不能使用Exception类 因此加上java.lang,但是强烈不建议源文件名用关键字命名!!!! */图一:
图二:
2.为什么要在函数上声明异常?
/* 为什么要在在函数上声明异常?? 当功能被封装时,只能看见函数声明,根本不知道使用功能会不会发生问题. 因此编写功能的人,在功能后加了个标识(trows+异常类),在功能上通过throws的关键字声明了该功能有可能会出现问题 便于提高安全性,让调用者进行处理,不处理则编译失败 */ class Arithmetic { public int div(int a,int b) throws Exception//把该问题抛给调用该功能(div)的调用者 { return a/b; } } class ExceptionDemo1 { public static void main(String[] args) throws Exception//主函数也不处理,把问题抛给了JVM { int x=new Arithmetic().div(1,0); System.out.println("x="+x); System.out.println("over"); } }
3.多异常处理:
/* 对多(个 )异常处理 1.声明异常时,建议声明更为具体的异常.这样处理的更为具体(可以抛该异常的直接或间接父类异常) 2.对方声明几个异常,就对应有几个catch块 如果多个catch块中的异常出现继承关系, 父类异常catch块放在最下面.因为父类catch可以处理子类异常,接收子类的catch没用 建议在进行catch处理时,catch中一定要定义具体的处理方式. 不要简单定义一句 e.printStackTrace() 也不要简单的就书写一条输出语句 因为用户看不懂(没任何用处),一般会把问题(信息)用一个文件记录下来(异常日志文件),记录着程序 的运行状况,便于管理员管理和解决. 如果出现未知异常,不要用catch(Exception e)处理 这样做就不能发现异常原因 */class Arithmetic { public int div(int a,int b) throws ArithmeticException,ArrayIndexOutOfBoundsException //算术异常,数组越界异常 //throws Exception 抛出的异常不具体//因为在功能上通过throws的关键字声明了该功能有可能会出现问题 { int[] arr=new int[a]; System.out.println(arr[4]); return a/b; } } class ExceptionDemo2 { public static void main(String[] args) //throws Exception//如果调用者不处理该问题,主函数把问题抛给JVM,JVM进行默认处理 { //一般都是检测问题,然后捕捉进行处理 try..catch try { int x=new Arithmetic().div(4,0); System.out.println("x="+x); } /* catch(Exception e)//这两个异常都能处理,因为多态 { //但是不建议这样写-->处理没有针对性 System.out.println(e.toString()); } //不能与以下两个catch共存 //因为这个catch块已处理异常,下面两个没用 */catch (ArithmeticException e)//针对算术异常处理 { System.out.println(e.toString()+"\n被零除了"); } catch(ArrayIndexOutOfBoundsException e)//针对数组越界异常处理 { System.out.println(e.toString()+"\n数组越界"); } System.out.println("over"); } } /* 以上两个异常不会同时发生, 这是因为当角标一越界 System.out.println(arr[4]); 发生异常,函数结束-->抛出数组越界异常 */
4.自定义异常
/* 为什么要自定义异常???因为项目中会出现特有的问题,
而这些问题并未被java所描述并封装对象 所以对于这些特有的问题可以按照java的对问题封装的思想. 将特有的问题.进行自定义的异常封装. 自定义异常: 需求: 在本程序中,对于除数是-1,也视为是错误的是无法进行运算的. 那么就需要对这个问题进行自定义的描述 当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作. ①要么在内部try..catch处理. ②要么在函数上声明让调用者处理. 一般情况下,函数内出现异常,函数上需要声明 自定义异常: 必须是自定义类继承Exception 也可以继承Throwable,Throwable下已有一个Exception体系,因此继承了Exception 继承Exception原因: 异常体系有一个特点:因为异常类(throws)和异常对象(throw)都被抛出 因为它们都具备可抛性.这个可抛性是Throwable这个体系中独有的特点. 只有这个体系中的类和对象才可以被throws和throw操作 例如: class Person { } class Demo { public void method() throws Person//错误,Person不可抛 { } } */ class NegativeException extends Exception { private int value;//显示出该负数 /* ① private String mes; public NegativeException(String mes) { this.mes=mes; } public String getMessage()//复写父类中的方法 { return mes; } */ /* 由于Throwable中有构造函数 public Throwabel(String message) 因此有了以下代码: */ //把①简化 //继承关系:Object<-Throwable<-Exception<-NegativeExceptionpublic NegativeException(String mes,int value) { super(mes); //首先执行Object构造函数初始化—>Throwable构造函数初始化-->接着Exception的-->最后是NegativeException //在下面toString()中会调用从父类继承的getMessage()方法 //也就是说:子类内容通过父类初始化 this.value=value; } public int getValue() { return value; } } //以上类的目的为了创建对象 class Arithmetic { public int div(int a,int b) throws NegativeException//①要么在内部try..catch处理. //②要么在函数上声明让调用者处理. { if(b<0) throw new NegativeException("出现了除数是负数",b);//由于自定义异常JVM不能识别,因此通过手动抛出 //手动通过throw抛出一个自定义异常对象 return a/b; } } class CustomException { public static void main(String[] args) //throws NegativeException //如果抛给JVM,JVM会报Exception in thread "main" NegativeException.. { try { int x= new Arithmetic().div(4,-10); System.out.println("x="+x); } catch(NegativeException e) { System.out.println(e.toString()+e.getValue());//e.toString()只有异常名称,却没有异常的信息(当NegativeException类中未定义内容时) } //因为系统内部自定义的异常并未定义信息 //toString()方法内会调用子类复写的getMessage(); System.out.println("over"); } }
5.特殊的异常子类RuntimeException
/* Exception中有一个特殊的 子类异常RuntimeException体系(运行时异常). 如果在函数内容抛出异常,函数上可以不用声明,编译一样通过 如果在函数上声明了该异常.调用者可以不用进行处理(try..catch/throws).编译一样通过. 之所以不用在函数声明,是因为不需要让调用者处理. 当该异常发生,希望程序停止,不再向下执行.因为在运行时, 出现了,无法继续运算的情况,希望停止程序后,让使用者对代码进行修正. 自定义异常时: 如果该异常的发生,无法在继续进行运算(无法处理), 就让自定义异常继承RuntimeException 反之,有些可以处理的异常,就在函数上标识(声明) 进行处理 对于异常分两种: 1.编译时被检测的异常 javac.exe编译时,检测如果在类的方法中抛出非RuntimeException 或非其子类(例如Exception等),视为可处理的异常 2.编译时不被检测的异常(运行时异常.RuntimeException及其子类) 目的让程序停下来 因此在自定义问题时: 考虑该问题发生时能否处理,不能处理-->继承RuntimeException-->停止程序-->修正代码 能处理->处理完程序可以继续运行-->继承Exception */ class Arithmetic { public int div(int a,int b) //throws ArithmeticException//注意在这我不声明,也可以通过编译 { if(b==0) throw new ArithmeticException("被零除了");//注意在这里抛出了一个异常对象 return a/b; } } class ExceptionDemo { public static void main(String[] args) //throws ArithmeticException { int x=new Arithmetic().div(1,0); System.out.println("x="+x); System.out.println("over"); } } //例二 class Person { public void checkName(String name) { //if(name.equals("lisi"))//由于name值为null,不能调用Equals if("lisi".equals(name))//修正或if(name!=null&&name.equals("lisi")) System.out.println("YES"); else System.out.println("NO"); } } class Demo { public static void main(String[] args) { new Person().checkName(null); } }
异常练习:
/* 老师用电脑上课 开始思考上课中出现的问题:(增强代码健壮性) --> 电脑蓝屏 电脑冒烟 -->对对象描述,封装成对象 注意: 当冒烟发生后,会出现讲课进度无法继续(连锁问题) 这时候就出现了讲师的问题,课时计划无法完成 *///蓝屏异常类 class BlueScreenException extends Exception//蓝屏视为可解决问题 { public BlueScreenException(String message) { super(message); } }//冒烟异常类 class BrokenException extends Exception { public BrokenException(String message) { super(message); } } //计划无法进行 class NoPlanException extends Exception { public NoPlanException(String message) { super(message); } } class Pc { private int state=3;//1代表正常 public void run() throws BlueScreenException,BrokenException //电脑运行时发生异常 { if(state==2) throw new BlueScreenException("蓝屏"); else if(state==3) throw new BrokenException("冒烟"); System.out.println("电脑启动"); } public void reset() { System.out.println("电脑重启"); state=1;//回到正常状态 } } class Teacher { private String name; private Pc p; public Teacher(String name) { this.name=name; p=new Pc(); } public void teach() throws NoPlanException { try { p.run(); } catch (BlueScreenException e)//处理蓝屏问题 { System.out.println(e.toString()); p.reset(); } catch(BrokenException e)//接收了 冒烟问题 对象 { //但抛出 无法上课 对象 test(); throw new NoPlanException("无法讲课\n"+e.getMessage());//处理不了冒烟,因此抛出无法上课,符合实际 //getMessage()获得原因(为什么不能上课?)//throw下面不能有语句//因为根本执行不到-->已抛出,该函数结束-->类似return } System.out.println("开始讲课"); } public void test() { System.out.println("练习");//做练习吧 } } class ExceptionTest { public static void main(String[] args) { try { new Teacher("zhang").teach(); } catch (NoPlanException e) { System.out.println(e.toString()+"\n换老师或放假"); } } } /* 最后注意: throws 和 throw 的区别 1.位置 throws使用在函数上 throw使用在函数内 2. throws后面跟的异常类.可以跟多个以","隔开 throw后跟的是异常对象(异常类的实例) */