1.介绍
在开发过程中,都会遇到程序运行时的异常,例如逻辑错误、硬件故障等,本篇博客将会详细的介绍一下Java中的异常处理机制。
2.Try和Catch捕获异常
我们在处理异常过程中经常用到的就是Try和Catch语句了。try语句块能够指出可能出现的异常,随后通过一个或者多个Catch语句块来捕获。
2.1.简单的Try和Catch语法
package com.RuntimeException;
public class Sample11_1
{
public static void main(String[] args)
{
//被监视的代码块
try
{
//创建数组对象
int[] a=new int[4];
System.out.println("整型数组创建完毕!!");
//访问数组元素
a[3]=9;
System.out.println("整型数组中第四个元素的数值为"+a[3]+"!!!");
}
//处理下标越界异常
catch(ArrayIndexOutOfBoundsException aiobe)
{
//打印提示信息
System.out.println("这里出现的错误类型是:数组下标越界!!");
}
//处理空引用异常
catch(NullPointerException npe)
{
//打印提示信息
System.out.println("这里出现的错误类型是:空引用!!");
}
System.out.println("主程序正常结束!!!");
}
}
2.2.异常的传播过程
在异常的传播过程中,如果没有Catch语句进行捕获的话,异常将会沿着方法的调用栈一直向上传播。如果传播的过程中一直没有Catch语句块捕获的话,则最终传播到main方法中,最后由Main方法进行抛出,由java运行时的环境来处理。
package com.RuntimeException;
import java.io.IOException;
import java.net.ServerSocket;
public class Sample11_2 {
public static void main(String[] args) {
// 主方法中调用method1方法
method1();
}
static void method1() {
// method1方法中调用method2方法
method2();
}
static void method2() {
int[] a = new int[3];
// 产生数据下标越界错误
a[4] = 12;
System.out.println("OK!!!");
}
}
2.3.finally语句块的作用
某些特殊情况下,当程序出错后,无论抛出异常与否都必须保证执行。例如,打开了一个数据库连接,无论处理过程中是否抛出异常,最后都要关闭连接,不能由于抛出异常就影响了其执行。那么此时可以把必须要执行的语句放置到finally中。
package com.RuntimeException;
public class Sample11_3 {
public static void main(String[] args)
{
//受监视的代码块
try
{
//创建长度为4的int型数组
int[] a=new int[4];
System.out.println("整型数组创建完毕!!");
//为数组最后一个元素赋值
a[3]=9;
System.out.println("整型数组中第四个元素的数值为"+a[5]);
}
//处理空引用异常代码块
catch(NullPointerException npe)
{
//打印提示信息
System.out.println("这里出现的错误类型是:空引用!!");
}
//finally块
finally
{
//打印提示信息
System.out.println("这里是finally块,无论是否抛出异常,这里总能执行!");
}
}
}
2.4.try、catch以及finally之间需要注意的问题
~没有catch的情况下,finally必须紧跟try
~catch和finally不能同时省略
~try、catch以及finally块之间不能插入其他代码
3.异常的层次结构
当发生异常的时候,java会将异常包装成一个异常类的对象,并将其引用作用参数传递给相应的catch语句来处理。在java中异常分为两部分捕获异常和未捕获异常
3.1.异常层次图
~Throwable类有两个直接子类,Error与Exception类,Exception类有一个子类RuntimeException。
~捕获异常一般都是由外界因素产生,并且是可以恢复的,并不是由程序引起的,这些异常程序本身没有问题也可能会产生。
~未捕获异常,即Error类以及其子类以及RuntimeException类及其子类
~捕获的异常类型,都需要在程序中进行捕获处理。
3.2.异常的显性再抛出
对于捕获的异常,在方法声明时抛出后,可以在其中通过Try以及Catch再次抛出。
package com.RuntimeException;
import java.io.IOException;
import java.net.ServerSocket;
public class Sample11_10 {
// 定义connect方法将有可能抛出IOException异常
public void connect() throws IOException {
// 受监视的代码
try {
// 创建ServerSocket对象
ServerSocket ss = new ServerSocket(9999);
}
// 异常处理代码
catch (IOException e) {
// 将异常抛出
throw e;
}
}
}
3.3.隐形再抛出
如果只是想把异常再抛出的话,不必使用显性再抛出,直接使用隐形再抛出即可。
package com.RuntimeException;
import java.io.IOException;
import java.net.ServerSocket;
public class Sample11_12 {
//声明方法myFunction将有可能抛出IOException异常
public static void myFunction() throws IOException
{
//创建ServerSocket对象
ServerSocket ss=new ServerSocket(9999);
}
public static void main(String[] args)
{
//受监视的代码
try
{
//调用myFunction方法
myFunction();
}
//异常处理代码
catch(IOException e)
{
//打印调用栈信息
e.printStackTrace();
}
System.out.println("恭喜你,程序正常运行结束!!!");
}
}
4.自定义异常类
我们也可以自定义自己的异常类来满足自己使用时的需求。
4.1.创建自定义异常类
一般创建自定义异常类的话,只需继承Exception或者其他捕获异常类,常用到的方法如下
~printStackTrace():该方法将在控制台打印异常调用栈的信息
~toString():该方法将返回该异常对象的字符串表示
~getMessage():返回异常中携带的出错信息。
package com.RuntimeException;
class MyException extends Exception
{
//两种版本的构造器
public MyException()
{ }
public MyException(String msg)
{
super(msg);
}
}
//主类
public class Sample11_15
{
public static void main(String[] args)
{
//创建自定义异常类对象
MyException me=new MyException("自定义异常类");
me.printStackTrace();
//调用继承的方法
System.out.println("自定义异常对象的字符串表示为:“"+me.toString()+"”。");
System.out.println("自定义异常对象携带的出错信息为:“"+me.getMessage()+"”。");
}
}
4.2.同时捕获多次异常
如果try语句后要跟多个catch的话,最后catch语句的先后顺序满足从小到大的范围进行捕获,也就是异常先从子类开始,慢慢的递增。