------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1,异常的由来:
一切事物皆对象, 问题也是现实生活中一个具体的事物,就像疾病,因此也可以通过java的类的形式进行描述,并对其进行封装成对象;
其实就是java对不正常情况进行描述后的对象的体现。
对于问题的划分:两种:
一种是严重的问题;一种非严重的问题;
对于严重的:java通过Error类进行描述。
对于Error一般不编写针对性的代码对其进行处理, 因为就像疾病,太严重了,就治愈不了了。
对于非严重的:java通过Exception类进行描述。对于Exception可以使用针对行的处理方法进行处理
但是无论Error或者Exception都具有一些共性的内容, 因此可以向上抽取出来形成异常的体系。
异常的体系:
Throwable
|---Error
|---Exception
|---RuntimeException
|---ArrayIndexOutOfBoundsException
异常体系的特点:
异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throw和throws关键字所操作。
只有异常体系具备这种特点;
其中throw和throws的用法区别:
throw定义在函数内,用于抛出异常的对象。
throws定义在函数上, 用于抛出异常类, 可以抛出多个,多个需要用逗号隔开;
如果需要具体的处理异常,就需要在调用者的函数中写上多个对应的catch(..Exception e)。
--------------------------------------------------------------------------------------,
2,异常的处理:
a:try处理语句————
java提供了特有的语句进行处理。
try{
需要被检测的代码;
}
catch(异常类 变量){
处理异常的代码;(处理方式)
}
finally{
一定会执行的语句;一般是关闭某种资源(像数据库)
}
异常处理执行的过程如图:
异常对象,抛给了d.div(4.0)这个位置,主函数中这个方法的位置上;
有try , 就尝试着去检测,有问题就检测到了,这个就是这个语句的功能;
Try 检测到了这个对象后,catch就捕捉到了, 定义的这个形参Exception e=new ArithmeticException();,这里体现多态的思想;
如果不写这个try的话, 那么主函数就没有检测, 说明主函数无法处理,那么他就最终抛给了JVM(java虚拟机),JVM就默认处理,调用默认的异常处理机制,具体调用了父类中的e.printStackTrace();这个方法,从而导致程序的停止。
b:声明异常————
如上图中, 这个除法的功能是别人编写的,主函数在调用的时候,try语句不一定会执行,因为不知道这个功能是否发生问题,那么这样就可处理,可不处理,那么在传参数的时候,就会出现程序异常, 因此这样的开发不是最优的;
传值的时候,值是不确定的, 所以b为0,就可能传递进来了, 就容易发生异常,因此在这个函数的后面加上一个标识,表示这个函数在调用的时候,可能出现问题,希望调用着处理,函数 函数名 throws 异常类名{}
那么声明标识和不声明有什么区别呢?
声明了, 就表明这个函数可能有问题,抛了的话, 那么调用这个函数中必须要有异常处理的语句, 或是再次抛出,最终jvm处理了。如果没有处理的话, 就会编译失败;
这样就提高了安全性。
如果不声明的话, 是没有问题的;
说明黄条部分:
一般不会再次抛出,会在调用的地方给处理;
3,多异常处理:
由来:在定义函数(功能)的时候,不止发生一个问题,那么函数的声明的问题就不止一个了,如果声明的越具体,处理的就会越具体,
这才是声明的特点;
声明多个具体,就声明多个具体的异常,
在try..catch语句中, 就需要对应有多个catch(异常类 变量);
如果抛的异常,不是指定的异常,那么就可以在最下面写上
catch(Exception e)
但是这样写有两点不好:
3_1:不具体, 不知道发生了什么事情,程序还在运行中,
如果发生了对方指定的异常以外的情况,这时应该让程序停止,需要知道那个程序出错, 错在哪里。
3-2如果catch(Exception e)放在最上面, 下面的两个异常处理的就不会执行了,这样就会失去意义。
代码如下:
<pre name="code" class="java">class Demo
{
int div(int a,int b) throws ArithmeticException,ArrayIndexOutOfBoundsException//表明这个程序在运行时可能发生异常,希望被处理
{
int[] arr=new int[a];
System.out.println(arr[4]);//如果发生异常, 这里new ArrayIndexOutOfBoundsException();
return a/b;//如果这里发生异常,这里new ArithmeticException();
}
}
class ExceptionDemo5
{
public static void main(String[] args)
{
Demo d=new Demo();
try{
int x =d.div(5,-1);
System.out.println("X="+x);
}
catch(ArithmeticException e){
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("出错了, 算术异常");
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("数组角标越界了");
}
catch(Exception e){
System.out.println("其他异常的处理");
System.out.println(e);
}
System.out.println("over");
}
}
真发生了问题,不会打印的,会用一个硬盘的文件记录下来,这个文件称为异常日志文件,那么网站的维护人员, 就经常的看日志;
3,对自定义异常的理解:
Java可以对问题进行封装, 我们自己是否可以对可能出现的问题进行特有的封装呢?
他是因为在异常体系中没有的异常, 就像是新出现的疾病,这些问题并未被java所描述和封装。
例如:在上述的程序中, 对于除数是负数, 也视为是错误的是无法进行运算的,那么久需要对这个问题进行自定义的描述。
首先想定义成异常类, 就首先需要继承Exception这个类, 这样就具备了异常类的可抛性,就可以使用throw和throws这两个关键字了。
但是自定义的异常类, java是不认识的, 这时 对出现异常的情况,就需要手动的判断,建立对象,并抛出。
这里有错误,
这时当在函数内部出现有throw抛出异常对象,那么必须给出对于的处理动作,
要么在函数的内部try ...catch 语句处理异常,
要么在函数上声明让调用者去处理,
一般情况下:在函数上声明让调用者去处理,
这时:
调用者主函数也有两种选择,一个是try catch , 一个是抛出异常给JVM
这里异常中没有信息,只有名称,
以前看的是java 在描述异常的时候, java自己定义的,
而这里是我们自己定义声明的,
父类中有getMessage()这个方法,
因此可以复写,
但是这样的写的话, 就比较繁琐,
可以优化,直接调用父类的构造方法,
也想拿到到底是哪里出现问题,
把负数的值赋给他,可以对自定义异常类进行更改:代码如下:
//自定义异常,创建这个类,方便创建对象
/*异常体系中, 异常类和异常对象都需要被抛出,因为会导致程序跳转,
他们都具备可抛性, 这个可抛性是Throwable体系中的独有特点,
只有这个体系中的类和对象才可以被Throw和Throws操作,*/
class FuShuException extends Exception
{
/*private String msg;
FuShuException(String msg){
this.msg=msg;
}
public String getMessage(){//可以重写这个getMessage()方法,
return msg;
}*/
private int value;//看到底哪里出错
FuShuException(){}
FuShuException(String msg){//这样就可以优化了代码
super(msg);
}
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//表明这个程序在运行时可能发生异常,希望被处理
{
//int[] arr=new int[a];
//System.out.println(arr[4]);//如果发生异常, 这里new ArrayIndexOutOfBoundsException();
if(b<0){
throw new FuShuException("出现了除数为零的情况.../by 负数",b); //自定义的异常, java是不认识的,这时就需要手动的,建立对象,并抛出,
}
return a/b;//如果这里发生异常,这里new ArithmeticException();
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d=new Demo();
try{
int x =d.div(5,-1);
System.out.println("X="+x);
}
catch(FuShuException e){
System.out.println("haaa,出现异常了"+e.toString());//toString()方法中调用了getMessage()这个方法;
System.out.println(e.getMessage()+"\n错误的值是"+e.getValue());
}
System.out.println("over");
}
}
4,RuntimeException的理解
Exception中有一个特殊的子类异常RuntimeException运行时异常。
一般算术异常他是自动抛的, 那也可以手动抛出,可以调用这个类中的另一个构造方法,
代码如下:
class Demo
{
int div(int a,int b) //throws ArithmeticException,ArrayIndexOutOfBoundsException//表明这个程序在运行时可能发生异常,希望被处理
{
if(b==0)
{
throw new ArithmeticException("被零除了")
}
return a/b;//如果这里发生异常,这里new ArithmeticException();
}
}
class ExceptionDemo6
{
public static void main(String[] args)
{
Demo d=new Demo();
int x =d.div(5,-1);
System.out.println("X="+x);
System.out.println("over");
}
}
结果:
这里函数内抛了, 但是在函数上并没有声明,而且调用函数出没有try ..catch 语句,
但是如果:
这里编译失败,那么就是函数内抛, 函数就需要标识,不标识, 就编译失败;
原因:
因为ArithmeticException类是RuntimeException类的子类,
这就是RuntimeException类的特点:(运行中的异常类)
如果是RuntimeException及其子类在函数内抛出了,函数上不用声明,
还有:
也没有问题。
总结1:
如果在函数内容抛出改异常, 函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过
为什么在这里抛, 函数上不用声明呢?
如果b=0,下面的return就不能运行了,那么x就没有值了,
那么下面的程序都是失败的,这个时候, java虚拟机就认为这个不要处理,希望程序停掉;
因为:
这里定义功能没有错, 而是使用的时候出错了,瞎传参数造成的,那么产生的问题有你来解决,只有把参数改掉,才可以,
那么在函数内抛了, 为什么不用声明呢?
如果声明了, 就希望调用者主函数处理,如果对方给出了处理方式 ,相当于这个问题就被隐藏了,不声明的话,就不知道这里有问题, 所以只要瞎传值, 程序就会结束,这样就迫使使用者改动;
为的是不让错误不清楚,尽早给出解决方案;
如果该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException。
总结2:
异常有两种:
编译是被检测的异常:
该异常在编译时,如果没有处理(没有抛出也没有try),编译会失败。
该异常在标识,代表着可以被处理。
运行时异常(RuntimeException类)
在编译是, 不需要处理,编译器不检查。
该异常的发生,建议不处理,让程序停止,需要对代码进行修正。
5,finally的用处
什么时候用:
写程序去链接数据库, 数据库的服务器存储着很多的数据;
客户机拿到了数据库中的数据,需要断开链接,数据库的链接数一般是有限的,
数据库的服务器是一台主机,主机中的CPU处理性能是有限的
如果不断开, 在耗费数据库中的资源和在占用一个数据库的链接,那么第11个人就连不上(只能连10个),那么数据库的CPU就处理不了了, 所以需要连完一次,就关闭,
所以连接了数据库, 取了值后, 就断开,
所以不管是否在数据库中取到数据, 这个断开的动作都需要执行,
因此:
finally代码块:定义一定执行的语句,通常用于关闭资源;
举个例子,调用数据库中的数据,
抛给了调用者主函数,数据库异常和我(调用者)是没有关系的, 但是数据没有存成功, 这也是一个异常,
这个调用者就可以处理了,
这个是分层思想,定义分层, 模块式开发
6,异常的格式:
1,try{
别检测的语句
}
catch(异常类名 变量){
处理的语句
}
2,
try{
被检测的语句
}
catch(异常类名 变量){
处理的语句
}
finally{一定执行的代码块, 一般用于关闭资源
}
3,
try{
别检测的语句
}
finally{
一定执行的代码块, 一般用于关闭资源
}
7,覆盖是的子父类的理解:1,,子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者是该异常的子类或者不抛。
2,如果子类中有新的异常问题, 需要直接在子类中进行处理,
为啥不能覆盖父类中没有的异常呢?
代码如下:
class CException extends Exception//C异常继承异常类
{
}
class AException extends Exception//A异常继承异常类
{
{
}
class BException extends AException//B异常继承异常类
{
{
}
class Fu
{
void show()throws AException//在show()方法中继承的是AException
{
}
}
class Zi extends Fu
{
void show()throws BException//子类只有继承BException
{
}
}
class Test
{
void Function(Fu f)
{
try
{
f.show();
}
catch (AException e)
{
}
}
}
class FE_3
{
public static void main(String[] args)
{
new Test().Function(new Zi());
}
}
如果子类继承CException , 那么就在子类的catch(AException e) ,=new CException();但是这两个异常类是没有继承关系的, 因此会报错。
最后:
异常的好处:
1,将问题进行封装;
2,将正常流程的代码和问题的代码相分离,方便于阅读。
异常的处理原则:
1,处理方式有两个:try 或throw.
2,调用到抛出异常的功能是, 抛出几个,就处理几个。
3,一个try可以对应多个catch, 多个catch,父类的catch放在最下面。
4,catch内,需要定义针对性的处理方式,不要简单的定义printStackTrace,输出语句,也不要不写。
5,当捕获到的异常,本功能处理不了的,可以继续在catch中抛出。然后再在catch 代码块中,try ..catch,在接收新的异常,并处理(汇款的例子)
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------