我是一个更新鲜的人,在成功完成我的项目后,我通过强化扫描了它。它给了我一个问题清单,其中只剩下一个类别。 (强化代码质量扫描工具)
它说,“不使用Broader Exception即System.Exception”来捕获异常,除非在某些情况下。
但是我有一些方法有25-30行代码和不同类型的操作,在这种情况下如何确定要捕获的所有特定异常。
我们是否应该抛出所有这些异常,以便在更高级别的捕获中捕获,因为我读到“先抛出后退”。
请建议我干净利落的方法。
e.g。
public SomeMethod(arg) throws IOException,SQLException,beanNotFoundException {
try {
someCode
} catch (IOException|SQLException|beanNotFoundException ex) {
logger.log(ex);
throw ex;
}
}
但是如果我直到最后都不使用Exception Class,我还必须确保我没有错过任何处理异常。
是否有更好的方法。
答案 0 :(得分:2)
通常,您只需要担心处理/抛出已检查的异常。编写方法的简洁方法取决于应用程序的代码库和结构。以下是一些替代方案:
如果您希望客户端代码(代码调用 html, body{
margin:0;
padding:0;
height:100%;
}
.wrap {
margin: 0 auto;
width: 600px;
}
.top {
border: 1px solid red;
background: lightgray;
line-height: 50px;
text-align: center;
}
.scroll {
background: pink;
height: 200px;
overflow-y: auto;
}
.text {
height: 1000px;
}
)处理异常,您可以这样做:
someMethod
如果您可以在public void someMethod(String arg) throws IOException, SQLException,
BeanNotFoundException {
doSomething(arg);
}
中本地处理异常,请执行此操作,但不要在调用堆栈中重新抛出相同的异常:
someMethod
如果异常是您无法恢复的,并且您不想强制执行客户端代码来处理异常,则可以将其包装为public void someMethod(String arg) {
try {
doSomething(arg);
} catch (IOException | SQLException | BeanNotFoundException ex) {
logger.log(ex);
handleException(ex);
}
}
。这可能会终止程序(通常是发生致命错误时你想要的):
RuntimeException
有关已检查与未检查的例外的详细说明,您可以参考Java: checked vs unchecked exception explanation。
答案 1 :(得分:1)
我的建议是忽略Fortify所说的话。或者更好的是,弄清楚如何有选择地抑制这些警告。
Fortify建议的当前版本的替代方案在大多数情况下都是客观上不好的。你当前的版本不错,只是(有点)冗长。
另一种方法是将异常包装在自定义异常中,或将具有公共自定义检查异常的异常包装为基类(NOT Exception
或Throwable
或RuntimeException
)。这可能会使像你这样的代码更整洁,但这意味着你需要添加代码(某处)来进行包装和(可能)解包。简而言之,这也不是一个想法。
好的,那么捕捉Exception
有什么问题?基本上,它捕获所有 ...包括任何意外取消检查异常,这些都是bug的证据。如果您将方法声明为throws Exception
,则只会变得更糟。
我见过一个例子(没有名字),有人在大型Java产品中做过这个。结果是产品的Java API编程很糟糕。每个非平凡的API方法都声明为throws Exception
,因此针对API编程的人不知道会发生什么。代码库是专有的并且是混淆的。 (最糟糕的是,首席开发人员仍然认为这是一个好主意!)
如何在RuntimeException
中包含例外?这与Exception
并不像 1 那么糟糕,但问题在于程序员仍然不能告诉我们预期会有哪些例外。
1 - 在throws Exception
有效"噪音"的意义上并不是那么糟糕。一旦你完成了任何可以抛出任何东西的道路。
答案 2 :(得分:1)
静态分析
首先,让我从一个大多数人都会受到影响的小谬论开始(我在企业界看到了很多):静态分析工具并非绝对可靠。他们犯了错误。有一些警告类,具有人类已知的所有计算能力以及宇宙的剩余寿命,该工具可能无法详尽地分析与特定警告类相关的一段代码。此外,有一些警告类可以在时间结束之前完成,但是期望您等待7天以获得针对一个执行路径的一段代码的一个警告的答案是不合理的。也就是说,静态分析工具有时必须做出猜测。更好的工具更好地做出猜测(你为所得到的付出代价),但最终,他们都能够正确猜测(真正的正面/负面)或错误的(假阳性/负面)。
此外,某些警告类可能适用,有些可能不适用。其他一些警告类可能对您很重要,有些可能不是。例如,PMD有一个警告类来解释" Unused Import"的效果。未使用的导入对运行时没有影响,虽然它可能不太理想,但您可以将其留在代码中,除非您的代码库很大并且需要时间编译,并且您有很多这些(未使用的导入),否则它不会影响您让你建立你的项目更长时间)。我特别提到静态分析,因为听起来你在你的代码库上运行Fortify并修复了所有内容而没有质疑有效性。 大多数时间它可能是正确的,但是不要把它当成事实,因为它告诉了你。您需要质疑这些工具是否始终正确。
您的示例
所以你的榜样不是误报。如果你这样做
throw new Exception("");
这通常不是你想要做的。它可以很好地进行调试,并且可以快速地将代码放在一起。但是,随着您的代码库变大,处理Exception
将变得更加困难。
这引导我进入下一点。你发表声明
但是我有一些方法有25-30行代码和不同类型的操作,在这种情况下如何确定要捕获的所有特定异常。
...
但是,如果我不完全使用Exception Class直到最后,我还必须确保我没有错过任何处理异常。
这似乎向我表明你要么有某种效果
try{
//25-30 lines of code
} catch (Exception ex) { /*ex.printStackTrace(); Logger.getLogger(...).log(...); etc etc...whatever it is you do here, or maybe nothing at all*/
这很糟糕 - 在大多数情况下。
在我解释原因之前回过头来回答你的问题,是的,你绝对应该抓住每个例外。这样做的原因,以及为什么你的一切都很糟糕,是因为如果出现特定的错误,你就无法进行特定类型的错误处理。
例如,使用函数Foo#bar(java.lang.String)
,当磁盘访问由于您尝试写入错误文件而失败时抛出IOException,如果传入没有特殊字符的String,则抛出BooException,或者一些其他任意原因的BazException。现在让我们回到上面的例子:如果我想知道我写的文件是坏的,可能会在我继续之前提示用户做一些澄清怎么办?如果我知道如果抛出一个BooException,那么用户应该从未出现在这里,我需要将它们重定向到某个位置,该怎么办?如果我知道系统与其他系统不同步的BazException并且这是一个致命的问题,现在我需要在强行崩溃JVM之前进行资源清理会怎么样?
以上原因是您应该为每个语句和每个例外执行单独的try / catch块的原因。
话虽如此,有理由不这样做。想象一下,在上面的例子中,你只想做IOException,BooException和BazException(以及任何运行时异常,即NullPointerException),我只想记录异常并继续前进。在这种情况下,我会说可以尝试/捕捉一段代码 - 只要它适用于适用的代码。
编辑:你提出了一个关于错过异常的观点,但在评论中对此做出回应似乎是不守规矩的。在任何情况下,让我先说它是不是运行时异常,然后你甚至无法编译而不处理它。捕获Exception
捕获所有内容,运行时与否,这是您现在编译的方式。从上面的示例中,如果存在运行时异常,您担心会丢失,而不是仅仅从捕获Exception
开始:
Foo myFooInstance = new Foo();
String someValue = "value";
try {
myFooInstance.bar(someValue);
} catch (IOException ioe) {
/*handle file access problem*/
} catch (BooException boe) {
/*handle user in wrong spot*/
} catch (BazException bze) {
/*handle out-of-sync fatal error*/
} catch (Exception ex) {
LogRecord lr = new LogRecord(Level.SEVERE, "Unhandled exception!! returning immediately!!");
lr.setThrown(ex);
lr.setParameters(new Object[]{someValue});
Logger.getLogger(MyClass.class.getName()).log(lr);
return;
}
在这里,你结束了所有人,而不是从它开始。把它想象成你努力处理一切的最后努力。这可能并不意味着异常对您的程序是致命的,但您可能也不应该继续(因此我的示例使用return;
)
要考虑的另一个小问题是,当try块获得的越大(如果从不抛出异常,没有开销)时,JVM正在以指数方式更难以正确捕获异常。对于功能强大的机器来说,这是微不足道的,但要记住一些事情。考虑到这一点,我也没有任何关于在大型尝试块中抛出异常的表现的来源,所以除非有人发现并提及它,否则请加上一点点。
答案 3 :(得分:0)
一般的经验法则是针对外部或不可预测的错误,和抛出已检查的异常,调用者可以合理地预期这些错误,即使这仅仅意味着显示消息给用户。 无论是抓住它们还是将它们传递出去,调用堆栈都取决于处理它们最有意义的地方。
对于未满足的前提条件或无效参数,将抛出未经检查的异常。换句话说,程序员搞砸了。这就是为什么你不被迫抓住它们的原因;他们通常是不应该发生的事情。" 如果您正确使用API,通常不必担心它们。您应该捕获未经检查的例外的唯一时间是API不遵循这些约定并且出于外部或不可预测的原因抛出未经检查的异常。