1. 导引问题
实际工作中, 遇到的情况不可能是非常完美的. 比如: 你写的某个模块, 用户输入不一定符合你的要求, 你的程序要打开某个文件, 这个文件可能不存在或者文件格式不对, 你要读取数据库的数据, 数据可能是空的等. 我们的程序再跑着, 内存或硬盘可能满了. 等等.
软件程序在运行过程中, 非常可能遇到刚刚提到的这些异常问题, 我们叫异常, 英文是: Exception, 意思是例外. 这些, 例外情况, 或者叫异常, 怎么让我们写的程序做出合理的处理. 而不至于程序崩溃.
1.1 常见的错误:
用户输入错误
设备错误. 硬件问题, 比如打印机关掉, 服务器问题
物理限制. 磁盘满了
代码限制. 数组下标越界等
设计良好的程序应该在异常发生时提供处理这些错误的方法, 使得程序不会因为异常的发生而终断或产生不可预见的结果.
如果没有异常处理机制, 那么:
1.2 两个坏处:
1.逻辑代码和错误处理代码放一起!
2.程序员本身需要考虑的例外情况较复杂, 对程序员本身要求较高!
异常机制就是当程序出现错误, 程序如何安全退出的机制.
2. 异常 (Exception) 的概念
Java 如何处理异常?
第一个异常示例和解析:
- public static void main(String[]args){
- int i=1/0;
- }
执行上述代码的异常信息如下:
Exception in thread"main"java.lang.ArithmeticException:/by zero at chuji.BubbleSort.main(BubbleSort.java:11)
Java 是采用面向对象的方式来处理异常的.
3. 处理过程
抛出异常: 在执行一个方法时, 如果发生异常, 则这个方法生成代表该异常的一个对象, 停止当前执行路径, 并把异常对象提交给 JRE.
捕获异常: JRE 得到该异常后, 寻找相应的代码来处理该异常. JRE 在方法的调用栈中查找, 从生成异常的方法开始回溯, 直到找到相应的异常处理代码为止.
4. 异常分类
JDK 中定义了很多异常类, 这些类对应了各种各样可能出现的异常事件, 所有异常对象都是派生于 Throwable 类的一个实例. 如果内置的异常类不能够满足需要, 还可以创建自己的异常类.
异常类之间的关系图
图引用链接: https://www.cnblogs.com/hwaggLee/p/4509038.html
4.2 Error
Error 类层次描述了 Java 运行时系统内部错误和资源耗尽错误. 这类错误是我们无法控制的, 同时也是非常罕见的错误. 所以在编程中, 不去处理这类错误.
Error 表明系统 JVM 已经处于不可恢复的崩溃状态中. 我们不需要管他.
打开 JDK 的: java.lang.error, 查看他的所有子类.
4.3 Exception
所有异常类的父类, 其子类对应了各种各样可能出现的异常事件.
4.4 Error 和 Exception 的区别
我开着车走在路上, 一头猪冲在路中间, 我刹车. 这叫一个异常.
我开着车在路上, 发动机坏了, 我停车, 这叫错误. 系统处于不可恢复的崩溃状态. 发动机什么时候坏? 我们普通司机能管吗? 不能. 发动机什么时候坏是汽车厂发动机制造商的事.
4.4.1 Runtime Exception
出现 RuntimeException 就一定是你的问题, 可以不捕获, 因为小心点这些异常是可以避免的. 派生于 RuntimeException 的异常. 是一类特殊的异常, 如被 0 除, 数组下标超范围等, 其产生比较频繁, 处理麻烦, 如果显式的声明或捕获将会对程序可读性和运行效率影响很大. 因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理). 这类异常通常是由编程错误导致的, 因为只有小心点, 这些异常都是可以避免的, 所以在编写程序时, 并不要求必须使用异常处理机制来处理这类异常, 所有这类异常都继承自 java.lang.RuntimeException.
常见的运行时异常有:
ArithmeticException
如试图除以 0
NullPointerException
当程序访问一个空对象的成员变量或方法, 访问一个空数组的成员时发生
ClassCastException
发生多态后, 如果强制转化的并不是父类的子类时发生. 编译的时候可以通过, 因为编译的时候并不会检查类型转化的问题
ArrayIndexOutOfBoundsException
访问的元素下表超过数组长度
NumberFormatException
数字格式异常!
心得:
大家平时在遇到 NullPointerException, 也就是空指针的问题时, 不要只记得百度, 应该从报错的地方去分析自己的代码, 因为空指针其实是你的代码写的不严谨造成的. 空指针解决方案: Debug, 看你对应的值是否为 null.
4.4.2 Checked Exception
所有不是 Runtime Exception 的异常, 统称为 Checked Exception, 又被称为 "已检查异常". 这类异常的产生不是程序本身的问题, 通常由外界因素造成的. 为了预防这些异常产生时, 造成程序的中断或得到不正确的结果, Java 要求编写可能产生这类异常的程序代码时, 一定要去做异常的处理.
编译器将检查是否为所有已检查异常提供异常处理.
这一类异常, 我们必须捕获进行处理.
Java 语言将派生于 RuntimeException 类或 Error 类的所有异常称为 "未检查异常".
5. 异常的处理办法之一: 捕获异常
5.1 try
try 语句指定了一段代码, 该段代码就是一次捕获并处理的范围. 在执行过程中, 当任意一条语句产生异常时, 就会跳过该段中后面的代码. 代码中可能会产生并抛出一种或几种类型的异常对象, 它后面的 catch 语句要分别对这些异常做相应的处理
一个 try 语句必须带有至少一个 catch 语句块或一个 finally 语句块 .
注: 当异常处理的代码执行结束以后, 是不会回到 try 语句去执行尚未执行的代码.
5.2 catch
1, 每个 try 语句块可以伴随一个或多个 catch 语句, 用于处理可能产生的不同类型的异常对象.
2., 常用方法:
toString ( )方法, 显示异常的类名和产生异常的原因
getMessage( ) 方法, 只显示产生异常的原因, 但不显示类名.
printStackTrace( ) 方法, 用来跟踪异常事件发生时堆栈的内容.
这些方法均继承自 Throwable 类
3,catch 捕获异常时的捕获顺序:
如果异常类之间有继承关系, 在顺序安排上需注意. 越是顶层的类(父类), 越放在下面. 再不然就直接把多余的 catch 省略掉.
5.3 finally
有些语句, 不管是否发生了异常, 都必须要执行, 那么就可以把这样的语句放到 finally 语句块中.
通常在 finally 中关闭程序块已打开的资源, 比如: 文件流, 释放数据库连接等.
5.4 典型代码
- public class TestException {
- public static void main(String[] args) {
- FileReader reader = null;
- try {
- reader = new FileReader("d:/a.txt");
- char temp = (char) reader.read();
- System.out.println("读出的内容:" + temp);
- } catch (FileNotFoundException e) {
- System.out.println("文件没有找到!!");
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- System.out.println("文件读取错误!");
- } finally {
- System.out.println("不管有没有异常, 我肯定会被执行!");
- try {
- reader.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
5.5 try, catch,finally ,return 执行顺序
执行顺序:
1.执行 try,catch , 给返回值赋值
2.执行 finally
3.return
6. 异常的处理办法之二: 声明异常(throws 子句)
当 Checked Exception 产生时, 不一定立刻处理它, 可以再把异常 Throws 出去.
在方法中使用 try-chatch-finally 由这个方法处理异常. 在一些情况下, 当前方法并不需要处理发生的异常, 而是向上传递给调用它的方法处理.
如果一个方法中可能产生某种异常, 但是并不能确定如何处理这种异常, 则应根据异常规范在方法的首部声明该方法可能抛出的异常.
如果一个方法抛出多个已检查异常, 就必须在方法的首部列出所有的异常, 之间以逗号隔开.
6.1 典型代码
- public class FileTest {
- public static void main(String[] args) {
- try {
- readFile("d:/a.txt");
- } catch (FileNotFoundException e) {
- System.out.println("所需要的文件不存在!");
- e.printStackTrace();
- } catch (IOException e) {
- System.out.println("文件读写出错误!");
- e.printStackTrace();
- }
- }
- public static void readFile(String fileName) throws IOException {
- FileReader in = new FileReader(fileName);
- try {
- int tem = 0;
- tem = in.read();
- while (tem != -1) {
- System.out.println((char) tem);
- tem = in.read();
- }
- } finally {
- in.close();
- }
- }
- }
6.2 方法重写中声明异常原则
子类声明的异常范围不能超过父类声明的范围. 包含如下意思:
父类没有声明异常, 子类也不能;
不可抛出原有方法抛出异常类的父类或上层类
抛出的异常类型的数目不可以比原有的方法抛出的还多(不是指个数)
7. 异常的处理办法之三: 手动抛出异常(throw 子句)
Java 异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出, 也可根据需要手工创建并抛出.
在捕获一个异常前, 必须有一段代码先生成异常对象并把它抛出. 这个过程我们可以手工做, 也可以由 JRE 来实现, 但是他们调用的都是 throw 子句.
对于一个已经存在的异常类, 抛出该类异常对象过程如下:
找到一个合适的异常类.
创建一个该类的对象.
将对象抛出
- File f=new File("c:/tt.txt");
- if(!f.exists()){
- try{
- throw new FileNotFoundException("File can't be found!");
- }catch(FileNotFoundException e){
- e.printStackTrace();
- }
- }
8. 自定义异常
在程序中, 可能会遇到任何标准异常类都没有充分的描述清楚的问题, 这种情况下可以创建自己的异常类.
怎么做:
从 Exception 类或者它的子类派生一个子类即可
习惯上, 定义的类应该包含 2 个构造器: 一个是默认的构造器, 另一个是带有详细信息的构造器.
典型代码
- class IllegalAgeException extends Exception {
- public IllegalAgeException() {
- }
- public IllegalAgeException(String msg) {
- super(msg);
- }
- }
- class Person {
- private String name;
- private int age;
- public void setName(String name) {
- this.name = name;
- }
- public void setAge(int age) {
- if (age < 0) throw new IllegalAgeException("人的年龄不应该为负数");
- this.age = age;
- }
- public String toString() {
- return "name is" + name + "and age is" + age;
- }
- }
- public class MyExceptionTest {
- public static void main(String[] args) {
- Person p = new Person();
- try {
- p.setName("Lincoln");
- p.setAge(-1);
- } catch (IllegalAgeException e) {
- e.printStackTrace();
- System.exit(-1);
- }
- System.out.println(p);
- }
- }
9. 使用异常机制建议
要避免使用异常处理代替错误处理, 这样会降低程序的清晰性, 并且效率低下( Java 是采用面向对象的方式来处理异常的, 所以也是会有一定的开销)
只在异常情况下使用异常机制
不要进行小粒度的异常处理 --- 应该将整个任务尽可能包装在一个 Try 语句块中
异常往往在低层抛出, 高层处理(捕获)
10. 总结
一个图
五个关键字(try, catch, finally, throws, throw)
先逮小的(子类), 再逮大的(父类)
异常和重写的关系
自定义异常
现在的喜欢, 其实不是真正的喜欢, 只是因为不了解而已, 真正的喜欢, 是建立在非常了解的基础上. 了解 java 基础, 喜欢上编程, 不再迷茫.
来源: https://www.cnblogs.com/deveypf/p/11437566.html