异常的总体结构
注意所有的异常都是trowable的子类,只有是这个类的子类异常才能被jvm抛出,一个好的程序必然是对我们的异常进行了大量的处理,使得我们程序的健壮性大大增强的。
异常的分类
1)根据错误程度
Error一般来说是硬件错误或者是JVM错误。无法在代码中进行处理
Exception是异常,可以抓取并进行处理,良好的异常处理机制可以增强代码的健壮性。
2)根据是否编译前就能检查出来
unchecked exception(未检查的异常): Error 和 RuntimeException 及其子类
checked exceptions (检查了的异常):除了未检查的异常都是检查了的异常
** checked exceptions**: 通常是从一个可以恢复的程序中抛出来的,并且最好能够从这种异常中使用程序恢复。比如 FileNotFoundException, ParseException 等。检查了的异常发生在编译阶段,必须要使用 try…catch(或者 throws )否则编译不通过
unchecked exceptions: 通常是如果一切正常的话本不该发生的异常,但是的确发生了。发生在运行期,具有不确定性,主要是由于程序的逻辑问题所引起的。比如 ArrayIndexOutOfBoundException, ClassCastException 等。从语言本身的角度讲,程序不该去 catch 这类异常,虽然能够从诸如 RuntimeException 这样的异常中 catch 并恢复,但是并不鼓励终端程序员这么做,因为完全没要必要。因为这类错误本身就是 bug,应该被修复,出现此类错误时程序就应该立即停止执行。 因此,面对 Errors 和 unchecked exceptions 应该让程序自动终止执行,程序员不该做诸如 try/catch 这样的事情,而是应该查明原因,修改代码逻辑。
RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理 RuntimeException 的原则是:如果出现 RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。其他(IOException等等)checked 异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。
当抛出异常之后,会有如下的事情发生。
1)在堆上创建异常对象。
2)当前执行路径被终止
3)从当前环境弹出对异常对象的引用。
4)异常处理机制接管程序,
5)异常处理程序执行,将程序从错误的状态中恢复,使得我们的程序可以从另一个地方继续执行或者是继续执行下去
捕获异常基本模块
/**
* 一般的关于异常的处理流程
*/
try{
//try块,尝试产生各种(可能产生异常的)方法的程序块
}catch(RuntimeExceptione){
//catch 块, 用于捕获我们产生各种异常的程序块,对我们的异常进行必要的处理
//注意我们这里有着不同的catch块,但是我们这里并不是和switch一样直接和某个case进行匹配
//而是说我们这类是依次的进行比较,进入第一个符合的catch块,而后面的catch块就不会再去尝试匹配
//所以在使用的时候我们应该注意顺序,从上到下范围应该是逐渐增大的。否则如果第一个就是Catch(Exception e)
//那么后面的catch代码都将不会执行
e.printStackTrace();
}catch(Exceptione){
e.printStackTrace();
}finally{
//finally块 无论如何都会执行的代码。一般用于将我们的对象恢复到安全状态,
//比如流的关闭,比如说网络的关闭等
//不能在这里有return语句,否则的话所有的异常都会丢失。
//还有一种异常丢失的可能性就是说我们的try块中产生异常,然后在我们的finally块中仍然产生了异常,这个
//时候我们的try块中的异常就会丢失
}
异常处理和异常上抛
如果在方法或者说类的最后显示的表明throws **Exception。这就意味着说是可能会抛出这个异常,向上抛出,自身不做处理
如果在try catch块中进行处理的exception就是自己处理,没有向上抛出。
/**
* 用于表示异常自身处理和异常抛出的用例,其中 FileNotFoundExceptiion是向上抛出,而IOException是自己处理
* 本方法的作用是说通过文件路径获得文件的内容
* @param filePath 文件的路径
* @return 文件的内容
* @throws FileNotFoundException
*/
publicstaticStringgetFileContent(StringfilePath)throwsFileNotFoundException{
//数据的暂时存储
Stringcontent="";
//创建输入流对象
FileReaderfReader=newFileReader(newFile(filePath));
BufferedReaderbReader=newBufferedReader(fReader);
//将我们文件中的数据按行读取,并对可能产生的ioException进行处理
try{
while(bReader.readLine()!=null){
content+=bReader.readLine();
}
}catch(IOExceptione){
e.printStackTrace();
}
returncontent;
}
定义属于自己的异常
/**
* 自定义的异常类
* @author kingwen
*
*/
publicclassMyExceptionextendsException{
//构造函数:每次创建实例的时候会输出this is myexception
publicMyException(){
System.out.println("this is myexception");
}
publicMyException(Strings){
super(s);
System.out.println("this is myexception"+s);
}
}
在我们的方法中进行抛出,注意,这里要显示的表示我们这个类可能会抛出这个异常,然后用于让使用这个方法的人自身通过try catch块进行处理或者是继续通过显示说明来进行上抛。
publicclassInputAge{
publicstaticvoidAgeChecked(inti)throwsMyException{
if(0
System.out.println("年龄正常");
}else{
thrownewMyException("年龄不正常");
}
}
}
最后,我们调用这个方法,并通过try catch块进行处理
/**
* 本例用于测试java异常处理
*/
publicclassDemo{
publicstaticvoidmain(String[]args){
try{
InputAge.AgeChecked(109);
}catch(MyExceptione){
System.out.println("自定义的Exception");
e.printStackTrace();
}catch(Exceptione){
System.out.println("excertion e");
e.printStackTrace();
}
}
}
这个是运行结果:
首先是先new了我们的异常对象,在构造方法的时候输出了第一个语句
然后是在我们的catch块中进行的处理,
最后是通过e.printStackTrace()方法将我们的方法调用顺序打印了出来
(就是后面的栈轨迹)
关于异常处理我们平时应该注意的一些代码部分。
尝试使用try catch 嵌套块:对于创建可能失败,处理完之后需要释放资源的代码
publicstaticStringgetFileContent1(StringfilePath){
//数据的暂时存储
Stringcontent="";
//创建输入流对象
FileReaderfReader=null;
BufferedReaderbReader=null;
try{
fReader=newFileReader(newFile(filePath));
bReader=newBufferedReader(fReader);
//将我们文件中的数据按行读取,并对可能产生的ioException进行处理
try{
while(bReader.readLine()!=null){
content+=bReader.readLine();
}
}catch(IOExceptione){
e.printStackTrace();
}finally{
try{
bReader.close();
}catch(IOExceptione){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}catch(FileNotFoundExceptione1){
// TODO Auto-generated catch block
e1.printStackTrace();
}finally{
try{
fReader.close();
}catch(IOExceptione){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
returncontent;
}
对于构造不可能失败但是需要使用完释放资源的对象使用try-finally块
在可去除对象之后紧跟try-finally 原则:
//创建对象
MyObjectobj=newMyObject()
try{
//对于我们的obj对象进行操作
}finally{
//释放我们的对象资源
obj.dispose();
}
最后是我目前来说觉得不怎么重要的部分
栈轨迹:printStackTrace方法所提供的信息可以通过getStackTrace来进行直接访问。
实际上就是我们通过方法e.printStackTrace();进行依次打印的东西。
我们的方法的调用信息实际上是通过一个栈来进行保存的,后进后出,
我们通过e.printStackTrace()将栈中的数据放入到一个数组中,
其中数组中的第一个元素是调用序列的最后一个,在打印的时候我们从第一个开始打印,
所以我们看到的依次是我们报错的地方,调用它的地方,。。。。最后是main方法
重新抛出异常
方法在catch中得到我们的异常对象的引用之后,从当前位置重新抛出
try{
//try块
}catch(Exceptione){
e.printStackTrace();
//重新抛出这个异常,但是这个异常的信息和原来的信息是一致的,
throwe;
//重新抛出这个异常,但是异常的信息变为当前位置的信息,原来的信息丢弃
//throw e.fillInStackTrace();
}
关于异常的知识暂时的了解是这样的,欢迎觉得文章有问题的小伙伴们找我交流。