编程规约
日期时间
-
【强制】日期格式化时,传入
pattern
中表示年份统一使用小写的y
。
说明:日期格式化时,yyyy
表示当天所在的年,而大写的YYYY
代表是week in whichyear
,意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的YYYY
就是下一年。
正例:表示日期和时间的格式如下所示:java new SimpleDateFormat("yyy-MM-dd HH:mm:ss")
反例:某程序员因使用
YYYY/MM/dd
进行日期格式化,2017/12/31
执行结果为2018/12/31
,造成线上故障。 -
【强制】在日期格式中分清楚大写的
M
和小写的m
,大写的H
和小写的h
分别指代的意义。
说明:日期格式中的这两对字母表意如下:
(1)表示月份是大写的M
(2)表示分钟则是小写的m
(3)24小时制的是大写的H
(4)12小时制的则是小写的h
-
【强制】获取当前毫秒数:
System.currentTimeMilli()
;而不是new Date().getTime()
。
说明:获取纳秒级时间,则使用System.nanoTime
的方式。在JDK8
中,针对统计时间等场景,推荐使用Instant类。 -
【强制】不允许在程序任何地方中使用:
(1)java.sgl.Date
(2)java.sql.Time
(3)java.sql.Timestamp
说明:第1个不记录时间,getHours()
抛出异常;第2个不记录日期,getYear()
抛出异常;第3个在构造方法super((time/1000)*1000)
,在Timestamp
属性fastTime
和nanos
分别存储秒和纳秒信息。
反例:java.util.Date.after(Date)
进行时间比较时,当入参是java.sql.Timestamp
时,会触发JDK BUG
(JDK9已修复),可能导致比较时的意外结果。 -
【强制】禁止在程序中写死一年为365天,避免在公历闰年时出现日期转换错误或程序逻辑错误。
正例://获取指定某年的天数 LocalDate.of(2011,1,1).lengthOfYear(); ``` <font color =A52A2A>反例:</font> ```java //第一种情况:在闰年366天时,出现数组越界异常 int[] dayArray = new int[365]; //第二种情况:一年有效期的会员制,2020年1月26日注册,硬编码365返回的却是2021年1月25日 Calendar calendar = Calendar.getInstance(); calendar.set(2020, 1, 26); calendar add(Calendar DATE, 365); ```
-
【推荐】使用枚举值来指代月份。如果使用数字,注意
Date
,Calendar
等日期相关类的月份month
取值范围从0到11之间。
说明:参考JDK原生注释,Month value is 0-based.e.g.,0 for January.
正例:Calendar.JANUARY,Calendar.FEBRUARY,Calendar.MARCH
等来指代相应月份来进行传参或比较。
集合处理
-
【强制】关于
hashCode
和equals
的处理,遵循如下规则:
(1)只要覆写equals
,就必须覆写hashCode
。
(2)因为Set
存储的是不重复的对象,依据hashCode
和equals
进行判断,所以Set
存储的对象必须覆写这两种方法。
(3)如果自定义对象作为Map
的键,那么必须覆写hashCode
和equals
。
说明:String因为覆写了hashCode和equals方法,所以可以愉快地将String对象作为key来使用。 -
【强制】判断所有集合内部的元素是否为空,使用
isEmpty()
方法,而不是size() == 0
的方式。
说明:在某些集合中,前者的时间复杂度为O(1)
,而且可读性更好。
正例:Map<String, Object>map = new HashMap<>(16); if (map.isEmpty() { System.out.println("no element in this map."); }
-
【强制】
Collections
类返回的对象,如:emptyList()/singletonList()
等都是immutable list
,不可 对其进行添加或者删除元素的操作。
反例:如果查询无结果,返回Collections.emptyList()
空集合对象,调用方一旦在返回的集合中进行了添加元素的操作,就会触发UnsupportedOperationException
异常。 -
【强制】使用集合转数组的方法,必须使用集合的
toArray(T[] array)
,传入的是类型完全一致、长度为0的空数组。 反例:直接使用toAray无参方法存在问题,此方法返回值只能是Object[]
类,若强转其它类型数组将出现ClassCastException
错误。
正例:list.add("bao"); String[] array = list.toArray(new String[0]); ``` <font color =#CD853F>说明:</font>使用`toArray`带参方法,数组空间大小的`length`: (1)等于`0`,动态创建与`size`相同的数组,性能最好。 (2)大于`0`但小于`size`,重新创建大小等于`size`的数组,增加GC负担。 (3)等于`size`,在高并发情况下,数组创建完成之后,`size`正在变大的情况下,负面影响与`2`相同。 (4)大于`size`,空间浪费,且在`size`处插入`null`值,存在`NPE`隐患。
-
【强制】使用工具类
Arrays.asList()
把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear
方法会抛出
UnsupportedOperationException
异常。
说明:asList
的返回对象是一个Arrays
内部类,并没有实现集合的修改方法。Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。list = Arrays.asList(str); ``` 第一种情况:`list.add("yangguanbao");`运行时异常。 第二种情况:`str[0] ="change"`;`list`中的元素也会随之修改,反之亦然。
-
【强制】泛型通配符
<?extends T>
来接收返回的数据,此写法的泛型集合不能使用add
方法,而<?super T>
不能使用get
方法,两者在接口调用赋值的场景中容易出错。
说明:扩展说一下PECS(Producer Extends Consumer Super) 原则,即频繁往外读取内容的,适合用<?extends T>
,经常往里插入的,适合用<?super T>
-
【强制】在无泛型限制定义的集合赋值给泛型限制的集合时,在使用集合元素时,需要进行
instanceof
判断,避免抛出ClassCastException
异常。
说明:泛型是在JDK5后才出现,考虑到向前兼容,编译器是允许非泛型集合与泛型集合互相赋值。
反例:ArrayList(10); notGenerics.add(new Object()); notGenerics.add(new Integer(1)); generics = notGenerics; //此处抛出 ClassCastException异常 String string = generics.get(0); ```
-
【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用iterator方式,如果并发操作,需要对 iterator 对象加锁。
正例:list.add("2"); Iterator<String>iterator = list.iterator(); while (iterator hasNext() { String item = iterator next(); if(删除元素的条件) { iterator remove(); } ``` <font color =A52A2A>反例:</font> ```java for(String item :list) { if ("1".equals(item)) { listremove(item); } } ``` <font color =#CD853F>说明:</font>反例中的执行结果肯定会出乎意料,试一下把"1"换成"2"。
-
【强制】在
JDK7
版本及以上,Comparator
实现类要满足如下三个条件,不然Arrays.sort
,
Collections.sort
会抛IllegalArgumentException
异常。
说明:三个条件如下:
(1)x,y
的比较结果和y,x
的比较结果相反。
(2)x > y
,y > z
,则x > z
。
(3)x = y
,则x,z
比较结果和y,z
比较结果相同。
反例:下例中没有处理相等的情况,交换两个对象判断结果并不互反,不符合第一个条件,在实际使用中可能会出现异常。compare(Student o1, Student o2) { return o1.getld() > o2.getld() ? 1 : -1; } }; ```
-
【推荐】泛型集合使用时,在
JDK7
及以上,使用diamond
语法或全省略。
说明:菱形泛型,即diamond
,直接使用<>
来指代前边已经指定的类型。
正例:// diamond方式,即<> HashMap<String, String> userCache = new HashMap<>(16); //全省略方式 ArrayList<User> users = new ArrayList(10);
-
【推荐】集合初始化时,指定集合初始值大小。
说明:HashMap
使用构造方法HashMap(int initialCapacity)
进行初始化时,如果暂时无法确定集合大小,那么指定默认值(16)即可。
正例:initialCapacity =(需要存储的元素个数/负载因子)+1。注意负载因子(即loaderfactor)默认为0.75,如果暂时无法确定初始值大小,请设置为16(即默认值)。
反例:HashMap
需要放置1024个元素,由于没有设置容量初始大小,随着元素增加而被迫不断扩容,resize()
方法总共会调用8次,反复重建哈希表和数据迁移。当放置的集合元素个数达千万级时会影响程序性能。 -
【推荐】使用
entrySet
遍历Map
类集合KV
,而不是keySet
方式进行遍历。
说明:keySet
其实是遍历了2次,一次是转为Iterator
对象,另一次是从hashMap
中取出key
所对应的value
。而entrySet
只是遍历了一次就把key
和value
都放到了entry
中,效率更高。如果是JDK8
,使用Map.forEach
方法。
正例:values()
返回的是V
值集合,是一个list
集合对象;keySet
(返回的是K
值集合,是一个Set
集合对象;entrySet()
返回的是K-V
值组合的Set
集合。 -
【参考】合理利用好集合的有序性(
sort
)和稳定性(order
),避免集合的无序性(unsort
)和不稳定性(unorder
)带来的负面影响。
说明:有序性是指遍历的结果是按某种比较规则依次排列的,稳定性指集合每次遍历的元素次序是一定的。如:ArrayList 是 order / unsort; HashMap 是 unorder / unsor; TreeSet 是 order / sort;
-
【参考】利用
Set
元素唯一的特性,可以快速对一个集合进行去重操作,避免使用List
的contains()
进行遍历去重或者判断包含操作。