有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何从调用方的作用域抛出异常?

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

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

对于使用Perl/Java双语的程序员:如何使用Javacarp


共 (6) 个答案

  1. # 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中不行

  2. # 2 楼答案

    嗯。。您可以将exception子类化,覆盖其中的所有方法,并包装原始异常。在内部,使用getStackTrace()方法从包装的异常生成新的堆栈跟踪。我还没有研究异常的来源,但您甚至不必重写那么多方法

  3. # 3 楼答案

    根据Ordnugswidrig关于设置堆栈跟踪的说法,以及unknown(google)关于重写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;
      }
    }
    
  4. # 4 楼答案

    无法从堆栈跟踪中删除抛出函数。堆栈跟踪的全部目的是记录异常路径,因此允许函数选择退出将无法实现此目的

    唯一可以更改的方法是返回异常,而不是抛出异常。但这迫使您依赖调用方知道如何抛出异常

    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()

  5. # 5 楼答案

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

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

    这将在运行时打印以下stacktrace:

    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)
    

    请注意,如果需要,stacktrace中不会出现“carp”方法。然而,对stacktraces的操作必须非常小心

  6. # 6 楼答案

    如果您想使用异常来控制流以及随后发生的事情,建议您重写fillInStackTrace()方法:

    public Throwable fillInStackTrace() {
       return this;
    }
    

    因此,在没有stacktrace的情况下会出现异常,并且开销会减少(填充堆栈跟踪需要时间)