Java 项目中如何使用异常

Wesley13
• 阅读 1080
  1. 早抛出,晚捕获.

  2. 如果 finally 语句中有 return 语句,则 finally 中的 return 语句将会覆盖 try 中的 return 语句,如以下代码,将会输出 1。如果在 finally 语句里有抛出异常,那么此异常将会覆盖 try 块中抛出的异常。

    public class FinallyReturnTest { public static void main(String[] args) { System.out.println(testFinallyReturn(1)); } public static int testFinallyReturn(int n) { try { // do something return n+n; } finally { return n; } } } /* output --> 1 */

 3. 进行文件IO/网络操作时,最好按以下形式使用 try/catch 块(摘自《Java 核心技术 卷1》 P480),以确保资源能被正确释放。

InputStream in = ...;
try{ 
    try{ 
        // code that might throw exceptions }finally{
        in.close();
    }
} catch (IOException e){ 
    // show error dialog 
}
  1. 如果覆盖父类的方法,而父类的方法没有抛出异常,那么子类中的方法就必须捕获自己内部代码中出现的每一个异常。即,不允许子类的 throws 声明符中出现超过父类方法所声明的异常类范围。

  2. 实现一个函数时,怎么判断这个函数内 catch 到的异常是再次 throw 出去,还是就地处理呢?
    答:如果这个函数引发的异常,需要被外界(使用程序的人)所知,就需要 throw 出去,否则,就地处理。

6. 异常最好在需要向外界返回信息时进行处理(即与外界进行交互的第一层处,因为此时把导常发生的原因报告出去才是有意义的);
如一个按钮的 click 事件处理函数里面,直接写 try/catch,在 try 里调用相关处理函数,在 catch 里进行处理Exception;即内部调用的时候,都只要 throw 即可.

  1. 当准备把异常传给调用方时,要确保异常的抽象层次与子程序接口的抽像层次是一致的(摘自《代码大全2》P200)
    下面代码是一个抛出抽象层次不一致的异常的类:

    class Emoyee { ... public TaxId GetTaxId() throws EOFException { ... } ... }

GetTaxId() 把更低层的 EOFException(文件结束,end of file)异常返回给它的调用方。它本身并不拥有这一异常,但却通过把更低层次的异常传递给调用方,暴露了自身的一些实现细节。这就使得子程序的调用方法代码不是与 Employee 类的代码耦合,而是与比Employee类层次更低的抛出 EOFException 异常的代码耦合起来了。这样即破坏了封装性,也减低了代码的智力上的可管理性(intellectual manageablility)。
下面代码是一个抛出抽象层次一致的异常的类:

class Emoyee{
    ... 
    public TaxId GetTaxId() throws EmployeeDataNotAvailable{
        ...
    }
    ...
}

GetTaxId() 里的异常处理代码可能只需要把一个 io_disk_not_ready(碰盘IO未就绪) 异常映射为 EmployeeDataNotAvailable(雇员数据不可用)异常就好了, 因为这样可以充分地保持接口的抽象性.

  1. 最好是能把 Exception 都重新包装成自己系统定义的 Exception,这样可以减少上层函数需要声明的异常类型,否则有可能最高层函数后面要声明一长串 Exception, 同时也能确保异常的抽象层次与子程序接口的抽像导次是一致的.
    例如:
    读取一个JSON文件,然后解析JSON字符串,读取各个key的值,这时会碰到各种 exception,如 FileNotFoundException,JSONException,读取到的 value 进行类型转化时的 ParseException,这些 Exception 不能在 catch 到后直接重新抛出,应当把当前Context信息包装后,再抛出一个统一的更有意义的 Exception。

9. 一个比较好的项目实践总结.(整理自笨狐狸的架构)
假设现在开发一个 EmployeeManager 的系统, 则在异常管理模块, 先统一定义好两个相关的异常处理类, 代码如下:

// file EmployeeManagerException.java 统一的异常类
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

public class EmployeeManagerException extends RuntimeException {
    private final String defaultKey = "com.xxx.exception";
    private String exceptionKey;
    private Object[] args;

    // 国际化 
    private ResourceBundle rb = ResourceBundle.getBundle("exceptionMessages",
            Locale.getDefault());

    public EmployeeManagerException(ExceptionType type, Object... args) {
        this.exceptionKey = type.getExceptionKey();
        this.args = args;
    }

    public String getMessage() {
        if ((exceptionKey != null) && !exceptionKey.equals("")) {
            return MessageFormat.format(rb.getString(exceptionKey), args);
        }

        return MessageFormat.format(rb.getString(defaultKey), args);
    }
}

// file ExceptionType.java  异常枚举类
public enum ExceptionType {
    COMMON_EXCEPTION("com.xxx.exception"),
    EMPLOYEE_DATA_NOT_AVAILABLE("com.xxx.EmployeeDataNotAvailable");

    private ExceptionType(String exceptionKey) {
        this.exceptionKey = exceptionKey;
    }

    public String getExceptionKey() {
        return exceptionKey;
    }

    private String exceptionKey;
}

# file exceptionMessages_en.properties 定义需要进行格式化的 exception
com.sap.xxx.exception=Server side exception, the detail reason is {0}
com.xxx.EmployeeDataNotAvailable=Employee data not available, the detail reason is{0}

使用示例:

public static void main(String[] args) { 
    try{ 
        // do something  
    } catch (EOFException e){ 
        throw new EmployeeManagerException(ExceptionType.EMPLOYEE_DATA_NOT_AVAILABLE, 
                                           e.getMessage());
    }
}

可以看到, 系统中所有低层次的异常都被重新包装成 EmployeeManagerException,  并通过 ExceptionType  指定其具体的异常类型, 然后在最后提取异常信息时, 根据 exceptionKey和 args[], 可以获得一个具有良好可读性, 可国际化的 Exception 信息. 在系统规模不大时, 可以采用这样的架构, 避免过多的异常类.

 10. 对 9 架构的进一步思考. 
这样的架构在需要添加新异常时, 就需要往 Exception 里新增一个枚举数据, 不符合 OCP 原则, 更好的办法应该是根据 EmployeeManagerException 派生出各个不同的 EmployeeManagerException 子类, 每一个子类代表一种异常, 具体代码如下:

// file EmployeeManagerException.java 
import java.io.EOFException;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

public abstract class EmployeeManagerException extends RuntimeException {
    private final String defaultKey = "com.xxx.exception";
    private String exceptionKey;
    private Object[] args;

    // 国际化 
    private ResourceBundle rb = ResourceBundle.getBundle("exceptionMessages",
            Locale.getDefault());

    public EmployeeManagerException(Object... args) {
        this.exceptionKey = this.getExceptionKey();
        this.args = args;
    }

    abstract String getExceptionKey();

    public String getMessage() {
        if ((exceptionKey != null) && !exceptionKey.equals("")) {
            return MessageFormat.format(rb.getString(exceptionKey), args);
        }

        return MessageFormat.format(rb.getString(defaultKey), args);
    }
}

// file EmployeeDataNotAvailable.java 
public class EmployeeDataNotAvailable extends EmployeeManagerException { 
    public EmployeeDataNotAvailable(Object ... args){
        super(args);
    }
    
    @Override
    String getExceptionKey() { 
        return "com.xxx.EmployeeDataNotAvailable";
    }
}

# file exceptionMessages_en.properties 定义需要进行格式化的 exception
com.sap.xxx.exception=Server side exception, the detail reason is {0}
com.xxx.EmployeeDataNotAvailable=Employee data not available, the detail reason is{0}

使用示例

public static void main(String[] args) { 
    try{ 
        // throw new EOFException();  
    } catch (EOFException e){ 
        throw new EmployeeDataNotAvailable(e.getMessage());
    }
}

可以看到, 这个的架构功能是等同于9的, 而且能满足 OCP 原则,  即新增一种异常时, 我们只需要从 EmployeeMnagerException 派生一个新的子类,  重写 getExceptionKey 函数即可,然后在 properties 文件里添加新的一个 key/value 对即可。但是这样又会导致函数声明的 Exception 数过多的问题。

  1. 异常声明具有“多态性”
    如上例子中,如果有方法抛出了 EmployeeDataNotAvailable 异常,那么它的函数声明的异常列表可以直接写成 throws EmployeeManagerException 的,这与函数调用时的参数可以向上转型是一样的,所以 10 中可以使用统一 throws EmployeeManagerException 来避免函数声明的 Exception 数过多的问题?

  2. “checked exception” 转换为 "unchecked exception”(整理自《Java编程思想 第4版》,把“被检查的异常”转换为“不检查的异常”)
    即把所有异常都转化为 RuntimeException,这样就无需在函数声明里列出异常名,每一个调用此函数的函数也不需要再写 try/catch 块,只需要在最外层捕获到这个 RuntimeException, 然后使用  getCause() 函数把其实际的异常类型获取到即可。代码如下:

    try{ //.. todo sometime useful } catch (IDontKnowWhatToDoWIthThisCHeckedException e){ throw new RuntimeException(e); }

个人不推荐这样的做法,把所有的异常完全不处理,一股脑丢给最上层处理是不负责任的做法,当系统稍具规模时,最上层也根本无法针对如此多的异常逐个进行处理。

环境: JDK1.6.0_30

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
执键写春秋 执键写春秋
3年前
Java中的异常丢失与异常链
1.异常丢失1.1在finally子句中抛出异常。packageperson.xsc.datamanage;classMyException1extendsExceptionpublicStringtoString()return"测试异常————test1";classMyException2extendsException
Wesley13 Wesley13
3年前
Java笔试面试总结—try、catch、finally语句中有return 的各类情况
前言之前在刷笔试题和面试的时候经常会遇到或者被问到trycatchfinally语法块的执行顺序等问题,今天就抽空整理了一下这个知识点,然后记录下来。正文本篇文章主要是通过举例的方式来阐述各种情况,我这里根据trycatchfinally语法块分为两种大情况讨论:trycatch语法块和trycat
Stella981 Stella981
3年前
Python从入门到入土
异常处理tryexcept在Python中,异常处理,主要是tryexcept语句,通常语法格式如下.try:代码块1exceptExceptionase:print(e)代码2try语句按照如下方式工作;首先,执行try子句(在关键字try和关键字except之间的语句)如果没有异常
Wesley13 Wesley13
3年前
Java基础入门之异常的那些事儿
点击上方“Java进阶学习交流”,进行关注后台回复“Java”即可获赠Java学习资料今日鸡汤莫买沃洲山,时人已知处。前言大家好,我是Java进阶者。这篇文章我们主要介绍Java基础入门之try...catch、finally、throws关键字和抛出异常知识,一起来学习下吧!一、try.
Wesley13 Wesley13
3年前
J2EE项目异常处理
       为什么要在J2EE项目中谈异常处理呢?可能许多java初学者都想说:“异常处理不就是try….catch…finally吗?这谁都会啊!”。笔者在初学java时也是这样认为的。如何在一个多层的j2ee项目中定义相应的异常类?在项目中的每一层如何进行异常处理?异常何时被抛出?异常何时被记录?异常该怎么记录?何时需要把checkedExc
Stella981 Stella981
3年前
Return出现在try、catch、finally中的不同执行结果
前几天,去一家公司面试,碰到一道面试题大致内容为:如果在try中存在return语句,那么finally中的语句是否会执行,如果会执行,那先后顺序又是怎样。当时自己的解题思路是:坚信大学时候,java编程基础的一句话,finally中的语句一定会执行,所以我觉得finally中的语句一定会执行,而return语句会跳出当前方法,所以return语句应该在
Stella981 Stella981
3年前
Python—语法基础(8) 分支、循环结构
分支紧凑格式:<表达式1if<条件else<表达式2分支结构if<条件1:<语句块1elif<条件2:<语句块2……else:<语句块N异常处理try:
小万哥 小万哥
8个月前
Java 异常处理与正则表达式详解,实例演练及最佳实践
Java异常Try...Catch在Java代码执行期间,可能会发生各种错误,包括程序员编码错误、用户输入错误以及其他不可预料的状况。当错误发生时,Java通常会停止并生成错误消息,这个过程称为抛出异常。try...catch语句try语句允许您定义一段代