手册下载地址java
反例:程序员
说明:web
正确的英文拼写和语法可让阅读者易于理解,避免歧义。注意,即便纯拼音命名方式也要避免使用。正则表达式
正例:spring
等国际通用的名称,可视同英文。数据库
反例:编程
正例:windows
反例:设计模式
正例:数组
正例:MAX_STOCK_COUNT
反例:MAX_COUNT
String[] args
;反例:
使用 String args[]
的方式来定义。
反例:
定义为基本数据类型 Boolean isDeleted;的属性,它的方法也是 isDeleted(),RPC 框架在反向解析的时候,“觉得”对应的属性名称是 deleted,致使属性获取不到,进而抛出异常。
正例:
应用工具类包名为 com.alibaba.open.util、类名为 MessageUtils(此规则参考 spring 的框架结构)
反例:
此类随意缩写严重下降了代码的可阅读性。
正例:从远程仓库拉取代码的类名为 PullCodeFromRemoteRepository。
反例:变量 int a; 的随意命名方式。
说明:
将设计模式体如今名字中,有利于阅读者快速理解框架设计理念。
正例:
正例:
反例:
接口方法定义:public abstract void f();
说明:
JDK8 中接口容许有默认实现,那么这个 default 方法,是对全部实现类都有价值的默认实现。
【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务必定是接口,内部的实现类用 Impl 的后缀与接口区别。
正例:
CacheServiceImpl 实现 CacheService 接口。
【推荐】若是是形容能力的接口名称,取对应的形容词作接口名(一般是 - able 的形式)。
正例:
AbstractTranslator 实现 Translatable。
说明:
枚举其实就是特使的常量类,且构造方法被默认强制是私有。
正例:
枚举的名字为 ProcessStatusEnum
的成员名称:SUCCESS
/ UNKOWN_REASON
。
反例:
String key = "Id#taobao_" + tradeId;
cache.put(key, value);
说明:Long a = 2l;
写的是数字 21,仍是 Long 型的 2?
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
正例:缓存相关常量放在 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下。
应用内共享常量:放置在一方库中,一般是 modules 中的 constant 目录下。
反例:
易懂变量也要统必定义成应用内共享常量,两位工程师在两个类中分别定义了表示 “是” 的变量:
类 A 中:public static final String YES = "yes";
类 B 中:public static final String YES = "y";
A.YES.equals(B.YES)
,预期是 true,但实际返回为 false,致使线上问题。
子工程内部共享常量:即在当前子工程的 constant 目录下。
正例:
public enum Enum {
MONDAY(1),
TUESDAY(2),
WEDNESDAY(3),
THURSDAY(4),
FRIDAY(5),
SATURDAY(6),
SUNDAY(7);
}
反例:
if (空格a == b空格)
说明:
运算符包括赋值运算符 =
、逻辑运算符 &&
、加减乘除符号等。
说明:
若是使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。IDEA 设置 tab 为 4 个空格时,请勿勾选 Use tab character;而在 eclipse 中,必须勾选 insert spaces for tabs。
正例:(涉及 1 - 5 点)
public static void main(String[] args) {
// 缩进 4 个空格
String say = "hello";
// 运算符的左右必须有一个空格
int flag = 0;
// 关键词 if 与括号之间必须有一个空格,括号内的 f 与左括号,0 与右括号不须要空格
if (flag == 0) {
System.out.println(say);
}
// 左大括号前加空格且不换行;左大括号后换行
if (flag == 1) {
System.out.println("world");
// 右大括号前换行,右大括号后有 else,不用换行
} else {
System.out.println("ok");
// 在右大括号后直接结束,则必须换行
}
}
正例:
// 注释内容
,注意在 //
和 注释内容
之间有一个空格。
正例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的状况下,换行缩进 4 个空格,点号和方法名称一块儿换行
sb.append("zi").append("xin")...
.append("huang")...
.append("huang")...
.append("huang");
反例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的状况下,不要在括号前换行
sb.append("zi").append("xin")...append
("huang");
// 参数不少的方法调用可能超过 120 个字符,不要在逗号前换行
method(args1, args2, args3, ...
,argsX);
正例:
在下例中实参的 “a”,后面必需要有一个空格,
method("a", "b", "c");
正例:
int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();
说明:
增长 sb 这个变量,若是须要对齐,则给 a、b、c 都要增长几个空格,在变量比较多的状况下,是一种累赘的事情。
说明:没有必要插入多个空行进行隔开。
@Override
注解。说明:
getObject()
与 get0bject()
的问题。一个是字母的 O
,一个是数字的 0
,加 @Override
能够准确判断是否覆盖成功。另外,若是在抽象类中对方法签名进行修改,其实现类会立刻编译报错。
说明:
可变参数必须放置在参数列表的最后。(提倡同窗们尽可能不用可变参数编程)
正例:
public User getUsers(String type, Integer... ids) {...}
@Deprecated
注解,并清晰地说明采用的新接口或者新服务是什么。说明:
java.net.URLDecoder 中的方法 decode(String encodeStr)
这个方法已通过时,应该使用双参数 decode(String source, String encode)
。接口提供方既然明确是过期接口,那么有义务同时提供新的接口;做为调用方来讲,有义务去考证过期方法的新实现是什么。
正例:
"test".equals(object);
反例:
object.equals("test");
说明:
推荐使用 java.util.Objects#equals
(JDK7 引入的工具类)
说明:
对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值能够直接使用 == 进行判断,可是这个区间以外的全部数据,都会在堆上产生,并不会复用已有对象,这是一个大坑, 推荐使用 equals 方法进行判断。
说明:
POJO 类属性没有初值是题型使用者在须要使用时,必须本身显式地进行赋值,任何 NPC 问题,或者入库检查,都有使用者来保证。
正例:
数据库的查询结果多是 null,由于自动拆箱,用基本数据类型接收有 NPE 风险。
反例:好比显示成交总额涨跌状况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。因此包装数据类型的 null 值,可以表示额外的信息,如:远程调用失败,异常退出。
反例:
POJO 类的 gmtCreate 默认值为 new Date();
可是这个属性在数据提取时并无置入具体值,在更新其它字段时又附带更新了此字段,致使建立时间被修改为当前时间。
说明:
注意 serialVersionUID 不一致会抛出序列化运行时异常。
说明:
在方法执行抛出异常时,能够直接调用 POJO 的 toString() 方法打印其属性值,便于排查问题。
说明:
String str = "a,b,c,,";
String[] ary = str.split(","); // 预期大于 3,结果是 3
System.out.println(ary.length);
说明:
公有方法是类的调用者和维护者最关心的方法,首屏展现最好:保护方法虽然只是子类关心,也多是 “模板设计模式” 下的核心方法;而私有方法外部通常不须要特别关心,是一个黑盒实现;由于承载的信息价值较低,因此 Service 和 DAO 的 getter / setter 方法放在类体最后。
反例:
public Integer getData() {
if (true) {
return this.data + 100;
} else {
return this.data - 100;
}
}
说明:
反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,而后进行 append 操做,最后经过 toString 方法返回 String 对象,形成内存资源浪费。
反例:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
说明:对象的 clone 方法默认是浅拷贝,若想实现深拷贝须要重写 clone 方法实现属性对象的拷贝。
说明:
任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。
思考:
若是是一个 private 的方法,想删除就删除,但是一个 public 的 service 方法,或者一个 public 的成员变量,删除一下,不得手心冒点汗吗?变量就像本身的小孩,尽可能在本身视野内,变量做用域太大,无限制的处处跑,那么你会担忧的。
说明:
String 重写了 hashCode 和 equals 方法,因此咱们能够很是愉快的使用 String 对象做为 Key 来使用。
说明:
说用 toArray 带参方法,入参分配的数组控件不够大时,toArray 方法内部将从新分配内存空间,并返回新数组地址;若是数组元素大于实际所需,下标为 [list.size()] 的数组元素将被置为 null,其它数组元素保持原值,所以最好将方法入参数组大小与集合元素个数一致。
正例:
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
反例:
直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[] 类,若强转其它类型数组将出现 ClassCastException 错误。
说明:
asList 的返回对象是一个 Arrays 内部类,并无实现集合相关的方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据还是数组。
String[] str = new String[]{"you", "wu"};
List list = Arrays.asList(str);
list.add("yangguangbao");
运行时异常。str[0] = "gujin";
那么 list.get(0)
也会随之修改。正例:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}
反例:
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
说明:
以上代码的执行结果确定会出乎你们的意料,那么试一下那 “1” 换成 “2”,会是一样的结果码?
说明:三个条件以下
反例:
下例中没有处理相等的状况,实际使用中可能会出现异常:
new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() > o2.getId() ? 1 : -1;
}
};
说明:HashMap 使用 HashMap(int initialCapacity) 初始化。
正例:
initialCapacity = (须要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,若是暂时没法肯定初始值大小,请设置为 16(即默认值)。
反例:
HashMap 须要放置 1024 个元素,因为没有设置容量初始值大小,随着元素不断增长,容量 7 次被迫扩大,resize 须要重建 hash 表,严重影响性能。
说明:
keySet 实际上是遍历了 2 次,一次是转为 Iterator 对象,另外一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。若是是 JDK8,使用 Map.foreach 方法。
正例:
values() 返回的是 V 值集合,是一个 list 集合对象了;keySet() 返回的是 K 值集合,是一个 Set 集合对象;entrySet() 返回的是 K - V 值组合集合。
集合类 | Key | Value | Super | 说明 |
---|---|---|---|---|
HashTable | 不容许为 null | 不容许为 null | Dictionary | 线程安全 |
ConcurrentHashMap | 不容许为 null | 不容许为 null | AbstractMap | 锁分段技术(JDK8:CAS) |
TreeMap | 不容许为 null | 容许为 null | AbstractMap | 线程不安全 |
HashMap | 容许为 null | 容许为 null | AbstractMap | 线程不安全 |
反例:
因为 HashMap 的干扰,不少人认为 ConcurrentHashMap 是能够置入 null 值,而事实上,存储 null 值时会抛出 NPE 异常。
说明:
有序性是指遍历结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次序是必定的。如:
集合类 | 有序性 | 稳定性 |
---|---|---|
ArrayList | order | unsort |
HashMap | unorder | unsort |
TreeSet | order | sort |
说明:资源驱动类、工具类、单例工程类都须要注意。
正例:
public class TimerTaskThread extends Thread {
public TimerTaskThread() {
super.setName("TimerTaskThread");
...
}
}
说明:
使用线程池的好处是减小在建立和销毁线程上花的时间以及系统资源的开销,解决资源不足的问题。若是不使用线程池,有可能形成系统建立大量同类线程而致使消耗完内存或者 “过渡切换” 的问题。
说明:Executors 返回的线程池对象弊端以下:
正例:注意线程安全,使用 DateUtils。亦推荐以下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:若是是 JDK8 的应用,可使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
说明:
尽量使加锁的代码块工做量尽量的小,避免在锁代码块中调用 RPC 方法。
说明:
线程一须要对表 A、B、C 依次所有加锁后才能够进行更新操做,那么线程二的加锁顺序也必须是 A、B、C,不然可能出现死锁。
说明:
若是每次访问冲突几率小于 20%,推荐使用乐观锁,不然使用悲观锁。乐观锁的重试次数不得小于 3 次。
说明:注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。
说明:Random 实例包括 java.util.Random 的实例或者 Math.random() 的方式。
正例:在 JDK7 以后,能够直接使用 API ThreadLocalRandom,而在 JDK7 以前,须要编码保证每一个线程持有一个实例。
反例:
class Singleton { java
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized (this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other methods and fields...
}
AtomicInteger count = new AtomicInteger();
count.addAndGet(1);
若是是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减小乐观锁的重试次数)。
if (condition) statements;
if (condition) {
...
return obj;
}
// 接着写 else 的业务逻辑代码;
说明:
若是非得使用 if()...else if()...else...
方式表达逻辑,【强制】避免后续代码维护困难,请勿超过 3 层。
正例:
超过 3 层的 if-else 的逻辑判断代码可使用卫语句、策略模式、状态模式等来实现,其中卫语句示例以下:
public void today() {
if (isBusy()) {
System.out.println(“change time.”); return;
}
if (isFree()) {
System.out.println(“go to travel.”);
return;
}
System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
return;
}
说明:
不少 if 语句内的逻辑至关复杂,阅读者须要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,若是阅读者分析逻辑表达式错误呢?
正例:
// 伪代码以下
final boolean existed = (file.open(fileName, "w") != null) && (...) ||(...);
if (existed) {
...
}
反例:
if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}
/** 内容 */
格式,不得使用 // xxx
方式。说明:
在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 能够正确输出相应注释;在 IDE 中,工程调用方法时,不进入方法便可悬浮提示方法、参数、返回值意义,提升阅读效率。
说明:对子类的实现要求,或者调用注意事项,请一并说明。
//
注释。方法内部多行注释使用 /* */
注释,注意与代码对齐。反例:
“TCP 链接超时” 解释成 “传输控制协议链接超时”,理解反而费脑筋。
说明:
代码与注释更新不一样步,就像路网与导航软件更新不一样步同样,若是导航软件严重滞后,就失去了导航的意义。
说明:
代码被注释掉有两种可能性:
前者若是没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
彻底没有注释的大段代码对于阅读者形同天书,注释是给本身看的,即便隔很长时间,也能清晰理解当时的思路;注解也是给继任者看的,使其可以快速接替本身的工做。
反例:
// put elephant into fridge
put(elephant, fridge);
方法名 put,加上两个有意义的变量名 elephant 和 fridge,已经说明了这是在干什么,语义清晰地代码不须要额外的注释。
说明:不要在方法体内定义:Pattern pattern = Pattern.compile(规则);
说明:
注意若是是 Boolean 包装类对象,优先调用 getXxx() 的方法。
$!{var}
– 中间的感叹号。说明:若是 var = null
或者不存在,那么 ${var}
会直接显示在页面上。
System.currentTimeMillis();
而不是 new Date().getTime();
说明:若是想获取更加精确的纳秒级时间值,使用 System.nanoTime()
的方式。在 JDK8 中,针对统计时间等场景,推荐使用 Instant 类。
说明:根据 MVC 理论,视图的职责是展现,不要抢模型和控制器的活。
说明:对于垃圾代码或过期配置,坚定清理干净,避免程序过渡臃肿,代码冗余。
正例: 对于暂时被注释掉,后续可能回复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///)来讲明注释掉代码的理由。