【问题标题】:How to avoid many try catch blocks in java如何避免Java中的许多try catch块
【发布时间】:2011-09-29 04:33:22
【问题描述】:

我对 java 和 try catch 块处理异常的想法非常陌生。

这大致就是我的最终结果,而且必须有更好的方法:

    try {
        JSONObject jsonObject = new JSONObject(jsonString);
        int aCount = jsonObject.getInt("acount");
        String devTok = jsonObject.getString("dt");
        String qURL = jsonObject.getString("qu");
        try {
            DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
            Key qKey = KeyFactory.createKey("qu", qURL);
            int dsACount = (Integer) datastore.get(qKey).getProperty(kLastKnownANumber);
            //..etc.. more try catch blocks needed
        } catch (EntityNotFoundException e) {
            e.printStackTrace();
        }
    } catch (com.google.appengine.repackaged.org.json.JSONException e) {
        e.printStackTrace();
    }

有更多的try catch块以相同的方式嵌入,所以最后只有一堆catch块。否则应该如何处理异常,Eclipse 一直要求我用 try catch 块或“添加 throws 声明”。

有时我想捕获某些异常,例如,如果它找不到实体,我想打印类似“Entity not found”之类的内容,如果 JSON 字符串无法解析为对象,我想打印诸如“无法解析 JSON”之类的内容。

(我习惯于在 Objective-c 中失败的委托方法,或者该方法返回 null 并且您已经传递了一个指向 [a pointer to] NSError 对象的指针,该对象将被“填充”,是有什么地方可以学习 try-catch 吗?)

【问题讨论】:

  • 哇,人们快速回答 java 问题 :)

标签: java exception-handling


【解决方案1】:

如果您所做的只是捕获它们并打印堆栈跟踪而不管异常类型如何,您可以将代码包装在一个大的 try/catch 块中。为了节省许多“捕获”,您可以捕获java.lang.Throwable,这是所有异常都实现的接口。如果没有,您可以对您调用的代码抛出的每种类型的检查异常进行捕获,并专门处理它们。

Eclipse 一直要求您这样做,因为如果检查的异常没有被捕获或被调用者声明为抛出,Java 代码将无法编译。

+将此评论添加到答案中(谢谢,Paul Tomblin):

在生产质量应用程序中,您将记录跟踪、添加一些逻辑以正确处理异常、采用备用流程和/或将其重新包装到另一个异常中并抛出,等等. 这完全取决于您要解决的特定问题。

【讨论】:

  • 为了清楚起见,如果您要做的只是打印出堆栈跟踪,这就是您应该做的所有事情,但不建议这样做(即仅打印出堆栈跟踪)除了玩具程序之外的任何东西。
  • 对不起,我没有输入我应该输入的段落,我已经编辑了我的问题。 (完成后我将删除打印堆栈跟踪功能)
  • 是的,没错。在生产质量应用程序中,您将记录跟踪,添加一些逻辑以正确方式处理异常,采用备用流程,和/或将其重新包装到另一个异常中并抛出,等等。这一切取决于您要解决的特定问题。
  • 即使在“玩具”程序中,这也是你可以用异常做的最糟糕的事情,因为它会继续代码流,就像什么都没发生一样,而且大多数时候下面的代码甚至都不起作用。我不知道为什么 IDE 默认采用这种可怕的做法。如果您不想处理异常,请将其包装在某种类型的 RuntimeException 中并重新抛出。
  • 进行此更改会产生一个大问题(我现在正在处理)。似乎不断出现的示例是一组必须释放资源的 close() 语句。它们通常都可以抛出异常,但至少都必须尝试执行,即使之前抛出了异常。
【解决方案2】:

如果您的代码块中可能会抛出一种以上的异常,您可以声明两个单独的 catch 块:

try {
    JSONObject jsonObject = new JSONObject(jsonString);
    int aCount = jsonObject.getInt("acount");
    String devTok = jsonObject.getString("dt");
    String qURL = jsonObject.getString("qu");

    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
    Key qKey = KeyFactory.createKey("qu", qURL);
    int dsACount = (Integer) datastore.get(qKey).getProperty(kLastKnownANumber);
} catch (EntityNotFoundException e) {
    e.printStackTrace();
} catch (com.google.appengine.repackaged.org.json.JSONException e) {
    e.printStackTrace();
}
//..etc.. as many catch blocks as needed

或者,如果您不关心异常的确切类型,您可以只使用一个 catch 块来捕获 Exception(或者可能是 Throwable;我不记得确切的超类是什么异常在 Java 中)。

我现在要说明的另一点是,您可能没有最模块化的代码。请记住,做好一件事的代码会产生好的模块化代码。如果你发现你有很多嵌套的黑色(无论是 try/catch 块,if/else 块等)你可能想检查一些代码是否可以提取到它自己的方法中。当必须处理许多异常时,这也可能使您的代码看起来更好。

【讨论】:

    【解决方案3】:

    异常处理的想法是,您可以在程序流程中处理错误的地方进行有意义的处理。而不是像在 C 中那样检查每个函数的返回值,在大多数情况下,除了将错误进一步向上传递之外,您无法做任何明智的事情,而是在您的 明智点程序:

    基本上,只要您可以对错误做出有意义的反应,然后捕获该错误,然后传递其他所有内容。这样一来,只有在从错误中合理恢复时才会调用错误处理。

    例如,最坏的情况是,如果 any 错误会阻止您的程序有意义地执行,那么您可能几乎什么都抓不到,而只是让操作系统处理这种情况(好吧,也许一次尝试/ catch 以产生友好的错误消息)。

    示例(在 C++ 中,抱歉,我无法输入 Java):

    int main()
    {
      try {
        while (masterloop()) { }
      catch (...) {
        LOG("Fatal program error, terminating!"); // nothing else we can do!
      }
    }
    
    /* lots of program logic */
    
    void process_image()
    {
      try {
        Image im = load_image_from_disk();
        /* ... */
      }
      catch (const OutOfMemoryExc & e) {
        LOG("Not enough memory to process the image.");
        return;
      }
      catch (const DataErrorExc & e) {
        LOG("Could not read the image data.");
        return;
      }
      catch (...) {
        throw; // pass everything else along
      }
    }
    

    在此示例中,我们可能会尝试处理图像但由于某些可预期的原因(内存不足或无法读取图像)而失败。在这种情况下,我们只是不做任何工作就返回,让程序优雅地继续。所有其他错误都会传播到更高的点。最重要的是,我们确实不需要始终在实际的图像处理函数中添加错误检查和响应,这足以让任何代码抛出我们的两个好的异常之一,而不必再担心了。

    道德:如果你绝对到处都有 try/catch 块,那你就错了。

    【讨论】:

    • 遗憾的是,我正在编写的 prgram 很大程度上依赖于前面的代码,所以我得到了一个字符串,需要将其解析为 JSONObject(执行此操作的函数会在失败时抛出异常),从数据存储中检索一个值(再次可以引发异常),然后与 JSONObject 中的值进行比较,等等
    • 你说在有意义的地方捕获错误,当它没有意义时我会做什么,它仍然希望我放入一个 try-catch 块
    • @Jonathan:如果有您无法有意义地处理的异常,请继续抛出它们并让其他人处理它们。 (即调用堆栈更高的代码。)
    • @Johnathan:看起来如果您的解析操作中的任何一点失败,您基本上无法对整个数据集做任何有意义的事情。因此,尝试/捕获的逻辑点是当您检索字符串并将其传递给解析器时。如果出现任何问题,请放弃并继续下一个字符串。
    • @Johnathan:抛出是异常处理的基本原语。您停止执行您正在做的任何事情并立即向上移动调用堆栈(在途中销毁所有自动对象(我认为,Java 专家请纠正我)),直到您最终进入一个 try 块,控制权转移到适当的 catch 块。把调用函数的函数想象成一组嵌套的大盒子,“throw”直接打出一个洞,直到它成为一个捕捉盒子,如果这有意义的话。
    【解决方案4】:

    首先,从设计的角度来看,捕获和打印异常并不是一件好事。出了点问题,而您的代码只是继续以与正确的方式相同的方式运行。这通常是不正确的。所以:也许你的方法需要抛出这些异常而不是捕获它们。也许只有调用者才能决定如果这样的事情失败会发生什么。

    但除此之外,我可以提供的唯一建议是从语法上清理代码的外观:告诉您可以编写:

    try {
      ...
    } catch (...) {
      ...
    } catch (...) {
      ...
    }
    

    您还可以捕获更广泛的异常类,例如Exception,然后只编写一个 catch 块,但这是一个糟糕的设计。在 Java 7 中,您将能够在一个块中捕获多种异常类型。

    【讨论】:

    • 如果发生异常,我抛出它而不是捕获它,是否仍然执行下面发生异常的代码。
    • 不,throw 会在此处中断执行。不要捕获并重新抛出异常,这是多余的。如果它没有被捕获,它只会传播到方法之外。这是否合适是一个不同的问题,但你就是这样做的。
    【解决方案5】:

    您可以在同一次尝试中捕获多个异常,例如

    try{
    
      xyz;
    
    }catch(NullPointerException npx){
      npx.getMessage();
    }catch(ArrayOutOfBoundsException ax){
      ax.getMessage();
    }
    

    此外,通过在方法签名中将异常声明为throws,您可以将异常向上传递到堆栈。

    【讨论】:

    • 但是,如果它们是相同类型的异常,例如 JSON 不可解析时抛出的异常与找不到属性时抛出的异常类型相同,该怎么办?
    • 这样想,你的 JSON Exception try 块之前的代码永远不会抛出 JSON 异常。如果这些异常共享相同的 try 块,那么如果您必须进行调试,则几乎没有歧义,并且您的代码将执行相同的操作。
    【解决方案6】:

    如果你有办法从异常中恢复,你应该使用 try/catch 块,例如,如果你想检查一个字符串是否是一个有效的整数,你可以编写一个方法(这是一个蹩脚的方法,但只是展示这个想法):

    public boolean isInteger(String str) {
        try {
            new Integer(str);
        }
        catch(NumberFormatException e) {
            return false;
        }
        return true;
    }
    

    如果您没有办法从异常中恢复并且您所做的只是打印堆栈跟踪,建议在方法中添加 throws 声明(如 eclipse 建议的那样),并让调用者处理异常(或者把它扔给它的调用者)。

    如果你想处理一些异常并抛出一些异常,你也可以这样做。

    【讨论】:

      【解决方案7】:

      我喜欢将调用封装在一个静态方法后面,只是为了让它更整洁。例如,这是我简化的 Set Json Value 调用。

      private static boolean setJsonValue(JSONObject j,String key,Object value)
      {
          try 
          {
              if(value instanceof Integer)
              {
                  // numbers are special. We want them unquoted.
                  int valueI = (Integer)value;
                  j.put(key,valueI);
              }
              else
                  j.put(key,value);
              return true;
          }
          catch (JSONException e) 
          {
              // do nothing, it turns out
              return false;
          }
      }
      

      ...然后我忽略返回值,因为我很糟糕。

      在某处或其他地方我有一个类似的 Get 方法,如果失败则返回 null。你明白了。

      【讨论】:

        【解决方案8】:

        这里有两种基本的代码风格选择(不涉及更改方法签名)

        方法1:把所有东西都放在一个try catch中,并有多个catch块,像这样:

        try {
            JSONObject jsonObject = new JSONObject(jsonString);
            int aCount = jsonObject.getInt("acount");
            String devTok = jsonObject.getString("dt");
            String qURL = jsonObject.getString("qu");
            DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
            Key qKey = KeyFactory.createKey("qu", qURL);
            int dsACount = (Integer) datastore.get(qKey).getProperty(kLastKnownANumber);
            //..etc.. more try catch blocks needed
        } catch (EntityNotFoundException e) {
            e.printStackTrace();
        } catch (com.google.appengine.repackaged.org.json.JSONException e) {
            e.printStackTrace();
        }
        

        方法 2:将您的代码分成多个部分,每个部分都有一个 catch,如下所示:

        String qURL = null;
        try {
            JSONObject jsonObject = new JSONObject(jsonString);
            int aCount = jsonObject.getInt("acount");
            String devTok = jsonObject.getString("dt");
            String qURL = jsonObject.getString("qu");
        } catch (EntityNotFoundException e) {
            e.printStackTrace();
        } 
        
        try {    
            DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
            Key qKey = KeyFactory.createKey("qu", qURL);
            int dsACount = (Integer) datastore.get(qKey).getProperty(kLastKnownANumber);
        } catch (EntityNotFoundException e) {
            e.printStackTrace();
        } 
        

        方法 2 是推荐的方法,因为它可以让哪些行抛出哪些异常一目了然,并且通常将代码分割成自然处理块。

        【讨论】:

        • 方法2中,如果第一个try-catch块中捕获到异常,是否会执行第二个try块中的代码?
        • 是的,因为第一个 try-catch 不会终止方法(不会 returnthrow
        • 所以当它尝试创建一个带有 null 的键时不会有问题(因为如果第一个 try-catch 有异常,qURL 将为 null)
        【解决方案9】:

        如果你只是在做这样的事情:

        try {
          do smth
          try {
            do smth more
            ...
          } catch (Exception1 e1) {reaction to e1}
        } catch (Exception2 e2) {reaction to e2}
        

        您可以在一个try-block 中完成所有操作:

        try {
          do smth
          do smth more
          ...
        }
        catch (Exception1 e1) {reaction to e1}
        catch (Exception2 e2) {reaction to e2}
        

        如果您只是打印异常,也可以将其分解为一个 catch 块:

        try {
          do smth
          do smth more
          ...
        }
        catch (Exception e) {e.printStackTrace();}
        

        但是如果你想做更多的事情,即使e1被抛出,这也不是,比如:

        try {
          do smth
          try {
            do smth more
            ...
          } catch (Exception1 e1) {reaction to e1}
          do smth even if e1 was thrown
        } catch (Exception2 e2) {reaction to e2}
        

        最后一个例子不能写得更短。

        【讨论】:

          【解决方案10】:

          我知道这里有很多答案,它们很好地涵盖了如何构建 try/catch 块。但是,我认为困扰您的一件事是显着的……缩进和代码增长(……因为我知道这不是缩进或代码量,而是通过包装和转移隐含的复杂性和在开始尝试和封闭捕获之间变得越来越长,我无法表达这种担忧)。

          解决此问题的方法是将代码中的不同位重构为函数。我知道这是一个简单的答案,但它是隔离单个任务并将错误处理保持在需要它的代码的相当局部的好方法,而无需使用嵌套的 try/catch 块垂直和水平填充。

          您可以将这些方法设为私有,因为它们可能仅供内部使用。

          private Integer getDatastoreACount() {
              try {
                  DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
                  Key qKey = KeyFactory.createKey("qu", qURL);
                  return (Integer) datastore.get(qKey).getProperty(kLastKnownANumber);
                  //..etc.. more try catch blocks needed
              } catch (EntityNotFoundException e) {
                  // expects an Integer return, so need to deal with this
                  // but for simplicity I'm just simply recycling 'e'
                  throw e;
              }
          }
          
          public void parseJSON(String jsonString) {
              try {
                  JSONObject jsonObject = new JSONObject(jsonString);
                  int aCount = jsonObject.getInt("acount");
                  String devTok = jsonObject.getString("dt");
                  String qURL = jsonObject.getString("qu");
                  Integer dsACount = getDatastoreACount();
                  //etc etc
              } catch (com.google.appengine.repackaged.org.json.JSONException e) {
                  e.printStackTrace();
              }
          }
          

          【讨论】:

          • 我认为这是最干净的方法。并且更具可读性。
          【解决方案11】:

          创建另一个异常并将其放在另一个异常的下方或上方。取决于您的应用程序的上下文。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-11-05
            • 1970-01-01
            • 2018-06-21
            • 2011-12-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多