黑马程序员-Java异常详解

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

异常的介绍

Java中的异常就是那些会阻碍当前程序运行,使程序执行可能失败的一些可能情况,如程序中出现除零错误,数组下标越界等。异常在Java中被封装成了一个类,继承自Throwable,名为Exception,它有很多子类,分别描述了系统中很多常见的异常情况,这些异常机制的出现使得编写程序时对一些问题的处理变得尤为方便,下面是一些简单的使用情况。

异常捕获的一般格式

/**
 * javac ExceptionDemo.javac
 * java ExceptionDemo
 * 输出:String index out of range: 3
 */
class ExceptionDemo {
    public static void main(String[] args) {
        try {
            // 可能产生异常的代码放到try内
            System.out.println("Hi".charAt(3));
        } catch(Exception e) {
            // 对异常的处理
            System.out.println(e.getMessage());
        }
    }
}

当将catch中的Exception换成StringIndexOutOfBoundsException时,输出结果同样为String index out of range: 3,如下:这里ExceptionStringIndexOutOfBoundsException的父类,子类的对象赋值给父类的类型,这在Java中称作多态。

/**
 * 输出:String index out of range: 3
 */
class ExceptionDemo {
    public static void main(String[] args) {
        try {
            // 可能产生异常的代码放到try内
            System.out.println("Hi".charAt(3));
        } catch(StringIndexOutOfBoundsException e) {
            // 对异常的处理
            System.out.println(e.getMessage());
        }
    }
}

异常的继承关系

Exception的继承关系图可以清楚的知道Exception的父类及其子类的关系,超类是ThrowableErrorException的共同超类),Throwable的超类便是Object(除本身外Java中所有类的直接或者间接超类)。以Exception结尾的类都是继承自Exception

try内有多条语句时,可能会产生多种异常,下列代码虽然加上了异常捕获操作但是没有还是产生了异常,程序崩溃。

/**
 * 输出:H
 *       Exception in thread "main" java.lang.ArithmeticException: / by zero
 *       at ExceptionDemo3.main(ExceptionDemo3.java:10)
 */
class ExceptionDemo3 {
    public static void main(String[] args) {
        try {
            // 可能产生异常的代码放到try内
            System.out.println("Hi".charAt(0));
            System.out.println(123 / 0);
        } catch(StringIndexOutOfBoundsException e) {
            // 对异常的处理
            System.out.println(e.getMessage());
        }
    }
}

原因是虽然有catch来捕获异常,但是仅仅捕获的是StringIndexOutOfBoundsException异常,这条异常只有System.out.println("Hi".charAt(0));才会产生。而System.out.println(123 / 0);这条语句会产生另一种异常,叫做ArithmeticException,即运算异常。而这条异常没有相应的捕获语句,所以虚拟机采用默认处理方式,即让程序崩溃。

加上ArithmeticException 异常捕获后便可以正常处理异常,如下:

/**
 * 输出:H
 *       异常:/ by zero
 */
class ExceptionDemo3 {
    public static void main(String[] args) {
        try {
            // 可能产生异常的代码放到try内
            System.out.println("Hi".charAt(0));
            System.out.println(123 / 0);
        } catch(StringIndexOutOfBoundsException e) {
            // 对异常的处理
            System.out.println(e.getMessage());
        } catch(ArithmeticException e) {
            System.out.println("异常:" + e.getMessage());
        }
    }
}

难道try内有大量语句时,会产生很多异常的情况就要加很多个catch?当然你可以像第一个示例那样使用Exception来接收所有异常,但是这样又会有问题,那就是所有异常都会统一处理,那么就使用Exception和其他异常混合使用的情况,这种情况的时候要注意一点,Exception一定要放在最后面一条catch中,否则编译会报错。正确写法如下:


/**
 * 输出:H
 *       异常:/ by zero
 */
class ExceptionDemo3 {
    public static void main(String[] args) {
        try {
            // 可能产生异常的代码放到try内
            System.out.println("Hi".charAt(0));
            System.out.println(123 / 0);
        } catch(StringIndexOutOfBoundsException e) {
            // 对异常的处理
            System.out.println(e.getMessage());
        } catch(ArithmeticException e) {
            System.out.println("异常:" + e.getMessage());
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

finally关键字

Java异常捕获中的另一个关键字finally,同catch的用法类似,不过finally后没有类型,finally的功能是作为try...catch...finally中必然执行的一段。也就是说try内的代码产生或者没有产生异常,最终都会执行finally内的代码。这可以应用到一些网络操作中,如访问数据库时有打开数据库,当操作数据库时出现了错误,那么在finally中写上关闭数据库的操作便起到了很好的作用。避免系统打开很多数据库连接而无法关闭且又无法操作,这样会非常消耗系统资源。访问网络时也是同样的道理。一些finally的演示如下:

/**
 * 输出:
 * B
 * C
 */
class ExceptionDemo4 {
    public static void main(String[] args) {
        try {
            int num = 4 / 0; // 制作异常
            System.out.println("A");
        } catch (Exception e) {
            System.out.println("B");
        } finally {
            System.out.println("C");
        }
    }
}

/**
 * 输出:
 * A
 * B
 * C
 */
class ExceptionDemo4 {
    public static void main(String[] args) {
        try {
            System.out.println("A");
            int num = 4 / 0; // 制作异常
        } catch (Exception e) {
            System.out.println("B");
        } finally {
            System.out.println("C");
        }
    }
}

/**
 * 输出:
 * 4
 * 4
 * 0
 */
class ExceptionDemo4 {
    public static void main(String[] args) {
        int num = 4;
        try {
            System.out.println(num);
            int n = 10 / 0;  // 制造异常
            num += 2;   // 异常发生后会立即进入异常处理部分
        } catch (Exception e) {
            System.out.println(num);
            num = 0;
        } finally {
            System.out.println(num);
        }
    }
}

try内的代码在执行时碰到了异常后便不再继续执行,而是跳到对应的异常处理代码段执行,然后再执行finally段的代码。

在带有返回值的函数中时,finally的执行如下:

/**
 * 输出:
 * try:4
 * catch:4
 * finally:5
 * main:4
 */
class ExceptionDemo5 {
    public static void main(String[] args) {
        System.out.println("main:" + method());
    }

    public static int method() {
        int num = 4;
        try {
            System.out.println("try:" + num);
            int n = 10 / 0;  // 制造异常
        } catch (Exception e) {
            System.out.println("catch:" + num);
            return num;
        } finally {
            num ++;
            System.out.println("finally:" + num);
        }
        return 0;
    }
}

finally之前出现了return语句时,返回值的内容会被压栈,所以在finally中修改num的值是不会影响最终在main函数中接收到的返回值的内容的,这也体现了finally一定会执行的一点。

但是当遇到下面这种情况就要另当别论了:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * 文件内容为:Hello
 * 若将System.exit(0);注释掉,文件内容为Hi
 */
class ExceptionDemo5 {
    public static void main(String[] args) {
        try {
            FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt"));
            fout.write("Hello".getBytes());
            fout.close();
            System.exit(0); // 直接退出系统
        } catch (IOException e) {

        } finally {
            try {
                FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt"));
                fout.write("Hi".getBytes());
                fout.close();
            } catch (IOException e) {

            }
        }
    }
}

所以finally的内容必然执行也是要建立再程序还处于正在运行的状态,程序已退出虚拟机当然无法再执行finally的内容了。

try...finally组合

除了try...catch...finally还有一种try...finally组合方式,即去掉catch段。若代码段抛出的是RuntimeException便将异常抛给上一层,若是非RuntimeException或其子类便会编译错误。(ArithmeticException属于RuntimeException

/**
 * 输出:
 * method:finally
 * main:/ by zero
 */
class ExceptionDemo6 {
    public static void main(String[] args) {
        try {
            method();
        } catch(Exception e) {
            System.out.println("main:" + e.getMessage());
        }
    }

    public static void method() {
        try {
            int num = 6 / 0;
        } finally {
            System.out.println("method:finally");
        }
    }
}

异常的抛出throws

对于异常的处理可以使用try...catch,可以使用try...catch...finally,也可以使用try..finally,当然还有其他方式,你可以使用throws把异常抛给上一层,这里的上一层指的是如main函数调用method方法,对于method方法来说main函数就是上一层。

/**
 * 输出:
 * main:Hello
 */
class ExceptionDemo7 {
    public static void main(String[] args) {
        try {
            method();
        } catch(ClassNotFoundException e) {
            System.out.println("main:" + e.getMessage());
        }
    }

    public static void method() throws ClassNotFoundException {
        Class<?> c = Class.forName("Hello");
    }
}

多个异常的抛出时可以在throws后面使用,隔开,而抛出一条异常是使用throw来实现的,如下:

/**
 * 输出:
 * main:-Message-
 */
class ExceptionDemo7 {
    public static void main(String[] args) {
        try {
            method();
        } catch(ClassNotFoundException e) {
            System.out.println("main:" + e.getMessage());
        } catch(Exception e) {
            System.out.println("main:" + e.getMessage());
        }
    }

    public static void method() throws ClassNotFoundException, IllegalAccessException {
        // Class<?> c = Class.forName("Hello");
        throw(new IllegalAccessException("-Message-"));
    }
}

自定义异常

自定义异常其实很简单,只需要继承Exception即可,或者继承Exception的子类,如下:

/**
 * 输出:
 * main:My Exception
 */

class MyException extends Exception {

    public MyException(String msg) {
        super(msg);
    }
}
class ExceptionDemo7 {
    public static void main(String[] args) {
        try {
            method();
        } catch(MyException e) {
            System.out.println("main:" + e.getMessage());
        }
    }

    public static void method() throws MyException {
        throw(new MyException("My Exception"));
    }
}

RuntimeException简介

当抛出RuntimeException或者RuntimeException的子类时无需使用throws在方法名后面标出,这一类异常可能无法通过捕获处理很好地处理,如除零错误发生时即使捕获到了异常,但是后面的运算结果是会受到影响的,一定会出现一个不正确的运算结果。如上面出现过的除零异常,无需在方法名后加异常类型说明:

/**
 * 输出:
 * method:finally
 * main:/ by zero
 */
class ExceptionDemo6 {
    public static void main(String[] args) {
        try {
            method();
        } catch(Exception e) {
            System.out.println("main:" + e.getMessage());
        }
    }

    public static void method() /*这里不需要加 ArithmeticException*/{
        try {
            int num = 6 / 0;
        } finally {
            System.out.println("method:finally");
        }
    }
}
时间: 03-09

黑马程序员-Java异常详解的相关文章

黑马程序员 ---------- Java网络技术之 ---正则表达式 (Day06)

---------------------- ASP.Net+Unity开发..Net培训.期待与您交流! ---------------------- 正则表达式 正则表达式:基本知识 1  字符,   2 字符类 , 3 预定义字符类 , 4 边界匹配器 ,5 Greedy 数量词,6 Logical 运算符 详解: 1 字符 x   字符 x \\ 反斜线字符 \0n 带有八进制值 0 的字符 n (0 <= n <= 7) \0nn 带有八进制值 0 的字符 nn (0 <= n

黑马程序员——Java I/O流基础知识点(File类)

File工具 File类就是用俩将文件或者文件夹封装对象,弥补流对象的不足--流只能操作数据,不能操作文件夹的 封装的是路径!!! 构造方法演示 1.可以将已有的未出现的文件或者文件夹封装成对象. File f1=new File("c:\\abc\\a.txt"): File f2=new File("d:\\abc","ab.txt"打印,会打印路径.:目录分隔符,为了更好地跨平台File. File类常见功能 1,创建 createNewF

黑马程序员——Java基础---集合框架工具类

黑马程序员——Java基础<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------ 一.概述 Java为操作Set.List和Map提供了一系列工具类,主要有Collections和Arrays.这两个工具类的特点:类中的方法都是静态的,不需要创建对象,直接使用类名调用即可.Collections:是集合对象

黑马程序员——java基础---IO(input output)流字符流

黑马程序员——java基础---IO(input output)流字符流 ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- io(input output)流特点: 1,io流用来处理数据之间的传输 2,java对数据的操作是通过流的方式: 3,java用于操作流的对象都在io包中: 4,流按操作数据分为两种:字节流和字符流: 5,流按流向分为:输入流和输出流. 注意:流只能操作数据,而不能操作文件. 3.IO流的常用基类: 1)字节流的抽象

黑马程序员——java基础——集合(Collection)

 黑马程序员--java基础--集合(Collection) ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 集合框架的构成及分类,如下图: 1.为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式. 2.数组和集合类同是容器,有何不同? 数组虽然也可以存储对象,但长度是固定的:集合长度是可变的.数组中可以存储基本数据类型,集合只能存储对象. 3.

黑马程序员——Java基础---IO(下)

黑马程序员——Java基础---IO(下) ------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------ 一.概述 Java除了基本的字节流.字符流之外,还提供了File类.properties类.打印流.序列流等和输入输出相关的类,它们能够帮助我们更好的处理信息.下面将对它们进行简单的介绍. 一.正

黑马程序员——Java高新技术代理

代理 普通代理 很多时候,我们使用别人代码往往会发现别人代码的功能并不是十分符合我们的需求,调用别人的方法的时候,总是先new一个对象,然后我们的前处理做完,然后调用别人代码的方法,再加入后处理,这样做往往十分麻烦.代理就为其他类提供了一种控制其对象的方法.代理类和委托类必须实现同一个接口,这样代理类才能在需要的时候代替委托类对象,执行委托类的方法. interface Solution{ public void doSomething(); } //委托类Demo实现了接口 class Dem

黑马程序员——Java I/O基础知识之I/O流

I/O流基础知识--字节流和字符流 文件存储在硬盘中,是以二进制表示的,只有内存中才能形成字符.数据的来源可以有硬盘,内存,控制台,网络,Java把数据从一个地方转到另一个地方的现象称为流,用InputStream和OutputStream接口来表示,这两个流里面的都是以字节为单位的,后来加入了Reader和Writer,里面操作的是字符,是两个字节为单位的. 字节流 字节流将数据写入文件 try { File file =new File("d:" +File .separator+

黑马程序员——Java网络编程之UDP传输

网络编程 网络模型 通讯要素:InetAddress(对象):ip地址,网络中设备的标识,不可记忆,可用主机名,本地回环地址:127.0.0.1主机名localhost 端口号 传输协议:UDP,将数据的源及目的封装成数据包中,不需要建立连接,每个数据包的大小限制在64K内,无连接,是不可靠协议,不需要建立连接,速度快.力求速度,不求数据的准确性.比如聊天软件,网络会议. TCP:建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接效率稍低. S