(一)异常的概念
java异常是java提供的用于处理程序中错误的一种机制。
所谓错误是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界。。。)
设计良好的程序应该在异常发生时提供处理这些错误的方法,使得程序不会因为异常的发生而阻断或产生不可预见的结果(比如给个温馨提示,让别人看得懂)。
java程序的执行过程中如出现异常事件,可以生成一个异常类对象,该异常对象封装了异常事件的信息并将被提交给java运行时系统,这个过程称为抛出(throw)异常。
当java运行时系统接收到异常对象时,会寻找技能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
释例一:制造数组下标越界异常
package oop;
/**
* 说明:制造运行时,数组下标越界的异常
*
* @author huayu
* @date 2018/8/7 3:56 PM
*/
public class ExceptionDemo {
public static void main(String[] args) {
//数组arr容量为三,下标为0,1,2
int[] arr={1,2,3};
//输出下标为3的值
System.out.println(arr[3]);
}
}
结果:它会报一个数据下标越界的异常java.lang.ArrayIndexOutOfBoundsException编译器还会给你具体报错的位置
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at oop.ExceptionDemo.main(ExceptionDemo.java:18)
Process finished with exit code 1
在讲下面的处理以上异常栗子之前,先跟大家说一下try,catch俩个关键字的用法,及finally的用法。
try {
//try是尝试的意思,try代码段这儿写可能抛出异常的语句。
someMethod();
//当异常发生时,程序会终止当前的流程,根据获取异常的类型去执行相应的catch代码段
//try代码段后可以跟一个或者多个catch代码段,用于处理不同类型的异常。
} catch (SomeException1 e) {
//catch是捕获的意思,catch语句块里面写捕获到某种异常后,随后要干什么的代码
... ... ...
} catch (SomeException2 e) {
/* 异常对象catch (SomeException2 e)封装了异常信息发生的信息,在catch语句块中可以
使用这个对象的一些方法获取这些信息*/
... ... ...
//用来得到有关异常的信息
e.getMessage();
//打印错误栈路径,用来跟踪异常事件发生时执行堆栈的内容。
e.printStackTrace();
} finally {
//finally代码段的代码无论是否发生异常都会执行。
... ... ...
/* finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序的其他部分以前,
能 够对程序的状态作统一的管理。
通常在finally语句中可以进行资源的清除工作,如:关闭打开的文件,删除临时文件。
*/
}
对释例一的异常进行捕捉处理:
/**
* 说明:对运行时数组下标越界的异常进行捕捉并处理。
*
* @author huayu
* @date 2018/8/7 3:56 PM
*/
public class ExceptionDemo {
public static void main(String[] args) {
//数组arr容量为三,下标为0,1,2
int[] arr = {1, 2, 3};
try {
//输出下标为3的值
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
//e是错误对象的名字,相当于这是一个形参,在产生异常后,系统会帮我们初始化
//报错后程序就不能在往下运行了,考虑到用户友好性,这儿应该给予用户以友好提示
System.out.println("系统正在维护中,请与管理员联系");
//打印错误路径
e.printStackTrace();
}
}
}
打印结果:
java.lang.ArrayIndexOutOfBoundsException: 3
at oop.ExceptionDemo.main(ExceptionDemo.java:17)
系统正在维护中,请与管理员联系
Process finished with exit code 0
当用户联系管理员的时候告诉他“系统正在维护中,请与管理员联系”这个错误,
然后我们就可以根据提示语找到错误类型,再通过打印的错误栈信息去找到位置修改这个错误。
以上通过对异常处理,先反馈给用户温馨的信息,同时也留给了我们排错的时间。
(二)异常的分类
j2SDK定义的异常类
1)Throwable子类:Throwable所有异常类的根类
2)Error子类:
Error:称为错误,由java虚拟机生成并抛出,包括动态链接失败,虚拟机错误等,程序对其不做处理。
3)Exception子类:
Exception:所有异常类的父类,其子类对应了各种可能出现的异常事件,一般需要用户显示的声明或捕获。(非RuntimeException异常的话一定要处理)。
4)Exception子类RuntimeException的子类:
RuntimeException:一类特殊的异常,如被0除、数组下标越界等,其产生比较频繁,处理麻烦,如果显示的声明或捕获将会对程序的可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序(可处理可不处理)。
(三)异常的捕获和处理。
1)用try、catch捕获处理
注意:try、catch的时候要避免“抓大放小”的问题,捕捉异常的时候要先抓范围小的后抓范围大的。道理很简单,如果大异常里包含小异常,大的异常都让你给逮住了,小异常还有必要捕捉么。比如:你在第一个catch中捕获了exception,而你又在第二个catch中捕捉了ArrayIndexOutOfBoundsException,这样编译会报错。如果两个异常是同级的,这个可以,一点问题也没有。
2)用throw手动抛出,已知的错误用throws声明
异常处理释例:
package oop;
/**
* 说明:定义一个异常类
*
* @author huayu
* @date 2018/8/7 6:25 PM
*/
public class SomeException extends Throwable {
public SomeException(String exception) {
System.out.println("str= "+exception);
}
}
package oop;
/**
* 说明:异常处理测试类
* 注意:实际编程中,遇到错误就往外抛,是一个不好的编程习惯,如果能解决的我们应该用try catch给它
* 解决了。
* @author huayu
* @date 2018/8/7 6:20 PM
*/
public class ExceptionDealDemo {
//如果异常到了main方法还处理不了,就交给java运行时系统了,它会把相关的错误堆栈信息打印出来
public static void main(String[] args) {
ExceptionDealDemo exceptionDealDemo=new ExceptionDealDemo();
try {
exceptionDealDemo.method1();
} catch (SomeException e) {
e.printStackTrace();
}
}
//如果处理不了异常就往外抛
public void method1() throws SomeException{
method2();
}
//如果处理不了异常就往外抛
public void method2() throws SomeException{
method3();
}
//如果处理不了异常就往外抛
public void method3() throws SomeException{
throw new SomeException("出错了");
}
}
输出结果:
str= 出错了
oop.SomeException
at oop.ExceptionDealDemo.method3(ExceptionDealDemo.java:28)
at oop.ExceptionDealDemo.method2(ExceptionDealDemo.java:24)
at oop.ExceptionDealDemo.method1(ExceptionDealDemo.java:20)
at oop.ExceptionDealDemo.main(ExceptionDealDemo.java:13)
错误栈路径打印了每一行出错的位置
使用自定义的异常
使用自定义的异常一般有如下步骤:
1.通过继承java.lang.Exception类声明自己的异常类。
2.在方法适当的位置生成自定义异常的实例,并用throw语句抛出。
3.在方法的声明部分用throws语句声明方法可能抛出的异常。
package oop;
/**
* 说明:自己定义的异常类
*
* @author huayu
* @date 2018/8/7 7:47 PM
*/
public class MyException extends Exception {
private int id;
public MyException(String message, int id) {
super(message);
this.id = id;
}
public int getId() {
return id;
}
}
import oop.MyException;
/**
* 说明:测试自定义异常类
*
* @author huayu
* @date 2018/8/7 7:50 PM
*/
public class TestMyException {
public void regist(int num) throws MyException {
if (num < 0) {
//这儿抛出异常后,接下来这个方法中的语句就不会再执行了
throw new MyException("人数为负数,不合理", 3);
}
System.out.println("登记人数: " + num);
}
public void manager() {
try {
//分别传10跟-10执行一下
regist(10);
} catch (MyException e) {
System.out.println("登记失败,出错类型码" + e.getId());
e.printStackTrace();
}
System.out.println("操作结束");
}
public static void main(String[] args) {
TestMyException t = new TestMyException();
t.manager();
}
}
参数传10输出结果:
登记人数: 10
操作结束
参数传-10输出结果:
oop.MyException: 人数为负数,不合理
登记失败,出错类型码3
操作结束
at TestMyException.regist(TestMyException.java:12)
at TestMyException.manager(TestMyException.java:19)
at TestMyException.main(TestMyException.java:29)
Process finished with exit code 0
异常跟继承的关系:
重写方法需要抛出与原方法所抛出异常类型一致的异常或不抛出异常。(抛出的范围大了,范围小了都不行,抛多了也不行,抛少了也不行,错一点都不行!)
总结:这部分记住
1)一个图(异常组成架构图)
2)五个关键字(try、catch、finally、throw、throws)
3) 先逮小的,再逮大的
4)异常和重写的关系
Java 通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在 Java 中,每个异常都是一个对象,它是 Throwable 类或其子类的实例。当一个方法出现 异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个 异常并可以对其进行处理。Java 的异常处理是通过 5 个关键词来实现的:try、catch、throw、throws和 finally。一般情况下是用 try 来执行一段程序,如果系统会抛出(throw)一个异常对象,可以 通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try 用来指定一块 预防所有异常的程序;catch 子句紧跟在 try 块后面,用来指定你想要捕获的异常的类型;throw 语句用来明确地抛出一个异常;throws 用来声明一个方法可能抛出的各种异常(当然声明异常 时允许无病呻吟);finally 为确保一段代码不管发生什么异常状况都要被执行;try 语句可以嵌 套,每当遇到一个 try 语句,异常的结构就会被放入异常栈中,直到所有的 try 语句都完成。如 果下一级的 try 语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种 异常的 try 语句或者最终将异常抛给 JVM。
知识拓展:
1)运行时异常与受检异常有何异同?
异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可 能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟 程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java 编译器 要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异 常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,在 Effective Java 中对异常的 使用给出了以下指导原则:
- 不要将异常处理用于正常的控制流(设计良好的 API 不应该强迫它的调用者为了正常的控制 流而使用异常)
- 对可以恢复的情况使用受检异常,对编程错误使用运行时异常
- 避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)
- 优先使用标准的异常
- 每个方法抛出的异常都要有文档
- 保持异常的原子性
- 不要在catch中忽略掉捕获到的异常
2)常见的一些运行时异常。
- ArithmeticException(算术异常)
- ClassCastException (类转换异常)
- IllegalArgumentException (非法参数异常)
- IndexOutOfBoundsException (下标越界异常)
- NullPointerException (空指针异常)
- SecurityException (安全异常)
若有问题欢迎大家与我互动交流,可评论,可留言,以后每周我会坚持至少更新一篇博客文章,喜欢的朋友可以加一下关注。