从《无效的Java》到《高效的Java》
在不存在《Effective Java》的世界里,它就成了《Ineffective Java》。
我认为最近如果要使用Java进行开发,常常会使用像SpringBoot这样的框架来构建应用程序。
SpringBoot真的做得非常好。它提供了很多优秀的机制,如依赖注入、自动配置等,可以减少代码的编写量。
熟悉Java的程序员通常会作为架构师来设计和实现框架。使用诸如模板方法模式等,构建易于批量生产的架构。然后将接口交给负责实现各功能的程序员,通过遵循框架来创建界面和业务处理。
然而,业务处理是当然必要的。在大家的工作现场中,关于如此大量且并行编写的代码的质量是怎样的呢?
我周围也存在这方面的问题,我认为在开发工作中需要有正确意识的工程师,因此我们开始为公司内部进行Java教育。本文总结了我在这个教育过程中通过代码审查发现的“原来是不理解这个啊”的问题。希望通过教育和审查,让管理者们在发现错误时不再常见的抱怨“为什么写这么烂的代码?”而是意识到教育和审查的重要性。也许,这样的工作环境中没有Effective Java/Readable Code等书籍,是否被尘封起来了呢?教育非常重要。
Java教育是什么样的?
举办了两个季度。
-
- 2018年6月-9月 26名 週1回 全16回
- 2018年11月-2019年2月 15名 週1回 全8回
每周我们会有两小时的讲义,并且会布置练习题,通过Pull Request收到后,我会进行审查并返回意见。在课程期间,我们会通过Slack进行跟进。教材我购买了Kasareal老师的教材进行实施。虽然比一般的书籍要贵一些,但是教程的顺序很好,很容易理解。如果一直处于可以进行结对编程等环境的话,可能就不需要教材了。不过,我对于这次内部教育非常满意,觉得很顺利。
这些人是谁
-
- Javaを業務で書き始めた1年目
-
- 実はJavaを書いているけどという3-4年目ぐらい
- 別の言語からJavaに転向してきた10年目など様々です。
以下是我在Pull Request中看到的一些你应该知道的内容的总结。排名是根据我的感觉。
如果你对某些内容说”这个肯定知道啦”,请一定要问问你周围的新手。你周围可能也有很多人不知道这些。不学的东西就不会知道。
无效的Java
「项目1中的toString()是什么意思?」
很多人不知道这个方法的作用,让我感到惊讶。
你有没有见过写着object.getXXX() + object.getYYY()的代码,尽管toString()方法已经在对象中实现了?
请问您的周围的年轻人也有没有遇到不了解toString()方法含义的情况?
項目2 声明左边的类型始终与右边的类型相同。
ArrayList<String> list = new ArrayList<>();
当然,左侧应该是List。但是,如果没有考虑接口和多态等概念,左侧会被声明为与右侧相同的类型。
对于处于这种状态的人来说,即使说OCP之类的东西,也会变得一头雾水,所以需要按照继承->接口->集合->多态的顺序逐步解释。
仅凭这一点,就能正确思考并定义左侧。
将项目3转换为说唱歌手
Long.valueOf(Files.size(pathA)).equals(Long.valueOf(Files.size(pathB)));
你可能是从某个地方复制过来的。或者,将其转化成饶舌歌手可能会带来一种安心感。正确理解如何处理原始数据是必要的。
通过理解这一点,你将逐渐学会避免创建无用的对象。
第四项,不必要的评论不需要。
你肯定见过这样的代码吧。Eclipse自动生成的注释仍然保留着,像变量的说明等等没什么用的注释。前者是因为写代码过于专注,注释根本没注意到。后者则是接受了注释很重要的教育,但注释却被忽略并被遗弃的例子吧。
如果你在进行配对编程等活动时,可以立刻意识到这一点,但如果不是这种情况,那么就没有人会学到,这种情况会一直持续下去。应该正确地教导他们。基本上,不需要评论。如果在工作中进行非正常操作时,可能是必要的。否则,在后续的维护阶段可能会因为评论而出现错误。
这个项目不需要这个 if 语句。
public boolean isLessThan1KB(String file) throws Exception {
// Pathオブジェクト生成
Path path = Paths.get(file);
if (Files.size(path) < 1024) {
return true;
} else {
return false;
}
}
这是非常常见的写法。如果条件满足,则可以使用return Files.size(path)<1024来替换后面的if语句。
当将代码视为只要工作就好时,很多人会使用这种写法。如果觉得这种写法有些冗长,那就需要上级进行代码审查。
为什么不看返回值的原因是什么?
public void delLine(String line) {
lines.remove(lines.indexOf(line));
}
List 的 remove 方法具有返回值。有时会看到忽略返回值直接使用的情况。这可能是因为在网络上有类似的写法。
其中的原因很大程度上是初学者不擅长错误处理。一旦理解了异常、错误处理等方面的知识,这个问题自然会解决。
第七项目不熟悉try-with-resources操作。
仍有很多人认为现场的JDK仍然是6版本。他们还要使用过时的写法到什么时候呢?不管是twr、for循环、Stream、lambda等,所有这些都是相同的处境。
我建议重新认真学习Java8。对于从Java11开始的新人来说,这些人只是威胁而已。
项目8 喜欢嵌套
if(xxx == true) {
//処理
} else {
//処理
}
一定会以这样的方式写作的认真的程序员。我想提醒他们世界上有一种叫做“守卫子句”的东西。使用守卫子句会使事情更加清晰明了。这是因为经常陷入嵌套代码的困扰。我知道一次减少一个嵌套可能看不出太大差别,但是如果进行仔细审查,他们会很快理解并享受到它的实际效果。
我不理解equals()方法的含义。
这个本来就非常困难。即使是Java的中级开发者,被要求即时解释可能也会觉得困难。但是,如果不理解这个,我会感到害怕,也无法使用TreeMap等。因为我不知道任何对象是否被正确排序。所以,我总是成为HashMap的使用者,在调用get()之后进行琢磨。实现equals()就自然要实现hashCode()。否则会发生很多奇怪的现象。只有真正理解了这一点,代码才能变得更加优雅。这正是Effective Java的精髓。
项目10 不可变的?
我不知道不可变对象。这是从初级到中级升级所需的非常重要的知识。有能力推断对象状态以消除潜在的错误。这就是一个Java中级开发者所需的能力。如何创建不可变对象?它在什么情况下使用?如果理解了这些,类的设计空间将大大扩展。相反,如果不了解这些,将只能一味地进行平庸的设计。
此外,虽然与不可变性没有直接关联,但对于类似于 final List stringList; 这样的 List,有些人可能无法正确解释 final 的含义,因此正确理解也非常重要。
从《无效的Java》到《有效的Java》的世界
初学者可以通过阅读《Effective Java》来提升自己,因为这本书中有许多初级到中级的过渡内容。初学者可以向中级者请教一些他们不懂的问题。中级者可以邀请初学者一起参加读书会讨论。《Effective Java》第三版
如果你想重新学习Java8,这里是一个推荐的选择。
作为一名Java程序员,掌握Java SE 8的实践编程是必备的。