【问题标题】:How do I throw an Exception from the caller's scope?如何从调用者的范围内抛出异常?
【发布时间】:2009-04-07 21:17:30
【问题描述】:

我想创建一个例程来执行一些日志记录,执行一些其他操作,然后引发异常。我希望从许多不同的位置调用此例程。但是,在此例程中创建异常意味着它们将在其堆栈跟踪中包含此例程。我宁愿堆栈跟踪不报告这个实用程序。有没有办法做到这一点,而无需在调用者中创建异常并将其传递给实用程序例程?

public static void die(String message) throws MyException {
  log(message);
  ...
  throw new MyException();
}

对于 Perl/Java 双语的程序员:我如何在 Java 中鲤鱼

【问题讨论】:

  • Javaland 中的成语似乎很奇怪
  • 除了调试之外,您还使用堆栈跟踪吗?我真的不明白为什么这很重要。
  • @ykaganovich,你做过吗?它一直在 Perl 中完成,并且是有原因的。通常当人们谈论一种语言缺乏的功能时,有很多人认为你永远不应该这样做,而他们自己从来没有做过。 :)
  • 我做过什么吗?有记录仪吗?有错误处理程序吗?是的。我有没有弄乱堆栈跟踪元素?是的,但不要从 printStackTrace 中删除某些内容。我有没有“宁愿堆栈跟踪不报告这个实用程序”?不,因为我认为这是个坏主意。

标签: java exception stack-trace


【解决方案1】:

您可以设置要抛出的任何异常的堆栈跟踪:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CarpTest {
    public static void main(String[] args) {
        new CarpTest().run();
    }

    public void run() {
        methodThatCarps();
    }

    private void methodThatCarps() {
        carp("Message");
    }

    private void carp(String message) {
        RuntimeException e = new RuntimeException(message);
        e.fillInStackTrace();
        List<StackTraceElement> stack = new ArrayList<StackTraceElement>(Arrays.asList(e.getStackTrace()));
        stack.remove(0);
        e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()]));
        throw e;
    }
}

这将在运行时打印以下堆栈跟踪:

Exception in thread "main" java.lang.RuntimeException: Message
    at CarpTest.methodThatCarps(CarpTest.java:18)
    at CarpTest.run(CarpTest.java:14)
    at CarpTest.main(CarpTest.java:10)

请注意,您希望方法“carp”不会出现在堆栈跟踪中。然而,堆栈跟踪的操作应该非常小心。

【讨论】:

  • 就是这样;谢谢你!我想我也可以将它与子类化 Exception 和覆盖 fillInStackTrace() 的方法结合起来,并将代码删除其中的第一个 StackTraceElement。
  • 如果可以的话,出于性能原因进行两次更正。 “e.fillInStackTrace();”没有必要,因为堆栈跟踪应该从创建异常的地方开始。 “stack.size()”应该是“0”。
【解决方案2】:

如果你想使用异常来控制流程以及之后发生的事情,一个很好的建议是重写 fillInStackTrace() 方法:

public Throwable fillInStackTrace() {
   return this;
}

因此,您将获得一个没有堆栈跟踪的异常,并且开销减少(填充堆栈跟踪需要时间)。

【讨论】:

    【解决方案3】:

    没有办法从堆栈跟踪中删除抛出函数。堆栈跟踪的全部目的是记录异常路径,因此允许函数选择退出会破坏目的。

    您可以改变这一点的唯一方法是返回异常而不是抛出它。但这迫使您依赖调用者知道抛出异常。

    throw die("someReason).fillInStackTrace();
    

    修改函数

    public static Exception die(String message) {  
      log(message);  
      ...  
      return new MyException();
    }
    

    编辑

    添加了 fillInStackTrace() 调用以确保堆栈重置到抛出点。

    http://java.sun.com/j2se/1.3/docs/api/java/lang/Throwable.html#Throwable()

    【讨论】:

    • 我认为堆栈跟踪显示异常是在哪里创建的,而不是从哪里抛出的,所以我认为即使返回它也不会实现这一点。我可能错了。会检查。 :)
    • @skiphoppy,我很确定这是从投掷点开始的。否则 JVM 会在异常被传递的情况下报告错误的堆栈跟踪。
    • Throwable() 调用 fillInStackTrace() -- 它在创建时完成
    • @Scott,哇,这是正确的(在 CLR 上正好相反)。我会更新我的示例
    【解决方案4】:

    嗯..你可以继承异常并覆盖其中的所有方法,并包装原始异常。在内部,使用包装异常的 getStackTrace() 方法生成新的堆栈跟踪。 Exception 的来源我没有看,但你可能甚至不必重写那么多方法。

    【讨论】:

      【解决方案5】:

      也许您应该考虑从不同的方向解决问题。与其修改堆栈跟踪,不如只使用您的异常生成器方法(在您的示例中为diereturn 异常而不是抛出它?那么您的电话是throw die();

      例如:

      // revised die() method:
      public static MyException die(String message){
        log(message);
        //...
        return new MyException();
      }
      
      
      // calling code:
      throw die("a-whoopsie daisy!");
      

      现在,当然,throw die() 可能看起来有点不美观,因此您可以将 die() 重命名为 newException() 或其他名称。但是满足了异常处理方法不显示在堆栈跟踪中的要求——die()(或newException())在抛出异常之前返回,因此不是要跟踪的堆栈的一部分。

      编辑:我的错。我花了很多时间使用 C#,以至于我忘记了在 Java 中异常堆栈跟踪是在实例化时生成的,而在 C#/.NET 中,异常堆栈跟踪是在抛出时生成的。

      所以这个技巧在 C# 中有效,但在 Java 中无效。

      【讨论】:

      • 我认为这行不通。 stacktrace 是在 Exception 被实例化时填充的,而不是它抛出的内容。
      • 嗯,很高兴知道它可以在 C# 中工作。有一天我可能需要那个。 :) 我投票给你从-1 到 0。 :)
      • 投掷前调用fillInStackTrace
      【解决方案6】:

      根据 ordnungswidrig 所说的关于设置堆栈跟踪的内容,以及未知(谷歌)所说的关于覆盖 fillInStackTrace() 的内容,我创建了一个 CarpException,它完全符合我的要求。请注意,我发现我必须删除四个堆栈跟踪帧而不是一个,因为我从 Throwable 和 Exception 中获取帧。

      public class CarpException extends Exception {
        @Override
        public Throwable fillInStackTrace() {
          super.fillInStackTrace();
          StackTraceElement[] origStackTrace = getStackTrace();
          StackTraceElement[] newStackTrace = new StackTraceElement[origStackTrace.length - 4];
          System.arraycopy(origStackTrace, 4, newStackTrace, 0, origStackTrace.length - 4);
          setStackTrace(newStackTrace);
          return this;
        }
      }
      

      【讨论】:

        【解决方案7】:

        不能...我前段时间尝试过这样做(我试图在 AOP 存在之前捕获堆栈跟踪以记录方法调用)。

        堆栈跟踪是在创建异常时填充的,这是本机完成的。对于我正在处理的事情,我最终阅读了堆栈跟踪并查看了第二个元素,但这对你没有帮助......

        【讨论】:

          【解决方案8】:

          您可以考虑让您的方法接收一个 Logger 作为该方法的参数。这将允许您根据调用类控制日志记录输出。

          我建议不要让您的异常排除堆栈跟踪的这一部分。当你离开并且一些新人开始维护你的代码时,他们不会欣赏这种非标准的错误处理。

          【讨论】:

            【解决方案9】:

            你抛出堆栈跟踪只是为了能够分析它吗?在这种情况下,您可以在返回 StackTraceElement[] 的异常上调用 getStackTrace() 方法。在那里你可以过滤你不想要的元素(例如“die”方法)。

            【讨论】:

              猜你喜欢
              • 2013-01-01
              • 1970-01-01
              • 2013-11-18
              • 1970-01-01
              • 1970-01-01
              • 2013-09-28
              • 2012-09-20
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多