我原本是个快乐的iOS开发者, 总是会看到一张图片:
我就在想crash就crash了呗, 弄这么多学名干嘛, 还抛出异常. 后来经过我对JAVA的研究我发现我的想法被啪啪打脸, 那叫一个左右开弓.
( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)
还是我太naive, 而今天, 就要详述一下异常这个东西.
我们要确立一个概念, 那就是: 什么是异常机制.
异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。
当你明白异常机制是怎么回事的时候,异常处理的流程你也不难接受.
当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,java虚拟机检测寻找和try关键字匹配的处理该异常的catch块,如果找到,将控制权交到catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行。如果没有找到处理该异常的catch块,在所有的finally块代码被执行和当前线程的所属的ThreadGroup的uncaughtException方法被调用后,遇到异常的当前线程被中止。
用代码表示就是:
try {
System.out.println("寻找到try关键字");
} catch (Exception e){
e.printStackTrace();
System.out.println("捕获异常");
} finally {
System.out.println("无论走try还是走catch, finally中的代码都会被执行");
}
java.lang.Throwable
既然要说明白异常的前世今生就要先说一说这些异常的前世, java.lang.Throwable是 Java 中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 JVM 或者 Java throw 语句来抛出, 换言之, 所有的异常都是从他这出来的, 他是所有异常的祖宗类.
而且, 类似的, 只有此类或其子类之一才可以是 catch 子句中的参数类型.
由图可见, 他有两个子类, 这两个子类就是区别开了两种不同的异常.
第一种.java.lang.Error
Error是 Throwable 的子类,用于指示合理的应用程序不应该试图捕获的严重问题, 这个问题有多严重? 十分严重, 不可恢复的错误, 这种情况下的程序只能终止运行, 但是要注意一点的是, 编译器不会检查Error是不是被处理, 程序中也不用捕获Error类型的异常, 一般情况下, 程序中也是绝对不应该抛出这种异常的, 但是一旦出现Error必须修改源代码.
第二种.java.lang. Exception
Exception是由JVM发出的异常, 他的异常类型包括两种, 一种是RuntimeException, 另外一种是Checked Exception异常或者说是(记成)非RuntimeException类型的异常也可.
RuntimeException类型的异常是很特殊的, 他是一种Unchecked Exception, 编译器不会检查程序是不是处理了RuntimeException, 在程序中也不去捕获RuntimeException类型的异常, 也不会再方法体中抛出RuntimeException. RuntimeException类型的异常出现的时候表示程序中出现了逻辑编程错误, 作为程序员应该找出错误并且修改.
Checked Exception异常, 或者说是非RuntimeException类型的异常, 是Exception异常种类中除了RuntimeException异常就是他了, 而JAVA规定了所有的Checked Exception异常都要处理, 编译器也会检查这种异常是否存在, 要么抛出, 要么try catch捕获, 处理, 否则将不能通过编译.
出现异常怎么办
对于异常, 真正有用的是意向的对象类型, 而不是异常对象本身, 每一种异常对象都有着自己的含义, 老规矩, 为了便于理解, 上代码!
public static double div(int a, int b){
return a / b;
}
// 然后调用
div(4, 0);
显而易见的, 我们犯了一个很基础的错误, 那就是拿一个整数去除以0, 当然, 我们亲爱的JAVA不会惯着我们.
我们来看一下抛出的异常信息.
在这里我们会看到几处有意思的东西.
- java.lang.ArithmeticException 这个东西好像是类?
- by zero 我好像是除以0了.
- at com.company.Main.div(Main.java:69)
at com.company.Main.main(Main.java:28) 这两行.
我们首先来看看java.lang.ArithmeticException, 这个类的意思是当出现异常的运算条件时,抛出此异常。就比如说我们所做的,一个整数“除以零”时,抛出此类的一个实例.
然后再看 by zero, 的确是除以0了, 没毛病.
最后再看最后这两行东西, div和main好像是你的两个方法, Main是不是主入口呢? 你看了一下 69 行和 28 行
return a/b; // 69行
div(4, 0); // 28行
大呼神奇.
现在你能看懂异常了.
自定义异常
你以为看懂异常就结束了吗? naive, 你不但要会看, 你还要会写, 编程瞬息万变, 你怎么能确定JAVA就考虑到所有你可能出错的情况呢? 所以, 当JAVA内置的异常都不能明确的说明异常情况的时候,需要创建自己的异常.
但是你需要注意的是, 唯一有用的就是类型名这个信息,所以不要在异常类的设计上花费精力, 他只是去给你提供一个信息的.
还是上代码来的清楚, 请看.
class Computer {
public String getStatus() {
return status;
}
public void setStatus(String status) throws DownException, EnterWaterException {
this.status = status;
if (status.equals("进水")){
throw new EnterWaterException("进水了, 烧了");
} else if (status.equals("摔了")){
throw new DownException("摔了, 有钱了");
}
}
private String status;
}
class DownException extends RuntimeException{
public DownException(String message){
super(message);
}
}
class EnterWaterException extends RuntimeException{
public EnterWaterException(String message){
super(message);
}
}
你可以清晰的看到我新建了两个异常, 并把他放入一个叫做Computer的类中, 并让 setStatus方法接着向上抛出异常.
Computer computer = new Computer();
try {
computer.setStatus("进水");
} catch (EnterWaterException e){
e.printStackTrace();
} catch (DownException e){
e.printStackTrace();
} finally {
System.out.println("finally");
}
然后我try catch了异常. 来看一下输出的结果.
怎么样, 是不是结果显而易见, 但是这里有几点需要注意一下, 并且要记住.
- 对于应该在声明方法抛出异常还是在方法中捕获异常, 我们应该遵守一个原则, 捕捉并处理哪些知道如何处理的异常,而传递哪些不知道如何处理的异常
- 多个异常捕获, 异常对象存在父子关系是, 要求父类型的异常, 写在子类的下面
- 多个异常可以合并在一个catch中 catch(Exception e), 但是为了看着清晰我没有这么做.
- throws 是声明这个方法的调用存在异常情况, 调用者需采取捕获处理或继续向外抛的操作才能通过编译
- 当代码出现问题时, JVM会创建一个异常对象并抛个调用者
- 调用者接收到异常之后不能处理, 继续往上抛直到抛给JVM, 最终将异常信息输出到控制台
这里是一些常见的异常, 方便与在大家在抛出异常的时候来对照:
ArithmeticException——由于除数为0引起的异常;
ArrayStoreException——由于数组存储空间不够引起的异常;
ClassCastException—一当把一个对象归为某个类,但实际上此对象并不是由这个类 创建的,也不是其子类创建的,则会引起异常;
IllegalMonitorStateException——监控器状态出错引起的异常;
NegativeArraySizeException—一数组长度是负数,则产生异常;
NullPointerException—一程序试图访问一个空的数组中的元素或访问空的对象中的 方法或变量时产生异常;
OutofMemoryException——用new语句创建对象时,如系统无法为其分配内存空 间则产生异常;
SecurityException——由于访问了不应访问的指针,使安全性出问题而引起异常;
IndexOutOfBoundsExcention——由于数组下标越界或字符串访问越界引起异常;
IOException——由于文件未找到、未打开或者I/O操作不能进行而引起异常;
ClassNotFoundException——未找到指定名字的类或接口引起异常;
CloneNotSupportedException——一程序中的一个对象引用Object类的clone方法,但 此对象并没有连接Cloneable接口,从而引起异常;
InterruptedException—一当一个线程处于等待状态时,另一个线程中断此线程,从 而引起异常;
NoSuchMethodException一所调用的方法未找到,引起异常;
IllegalAccessExcePtion—一试图访问一个非public方法;
StringIndexOutOfBoundsException——访问字符串序号越界,引起异常;
ArrayIdexOutOfBoundsException—一访问数组元素下标越界,引起异常;
NumberFormatException——字符的UTF代码数据格式有错引起异常;
IllegalThreadException—一线程调用某个方法而所处状态不适当,引起异常;
FileNotFoundException——未找到指定文件引起异常;
EOFException——未完成输入操作即遇文件结束引起异常。
怎么样, 大家是不是对于异常都已经有了自己的理解了呢, 希望大家要达道下图的水平.
我们作为程序员, 每天都在想的无非就是我怎么把代码敲得更牛逼, 更美观, 而我一直认为知识, 见解, 经验的分享和学习是自我进行学习的一个重要的途径, 所以, 知识的分享总会增值, 非常期待与各位看官切磋想法, 交流心得.
个人其他文章:
个人编程想法心得(不定期更新)
开发笔记之详述JAVA构造函数和代码块本身及其执行细节
开发笔记之冒泡排序, 选择排序, 折半查找
iOS开发笔记-基于AFNetworking 3.0的登录 注册
开发笔记之JAVA String StringBuffer StringBuilder
开发笔记之JAVA ArrayList 和 LinkedList