1. 当成员变量值无需改变时,尽量定义为静态常量
在类的每个对象实例中,每个成员变量都有一份副本,而成员静态变量只有一份实例 反例
public class HttpConnection {
private final long timeout = 5L ;
}
public class HttpConnection {
private static final long TIMEOUT = 5L ;
}
2. 尽量使用基本数据类型,避免自动装箱拆箱和空指针的判断
JVM支持基本数据类型和包装类的自动转换称为自动装箱和拆箱。装箱和拆箱都是需要消耗CPU和内存资源,所以应尽量避免使用自动拆装箱,其次还可以避免方法返回值的空指针判断。 反例
public static Integer isValid ( ) {
Integer sum = 0 ;
int [ ] values = . . . ;
for ( int value : values) {
sum += value;
}
return sum;
}
public static int isValid ( ) {
int sum = 0 ;
int [ ] values = . . . ;
for ( int value : values) {
sum += value;
}
return sum;
}
3. 尽量使用函数内的基本类型临时变量
在函数内,基本类型的参数和临时变量都保存在栈中,访问速度较快;对象类型的参数和临时变量的引用都保存在栈中,内容都保存在堆中,访问速度较慢。在类中,任何类型的成员变量都保存在堆中,访问速度较慢。 反例
public final class Accumulator {
private double result = 0.0D ;
public void addAll ( @NotNull double [ ] values) {
for ( double value : values) {
result += value;
}
}
}
public final class Accumulator {
private double result = 0.0D ;
public void addAll ( @NotNull double [ ] values) {
double sum = 0.0D ;
for ( double value : values) {
sum += value;
}
result += sum;
}
}
4. 尽量不使用反射赋值对象
用反射赋值对象,主要优点是节省了代码量,主要缺点却是性能的下降。 反例
List < UserDO > userDOList = . . . ;
List < UserVO > userVOList = new ArrayList < > ( userDOList. size ( ) ) ;
for ( UserDO userDO : userDOList) {
UserVO userVO = new UserVO ( ) ;
BeanUtils . copyProperties ( userDO, userVO) ;
userVOList. add ( userVO) ;
}
List < UserDO > userDOList = . . . ;
List < UserVO > userVOList = new ArrayList < > ( userDOList. size ( ) ) ;
for ( UserDO userDO : userDOList) {
userVOList. add ( new UserVO (
userDO. getName ( ) ,
userDO. getPhone ( ) ,
userDO. getIcon ( )
) ) ;
}
5. 把跟类成员变量无关的方法声明成静态方法
静态方法的好处是不用生成类的实例就可以直接调用。静态方法不属于对象,而是属于类。只需要通过其类名就可以访问,不需要再消耗资源去反复创建对象。即便在类内部的私有方法,如果没有使用到类成员变量,也应该声明为静态方法。 反例
public int getMonth ( Date date) {
Calendar calendar = Calendar . getInstance ( ) ;
calendar. setTime ( date) ;
return calendar. get ( Calendar . MONTH) + 1 ;
}
public static int getMonth ( Date date) {
Calendar calendar = Calendar . getInstance ( ) ;
calendar. setTime ( date) ;
return calendar. get ( Calendar . MONTH) + 1 ;
}
6. 协议方法参数值非空,避免不必要的空指针判断
协议编程,可以用@NonNull和@Nullable标注参数,是否遵循全凭调用者自觉 反例
public static boolean isValid ( UserDO user) {
if ( Objects . isNull ( user) ) {
return false ;
}
return Boolean . TRUE. equals ( user. getIsValid ( ) ) ;
}
public static boolean isValid ( @NotNull UserDO user) {
return Boolean . TRUE. equals ( user. getIsValid ( ) ) ;
}
7. 尽量减少方法的重复调用
List < UserDO > userList = . . . ;
for ( int i = 0 ; i < userList. size ( ) ; i++ ) {
. . .
}
List < UserDO > userList = . . . ;
int userLength = userList. size ( ) ;
for ( int i = 0 ; i < userLength; i++ ) {
. . .
}
8. 尽量避免不必要的方法调用
List < UserDO > userList = userDAO. queryActive ( ) ;
if ( isAll) {
userList = userDAO. queryAll ( ) ;
}
List < UserDO > userList;
if ( isAll) {
userList = userDAO. queryAll ( ) ;
} else {
userList = userDAO. queryActive ( ) ;
}
9. 尽量使用位移来代替正整数乘除
用位移操作可以极大地提高性能。对于乘除2^n(n为正整数)的计算,可以用位移操作来代替。 反例
int num1 = a * 4 ;
int num2 = a / 4 ;
int num1 = a << 2 ;
int num2 = a >> 2 ;
10. 对于多常量选择分支,尽量使用 switch 语句而不是 if-else 语句
if-else语句,每个if条件都要加装计算,直到if条件语句为true为止。switch语句进行了跳转优化,对于多常量选择分支处理效率更高。经过经验证明:在每个分支出现概率相同的情况下,低于5个分支时if-else语句效率更高,高于5个时switch语句效率更高。 注意如果业务复杂,可以采用Map实现策略模式。
11. 不要使用 “”+ 转化字符串
使用""+进行字符串转化,使用方便但是效率低,建议使用String.valueOf,效率高还优雅。 反例
int i = 12345 ;
String s = "" + i;
int i = 12345 ;
String s = String . valueOf ( i) ;
12. 尽量使用 Arrays.asList 转化数组为列表
public void demo ( ) {
List < String > typeList = new ArrayList < > ( 8 ) ;
typeList. add ( "Short" ) ;
typeList. add ( "Long" ) ;
typeList. add ( "Integer" ) ;
String [ ] names = . . . ;
List < String > nameList = . . . ;
for ( Sting name : names) {
nameList. add ( name) ;
}
}
public void demo ( ) {
List < String > typeList = new ArrayList < > ( 8 ) ;
typeList. add ( "Short" ) ;
typeList. add ( "Long" ) ;
typeList. add ( "Integer" ) ;
String [ ] names = . . . ;
List < String > nameList = . . . ;
nameList. addAll ( Arrays . asList ( names) ) ;
}
13. 直接迭代需要使用的集合
直接迭代需要使用的集合,无需通过其他操作获取数据 反例
public void demo ( ) {
Map < Long , UserDO > userMap = . . . ;
for ( Long userId : userMap. keySet ( ) ) {
UserDO user = userMap. get ( userId) ;
}
}
public void demo ( ) {
Map < Long , UserDO > userMap = . . . ;
for ( Map. Entry < Long , UserDO > userEntry : userMap. entrySet ( ) ) {
Long userId = userEntry. getKey ( ) ;
UserDO user = userEntry. getValue ( ) ;
}
}
14. 不要使用 size 方法检测空,必须使用 isEmpty 方法测空
使用size方法上来检测空逻辑上没有问题,但使用isEmpty方法使得代码更易读,并且可以获得更好的性能。任何isEmpty方法实现的时间复杂度都是O(1),但是某些size方法实现的时间复杂度有可能是O(n)。 反例
public void demo ( ) {
List < UserDO > userList = . . . ;
if ( userList. size ( ) == 0 ) {
. . .
}
Map < Long , UserDO > userMap = . . . ;
if ( userMap. size ( ) == 0 ) {
. . .
}
}
public void demo ( ) {
List < UserDO > userList = . . . ;
if ( userList. isEmpty ( ) ) {
. . .
}
Map < Long , UserDO > userMap = . . . ;
if ( userMap. isEmpty ( ) ) {
. . .
}
}
15. 非随机访问的 List,尽量使用迭代代替随机访问
public void demo ( ) {
LinkedList < UserDO > userDOLinkedList = . . . ;
int size = userDOLinkedList. size ( ) ;
for ( int i = 0 ; i < size; i++ ) {
UserDO userDO = userDOLinkedList. get ( i) ;
}
}
public void demo ( ) {
LinkedList < UserDO > userDOLinkedList = . . . ;
int size = userDOLinkedList. size ( ) ;
for ( UserDO userDO : userDOLinkedList) {
. . .
}
}
16. 尽量使用 HashSet 判断值存在
在Java集合类库中,List的contains方法普遍时间复杂度是O(n),而HashSet的时间复杂度为O(1)。如果需要频繁调用contains方法查找数据,可以先将List转换成HashSet。 反例
public void demo ( ) {
List < Long > adminList = . . . ;
List < UserDO > userDOList = . . . ;
List < UserVO > userVOList = new ArrayList < > ( userDOList. size ( ) ) ;
for ( UserDO userDO : userDOList) {
if ( adminList. contains ( userDO. getId ( ) ) ) {
userVOList. add ( transUser ( userDO) ) ;
}
}
}
public void demo ( ) {
Set < Long > adminList = . . . ;
List < UserDO > userDOList = . . . ;
List < UserVO > userVOList = new ArrayList < > ( userDOList. size ( ) ) ;
for ( UserDO userDO : userDOList) {
if ( adminList. contains ( userDO. getId ( ) ) ) {
userVOList. add ( transUser ( userDO) ) ;
}
}
}
17. 避免先判断存在再进行获取
如果需要先判断存在再进行获取,可以直接获取并判空,从而避免了二次查找操作。 反例
public static UserVO transUser ( UserDO user, Map < Long , RoleDO > roleMap) {
UserVO userVO = new UserVO ( ) ;
userVO. setId ( user. getId ( ) ) ;
if ( roleMap. containsKey ( user. getRoleId) ) {
userVO. setRole ( transRole ( role) ) ;
}
}
public static UserVO transUser ( UserDO user, Map < Long , RoleDO > roleMap) {
UserVO userVO = new UserVO ( ) ;
userVO. setId ( user. getId ( ) ) ;
RoleDO role = roleMap. get ( user. getRoleId ( ) ) ;
if ( Objects . nonNull ( role) ) {
userVO. setRole ( transRole ( role) ) ;
}
}
18. 直接捕获对应的异常
直接捕获对应的异常,避免用 instanceof 判断,效率更高,代码更简洁。 反例
public void demo ( ) {
try {
saveData ( ) ;
} catch ( Exception e) {
if ( e instanceof IOException ) {
log. error ( "保存数据IO异常" , e) ;
} else {
log. error ( "保存数据其他异常" , e) ;
}
}
}
public void demo ( ) {
try {
saveData ( ) ;
} catch ( IOException e) {
log. error ( "保存数据IO异常" , e) ;
} catch ( Exception e) {
log. error ( "保存数据其他异常" , e) ;
}
}
19. 尽量使用线程池减少线程开销
多线程中两个必要的开销:线程的创建和上下文切换。采用线程池,可以尽量避免这些开销。
21. 工具类中屏蔽构造函数
工具类是一堆静态字段和函数的集合,其不应该被实例化;但是,Java为每个没有明确定义构造函数的类添加了一个隐式公有构造函数,为了避免不必要的实例化,应该显示定义私有构造函数来屏蔽这个隐式公有构造函数。
22. 返回空数组和集合而非 null
如果返回null,需要进行结果检查,否则报空异常;返回空数组或者空集合,有效避免了由于未检测null而抛出空指针的情况,还可以删除调用方检测null的语句使代码更简洁。 反例
public static List < Result > getResultList ( ) {
return null ;
}
public static List < Result > getResultList ( ) {
return Collections . EMPTY_LIST;
}
23. 优先使用常量或者确定值调用 equals 方法
对象调用equals方法容易抛空指针,应该使用常量或者确定值来调用equals方法。 反例
private static boolean fileReader ( String fileName) {
return fileName. equals ( "Java开发手册" ) ;
}
private static boolean fileReader ( String fileName) {
return "Java开发手册" . equals ( fileName) ;
}
24. 使用通用工具函数 Objects.equals()
thisName != null && thisName. equals ( name)
thisName == name || ( thisName != null && thisName. equals ( name) )
Objects . equals ( name, thisName)
25. 使用通用工具函数 CollectionUtil.isEmpty()
list != null && list. isEmpty ( )
CollectionUtils . isNotEmpty ( list)