1.任何布尔类型的变量,都不要加is。否则部分框架解析会引起序列化错误。
反例:boolean isSuccess,它的方法是isSuccess()。RPC框架在反向解析的时候,以为对应的属性名称是success。导致属性获取不到,进而拋出异常。
2.不要使用一个常量维护所有常量,应该按常量功能进行归类,分开维护。如:缓存相关的常量放在类CacheConsts下,系统配置相关的常量放在类:ConfigConsts下。
说明:大而全的常量类,不利于理解,也不利于维护。
3.所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。
说明:对于Integer var = ?在-128至127之间的赋值,是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象。推荐使用equals方法
4.final可提高程序的响应效率,声明为final的情况:
不需要重新赋值的变量,包括类属性、局部变量
对象参数前加final,表示不允许修改引用的指向
类方法确定不允许被重写
5.Map/Set的key为自定义对象时,必须重写hashCode和equals。String重写了hashCode和equals方法,所以可以非常愉快地使用String对象作为key来使用。
6.使用集合转数据的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。
直接使用toArray无参方法存在问题,此方法返回值只能是Object[]类,若强转其它类型数组将出现ClassCastException错误。
7.使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法。它的add/remove/clear方法会拋出UnsupportedOperationException异常。
说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] {“a”,”b”};
List list = Arrays.asList(str);
第一种情况,list.add(“c”);运行时异常
第二种情况,str[0]=“gujin”;那么list.get(0)也会随之修改
8.不要在foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
9.在JDK7版本以上,Comparator要满足自反性,传递性,对称性,不然Arrays.sort,Collections.sort会报IllegalArgumentException异常。
自反性:x,y的比较结果和y,x的比较结果相反
传递性:x>y,y>z
对称性:x=y,则x,z比较结果和y,z比较结果相同
反例:下例中没有处理相等的情况,实际使用中可能出现异常:
new Comparator<Student>(){
public int compare(Student o1,Student o2){
return o1.getId()>o2.getId()?1:-1;
}
}
10.集合初始化时,尽量指定初始化大小。如:ArrayList
11.使用entrySet遍历Map类集合KV,而不是使用keySet方式进行遍历。
说明:keySet其实遍历了2次.一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法
12.利用Set元素唯一的特性,可以快速对另一个集合进行去重操作,避免使用List的contains方法进行遍历去重操作。
14.获取单例对象要线程安全,在单例对象里面做操作也要保证线程安全。
15.线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存。
16.SimpleDateFormat是线程不安全的类,注意线程安全:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
protected DateFormat initialValue(){
return new SimpleDateFormat(“yyyy-MM-dd");
}
}
17.高并发时,同步调用应该去考量锁的性能损耗,能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体:能用对象锁,就不要用类锁。
18.多线程并行处理定时任务,Timer运行多个TimeTask时,只要其中之一没有捕获拋出的异常,其它任务便会自动终止运行。使用ScheduledExecutorService则没有这个问题。
19.线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则。
Executors各个方法弊端:
newFixedThreadPool和newSingleThreadExecutor
主要问题是堆积的请求处理队列可能耗费非常大的内存。
newCachedThreadPool,newScheduledThreadPool
主要问题是线程数最大数为Integer.MAX_VALUE,可能会创建数量非常多的线程。
20.创建线程或线程池时请指定有意义的线程名称,方便出错回溯。
public class TimerTaskThread extends Thread{
public TimerTaskThread(){
super.setName(“TimerTaskThread");
}
}
21.使用countDownLatch进行异步转同步操作,每个线程退出前必须调用countDown方法,线程执行代码注意catch异常,确保countDown方法可以执行,避免主线程无法执行至countDown方法,直到超时才返回结果。
22.避免Random实例被多线程使用。虽然共享该实例是线程安全的,但会因竞争同一seed导致的性能下降。
23.volatile解决多线程内存不可见问题,对于一写多读,是可以解决变量同步问题。但是如果多写,同样无法解决线程安全问题。如果想取回count++数据,使用如下实现:
AtomicInteger count = new AtomicInteger();count.addAndGet(1);count++
24.循环体内中的语句要注意性能,以下操作尽量移至循环体外处理,如定义对象,变量,获取数据库连接,进行不必要的try-catch操作。
25.大量的输出无效日志,不利于系统性能提升,也不利于快速定位错误点,记录日志请思考:这些日志真的有人看吗?