博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java异常简介及异常信息缺失处理
阅读量:7122 次
发布时间:2019-06-28

本文共 7915 字,大约阅读时间需要 26 分钟。

java异常简介

Error

Error用来表示编译时和系统错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如:系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。如java.lang.StackOverFlowError和Java.lang.OutOfMemoryError。对于这类错误,Java编译器不去检查他们。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。

Exception

Exception(异常)是应用程序中可能的可预测、可恢复问题。异常一般是在特定环境下产生的,通常出现在代码的特定方法和操作中。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

Exception又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception )。 RuntimeException:Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try……catch捕获,也没有用throws抛出,还是会编译通过,如除数为零的ArithmeticException、错误的类型转换、数组越界访问和试图访问空指针等。
Checked Exception:这类异常要么try...catch捕获处理,要么用throws字句声明抛出,交给它的调用方处理,否则编译不会通过。

try-catch-finally-return执行顺序:

1、不管是否有异常产生,finally块中代码都会执行,即便在try或catch中加入了continue、break或者return。

2、finally是在return后面的表达式运算后执行的,所以函数返回值是在finally执行前确定的。无论finally中的代码怎么样,返回的值都不会改变,仍然是之前return语句中保存的值;
例如:

public static void main(String[] args) {        System.out.println(Main.test());;    }    public static int test() {        int x = 1;        try {            x += 2;            return x;        } finally {            ++x;        }    }复制代码

程序返回值:3

3、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

public static void main(String[] args) {        System.out.println(Main.test());;    }    public static int test() {        int x = 1;        try {            x += 2;            return x;        } finally {            ++x;            return x;        }    }复制代码

程序返回值:4

异常信息缺失情况

重新抛出异常

有时我们在捕获到异常后,可能在捕获的地方不适合处理该异常,我们需要将它重新抛出:

catch(Exception e){        throw e;     } 复制代码

这样我们可以将异常交给上一级环境处理,但是这样抛出的异常携带的信息,也就是printStackTrace()方法显示的是原来异常抛出点的调用栈信息,而非重新抛出点的信息,这样重新抛出点的调用信息就被掩盖了。如果想更新重新抛出点信息到这个异常调用栈中,可以使用fillInStackTrace()方法,那么当前调用栈的信息就更新到了这个异常对象中了:

catch(Exception e){        throw e.fillInStackTrace();     }复制代码

还有一种情况,也会存在类似的丢失现象:

catch(Exception e){    throw new Exception();} 复制代码

这样我们上一级的抛出的异常信息就丢了,接收异常的地方就是只能得到new Exception()这个异常的信息。在JDK1.4以前如果你希望保存丢失的那个异常信息,只能通过编码的方式自己实现,而在JDK1.4后,Throwable类添加了一个Throwable类型的属性cause,用来表示原始异常,那么我们就可以通过异常链从新的异常追踪到异常最初发生的位置。我们可以通过构造函数或initCause(Throwable cause)方法传入一个Throwable对象用来记录原始异常。

Throwable.java:

/**     * The throwable that caused this throwable to get thrown, or null if this     * throwable was not caused by another throwable, or if the causative     * throwable is unknown.  If this field is equal to this throwable itself,     * it indicates that the cause of this throwable has not yet been     * initialized.     *     * @serial     * @since 1.4     */    private Throwable cause = this;复制代码

异常链

另外,finally语句也可能会造成异常信息丢失:

class SomeException extends Exception {    @Override    public String toString() {        return "Some exception";    }}class OtherException extends Exception {    @Override    public String toString() {        return "Other exception";    }}public class Main {    void some() throws SomeException {        throw new SomeException();    }    void other() throws OtherException {        throw new OtherException();    }    public static void main(String[] args) {        try {            Main test = new Main();            try {                test.some();            } finally {                test.other();            }        } catch (Exception e) {            e.printStackTrace();        }    }}复制代码

程序返回值

Other exception	at Main.other(Main.java:23)	at Main.main(Main.java:32)复制代码

把最外一层try看作是上一级程序的处理,在这个try里面发生了两次异常,但是我们只能获得从finally中抛出的异常信息,而在some()方法中的异常信息丢失,这种情况我们称上一个异常被抑制了。即finally中抛出的异常会抑制其对应的try或catch中抛出的异常。

在JDK1.7之后,Throwable添加了一个属性suppressedExceptions,用来表示被抑制的异常。我们可以使用addSuppressed(Throwable exception)和getSuppressed()方法解决此问题

public static void main(String[] args) {        try {            Main test = new Main();            Exception exception = null;            try {                test.some();            } catch (SomeException e) {                exception = e;                throw e;            } finally {                if (exception != null) {                    try {                        test.other();                    } catch (OtherException e) {                        exception.addSuppressed(e);                    }                } else {                    test.other();                }            }        } catch (Exception e) {            e.printStackTrace();        }    }复制代码

程序返回值

Some exception	at Main.some(Main.java:19)	at Main.main(Main.java:31)	Suppressed: Other exception		at Main.other(Main.java:23)		at Main.main(Main.java:38)复制代码

栈轨迹

捕获到异常时,往往需要进行一些处理。比较简单直接的方式就是打印异常栈轨迹Stack Trace。

通过查看源码Throwable.java中printStackTrace()方法,我们可以对上述异常信息丢失的解决办法有更加清晰地认识:

private void printStackTrace(PrintStreamOrWriter s) {        // Guard against malicious overrides of Throwable.equals by        // using a Set with identity equality semantics.        Set
dejaVu = Collections.newSetFromMap(new IdentityHashMap
()); dejaVu.add(this); synchronized (s.lock()) { // Print our stack trace s.println(this); StackTraceElement[] trace = getOurStackTrace(); for (StackTraceElement traceElement : trace) s.println("\tat " + traceElement); // Print suppressed exceptions, if any for (Throwable se : getSuppressed()) se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu); // Print cause, if any Throwable ourCause = getCause(); if (ourCause != null) ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu); } }复制代码

从源码中我们可以发现,在打印异常栈轨迹时,打印顺序为:异常本身栈轨迹信息、被抑制异常栈轨迹信息、原始异常栈轨迹信息。

try-with-resources 语法糖

在JDK 7之前,资源需要我们手动关闭。如:

String s = "Some String";    BufferedWriter writer = null;        try {        writer = new BufferedWriter(new FileWriter("test"));        writer.write(s, 0, s.length());    } catch (IOException x) {        System.err.format("IOException: %s%n", x);    } finally {        if (writer != null) {            try {                writer.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }复制代码

try-with-resources 是 JDK 7 中一个新的异常处理机制,它能够很容易地关闭在 try-catch 语句块中使用的资源。所谓的资源(resource)是指在程序完成后,必须关闭的对象。try-with-resources 语句确保了每个资源在语句结束时关闭。所有实现了 java.lang.AutoCloseable 接口(其中,它包括实现了 java.io.Closeable 的所有对象),可以使用作为资源。

在 try 语句中越是最后使用的资源,越是最早被关闭。

class Resource implements AutoCloseable {    void doSome() {        System.out.println("do something");    }    @Override    public void close() throws Exception {        System.out.println("resource is closed");    }}public class Main {    public static void main(String[] args) {        try(Resource res = new Resource()) {            res.doSome();        } catch(Exception ex) {            ex.printStackTrace();        }    }}复制代码

程序返回值

do somethingresource is closed复制代码

try-with-resources 声明在 JDK 9 已得到改进。如果你已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。

try-with-resources 是语法糖,那么背后又是如何实现的呢?

下面代码:

try(FileInputStream fstream = new FileInputStream("test")) {        fstream.read();    } catch (IOException e) {        e.printStackTrace();    }复制代码

编译后反编译为:

try {        FileInputStream fstream = new FileInputStream("test");        Throwable var2 = null;        try {            fstream.read();        } catch (Throwable var12) {            var2 = var12;            throw var12;        } finally {            if (fstream != null) {                if (var2 != null) {                    try {                        fstream.close();                    } catch (Throwable var11) {                        var2.addSuppressed(var11);                    }                } else {                    fstream.close();                }            }        }    } catch (IOException var14) {        var14.printStackTrace();    }复制代码

从反编译代码可以发现,其背后也是使用了 addSuppressed(Throwable exception) 方法来实现对异常信息的处理

参考资料:

转载于:https://juejin.im/post/5bf2940be51d457c042c313c

你可能感兴趣的文章
Angular2之路由学习笔记
查看>>
JSP中文件上传的关键步骤
查看>>
数据结构上机3栈-括号匹配
查看>>
xfire冲突问题解决(maven配置)
查看>>
UINavigationController_学习笔记
查看>>
.htaccess更改目录下的默认主页
查看>>
Android WindowManager实现悬浮窗效果 (一)——与当前Activity绑定
查看>>
hdu 4717 Tree2cycle(树形DP)
查看>>
镜像的使用(6-13)
查看>>
SQL Server 时间戳与时间格式互相转换
查看>>
RabbitMQ入门-Topic模式
查看>>
多线程面试体系列(13):多线程同步内功心法——PV操作下
查看>>
Work
查看>>
[开源]快速构建文件下载,支持文件加密,自定义限速
查看>>
Mac系统搭建java开发环境
查看>>
菜鸟对新技术的一点看法
查看>>
2016年2月23日----Javascript全局变量和局部变量
查看>>
iOS开发基础知识-多线程概念深入浅出
查看>>
论PHP框架设计模式及MVC的缺陷
查看>>
立flag(java)
查看>>