Java开发中的常见危险信号

在开发,阅读,复审和维护成千上万行Java代码的几年中,我已经习惯于看到Java代码中的某些“ 危险信号 ”,这些信号通常(但可能并非总是)暗示着代码问题。 我不是在谈论总是错误的做法,而是在有限的情况下谈论适当的做法,但通常都表明存在错误。 这些“ 危险信号 ”有时可能是无辜的,但经常警告不可避免地会不可避免地出现“ 伤害堆 ”。 在这里,我总结了其中的一些内容,并简要讨论了它们可能还不错的情况,并描述了它们通常不正常的原因。

这些“红色标志”中的许多标志意义重大,足以保证可从诸如FindBugs之类的代码分析工具发出警告。 流行的Java IDE也标记了其中许多。 但是,我已经看到开发人员错过了这些工具和IDE的更多文字标记,这是因为他们关闭了这些选项,或者是因为他们习惯了或者由于不了解与标志相关的风险而忽略了警告。

在引用中使用==(而不是.equals)

大多数Java开发人员都学会了使用==比较基元,以及使用.equals比较引用类型。 通常,这是一个容易记住的规则,通常可以很好地为Java开发人员服务。 有时使用==比较标准Java类型引用(String,Integer,Long等) 可以很好地工作 ,但是依靠要缓存的值来使它可行并不是一个好主意。 在某些情况下,可能需要检查身份相等性而不是内容相等性,然后==适合比较引用。 我在这里更喜欢Groovy的方法,其中==作用类似于.equals===并且显然传达了开发人员更严格地比较身份的愿望。 相同的参数适用于使用!=将两个引用比较为一个红色标记,因为如果被比较的两个对象即使共享相同的内容,也不共享相同的标识(内存地址),则始终返回true。

在枚举中使用.equals(而不是==)

坦白说,Java开发人员对枚举使用==还是.equals并不重要。 但是,我更喜欢将==与enums一起使用 。 此首选项的最重要原因是将==与枚举一起使用可避免将枚举与某个不相关的对象(永远不会相等)进行比较的可能错误。 Object.equals(Object)方法必须必须接受任何Object,但这意味着编译器无法强制传入的对象实际上是要比较的类型。 与动态运行时检测问题相比,我通常更喜欢静态问题的静态编译时间检测,并且将==与枚举一起使用可满足此偏好。 当然,在比较枚举时,同样的参数适用于!=!.equals使用。

幻数和文字字符串

我知道它是“计算机科学101”,但是我仍然经常看到在Java代码中经常使用“ 魔术数字 ”和文字字符串。 这些作为将来可维护性的“危险信号”而大声疾呼,使我对当前应用程序的正确性产生严重怀疑。 在单个位置将它们表示为常量(或者在适当时更好地表示为枚举)可以提高将来的可维护性,并且也使我对使用这些值的所有代码都使用相同的值具有更高的信心。 此外,集中定义的常量和枚举使使用IDE的“查找用法”功能轻松查找这些常量的所有“客户端”。

字符串常量

当我看到一组有限的相关String常量时,我​​常常认为枚举会更好。 对于具有高度内聚性的一组String常量,尤其如此,它允许枚举很好地表示这些String构成的概念。 与String常量相比,该枚举提供了编译时静态类型的安全性和潜在的性能优势。 在程序正确性方面,最让我感兴趣的是编译时安全。

使用Java的“ Goto”

在这篇文章中,我几乎没有在使用分支到带标签的代码上包含这个项目,因为事实是,我在生产代码中看到的大多数实例都是合理的 。 换句话说,在这篇文章中列出该项目的原因更多是因为它有可能以错误的方式滥用和使用,而不是我实际看到的。 在大多数情况下,我看到Java开发人员使用Java的“ goto”是为了避免过于混乱和难以阅读的代码。 很少使用此事实可能部分归因于其正确使用。 如果经常使用,那么这些用途中的大多数可能会变味。

取决于适用范围的同名变量的适当引用

在我看来,该项目属于绝对不合适的类别,但绝对可行,甚至某些时候某些Java开发人员有意地完成了。 最好的例子是Java开发人员在执行方法期间将传递给方法的变量指向另一个引用。 指向方法参数的变量暂时指向它所分配的任何替代方法,直到方法结束为止,此时该方法超出范围。 在这种情况下,将final关键字放在方法定义的参数定义之前会导致编译器错误,这也是我喜欢在所有方法参数之前使用final的原因之一。 对我来说,简单地在该方法的局部声明一个新变量更容易理解,因为无论如何该变量仅在该方法局部使用。 更重要的是,作为代码的读者,我无法知道开发人员是否有意让该参数的名称仅在本地用于不同的值,还是他们引入了一个错误,认为将参数重新分配给新的引用会实际上在主叫方进行更改。 当我看到这些内容时,我要么与原始开发人员合作,要么从单元测试和生产中搜集信息,然后使用意图并使其更加清晰(或者如果打算更改调用方客户的价值,则进行修复)。

equals(Object)和hashCode()方法不匹配

尽管我相信应该为几乎所有编写的Java类都编写一个toString()方法,但是我对equals(Object)和hashCode()重写的感觉并不相同。 我认为只有在打算将类用于需要这些方法的情况下,才应编写这些内容,因为它们的存在应暗示它们在设计和开发中具有一定程度的额外彻底性。 特别是,equals和hashCode方法需要满足它们的意图并进行广告宣传(在Object的API文档中),并且必须彼此对齐。 大多数IDE和分析工具会确定何时存在一种方法而没有另一种方法。 但是,我想确保将用于equals的相同属性用于hashCode,并且我希望在两种方法中以相同的顺序考虑它们。

缺少Javadoc注释

我喜欢将所有合同方法(尤其是公共方法)与Javadoc注释一起注释。 我还发现用属性要存储的内容进行注释非常有用。 我已经听过当代码“自我记录”时不使用Javadoc注释的借口,但是我几乎总是可以告诉提出该要求的人一个或多个示例,以了解简单的Javadoc注释如何能够传递与传递给您相同的信息。比解密代码花费的时间更长。 甚至更长的方法名称通常也不能足够长以指定给定方法的所有预期输入条件和输出预期。 我认为像我一样,许多Java开发人员都喜欢在使用JDK时阅读Javadoc注释,而不是阅读JDK代码。 为什么我们自己的内部代码应该有所不同? 当注释不足或行为与所宣传的不一样时,或者当我有理由认为注释可能陈旧或伪劣时,我会阅读源代码。

我还希望看到Javadoc对类属性的注释。 有些人喜欢用属性信息注释公共的get / set方法,但是我也不喜欢这种方法,因为它假定get / set方法将始终可用(我不喜欢这样的假设)。

方法的Javadoc注释中的实现详细信息

尽管我觉得没有Javadoc注释是一个危险信号,但是使用错误类型的Javadoc注释也是一个危险信号。 Javadoc注释不应解释实现细节,而应侧重于客户端对方法的期望(参数)和客户端对方法的期望(返回类型和可能引发的异常)。 在真正的面向对象的系统中,实现应该可以在公共接口下进行修改,因此将这些实现详细信息放在接口文档中似乎是不合适的。 这是一个“危险信号”,因为Javadoc中存在实现细节使我怀疑注释的及时性。 换句话说,这些类型的注释通常会随着代码的发展而Swift过时并且完全错误。

源代码中的注释

尽管在接口上缺少注释(通常在Javadoc中)是一个危险信号,但方法和其他源代码体内是否存在注释也是一个指示潜在问题的危险信号。 如果需要对代码进行注释以了解其功能,则代码可能比需要的复杂得多(“注释是臭代码的除臭剂”通常是正确的,也很有趣)。 就像这篇文章中的许多“红色标记”一样,当我看到代码中的注释内容丰富时,也有一些例外,在这些注释中,我无法想到一种更好的方式来呈现代码以消除对这些注释的需要。 但是,我认为代码内(而不是接口描述性Javadoc注释)应该相对较少,并且应该关注“为什么”做某事,而不是“怎么做”。 该代码应该说明“如何”的细节,但通常不能被编写为隐式地解释“为什么”(由于客户/管理方向,设计决策,正式接口要求,正式算法要求等)。

实现继承(扩展)

我已经看到许多情况下可以很好地使用extends (实现继承),并且适合这种情况。 我已经看到了很多情况相反的情况,实现继承带来的麻烦多于好处。 艾伦·霍鲁布(Allen Holub)曾写过“ 扩展是邪恶的” ,《 四种 设计模式帮派》 一书非常着重于为什么组合通常比实现继承更可取的原因。 开发Java代码的时间越长,更重要的是,我维护Java代码的时间越长,我就越相信组合的优点和实现继承的弊端。 正如我所说,我已经看到实现继承可以起到很好的作用,但是通常情况下,类会“苦苦挣扎”以适应继承层次结构,并且子类会遇到UnsupportedOperationException或“ noop ”实现,因为它们继承的方法没有实现。真的适用。 抛出一些在有效Java中概述的继承问题(例如,用于继承具体类的equals和hashCode方法实现),它可能会变成一团糟。 实现继承并不总是不好的,但是它经常被不好地使用,因此是一个危险信号。

死码

闲置未使用的代码永远不是一件好事。 人们很快就会忘记如何使用它或为什么使用它。 此后不久,开发人员开始怀疑它是否由于某种原因而遗留了下来。 死代码不仅增加了必须读取和可能维护的代码,而且在通过IDE完成编码的世界中,仅基于死代码的名称和参数列表,就可以轻松,意外地调用死方法。 死代码通常也是被忽略的代码库的症状。

注释代码

已注释掉的代码可能不像可执行的死代码那样糟糕,因为至少不能意外地调用它,并且更明显的是未使用它,但是它仍然是一个危险信号,因为它表明潜在的代码库被忽视了。 就像死掉的可执行代码一样,在注释掉代码之间经过的时间越长,就越难知道为什么代码在那里,为什么注释掉了以及为什么不再不再删除代码了需要。 开发人员可能会害怕删除它,因为它显然很重要,因此必须先离开,但没人记得为什么。

待办事项

在代码中添加“待办事项”语句变得非常普遍,以至于现代Java IDE为它们提供了特殊的支持和功能。 这些功能之所以有用,是因为它们经常将待办事项标记放在列表中以供查看,但是“待办事项”注释仍然是危险标记,并且可能带来一些与死代码和注释掉代码相同的问题。 我肯定会使用“做”注释来进行短期提醒,但是我认为最好在“做注释”中包括“有效期”和联系信息以及可能的人员或事件(如果有)需要蒸蒸日上,以使“工作”得以完成。 拥有到期日期,联系信息以及解决“待办事项”所必需的事件或人员,可以减少在代码中进行“待办事项”注释的可能性,因为没人记得确切的用途,但没人敢删除它们,因为是一件重要的事情。 当我在代码中看到“要做”的陈述时,我不禁想知道代码是否某种程度上缺乏功能。

编译器警告和IDE /工具警告/提示/发现

Java红旗的明显示例是javac编译器发出的警告以及IDE和其他代码分析工具提供的发现和提示。 这些发现和警告中的每一个都可能是其自己的危险信号。 实际上,javac编译器或工具和IDE会警告或暗示我在本文中引用的许多危险信号。 这些警告,提示和发现不仅直接对应于许多Java代码危险信号,而且它们的合计存在是一个巨大的危险信号,表明潜在的被忽略的代码库可能会在其中丢失严重的警告(甚至是某些定义的错误)。大量的警告和提示。

编译器警告和IDE /工具警告/提示/发现已关闭

我绝对不认为FindBugs在我的Java代码中发现的每个问题都必然是错误或缺陷。 实际上,在某些情况下,我什至禁用了一些发现,因为我不同意这些发现,并且它们使FindBugs输出变得混乱。 话虽如此,对发现的这种选择应该谨慎进行,以确保只忽略真正的非发现,并且对开发团队重要的事情是显而易见的。 同样,我希望打开许多IDE警告,但确实要关闭一些警告。 我也不认为在禁用Javac警告的情况下构建Java应用程序不是一个好主意。 禁用这些警告和发现可能会删除警告所代表的危险信号,但是将其关闭的动作可能会导致更大的危险信号,因为这些警告和提示指出了可以解决的许多潜在危险信号。

太聪明了/ Science Fair项目/过度设计/过早的优化

我最后一类危险信号是一大类过于复杂的软件,这通常是常见的开发人员功能失常行为之一的结果。 过于聪明以致于无法阅读的代码,或者不需要时以灵活性(过早的)优化为 重点的代码,但往往以可读性为代价,这通常会带来其他问题。 对代码进行过度工程化的开发人员可能正在这样做,以查看他们是否可以或尝试一些新的东西,或者只是想改变一些东西,但是这些行为很少会不利于高质量的软件。 一旦软件变得过于复杂且难以理解,就不太可能对其进行正确维护,并且更有可能对其进行不正确的更改。

通常,在阅读代码并想知道为什么开发人员没有以更明显和直接的方式实现此功能时,这种危险信号的例子就是一个明显的例子。 一方面,您可能会对他们知道并能够应用某些高级功能印象深刻,但另一方面,您知道这可能比原本应该的复杂。 这类危险信号有许多表现形式,但我似乎在一些地区普遍看到它们。 特别是其中一个领域是通过反射,Spring或其他依赖项注入,动态代理,观察者等,将过多的功能(这些功能在静态Java代码中能很好地发挥作用)推向更多的动态构造。 所有这些东西在正确应用时都是方便且有用的,但我也看到所有这些东西都被过度使用和滥用,这使开发人员难以跟踪代码中发生的事情。

结论

在本文中,我研究了在Java代码和Java开发环境中看到的一些常见的Java红色标记。 如果使用不当,这些事情不一定是错误的或负面的,但如果使用不当,则可以视为对潜在有害做法的警告。 当我看到这些危险信号时,我不会急于做出判断,直到我有机会更深入地研究以确定在这些危险信号下进行酿造是否有困难,或者它们是否处于可以采用该策略的情况下。 通常,这些危险信号是即将发生问题的早期指标。 在某些情况下,它们是对严重的现有问题(也许以前无法解释的问题)的解释或指示。


翻译自: https://www.javacodegeeks.com/2013/06/common-red-flags-in-java-development.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值