异常
什么是异常,java提供异常处理机制有什么用?
程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
。
java语言是很完善的语言,提供了异常的处理方式,程序执行过程中出现了不正常的情况,java把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更加健壮。
程序在执行时控制台出现了异常信息。这个信息是JVM打印的。
什么是异常:程序执行过程中的不正常情况。
异常的作用:增强程序的健壮性。
java语言中异常是以什么形式存在的呢?
1、异常在java中以类的形式存在,每一个异常类都可以创建异常对象。
2、异常对应的现实生活中是怎样的?
火灾(异常类):
2008年8月8日,小明家着火了(异常对象)
2008年8月9日,小刚家着火了(异常对象)
2008年9月8日,小红家着火了(异常对象)
类是:模板。
对象是:实际存在的个体。
异常的继承结构
我们可以使用UML图来描述一下继承结构。
画UML图有很多工具,例如:Rational Rose(收费的)、starUML等......
什么是UML?有什么用?
UML是一种统一建模语言。
一种图标式语言(画图的)
UML不是只有java中使用。只要是面向对象的编程语言,都有UML。
一般画UML图的都是软件架构师或者说是系统分析师,这些级别的人员使用的。
软件设计人员使用UML。
在UML图中可以描述类与类之间的关系,程序执行的流程,对象的状态等。
在java软件开发中,软件分析师/设计师负责设计类,java软件开发人员必须要能看懂。
Object类下的Throwable类下有两个子类,Error和Exception。
所有的错误只要发生,Java程序只有一个结果那就是终止程序的执行,退出JVM,错误是不能处理的。
Exception下有两部分,一部分是Exception的直接子类,一部分是RuntimeException。
所有的Exception的直接子类,都叫做编译时异常。编译时异常是在编译阶段发生的么?不是。编译时异常是表示必须在编写程序的时候预先对这种异常进行处理,如果不处理编译器报错。编译时异常又被称为受检异常,还有叫做受控异常。
所有的RuntimeException及子类都属于运行时异常。运行时异常在编写程序阶段,你可以选择处理,也可以不处理。运行时异常还有另一些名字:未受检异常、或者非受控异常。
不管是错误,还是异常,都是可抛出的。
注意:编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。
编译时异常为什么而得名?
因为编译时异常必须在编写代码时预先处理,如果不处理编译器报错,因此得名。
所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。
因为异常的发生就是new异常对象。
编译时异常和运行时异常的区别
编译时异常一般发生的概率比较高。
举个栗子:
你看到外面下雨了,你出门之前会预料到:如果不打伞,我可能会生病(生病是一种异常)。而且这个异常发生的概率很高,所以出门前要拿一把伞。
“拿一把伞”就是对“生病异常”发生之前的一种处理方式。
对于一些发生概率较高的异常,需要在运行之前对其进行预处理。
运行时异常一般发生的概率比较低。
举个栗子:
小明走在大街上,可鞥会被天上的飞机轮子砸到。被飞机轮子砸到也算是一种异常。但是这种异常发生的概率较低。在出门之前没必要提前对这种发生概率较低的异常进行预处理。如果预处理这种异常,你会活的很累。
假设Java中没有对异常进行划分,没有分为编译时异常和运行时异常,所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果呢?
首先,如果这样得话,程序肯定时绝对安全的。但是程序员编写程序太累,代码导出都是处理异常的代码。
Java语言中对异常的处理包括两种方式:
第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。谁调用我这个方法,我就抛给谁。抛给调用者来处理。
这种处理异常的方式:上报。
第二种方式:使用try..catch语句进行异常的捕捉。
这个异常不会上报,自己把这个事儿处理了。异常抛到此处为止,不再上抛了。
异常发生之后,如果我选择上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
注意:Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果,终止java程序的执行。
throws后面可以写多个异常,可以使用逗号隔开。
注意:只要异常没有捕捉,采用上报的方式,后续代码不会执行。
另外要注意:try语句块中的某一行出现异常,该行后面的后面的代码不会执行。
深入try..catch
catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试。
catch写多个的时候,从上到下,必须遵守从小到大。
JDK8新特性
catch小括号里面可以写多个异常中间用”|“或符号隔开
在以后的开发中,处理编译时异常,应该上报还是捕捉呢,怎么选?
如果希望调用者来处理,选择throws上报。
其他情况使用捕捉的方式。
异常对象的常用方法
异常对象有两个非常重要的方法:
获取异常简单的描述信息:这个信息实际上就是构造方法上面String参数。
String msg = exception.getMessage();
打印异常追踪的堆栈信息:
java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。
exception.printStackTrace();
我们以后查看异常的追踪信息,我们应该怎么看,可以快速的调试程序呢?
异常追踪信息,从上往下一行一行看。
但是需要注意的是:SUN公司写的代码就不用看了。(看包名就知道是自己的还是SUN的。)主要的问题是出现在自己编写的代码上
关于try..catch中的finally子句
在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。
finally子句必须和try一起出现,不能单独编写。
放在finally语句块中的代码时一定会执行的。
finally语句通常使用在哪些情况下呢?
通常在finally语句块中完成资源的释放/关闭。
因为finally中的代码比较有保障。
即使try语句块中的代码出现异常,finally中代码也会正常执行。
try和finally,没有catch可以么?可以
try不能单独使用。
try finally可以联合使用
System.exit();//退出JVM
退出JVM之后,finally语句中的代码就不执行了!
finally面试题
public class Text{
public static void main(String[] args){
int result = m();
System.out.println(result);//100
}
public static int m(){
int i = 100;
try{
return i;
}finally{
i++;
}
}
}
问这个输出结果是什么?
result的结果是100
反编译之后的效果是这样的
public static int m(){
int i = 100;
int j = i;
i++;
return j;
}
java语法规则(有一些规则是不能破坏的,一旦这么说了,就必须这么做)
java中有一条这样的规则:
方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)
java中还有一条语法规则:
return语句一旦执行,整个方法必须结束(亘古不变的语法!)
final finally finalize有什么区别?
final是一个关键字。表示最终的,不变的。
finally也是一个关键字,和try联合使用,使用在异常处理机制中。在finally语句块中的代码是一定会执行的。
finalize()是Object类中的一个方法。作为方法名出现。所以finalize是标识符。
final 关键字
final修饰的类无法继承。
final修饰的方法无法覆盖。
final修饰的变量不能重新赋值。
finally 关键字
和try一起联合使用。
finally语句块中的代码时必须执行的。
finalize 标识符
是一个Object类中的方法名。
这个方法是由垃圾回收器GC负责调用的。
自定义异常
SUN提供的JDK内置的异常肯定时不够用的。在实际的开发中,有很多业务,这些业务出现异常之后,JDK中都是没有的。和业务挂钩的。那么异常类我们程序员可以自定义吗?可以。
Java中怎么自定义异常呢?
第一步:编写一个类继承Exception(编译时异常)或者RuntimeException(运行时异常)。
第二步:提供两个构造方法,一个无参数的,一个带游String参数的。
死记硬背就行了。
public class MyException extends Exception{
public MyException(){
}
public MyException(String s){
super(s);
}
}
在以后的程序里面凡是出现异常的地方,你都可以通过throw new的方式去手动抛异常。
重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,可以更少。
以后开发的时候方法重写,就直接复制粘贴父类的就行了。