摘自《阿里巴巴Java开发手册》,只整理认为须要注意的部分html
推荐使用String[] args
,明确声明了参数类型为String[]
,而String args[]
只声明为String
java
抽象类(不是接口)命名用Abstract/ Base开头,异常类名以Exception结尾
AbstractUserService BaseUserService BusinessException
程序员
Reason:部分框架解析错误
反例:boolean isSuccess
定义为基本数据类型boolean isSuccess;
的属性,它的方法也是isSuccess()
,RPC框架在反向解析的时候,“觉得”对应的属性名称是success,致使属性获取不到,进而抛出异常。正则表达式
类名带上Enum后缀
成员名称大小,下划线分隔
正例:枚举名字:DealStatusEnum;成员名称:SUCCESS / UNKOWN_REASON数据库
getXxx
listXxx
countXxx
saveXxx
/ insertXxx
removeXxx
/ deleteXxx
updateXxx
反例:String key="Id#taobao_"+tradeId;
编程
必须大写
正例:Long a = 2L
反例:Long a = 2l
windows
if/for/while/switch/do等保留字与左右括号之间都必须加空格设计模式
相对上一行缩进一个tab
运算符,点(.)符号与下文一块儿换行
括号前,逗号不换行
正例:api
sb.append("zi").append("xin")…
.append("huang")……
+ aaa + …;
复制代码
反例:数组
sb.append("zi"). //.号
append("xin"). append
("huang"); //括号前不换行
//参数不少的方法调用也超过120个字符,逗号后才是换行处
method(args1, args2, args3, ...
, argsX);
复制代码
换行符使用Unix格式,不要使用windows格式
系统 | 换行符 |
---|---|
Windows | \n\r |
Unix | \n |
Mac | \r |
直接类名访问
避免经过对象引用访问,无谓增长编译器解析成本
覆写方法加@Override注解,编译检查,能够判断是否覆盖成功
原则上不容许修改,接口过期使用@Deprecated注解,并注明新接口是什么
使用工具类java.util.Objects.equals(a, b);
包装类务必使用equals方法
对于Integer var=?
在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值能够直接使用==进行判断,可是这个区间以外的全部数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。
Long short类型也是如此
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
复制代码
全部的POJO类属性必须使用包装数据类型
RPC(远程过程调用协议)方法的返回值和参数必须使用包装数据类型
全部的局部变量推荐使用基本数据类型
序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;
若是彻底不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值
注意serialVersionUID不一致会抛出序列化运行时异常
构造方法里面禁止加入任何业务逻辑,若是有初始化逻辑,请放在init方法
都加上toString()
,若有继承,前面加上super.toString()
抛出异常时能够调用toString方法打印属性值,方便排查问题
ArrayList的subList结果不可强转成ArrayList
subList 返回的是 ArrayList 的内部类 SubList,并非 ArrayList ,而是 ArrayList 的一个视图,对于SubList子列表的全部操做最终会反映到原列表上
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
复制代码
private class SubList extends AbstractList<E> implements RandomAccess 复制代码
Arrays.asList()
返回的也是Arrays内部类,实际上后台数据仍为数组
在subList场景中,高度注意对原集合元素个数的修改,会致使子列表的遍历、增长、删除均产生ConcurrentModificationException 异常
Arraylist中有成员变量
protected transient int modCount = 0;
当在原集合中修改元素个数后(结构性修改structurally modified,增删元素,更新元素就不会),modCount++
,记录下修改的次数
而在subList,因为记录下的modCount仍是原来的modCount,与原集合的没有同步,就会抛出异常 另,若在sublist中有结构性修改,那就能够反映到原集合中,不会报错,反之就不行
转数组使用toArray(T[] array)
若使用toArray()
,返回的Object[] 类型,在作转型时可能出现ClassCastException
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
//指定size,不然默认返回的多余数组值为null
String[] array = new String[list.size()];
array = list.toArray(array);
复制代码
泛型通配符<? extends T>
来接收返回的数据,此写法的泛型集合不能使用add方法。 说明:苹果装箱后返回一个<? extends Fruits>
对象,此对象就不能往里加任何水果,包括苹果。 增删集合元素
不要在foreach循环里进行元素的remove/add操做,能够用下标值/ Iterator增删
反例:
List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) {
if("1".equals(temp)){
a.remove(temp);
}
}
复制代码
正例: 单线程环境
Iterator<String> it = a.iterator();
while(it.hasNext()){
String temp = it.next();
if(删除元素的条件){
it.remove(); //not a.remove();
}
}
复制代码
多线程:CopyOnWriteArrayList,CopyOnWriteArraySet,每次add,remove等全部的操做都是从新建立一个新的数组,再把引用指向新的数组。这样即可实现线程安全,也不需modCount域,因此对其进行remove和add不会抛出并发异常
Iterator 中内部类实现
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
复制代码
数组类以插入元素数量指定大小,map set类型则需比实际数量稍大一点(maybe 10%)
what-is-the-optimal-capacity-and-load-factor-for-a-fixed-size-hashmap
集合类 | Key | Value | Super | 说明 |
---|---|---|---|---|
Hashtable | not null | not null | Dictionary | 线程安全 |
ConcurrentHashMap | not null | not null | AbstractMap | 线程局部安全 |
TreeMap | not null | null | AbstractMap | 线程不安全 |
HashMap | null | null | AbstractMap | 线程不安全 |
获取单例对象要线程安全。在单例对象里面作操做也要保证线程安全。 http://blog.csdn.net/cselmu9/article/details/51366946
线程资源必须经过线程池提供,不容许在应用中自行显式建立线程;
使用线程池的好处是减小在建立和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。若是不使用线程池,有可能形成系统建立大量同类线程而致使消耗完内存或者“过分切换”的问题
http://www.cnblogs.com/dolphin0520/p/3932921.html
SimpleDateFormat 是线程不安全的类,通常不要定义为static变量,若是定义为static,必须加锁,或者使用自定义的DateUtils工具类。
正例:注意线程安全,使用DateUtils。亦推荐以下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
复制代码
https://my.oschina.net/leejun2005/blog/152253
高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁
并发修改同一记录时,避免更新丢失,要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version做为更新依据。
若是每次访问冲突几率小于20%,推荐使用乐观锁,不然使用悲观锁。乐观锁的重试次数不得小于3次。
乐观锁:每次读取时认为数据不会修改,读写不加锁,更新时查看版本号是否一致
悲观锁:每次读取数据时认为数据可能会被修改,故读写加锁
多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。
让写的同窗更加明确线程池的运行规则,规避资源耗尽的风险。
Executors各个方法的弊端:
1) newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费很是大的内存,甚至OOM
2) newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会建立数量很是多的线程,甚至OOM
方便出错时回溯
使用CountDownLatch进行异步转同步操做,每一个线程退出前必须调用countDown方法,线程执行代码注意catch异常,确保countDown方法能够执行,避免主线程没法执行至countDown方法,直到超时才返回结果 注意,子线程抛出异常堆栈,不能在主线程try-catch到。
The "Double-Checked Locking is Broken" Declaration
freish.iteye.com/blog/100830…
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
复制代码
在多线编程环境下,因为没有同步,就可能致使hashmap resize时出现死循环,
https://my.oschina.net/hosee/blog/673521
【参考】ThreadLocal没法解决共享对象的更新问题,ThreadLocal对象建议使用static修饰。这个变量是针对一个线程内全部操做共有的,因此设置为静态变量,全部此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,全部此类的对象(只要是这个线程内定义的)均可以操控这个变量。
在一个switch块内,都必须包含一个default语句而且放在最后,即便它什么代码也没有
Condition 条件尽可能简单,复杂则抽出 尽可能少用else,if-else的方式能够改写成
if(condition){
…
return obj;
}
// 接着写else的业务逻辑代码;
复制代码
若是使用要if-else if-else方式表达逻辑,【强制】请勿超过3层,超过请使用状态设计模式
每一个枚举字段都需注释,说明每一个数据项用途
在使用正则表达式时,利用好其预编译功能
不要在方法体内定义:Pattern pattern = Pattern.compile(规则);
避免用Apache Beanutils进行属性的copy,Apache BeanUtils性能较差,可使用其余方案好比Spring BeanUtils, Cglib BeanCopier
使用System.currentTimeMillis();
而不是new Date().getTime();
在JDK8中,针对统计时间等场景,推荐使用Instant类
不要捕获Java类库中定义的继承自RuntimeException的运行时异常类,如:IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。
正例:if(obj != null) {...}
反例:try { obj.method() } catch(NullPointerException e){…}
只try catch最小非稳定代码,并区分异常类型,分别处理,或者传给上层业务使用者,业务使用者必须处理异常,将其转化为用户能够理解的内容
不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块中的return语句
当在try块或catch块中遇到return语句时,finally语句块将在方法返回以前被执行。在如下4种特殊状况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()
退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
方法的返回值能够为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么状况下会返回null值。
调用方须要进行null判断防止NPE问题
集合里的元素即便isNotEmpty,取出的数据元素也可能为null
公司外的http/api开放接口必须使用“错误码”;
应用内部推荐异常抛出
日志文件推荐至少保存15天,由于有些异常具有以“周”为频次发生的特色
异常信息应该包括两类信息:案发现场信息和异常堆栈信息。若是不处理,那么往上抛
正例:logger.error(各种参数或者对象toString + "_" + e.getMessage(), e);
使用is_xxx
的方式命名,数据类型是unsigned tinyint
( 1表示是,0表示否)
任何字段若是为非负数,必须是unsigned
小数类型为decimal,禁止使用float和double,会损失精度
若是存储的数据范围超过decimal的范围,建议将数据拆成整数和小数分开存储
表中字段储存长度过大,为text类型,独立出来,用主键对应,避免影响其余字段索引效率
业务上具备惟一特性的字段,即便是组合字段,也必须建成惟一索引
在varchar字段上创建索引时,必须指定索引长度,不必对全字段创建索引,根据实际文本区分度决定索引长度
索引的长度与区分度是一对矛盾体,通常对字符串类型数据,长度为20的索引,区分度会高达90%以上,可使用count(distinct left(列名, 索引长度))/count(*)
的区分度来肯定。
若是有order by的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,而且放在索引组合顺序的最后,避免出现file_sort的状况,影响查询性能
正例:where a=? and b=? order by c;
索引:a_b_c
反例:索引中有范围查找,那么索引有序性没法利用,如:WHERE a>10 ORDER BY b;
索引a_b没法排序。
使用count(*)
,不使用count(列名)
方式,count(*)
会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行
count(distinct col)
计算该列除NULL以外的不重复数量
注意 count(distinct col1, col2)
若是其中一列全为NULL,那么即便另外一列有不一样的值,也返回为0。
禁止使用存储过程,存储过程难以调试和扩展,更没有移植性
TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发trigger,有可能形成事故,故不建议在开发代码中使用此语句。
说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。
使用:#{}
,#param#
不要使用${}
此种方式容易出现SQL注入
<isEqual>
中的compareValue是与属性值对比的常量,通常是数字,表示相等时带上此条件;
<isNotEmpty>
表示不为空且不为null时执行;
<isNotNull>
表示不为null值时执行。