如何从调用者的范围中抛出异常?

时间:2009-04-07 21:17:30

标签: java exception stack-trace

我想创建一个例程来执行一些日志记录,执行其他操作,然后抛出异常。我希望从许多不同的地方调用这个例程。但是,在此例程中创建异常意味着它们将在其堆栈跟踪中具有此例程。我宁愿堆栈跟踪不报告此实用程序例程。有没有办法在调用者中创建Exception并将其传递给实用程序例程?

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

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

9 个答案:

答案 0 :(得分:11)

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

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”方法不会出现在堆栈跟踪中。然而,堆栈跟踪的操作只能通过greates care来完成。

答案 1 :(得分:4)

如果你想使用Exception控制流程以及之后发生的事情,建议它覆盖fillInStackTrace()方法:

public Throwable fillInStackTrace() {
   return this;
}

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

答案 2 :(得分:2)

无法从堆栈跟踪中删除throw函数。堆栈跟踪的整个目的是记录异常路径,因此允许函数退出将失败目的。

您可以更改此选项的唯一方法是返回异常而不是抛出异常。但这迫使你依赖于调用者知道抛出异常。

throw die("someReason).fillInStackTrace();

修改功能

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

编辑

添加了fillInStackTrace()调用以确保将堆栈重置为throw的点。

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

也许您应该考虑从不同的方向解决问题。而不是修改堆栈跟踪,为什么不只是让您的异常生成器方法(在您的示例中为die返回异常而不是抛出它?然后你的电话是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中则不行。

答案 5 :(得分:1)

根据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;
  }
}

答案 6 :(得分:0)

没有办法......我曾经尝试做过这样的事情(我试图在AOP存在之前捕获堆栈跟踪来记录方法调用)。

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

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

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