【问题标题】:The exception is thrown - and the code is executed further抛出异常 - 并进一步执行代码
【发布时间】:2017-08-20 00:30:53
【问题描述】:

遇到这种情况——在main方法中,调用了一个子方法,它检查对象,在这个子方法中抛出异常(列表中的对象之一为NULL)。但是main方法的代码还是继续执行! 示例代码:

@Transactional
public boolean addCompany(List<Company> companies, List<Address> addresses) throws Exception{
    checkAddress(addresses);
    try{
        for(int i = 0; i < companies.size(); i++){
            if(findCompany(companies.get(i).getId()) == null && !isExistsCompany(companies.get(i))){
                companies.get(i).setAddress(addresses.get(i));
                this.em.persist(companies.get(i));
            }
        }
    }catch(Exception e){
        return false;
    }
    return true;
}

public void checkAddress(List<Address> addresses) throws Exception{
    try{
        if(addresses == null)
            throw new Exception(Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName() + ": Invalid parameter: list is null");
        for(Address a : addresses)
            if(a == null)
                throw new Exception(Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName() + ": Invalid list item: object is null");
    }catch(Exception e){
        e.printStackTrace();
    }
}

在这方面,出现了几个问题: - 为什么代码不会停止? - 作为一种选择,现在是否有必要通过将 checkAddress 方法的类型从 void 更改为 boolean 并在 main 方法中处理 true/false 来摆脱这种情况? - 如何在前端正确处理此类错误 - 文本是否向前端发送异常或仅处理代码 500,如果是,那么为什么在后端生成异常 - 以帮助开发过程?如何胜任处理? 请指教。 提前致谢。

【问题讨论】:

  • 我知道你说这是示例代码,但我觉得有必要指出整个 getStackTrace()[n] 的东西是个坏主意,不仅是出于可读性和性能原因,还因为允许 JVM 删除堆栈帧。更好的做法是将类名和方法名作为字符串参数传递,例如certain Logger methods do
  • 如果没有getStackTrace(),我怎么能得到当前类和方法的名字传给Logger呢?
  • checkAddress 不应尝试获取该信息。调用者应该将它们作为参数传递。就像我链接的 Logger 方法一样。

标签: java exception


【解决方案1】:

您正在捕捉Exception(s),当您不重新抛出Exception Java 运行时认为它已处理时。如果您希望程序执行停止,那么您需要将 Exception(s) 传播给调用者。比如在checkAddress

} catch(Exception e) {
    e.printStackTrace();
}

类似

} catch(Exception e) {
    e.printStackTrace();
    throw e; // <-- re-throw the Exception
}

只需将trycatch 完全删除,然后Exception 就会自动抛出给调用者。此外,在 Java 8+ 中,您可以使用 Stream。喜欢,

public void checkAddress(List<Address> addresses) throws Exception {
    if (addresses == null) {
        StackTraceElement[] ste = Thread.currentThread().getStackTrace();
        throw new Exception(ste[2].getClassName() + "."
                + ste[2].getMethodName() + "." + ste[1].getMethodName()
                + ": Invalid parameter: list is null");
    }
    if (addresses.stream().anyMatch(a -> a == null)) {
        StackTraceElement[] ste = Thread.currentThread().getStackTrace();
        throw new Exception(ste[2].getClassName() + "."
                + ste[2].getMethodName() + "." + ste[1].getMethodName()
                + ": Invalid list item: object is null");
    }
}

【讨论】:

  • 你甚至可以做addresses.stream().anyMatch(Objects::isNull)
【解决方案2】:

您需要从 checkAddress() 方法中删除 try.. catch 块。这样,任何从checkAddress() 内部抛出的异常都会传播给它的调用者。

addCompany() 方法中,将对checkAddress() 方法的调用放入try .. catch 并在那里处理异常。

checkAddress()抛出异常时,代码执行会跳转到catch块。

【讨论】:

    【解决方案3】:
    e.printStackTrace();
    

    这一行抑制异常,导致您的代码继续而不是失败。它打印堆栈跟踪,这可能使它看起来像是正在抛出异常,但它并没有比那行更进一步。

    您几乎从不想使用printStackTrace(),而是应该正确处理您打算使用的异常,或者只是让异常传播给您的方法的调用者。

    【讨论】:

    • 打印堆栈跟踪不处理异常。如果你想让方法抛出异常,为什么要把它包装在 try/catch 块中?
    • @duffymo 我同意你的看法——这就是我不鼓励使用printStackTrace() 的原因。不知道你反对什么? .printStackTrace()(不幸的是)通常在用户尝试添加 try-catch 块作为 catch 的主体时由 IDE 自动添加。
    【解决方案4】:

    试试这个:

    public void checkAddress(List<Address> addresses) throws Exception{
            if(addresses == null)
                throw new Exception(Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName() + ": Invalid parameter: list is null");
            for(Address a : addresses)
                if(a == null)
                    throw new Exception(Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName() + "." + Thread.currentThread().getStackTrace()[1].getMethodName() + ": Invalid list item: object is null");
        }
    
    }
    

    关于线程的那些东西是什么?疯狂的代码。

    首先不允许任何人将空实例添加到列表中。

    我可以这样写:

    public void checkAddresses(List<Address> addresses) {
        if (addresses == null) throw new IllegalArgumentException("Address List cannot be null");
        for (Address a : addresses) {
            if (a == null) throw new IllegalArgumentException("Address cannot be null");
        }
    }
    

    【讨论】:

    • 我使用 getStackTrace() 向自己指示哪个包/类/方法发生了错误。同一个方法在代码中被多次调用,万一出现错误,需要快速找到这个错误发生的地方。打印这样的 stig 会给出错误方法的绝对路径。
    • 你不需要那么多噪音。这是一个未经检查的异常。我建议您让调用者捕获异常并让您知道发生了什么。 address 方法不应该以这种方式提供上下文。我写的时候它不需要知道它是如何被调用的。
    • 明白谢谢!还有一个问题 - Exception 什么时候起作用,你建议通过前端?默认发送错误码 500,是否值得传递异常文本,还是语气不同,只需要处理错误 500?
    • 不,异常不应传播到客户端。他们关心你的 NullPointerException 什么?它应该被翻译成用户友好的消息。
    猜你喜欢
    • 2011-03-01
    • 2015-11-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2012-11-28
    • 2012-03-30
    • 2011-05-03
    • 1970-01-01
    相关资源
    最近更新 更多