Java—应该在何处以及如何使用异常?

Java—应该在何处以及如何使用异常?,java,exception,exception-handling,Java,Exception,Exception Handling,我读了一些关于Java中异常处理的内容,以便能够编写更好的代码。好吧,我承认,我有罪;我使用了太多的try-catch{}块,在catch中使用了ex.printStackTrace(),甚至没有使用正确的记录器(实际上System.out和System.err被重定向到PrintWriter,因此生成了日志)。然而,经过几个小时的阅读,我发现自己在一个陌生的地方:未知。如果异常被设计为传递有关异常流状态的信息,那么如何知道在哪里使用该信息进行操作 例如,当发生数据库错误时,应该返回空值或错误代

我读了一些关于Java中异常处理的内容,以便能够编写更好的代码。好吧,我承认,我有罪;我使用了太多的try-catch{}块,在catch中使用了
ex.printStackTrace()
,甚至没有使用正确的记录器(实际上
System.out
System.err
被重定向到
PrintWriter
,因此生成了日志)。然而,经过几个小时的阅读,我发现自己在一个陌生的地方:未知。如果异常被设计为传递有关异常流状态的信息,那么如何知道在哪里使用该信息进行操作

例如,当发生数据库错误时,应该返回空值或错误代码,还是引发异常?如果抛出,那么应该在哪里处理该异常?我明白,如果你对此无能为力,即使记录一个异常也是没有用的。但是,在GUI应用程序中,这可能很容易杀死您的GUI(我正在使用SWT,而且我经常看到这种情况),即使是
menuShown()
方法(如果不处理
ArrayIndexOutOfBounds
异常,将关闭应用程序)。这个例子可能会一直持续下去,但下面是问题的总结:

  • 过度使用try-catch()是否会对性能产生负面影响
  • 使用特定的异常类型是否更好?如果我错过了一个呢 可能发生的X种类型的异常情况?
    坦率地说,在2-3年的时间里,我只听说过并使用了我认为只有10%的Java标准异常。是的,有人说如果调用方不知道如何处理抛出的异常,他就无权调用抛出方法。是这样吗
  • 我读过的这篇文章说,检查异常是不好的。这是否意味着在某些情况下建议进行方便的异常吞咽
  • 一幅画抵得上千言万语;我想一些例子会很有帮助 在这里

  • 我知道这个主题是永恒的,但实际上我期待着用你的建议来回顾一个150个班级的中等规模项目。非常感谢。

    我们在团队中做的一件事就是为我们的错误设置自定义异常。我们使用的是Hibernate验证器框架,但您可以在任何框架或股票例外情况下执行此操作

    例如,我们有一个ValidationException来处理验证错误。我们有一个ApplicationException来处理系统错误

    您确实希望尽量减少尝试捕捉的次数。在我们的例子中,我们将让验证器收集“InvalidValue”对象中的所有验证,然后抛出一个ValidationException,其中绑定了无效值信息。然后您可以向用户报告哪些字段出错,等等

    在您提到的数据库错误的情况下,您可能不想将stacktrace发送到UI(最好记录)。在这种情况下,您可以捕获数据库异常,然后将自己的ApplicationException抛出到GUI中。您的GUI不必知道如何处理无限多的服务器错误,但可以设置为处理更普遍的ApplicationException—可能报告服务器有问题,并指示用户应联系您的客户支持部门报告问题


    最后,由于所依赖的外部API,有时您会情不自禁地使用大量try/catch块。这很好。如前所述,捕获外部异常,并将其格式化为对应用程序更有意义的异常。然后抛出自定义异常。

    对于异常,一般的经验法则是,如果你能做些什么,捕获它并处理它,如果你不能,重新抛出到下一个方法。要了解您的一些具体情况:

  • 否,使用过多的try/catch不会对性能产生影响
  • 使用您可以使用的最特定类型的异常。例如,如果可以避免异常,通常不应该抛出异常。通过抛出特定类型,您可以让用户知道可能出现的错误。但是,您可以将其作为更通用的内容重新引用,这样与特定异常无关的调用方就不需要知道它(例如,GUI不会关心它是IOException还是ArrayIndexOutOFBoundsException)
  • 你会发现人们更喜欢检查的异常,你会发现人们更喜欢未检查的异常。一般来说,我尝试使用未检查的异常,因为对于大多数已检查的异常,您通常不能做很多事情,而且您仍然可以处理未检查的异常,您只是不必这样做。我经常发现自己会重新引用检查过的异常,因为我对它们无能为力(另一种策略是捕获检查过的异常并将其作为未检查的异常重新引用,这样链中更高的类就不需要捕获它,如果它们不想的话)
  • 我通常喜欢在异常被捕获时记录异常——即使我对此无能为力,它也有助于诊断问题。如果您不熟悉它,还可以查看Thread.setDefaultUncaughtExceptionHandler方法。这允许您处理未被任何人捕获的异常,并对其进行处理。这对于GUI应用程序特别有用,因为否则可能看不到异常

    要了解一些示例:

    try {
       // some database operation
    }
    catch (IOException ex) {
       // retry the database operation. then if an IO exception occurs rethrow it. this shows an example doing something other than just catch, logging and/or rethrowing.       
    }
    
    如果你愿意的话,我很乐意详细介绍其中的任何部分

  • 虽然我没有任何数字,但我不相信try-catch对性能有任何显著影响(我没有看到)。我认为,如果您没有遇到许多异常,那么对性能的影响基本上是零。但是在任何情况下,最好先正确地实现代码,然后再实现良好的性能——第一步完成后,第二步要容易得多

  • 我认为exception类应该具体说明异常的真正含义。我在Java的SQLExceptions中遇到的问题
    for (int retries = 0;; retries++) {
        try {
            commandService.execute(command);
            return;
        } catch (Exception e}
            Log.error(e);
            if (retries < 3) {
                continue;
            } else {
                saveForAnalysis(command, e);
                alertOperator();
                return;
            }
        }
    }
    
    
    interface DBAccess {
        public Result accessDB();
    }
    
    class DBOperation {
        static public void DoOperation(DBAccess pAccess) {
            try { return DBAccess.accessDB(); }
            catch(InvalidDBPasswordException IPE) {
                 // Do anything about invalid password
            }
            catch(DBConnectionLostException DBCLE) {
                 // Do anything about database connection lost
            }
            // Catch all possible DB problem
        }
    }
    
    ...
    
    private User[] ShowUserList_and_ReturnUsers() {
        // Find the used.
    
        // Show user list
    
        if (Users.count() == 0)
             return null;
        else return Users;
    
        // No need to handle DB connection problem here
    }
    private User[] GetUserProfile() {
        // Find the used and return
        // No need to handle DB connection problem here
    }
    ...
    
    /** An onClick event to show user list */ {
        DBOperation.DoOperation(new DBAccess() {
            public Result accessDB() {
                return ShowUserList_and_ReturnUsers();
            }
        });
    }
    /** An onClick event to show a user profile */ {
        DBOperation.DoOperation(new DBAccess() {
            public Result accessDB() {
                return GetUserProfile();
            }
        });
    }
    ... Many more DB access
    
    
    
    for(int i = 0; i < Users.length; i++) {
        User aUser = Users[i];
        // Do something with user
    }
    
    Replaced with
    
    try {
      for(int i = 0; ; i++) {
          User aUser = Users[i];
          // Do something with user
      }
    }
    catch(ArrayOutOfBoundException AOBE) {}