原文
Best coding practices every java developer should follow
目录
- 引言
- 4. 尽可能让变量私有化
- 6. 警觉冗余初始化
- 5. Stringbuilder 替换字符串拼接【争议】
- 15. dry 和 kiss
- 14. Solid
- 7. 尽可能应用加强 for 循环或者 foreach
- 13. 日志打印规定
- 12. Hardcoding 硬编码
- 11. 正文
- 10. 防止创立不必要对象
- 9. 返回空集合而不是 null
- 8. 精度抉择
- 1. 我的项目构造
- 2. 遵循命名标准
- 3. 不要吞异样
- 16. 应用枚举代替动态常量【倡议】
- 17. 按作用域划分成员变量
- 18. 在数字字段中应用下划线【倡议】
- 总结
- 参考
引言
把题目翻译成中文在国内也是一个陈词滥调的问题:编程习惯和编码标准。
这篇文章大部分观点和国内的标准习惯相似,令我好奇的是外国人是如何了解这些内容的?
留神本文的 Tips 排序是打乱的,集体把感兴趣放到了后面来了。这篇文章的评论区十分精彩,这里一并整合了评论区的读者观点。
不论是学习还是理解老外的思考形式,或是在评论区当中看读者的探讨和纠正错误,浏览本文都是值得举荐的。
抱着将信将疑的态度学习这些内容,你会失去比单纯的理解和承受倡议更多的播种。
4. 尽可能让变量私有化
如果变量不须要意对外拜访,那就倡议应用公有形容对于参数进行形容。
评论区的读者对于这一个大节做了补充:
- 如果是 dto 并且是 final 的,领有专用数据的时候 能够不私有化.。
- 比大量的 setter 更好倡议是更正当的利用设计模式构建对象(比方建造者),或者利用委托第三方对象生产适合的对象(动态工厂代替结构器)。
- 尽可能让对象不可变。
6. 警觉冗余初始化
Therefore, a java best practice is to be aware of the default initialization values of member variables and avoid initializing the variables explicitly.
Java 最佳实际是理解成员变量的默认初始化值并防止显式初始化变量,Java 语言很多变量存在默认值,在本人编写初始化的时候不倡议应用 Java 的默认值。
另一方面,有时候能够利用 Java 的初始化做数据库字段的优化,比方开关状态倡议把 0 设置为凋谢 1 设置为敞开。
上面的代码的的对象初始化代码是毫无意义的:
public class Person {
private String name;
private int age;
private boolean;
public Person() {
String name = null;
int age = 0;
boolean isGenius = false;
}
}
Although it is very common practice, it is not encouraged to initialize member variables with the values: like 0, false and null. These values are already the default initialization values of member variables in Java. Therefore, a java best practice is to be aware of the default initialization values of member variables and avoid initializing the variables explicitly.
只管这是十分常见的做法,但不激励应用以下值初始化成员变量:如 0、false 和 null。这些值曾经是 Java 中成员变量的默认初始化值。因而,Java 最佳实际是理解成员变量的缺省初始化值,并防止显式初始化变量。
See more here: Java default Initialization of Instance Variables and Initialization Blocks。
5. Stringbuilder 替换字符串拼接【争议】
实际上少数状况下“大可不必”,只有 for 循环的状况才思考是否应用 Stringbuilder 替换。日常状况下字符拼接操作是齐全没有问问题的,javac 编译之后会把字符串主动用 StringBuilder 替换,真正应该手动创立该对象的场景是在for 循环当中的大量的字符串拼接,外部会每次迭代新建 Stringbuilder。
随着 JDK 版本的降级,到 JDK9 版本 for 循环新建 StringBuilder 的状况曾经被改善了(但集体未从 OpenJDK 中找到对应源码验证)。
JDK13 从 Python 那边把多行文本的语法搬过去了,定义多行文本能够相似上面这样:
String htmlContent =“““<html>
<head>
</head>
<body>
<p>Hello world!</p>
</body>
</html>""";
最初须要留神的是应用Stringbuilder,仅仅应该用在多线程循环操作字符串当中,如果在同步办法外面效率会非常低并且很慢。
介绍
As a general rule, optimize at an architectural level, and write code for readability. Later on it’s easier to optimize a neat function, than to look for bugs in an optimized one.
下面意思大抵是说通用规定是优先写出具备高可读性的代码,而后再去进行优化这些简略的可优化点,这比在优化代码找谬误要简略很多。
15. dry 和 kiss
DRY stands for“Don’s Repeat Yourself
如果代码能够专用就尽量专用,不要复制粘贴代码。
kiss:KISS stands for“Keep It Simple, Stupid”.
KISS 就是放弃简略,愚昧。这个愚昧指的是办法最好只晓得干一件事件。这两点和 Unix 最后的设计的理念是一样的,简略好用即是美。
14. Solid
Single Responsibility Principle 繁多职责:每个类和接口有明确指标。
Open-Closed Principle 开闭准则:凋谢扩大,关闭批改。
Liskov Substitution Principle 里式代替:尽可能让代码多态。
Interface Segregation Principle 接口隔离:实现接口代替类继承。
Dependency Inversion Principle 依赖倒转准则:依赖形象而不是依赖实现。
7. 尽可能应用加强 for 循环或者 foreach
倡议应用加强 for 循环(JDK5)和 JDK8 的 foreach,当然最好的倡议是活学活用 stream 的 API。为什么会有这样的倡议?如同 for-index 也不是啥坏写法。
It’s because the index variable is error-prone, as we may alter it incidentally in the loop’s body, or we may starts the index from 1 instead of 0.
这是因为索引变量容易出错,因为咱们可能会在循环的主体中偶尔扭转它,或者咱们可能从 1 而不是 0 开始索引。上面是 Stream API 带来便当的简略例子:
public Integer findSmallesPositiveNumber(List<Integer> numbers) {
Integer smallestPositiveNumber= null;
for (Integer number: numbers) {if (number > 0) {
if (smallestPositiveNumber == null
|| number < smallestPositiveNumber) {smallestPositiveNumber = number;}
}
return smallestPositiveNumber;
}
应用 Stream APi 精简之后:
public Optional<Integer> findSmallesPositiveNumber(List<Integer> numbers) {return numbers.stream()
.filter(number -> number > 0)
.min(Integer::compare);
}
13. 日志打印规定
作者的上面几条规定 有待商讨 ,我集体倡议是 防止 上面的做法:
Developer should add logger on method entry and exit.
在进出重要办法打印入参和出参。
审慎应用,倡议波及金额的操作状况打印。
Developer should add logger in“if”and“else”case to track which condition is true in case of any error.
在 if 和 else 的条件判断处打印参数。
Developer should also log exception to track the issue
对于异样信息必须打印。
更为正当的做法是像上面这样:
Definitely don’t log every if-else statement!
不要在 if/else 分支中打印,更为正当的倡议是记录响应以及谬误。
Don’t log every method entry and exit!
不要在每个办法的入参和出参打印。当然这并不是铁律,比方三方接口调用就必须要在“入口”和“进口”中打印以便疾速定位问题 和甩锅。
**Log exceptions that are unexpected. **
记录“意外”的异样。比方多线程有可能的 interrupt 解决,文件读写有可能的 IO 异样。
从贬义和贬义两个层面看,日志都是十分具备“价值”的,好的日志能够帮你疾速定位问题,不好的日志就像是垃圾一样迅速占用服务器的磁盘。只有十分外围的日志才须要跟踪每一步的行为,绝大多数状况下日志只须要在影响业务的地位进行打印。在打印日志段时候多思考一下 日志等级 的抉择,这意味着你生产是否疾速定位问题。
12. Hardcoding 硬编码
硬编码回会导致程序难以了解。应用硬编码会减少了解难度,通常应用枚举代替是不错倡议。依据 dry 的准则,在定义硬编码的时候,如果魔法值在 JDK 中存在相似定义或者存在现实意义,应该果决通过上面的形式进行纠正,比方上面的例子:
private int storeClosureDay = 7;
应该被替换为上面这样:
private static final int SUNDAY = 7;
[…]
private int storeClosureDay = SUNDAY;
不要 这样应用,更为适合的解决形式是应用 DayOfWeek 的 API:
private DayOfWeek storeClosureDay = DayOfWeek.SUNDAY;
防止硬编码是十分好的编程习惯,更好的习惯是应用易懂的硬编码。
11. 正文
要害:正文只有在可读性较差并且须要正文形容要害用意的时候才应用。
As your code will be read by various people with varying knowledge of Java, Proper comments should be used to give overviews of your code and provide additional information that cannot be perceived from the code itself. Comments are supposed to describe the working of your code to be read by Quality assurance engineer, reviewer, or maintenance staff .
因为代码将被具备不同 Java 常识的各种人浏览,因而应该应用适当的正文来概述代码,并提供无奈从代码自身感知的其余信息。正文应该形容代码的工作形式,以供质量保证工程师、审阅者或保护人员浏览。
正文关键点应该具备 可读性,有时候没有任何正文的代码的确让人苦楚,更可怕的是因为代码更新然而正文不更新,这样的事件也时有发生 =-=。最好的正文应该是代码自身会告知作者用意。
PS:集体看过的没啥正文也能齐全看懂,并且看完感觉写的十分美的代码目前只有 Redission 和 Spring。
10. 防止创立不必要对象
对象创立是最耗费内存的操作之一,这就是为什么最好的 Java 实际是防止创立任何不必要的对象,并且只应在须要时创立它们。
9. 返回空集合而不是 null
NPE is the most frequent exception in production with absolute leadership, do not give it a chance.
更进一步说是让整个零碎不要呈现空指针异样,不应该因为我的项目代码斗争老旧的编程格调。肯定不要让空指针有可乘之机。除了返回空集合以外,还能够利用 Optional 的工具类包装有可能为 Null 的对象,显式的通知调用者对象有可能为 Null。
An empty collection might have a different meaning than no collection at all.
有时候 null 也可能是“有意义”的,应用的时候也须要思考具体情况,当然返回空集合必定是没有任何问题的。
Collections
空集合相干的轮子在 Collections 能够找到答案,然而须要留神须要正当应用,JDK5 之后呈现了 emptyXXX 等汇合类,然而这些类“暗藏杀机”,因为它们的成果和List.subList() 一样是immutable 的。
尽管 JDK 的工具类能够缩小不必要的对象创立,然而假如接管方须要在收到空集合之后却还要往里面设置数据,这时候毫无疑问就会抛异样了。
/**
* Returns an empty list (immutable). This list is serializable.
*
* <p>This example illustrates the type-safe way to obtain an empty list:
* <pre>
* List<String> s = Collections.emptyList();
* </pre>
*
* @implNote
* Implementations of this method need not create a separate <tt>List</tt>
* object for each call. Using this method is likely to have comparable
* cost to using the like-named field. (Unlike this method, the field does
* not provide type safety.)
*
* @param <T> type of elements, if there were any, in the list
* @return an empty immutable list
*
* @see #EMPTY_LIST
* @since 1.5
*/
@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {return (List<T>) EMPTY_LIST;
}
我认为 emptyList()
更为正当的办法名称是 immutableEmptyList()
和 mutableEmptyList()
,当然 mutableEmptyList()
就是 new 新的 ArrayList()
这样的办法,看起来是毫无意义的,兴许 JDK 设计的时候也是思考这种状况?
8. 精度抉择
Most processors take almost same time in processing the operations on float and double but double offers way more precision then float that is why it is best practice to use double when precision is important otherwise you can go with float as it requires half space than double.。
大型处理器在解决 double 和 float 的时候计算速度是相似的。然而在 Java 中大部分状况只有是波及浮点数计算都是闭着眼睛用 BigDecimal。如果精度很重要间接无脑应用 BigDecimal,double 和 float 都会骗人。
When precision is important, use BigDecimal. Double and Float aren’t accurate. They will betray you when you least expect it. 1 + 1 can be 1.99999999999.
当精度很重要时请应用 BigDecimal,Double 和 Float 并不准确。它们会在你最不冀望的时候背离你。1+ 1 可能是 1.99999999999。
1. 我的项目构造
有点过于根底了。上面是 Maven 的经典构造,如果是 Maven 老手能够看看我的项目的根本布局
- src/main/java: For source files
- src/main/resources: For resource files, like properties
- src/test/java: For test source files
- src/test/resources: For test resource files, like properties
2. 遵循命名标准
没啥好讲的,程序员的根底素质。最好的命名标准不是参考某一个规范,而是能对立格调布局代码。
3. 不要吞异样
在异样解决时在 catch 块中编写适当且有意义的音讯是精英 java 开发人员首选的 java 最佳实际。老手经常会把异样捕捉之后不做任何解决,吞异样是十分危险的行为。
得益于 IDE 的帮忙,catch 之后不打印任何信息的状况不是很多见,然而打印堆栈其实也是十分耗费资源的操作,同时因为是打印在控制台,如果不调用日志保留要害信息也有可能导致要害信息失落。
16. 应用枚举代替动态常量【倡议】
在 Java 推出枚举之前,定义一个常量根本只能应用上面的接口方式。在很多优良框架的最晚期版本中常常能看到这样的写法,并且到当初应用这种写法的不在少数。
public interface Color {
public static final int RED = 0xff0000;
public static final int BLACK = 0x000000;
public static final int WHITE = 0xffffff;
}
It’s because the purpose of interfaces is for inheritance and polymorphism, not for static stuffs like that. So the best practice recommends us to use an enum instead.
这是因为接口的目标是用于继承和多态性,而不是用于相似的动态货色。所以最佳实际倡议咱们应用枚举来代替。
public enum Color {BLACK(0x000000),
WHITE(0xffffff),
RED(0xff0000);
private int code;
Color(int code) {this.code = code;}
public int getCode() {return this.code;}
}
应用枚举和动态常量的比照:
- 枚举更具备描述性
- Keep It Simple, Stupid。接口能够用于继承和多态,枚举无能的事件根本是常量定义。
- 接口的动态常量往往有其余的用处。
很少有人会在枚举设计简单的逻辑,因为枚举的可扩展性很差并且了解和学习老本较高
17. 按作用域划分成员变量
The best practice to organize member variables of a class by their scopes from most restrictive to least restrictive.
最好的做法是按其作用域从最严格到最不严格来组织一个类的成员变量。
public class StudentManager {
protected List<Student> listStudents;
public int numberOfStudents;
private String errorMessage;
float rowHeight;
float columnWidth;
protected String[] columnNames;
private int numberOfRows;
private int numberOfColumns;
public String title;
}
下面的成员变量定义目迷五色,通过作用域划分如下:
public class StudentManager {
private String errorMessage;
private int numberOfColumns;
private int numberOfRows;
float columnWidth;
float rowHeight;
protected String[] columnNames;
protected List<Student> listStudents;
public int numberOfStudents;
public String title;
}
18. 在数字字段中应用下划线【倡议】
来自 Java 7 的小更新能够帮忙咱们把简短的数字字段写得更易读。
int maxUploadSize = 20971520;
long accountBalance = 1000000000000L;
float pi = 3.141592653589F;
和上面的代码作比拟:
int maxUploadSize = 20_971_520;
long accountBalance = 1_000_000_000_000L;
float pi = 3.141_592_653_589F。
哪一个更易懂?记得在数字字面中应用下划线,以进步代码的可读性。
集体:难堪,学了这么多年 Java 竟然不晓得有这种写法 …..
总结
这里列举一下存在争议以及印象比拟深的局部:
-
StringBuilder 替换字符串拼接。如果单纯拼接几个变量或者办法的后果,”+” 号是齐全没有问题的。
- 尽管 JDK 9 对于每次 for 循环 new StringBuilder 的 BUG 做了修复,但国内宽泛应用 JDK8 的场景下仍然须要防止 for 循环大量的字符串拼接。
- 这种优化不是要害,请更加关注正当的架构设计,因为这一点性能损耗对于古代处理器影响真的不大。
- 如果不须要思考多线程的状况,更倡议应用 StringBuffer。
- 精度抉择,实际上不论有没有精度要求,碰到须要小数的运算倡议应用 BigDecimal,因为 double 和 float 的数学运算真的很容易出问题。
-
正文
- 训练写出易懂的代码而不是易懂的正文
- 正文应该听从 KISS。“愚昧”的代码最好了解。
- 在大型的数字参数值中应用下划线,这是一个很容易疏忽的 Java 小技巧,对于和金额无关的解决,这种写法十分有帮忙。
参考
另一位读者:Best coding practices every Java developer should follow?
10 Java Core Best Practices Every Java Programmer Should Know