1、什么叫异常

先来看个例子

public class ExceptionDemo {
public static void main(String[] args) {
int x = 6 / 0;
System.out.println("x = " + x);
System.out.println("程序正常结束。");
}
}

当运行这段代码时,控制台输出如下内容,我们看到第4行的打印语句没有被执行

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at ExceptionDemo.main(ExceptionDemo.java:3)

根据输出的内容可以得知是代码的第三行出了问题导致程序无法正常继续执行,原因是Exception in thread "main" java.lang.ArithmeticException: / by zero,意思是说出现了算数异常,由于0,这是因为在数学中规定除数不能为0。这里的java.lang.ArithmeticException是Java中定义的一个异常类,用来专门处理数学运算中出现的问题,像这种在程序中阻止当前方法继续执行的问题,就叫做异常,异常发生的原因有很多,比如说上面的数学运算中的除数为0,又如我们操作IO流时打开了不存在的文件时,则会抛出java.io.FileNotFoundException异常,还有一些则是因为用户输入了非法的数据而造成的。

2、异常的分类

因为java是一门面向对象的语言,所以异常也被java的设计者们封装成了对象,并定义了一个基类java.lang.Throwable作为所有异常的超类。在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。下图很好的展示了java中异常的体系结构:

首先说明一点,java中的Exception类的子类不仅仅只是像上图所示只包含IOExceptionRuntimeException这两大类,事实上Exception的子类很多很多(可通过查询API)。从图中可以看出所有异常类型都是内置类Throwable的子类,因而Throwable在异常类的层次结构的顶层,接着便分成了2个派系,一个是Error,另一个是Exception,分别表示错误和异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常,这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。

Error与Exception:

  • Error一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断,这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况,通常应用程序不应该试图使用catch块来捕获Error对象,在定义该方法时,也无须在其throws子句中声明该方法可能抛出Error及其任何子类。
  • Exception是程序本身可以处理的异常,这种异常分两大类,运行时异常和非运行时异常,程序中应当尽可能去处理这些异常。

运行时异常和非运行时异常:

  • 运行时异常都是RuntimeException类及其子类异常,如NullPointerExceptionIndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理,这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
  • 非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过,如IOExceptionSQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

3、如何处理异常

Java的异常处理本质上是抛出异常和捕获异常,其机制主要依赖于try、catch、finally、throw和throws五个关键字。
try:字面意思是尝试,试验,就是试着运行可能出现异常的代码
catch:捕捉,抓住的意思,用于捕获在try语句块中发生的异常
finally:最后,finally语句块总是会被执行,它主要用于回收在try块里打开的资源,如数据库连接、网络连接与磁盘文件等,只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止
throw:该关键字是用于方法体内部,用来抛出一个Throwable类型的异常。如果抛出了检查异常, 则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常
throws:用在方法签名中,用于声明该方法可能抛出的异常。仅当抛出了检查异常,该方法的调用者才必须处理或者重新抛出该异常

异常处理的语法
try...catch...比较适用于有异常要处理,但是没有资源要释放的。

try {
// 可能发生异常的代码
} catch (Exception e) {
// 处理异常的代码
}

try...catch...finally...比较适用于既有异常要处理又要释放资源的代码。

try {
// 可能发生异常的代码
} catch (Exception e) {
// 处理异常的代码
} finally {
// 释放资源的代码;
}

try...finally...比较适用于内部抛出的是运行时异常,并且有资源要被释放。

try {
// 可能发生异常的代码
} finally {
// 释放资源的代码
}

将开始的代码用try...catch...改写下

public class ExceptionDemo {
public static void main(String[] args) {
try {
int x = 6 / 0;
System.out.println("x = " + x);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
} System.out.println("程序正常结束。");
}
}

运行结果

/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:4)
程序正常结束。

可以看到即使try语句块中出现了异常,在catch中处理了异常之后,后面的语句正常执行,接下来再看看加上finally的情况

public class ExceptionDemo {
public static void main(String[] args) {
try {
int x = 6 / 0;
System.out.println("x = " + x);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
} finally {
System.out.println("一定会执行的代码。");
} System.out.println("程序正常结束。");
}
}

运行结果

/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:4)
一定会执行的代码。
程序正常结束。

当catch语句块中处理完异常后,接着会执行finally中的代码,在finally中的代码一定会被执行(通常用于资源的释放操作),即使catch中有return语句

public class ExceptionDemo {
public static void main(String[] args) {
try {
int x = 6 / 0;
System.out.println("x = " + x);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
return;
} finally {
System.out.println("一定会执行的代码。");
} System.out.println("程序正常结束。");
}
}

运行结果

/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:4)
一定会执行的代码。

当然在catch中return之后,程序就结束了,所以"程序正常结束。"这句话也就没有输出了。

很多情况下,一条语句或者单个的代码段可能引起多个异常。处理这种情况,我们需要定义两个或者更多的catch子句,每个子句捕获一种类型的异常,当异常被引发时,每个catch子句被依次检查,第一个匹配异常类型的子句执行,当一个catch子句执行以后,其他的子句将被忽略。编写多重catch语句块时应注意顺序问题:先小后大,即先子类后父类,否则更具体的子类异常将被屏蔽。

finally子句是可选项,可以有也可以无,但是每个try语句至少需要一个catch或者finally子句。

现在,我们学习了如何捕获并处理程序中发生的异常,不过有时候我们并不知道如何处理发生的异常,这是就可以用java提供的throw关键字,Throw的语法形式如下

throw ThrowableInstance;

这里的ThrowableInstance一定是Throwable类类型或者Throwable子类类型的一个对象。简单的数据类型,例如int,char以及非Throwable类,例如String或Object,不能用作异常。有两种方法可以获取Throwable对象:在catch子句中使用参数或者使用new操作符创建。

程序执行完throw语句之后立即停止;throw后面的任何语句不被执行,最邻近的try块用来检查它是否含有一个与异常类型匹配的catch语句,如果发现了匹配的块,控制转向该语句;如果没有发现,次包围的try块来检查,以此类推。如果没有发现匹配的catch块,默认异常处理程序中断程序的执行并且打印堆栈轨迹。

public class TestThrow {
public static void main(String[] args) {
int x = div(6, 0);
System.out.println("x = " + x);
} public static int div(int a, int b) {
if (b == 0) {
throw new ArithmeticException("除数不能为0。");
} return a / b;
}
}

这里直接在div方法中用throw关键字抛出异常,如果一个方法可以导致一个异常但不处理它,我们可以在方法声明中包含一个throws子句。一个throws子句列举了一个方法可能引发的所有异常类型。这对于除了Error或RuntimeException及它们子类以外类型的所有异常是必要的。一个方法可以引发的所有其他类型的异常必须在throws子句中声明,否则会导致编译错误,下面是throws子句的方法声明的通用形式:

public static int div(int a, int b) throws ArithmeticException{
// code
}

ArithmeticException是该方法可能引发的的异常,这里也可以是异常列表,中间以逗号隔开。

public static int div(int a, int b) throws ArithmeticException, NullPointerException{
// code
}

当然也可以直接抛出Exception,表示该方法可能引发的的所有异常,不过不建议这么做,因为当我们代码逻辑很复杂时,如果没有看代码,并不知道是哪里的代码引起的异常

public static int div(int a, int b) throws Exception{
// code
}

将上面的例子用throws关键字

public class TestThrows {
public static void main(String[] args) {
try {
int x = div(6, 0);
System.out.println("x = " + x);
} catch (ArithmeticException e) {
System.out.println("发生了算数异常。");
}
} public static int div(int a, int b) throws ArithmeticException {
return a / b;
}
}

Throws抛出异常的规则

  • 如果发生的异常是是不受检查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出
  • 必须声明方法可抛出的任何检查异常(checked exception),即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误
  • 仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常,当方法的调用者无力处理该异常的时候,应该继续抛出给调用者,而不应该简单的打印或者干脆不处理
  • 调用方法必须遵循任何可查异常的处理和声明规则,若覆盖一个方法,则不能声明与覆盖方法不同的异常,声明的任何异常必须是被覆盖方法所声明异常的同类或子类

4、自定义异常

Java内置的异常类可以描述在编程时出现的大部分异常情况,不过有时候在实际项目中需要结合业务抛出跟接地气的异常,只需继承Exception类来自定义我们需要的异常即可

public class SelfDefinitionException {
public static void main(String[] args) {
try {
compare(-1);
} catch (MyException e) {
System.out.println(e);
}
} static void compare(int a) throws MyException {
if (a < 0) {
throw new MyException("输入的参数不能小于0。");
}
}
} class MyException extends Exception {
public MyException(String message) {
super(message);
}
}

5、异常总结

  • 异常是程序运行过程过程出现的错误,在Java中用类来描述,用对象来表示具体的异常,Java将其区分为Error与Exception,Error是程序无力处理的错误,Exception是程序可以处理的错误,异常处理是为了程序的健壮性
  • Java异常类来自于Java API定义和用户扩展,通过继承Java API异常类可以实现异常的转译
  • 异常能处理就处理,不能处理就抛出,最终没有处理的异常JVM会进行处理

Java集合框架之List接口的更多相关文章

  1. Java集合框架之Collection接口

    Java是一门面向对象的语言,那么我们写程序的时候最经常操作的便是对象了,为此,Java提供了一些专门用来处理对象的类库,这些类库的集合我们称之为集合框架.Java集合工具包位于Java.util包下 ...

  2. java 集合框架(二)Iterable接口

    Iterable接口是java 集合框架的顶级接口,实现此接口使集合对象可以通过迭代器遍历自身元素,我们可以看下它的成员方法 修饰符和返回值 方法名 描述 Iterator<T> iter ...

  3. Java集合框架之四大接口、常用实现类

    Java集合框架 <Java集合框架的四大接口> Collection:存储无序的.不唯一的数据:其下有List和Set两大接口. List:存储有序的.不唯一的数据: Set:存储无序的 ...

  4. Java集合框架之Map接口浅析

    Java集合框架之Map接口浅析 一.Map接口综述: 1.1java.util.Map<k, v>简介 位于java.util包下的Map接口,是Java集合框架的重要成员,它是和Col ...

  5. Java集合框架之Set接口浅析

    Java集合框架之Set接口浅析 一.java.util.Set接口综述: 这里只对Set接口做一简单综述,其具体实现类的分析,朋友们可关注我后续的博文 1.1Set接口简介 java.util.se ...

  6. Java集合框架之List接口浅析

    Java集合框架之List接口浅析 一.List综述: 毫无疑问List接口位于java.util包下,继承自 Collection接口 存储元素的特点: 有序可重复(有序:即存进去是什么顺序,取出来 ...

  7. Java集合框架中Map接口的使用

    在我们常用的Java集合框架接口中,除了前面说过的Collection接口以及他的根接口List接口和Set接口的使用,Map接口也是一个经常使用的接口,和Collection接口不同,Map接口并不 ...

  8. Java集合框架中List接口的简单使用

    Java集合框架可以简单的理解为一种放置对象的容器,和数学中的集合概念类似,Java中的集合可以存放一系列对象的引用,也可以看做是数组的提升,Java集合类是一种工具类,只有相同类型的对象引用才可以放 ...

  9. Java集合框架的基础接口有哪些?

    Collection为集合层级的根接口.一个集合代表一组对象,这些对象即为它的元素.Java平台不提供这个接口任何直接的实现. Set是一个不能包含重复元素的集合.这个接口对数学集合抽象进行建模,被用 ...

  10. java集合框架部分相关接口与类的介绍

    集合基础 接口 Iterable //Implementing this interface allows an object to be the target of the "for-ea ...

随机推荐

  1. html_02之表单、其它

    1.表单属性action:处理表单数据服务器端处理程序地址,默认提交本页: 2.表单属性method:①get:明文,数据显示地址栏,长度<2KB,向服务器请求数据时使用:②post:密文,提交 ...

  2. Java Synchronized Blocks

    From http://tutorials.jenkov.com/java-concurrency/synchronized.html By Jakob Jenkov   A Java synchro ...

  3. SQL优化快速入门

    最近遇到一个专门进行SQL技术优化的项目,对很多既有的老存储过程进行调优(现在已经不再新增任何存储过程),因此系统的对SQL语句编写进行一次科学的学习变得很有必要.这儿将基于黄德承大神的Oracle ...

  4. CSS技能汇总,研究及实践

    最近一直在研究CSS,因为发现实践中大部分时间都在写CSS,且自己感觉写的很烂,虽然以前看的很多,但却很少有去实践过,更别提研究了,现在发现根本就不是你懂你就会,很多都是你用着用着才真的会了的,于是现 ...

  5. ::after::before清除浮动原理

    先来看一段代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  6. 修改NLS_DATE_FORMAT的四种方式

    一. 在用户环境变量中指定(LINUX) 在用户的.bash_profile中增加两句: export NLS_LANG=AMERICAN ---这一句必须指定,否则下一句不生效.export NLS ...

  7. TFS简介

    Team Foundation Server(TFS)是一种为 Microsoft 产品提供 源代码管理.数据收集.报告和项目跟踪,而为协作软件开发的项目.可作为独立的软件,或 Visual Stud ...

  8. [转]JVM指令详解(上)

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 本文主要记录一些JVM指令,便于记忆与查阅. 一.未归类系列A 此系列暂未归类. 指令码    助记符      ...

  9. javascript学习6

    JavaScript  Boolean(逻辑)对象 Boolean(逻辑)对象用于将非逻辑值转换为逻辑值(true 或者 false). 实例 检查逻辑值 检查逻辑对象是 true 还是 false. ...

  10. JAVA 设计模式 职责链模式

    用途 职责链模式 (Chain Of Responsibility) 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系. 将这个对象连成一条链,并沿着这条链传递该请求,直到有一个 ...