Java 异常
来源:互联网 发布:生成对抗网络理解 编辑:程序博客网 时间:2024/06/05 10:02
面对脚本类语言的突飞猛进,穷追猛打,Java还有什么优势?
一个优势就是Java强大的异常处理机制,深入学习并掌握Java异常和其处理机制
可以促进我们在项目中处理更有效的处理异常,使程序更健壮,对测试和调试友好。
0 . 什么是异常?
简单来说,异常是Java程序运行中出现的错误或者未达到预期的程序设定。
一.Java异常的分类
Java把异常当做对象来处理(作为面向对象语言,Java中一切都是对象,都由Object派生),并定义一个基类java.lang.Throwable作为所有异常的超类。Java中的异常分为两大类:错误Error和异常Exception。
1.1Throwable(可抛出):
异常类的最终父类,它有两个子类,Error与Exception。
Throwable中常用方法有:
getMessage():返回异常的消息内容,打日志时常用的e.getMessage()就是继承自方法。
printStackTrace():输出错误堆栈信息到控制台,catch方法块中对异常exception处理的默认方法,但在生产程序中一般不使用,通常做法是通过日志组建将异常信息打印到日志中。
Thorwable虽然是一个实体类,但一般还是当作接口或者虚基类看待,不直接在程序中使用
1.2 Error(错误):表示程序无法处理的错误,一般是指java虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态链接失败等,与程序员的执行操作无关。理论上这些错误是不允许发生的,通常应用程序也无法处理这些错误,如果发生,也不应该试图通过程序去处理,所以Error不是try-catch的处理对象,也无须在其throws子句中声明该方法抛出任何Error或其子类,而JVM一般的处理方式是终止发生错误的线程,所以这些错误很可能使JVM虚拟机停止运行。
Error类常见子类有VirtualMachineError与AWTError。
1.3 VirtualMachineError(虚拟机错误):表示虚拟机出现错误。
在Java运行时内存中,除程序计数器外的虚拟机栈、堆、方法区在请求的内存无法被满足时都会抛出OutOfMemoryError;
而如果线程请求的栈深度超出虚拟机允许的深度时,就会抛出
StackOverFlowError。
1.3.1 LinkageError(连接错误):表示Java类的依赖错误
未识别类文件会抛出ClassFormatError,一般程序文件和编译器JDK版本不一致时会抛出此错误
类编译后做了不兼容更改时会抛出AbstractMethodError
1.3.2 AWTError(AWT组件出错):
AWT是使用操作系统中的图形函数的抽象窗口工具,用C\C++编写,为了实现Java“一次编译,处处运行”的理念,AWT使用各个操作系统图形函数的交集,所以功能较差,但运行速度较快,适合嵌入式Java;
(原来主要用于功能机和简单智能设备,然而现在嵌入式Java都被安卓占了)
1.3.2 SwingError(SwingError组件出错):
Swing组件是基于AWT编写的图形界面系统,它用纯Java编写,所以必然实现了“一次编译,处处运行”,但相较于AWT运行速度较慢,适合PC使用。
(自JDK1.8之后官方提倡使用JavaFX开发图形界面)
1.3.3、Exception(异常):出现原因取决于程序,所以程序也理应通过try-catch处理。
I.可查异常和不可查异常
Java的异常(包括Exception和Error)分为可检查异常(checked exceptions)和不可检查异常(unchecked exceptions)。
可查异常(编译器要求必须处置的异常):正确的程序在运行中,预计会出现的异常,也是允许出现的异常,比如连接超时,文件未找到文件,IO错误,但这些异常出现后,必须指定处理的方式。
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可检查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
不可检查异常(编译器不要求强制处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error)。
如果使用throw在方法体中抛出可查异常,则需要在方法头部声明方法可能抛出的异常类型。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
II.运行时异常和非运行时异常
(1)运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
当出现RuntimeException的时候,我们可以不处理。当出现这样的异常时,总是由虚拟机接管。比如:NullPointerException空指针异常,它就是运行时异常,并且这种异常还是最常见的异常之一。
出现运行时异常后,如果没有捕获处理这个异常(即没有catch),系统会把异常一直往上层抛,一直到最上层,如果是多线程就由Thread.run()抛出,如果是单线程就被main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。运行时异常是Exception的子类,也有一般异常的特点,是可以被catch块处理的。只不过往往我们不对他处理罢了。也就是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。
如果不想终止,则必须捕获所有的运行时异常,决不让这个处理线程退出。队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。不应该由于异常数据而影响下面对正常数据的处理。
常见运行时异常
①.NullPointerException:空指针异常。调用了不存在的对象或未经实例化或初始化的对象时会抛出,对null对象的方法属性调用就会抛出此异常(最常见的就是.toString方法抛出空指针异常)
②.ArithmeticException:算术条件异常。最常见的就是0作除数时会抛出。
③.ClassNotFoundException:类未找到异常。在通过反射Class.forName(“类名”)来获取类时,如果未找到则会抛出异常。
④.ClassCastException:类转换失败时抛出此异常
⑤.ArrayIndexOutOfBoundsException:数组索引越界异常。当试图操作数组的索引值为负数或大于等于数组大小时会抛出。
⑥.NegativeArraySizeException:数组长度为负值异常。一般在初始化数组大小为负值时抛出。
⑦. ArrayStoreException:数组类型不匹配值异常。例如将一个Object数组中加入一个Integer对象与一个String对象时,类型不匹配就会抛出。
⑧.IllegalArgumentException:非法参数异常。会在使用Java类库方法时传入参数值越界时或者参数数量,类型不匹配时抛出。
二. 异常处理
Java异常处理机制要求出现异常时在异常位置抛出异常,然后捕获异常并处理
从方法中抛出的任何异常都必须使用throws子句。捕捉异常通过try-catch语句或者try-catch-finally语句实现。
总体来说,Java规定:对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error。
2.1异常捕获
2.1.1 try-catch语句
在Java中,异常通过try-catch语句捕获。其一般语法形式为:
- try {
- // 可能会发生异常的程序代码
- } catch (ExceptionTypeA e1){
- // 处理抛出的异常类型A
- }
- catch (ExceptionTypeA e2){
- //处理的抛出的异常类型B
- }
- catch (ExceptionTypeA e3){
- //处理抛出的异常类型C
- }
try后的大括号内的程序块即可能发生异常的代码块,如果在代码块中发生异常,JVM将创建相应的异常对象,如果与catch块要捕获的异常对象相匹配,则进入处理块,否则将向上抛出,如上级无处理则线程死亡。
需要注意的是,一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch子句不再有匹配和捕获异常类型的机会。
Java通过异常类描述异常类型,异常类的层次结构如图1所示。对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子 句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。
finally语句 表示无论异常是否发生都会执行的代码段
· try {
· // 可能会发生异常的程序代码
· } catch (Exceptione) {
·
} finally {
· // 无论异常是否发生,都将执行的代码块
· }
fnally语句一般用来保护程序,避免使异常直接暴露或者线程终止
注意:finally语句只会在对应的try catch块执行时才会执行,程序在本finally 对应的try catch块之前抛出异常时,不会执行finally块。
当在try块或catch块中遇到return,continue,break等程序转移语句时,finally块将在方法返回之前被执行。
public class Test {
public static void main(String[] args) {
System.out.println("reture value of test() : "+ test());
}
public static int test(){
int i = 1;
try {
System.out.println("try block");
i= 1 / 0;
return 1;
}catch (Exception e){
System.out.println("exception block");
return 2;
}finally {
System.out.println("finally block");
}
}
}
执行结果
try block
exception block
finally block
reture value of test() : 2
在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
又及:这只是通常意义上的try catch finally运行规则,实际上finally的运行规则十分复杂,需要深入JVM虚拟机运行原理进行探究,初步学习只需知道一上内容即可。
2.2抛出异常
任何Java代码都可以抛出异常,如:自己编写的代码、来自Java开发环境包中代码,或者Java运行时系统。无论是谁,都可以通过Java的throw语句抛出异常。从方法中抛出的任何异常都必须使用throws子句。
2.2.1. throws语句声明异常
如果一个方法可能会出现异常,但没有能力处理这种异常或者不需要在本层级处理异常可以通过throws子句来声明向上层抛出异常。
throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。
例:test方法向上级抛出异常,上级方法需要捕获并处理异常
publicclass Test {
public static void main(String[] args) {
try {
System.out.println("returnvalue of test(): " + test());
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static int test() throws Exception{
int i = 1;
try {
System.out.println("tryblock");
return i;
}finally {
System.out.println("finallyblock");
}
}
}
2.2.2throw语句主动抛出异常
throw总是出现在函数体中,用来抛出一个Throwable类型的异常。经常被用来处理用户自定义的异常,在程序逻辑满足某些条件时,主动的抛出异常。
程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
我们知道,异常是异常类的实例对象,我们可以创建异常类的实例对象通过throw语句抛出。该语句的语法格式为:
throw new exceptionname;
例如抛出一个IOException类的异常对象:
throw new IOException;
要注意的是,throw 抛出的只能够是可抛出类Throwable或者其子类的实例对象。下面的操作是错误的:
throw new String("exception");
这是因为String不是Throwable 类的子类。
如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。
如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。
三.异常处理的技巧
程序中异常的出现,通过异常堆栈信息,我们可以明确一下几个问题
- 什么出了错?
- 在哪出的错?
- 为什么出错?
异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为什么“会抛出
一个异常的错误信息堆栈,表明了异常的类型(数据库连接异常),异常的错误信息(端口不允许连接),异常的线程,异常发生的位置,具体到每个调用块的行数。
Java提供了大量异常子类,如需更加具体,你也可以定义自己的异常类。通过大量的具体的异常子类,Java让明确捕获异常变得容易,因为我们可以对同一try块定义多个catch块,从而对每种异常分别进行恰当的处理。
- java 异常,java,异常
- Java 异常 异常链
- Java异常
- Java 异常
- JAVA 异常
- Java异常
- java 异常
- Java异常
- java异常
- Java异常
- java异常
- java异常
- java异常
- java 异常
- java异常
- java异常
- java异常
- Java异常
- 关于 DataTables 本地储存那点事,又爱又恨 (stateSave参数应用)
- linux里如何查看文件大小
- 从 Windows 到 Linux: 艰难迈出第一步
- option stateSaveParams 状态数据保存操作回调函数
- 软件比较
- Java 异常
- [Loj]#6002. 「网络流 24 题」最小路径覆盖
- option stateSaveCallback 定义表格状态怎样、在哪里存储
- MOM初窥:AcitveMQ HelloWorld
- C语言问题笔记(私用)
- Poedu_Python_Lesson002_基础语法_数据类型
- 【大数据部落】电信公司churn数据客户流失knn预测分析(二)
- python导入第三方库
- PyQt4入门教程(2)_PyQt4的第一个程序