因为软件缺陷会让我们在开发人员中显得很糟糕,并导致其他人对我们的看法降低,所以最好避免编写缺陷,快速识别和修复缺陷,或者掩盖我们的缺陷。有许多博客文章和文章讨论如何避免bug以及如何识别和修复bug,因此,在这篇文章中,介绍一些最有效的策略,以彻底解决Java代码库中的问题。
1. 吞咽检查异常
当我们不小心在代码中引入了bug时,异常就是其中之一。在方法上声明throws子句或捕获已检查的异常也是一件麻烦事。这两个问题的解决方案都是在异常可能被抛出时捕获异常(即使它是正在运行时一场),而不执行任何操作。这使API保持简洁,对于检查的异常,人们几乎无能为力。通过不记录它或对它做任何事情,甚至没有人需要知道它曾经发生过。
2. 注释掉或删除不满意的单元测试
失败的单元测试可能会让人分心,并且很难确定新功能何时破坏了测试。它们还可以揭示我们在代码更改时破坏了某些东西。注释掉这些失败的单元测试将使单元测试报告更清晰,并且更容易查看是否有其他人的新功能破坏了单元测试。
3. 在基于JUnit的单元测试中使用@Ignore
注释掉失败的单元测试似乎令人不快,所以另一个可能更令人愉快的选择是用@Ignore注释失败的基于JUnit的单元测试方法。
4. 完全删除单个测试
如果用@Ignore注释一个中断的测试或用@Ignore注释一个中断的测试是不令人满意的,因为有人可能仍然检测到我们破坏了一个单元测试,那么我们可以简单地将有问题的单元测试全部删除。
5. 注释掉令人不快的断言
我们不一定需要注释掉或删除整个测试。它可以简单到注释掉或删除单元测试方法中的断言语句。该方法每次都可以成功执行和运行,因为没有断言意味着没有失败的方法。当单元测试方法非常长且复杂,因此不容易发现缺少断言时,这种方法尤其有效。
6. 用无用和冗余测试的噪音分散注意力
注释掉单元测试,用@Ignore注释基于JUnit的单元测试,甚至删除单元测试,对于在Java中彻底解决问题来说,这些策略可能太明显了。为了使这些不那么明显,另一个有效的策略是在同一个单元测试类中编写大量不必要的和冗余的测试方法,这样看起来似乎正在进行全面的测试,但实际上,只有一小部分功能(我们知道的子集正在工作)正在进行测试。
7. 编写单元测试,以“证明”您的代码是正确的,即使它不是
我们可以利用这样一个事实,即单元测试只能测试单元测试的作者认为被测试软件的预期行为,从而编写单元测试来证明我们的代码是正确的。如果我们将两个整数相加的代码在提供2和2时意外返回5的总和,那么我们可以简单地将单元测试中的预期结果也设置为5。给出了一份漂亮的单元测试报告,没有人会更明智。
8. 避免记录详细信息
日志可能会暴露软件问题,处理此风险的有效方法是根本不记录日志,只记录例行操作和结果,并在记录的消息中留下详细信息(尤其是上下文)。对平凡细节的过度记录也会掩盖任何可能暴露代码弱点的更有意义的消息。
9. 避免使用描述性的toString()实现
一个描述性的toString()方法可以揭示太多关于任何给定实例的状态,并揭示我们的错误。不覆盖对象。toString()会使识别问题和将问题与任何给定代码或开发人员关联变得更加困难。跟踪问题所需的额外时间为您提供了更多的时间,以便在发现是您的代码出了问题之前进入下一个项目。如果您编写的Java类使用描述性toString()扩展了一个类,那么您可以在扩展类中重写该方法而不做任何事情(有效地删除可能导致toString()错误的输出)。如果您想让它看起来好像从未在继承层次结构中实现过一样,请确保扩展类的toString()方法返回系统。标识hash代码(此)。
10. 不要让NullPointerExceptions背叛你
NullPointerExceptions可能是Java开发人员处理的最常见的异常。这些特别危险,因为它们经常暴露代码的弱点。一种策略是简单地用try-catch包装每一行代码,并简单地吞下异常(包括NPE)。
另一个不太明显的策略是通过从不返回或传递空值来避免NPE。有时,使用默认值代替null(例如空字符串或集合)是有意义的,但有时我们必须更具创造性地避免null。在这一点上,使用“默认”非空值代替空值是很有用的。
关于如何处理这种任意的非空缺省值,有两种观点。一种方法是使用数据集中最常见的值作为默认值,因为如果它是通用的,那么当更多的值出现时,可能不会注意到它,并且您更有可能拥有处理该通用值的代码,而不会发生意外。另一方面,如果您有一个几乎从未使用过的值,这可以成为一个良好的默认值,因为受它影响的代码(尤其是经过良好测试的代码)可能比通常预期的值少。