国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Java工程師該如何編寫高效代碼?

阿里妹導(dǎo)讀:世界上只有兩種物質(zhì):高效率和低效率;世界上只有兩種人:高效率的人和低效率的人?!挷{

同理,世界上只有兩種代碼:高效代碼和低效代碼;世界上只有兩種人:編寫高效代碼的人和編寫低效代碼的人。如何編寫高效代碼,是每個研發(fā)團(tuán)隊都面臨的一個重大問題。
本文作者根據(jù)實際經(jīng)驗,查閱了大量資料,總結(jié)了'Java高效代碼50例',讓每一個Java程序員都能編寫出'高效代碼'。

1.常量&變量

1.1.直接賦值常量值,禁止聲明新對象

直接賦值常量值,只是創(chuàng)建了一個對象引用,而這個對象引用指向常量值。

反例:


Long i = new Long(1L);String s = new String('abc');

正例:


Long i = 1L;String s = 'abc';


1.2.當(dāng)成員變量值無需改變時,盡量定義為靜態(tài)常量

在類的每個對象實例中,每個成員變量都有一份副本,而成員靜態(tài)常量只有一份實例。

反例:




public class HttpConnection { private final long timeout = 5L; ...}

正例:




public class HttpConnection {    private static final long TIMEOUT = 5L;    ...}

1.3.盡量使用基本數(shù)據(jù)類型,避免自動裝箱和拆箱

Java 中的基本數(shù)據(jù)類型double、float、long、int、short、char、boolean,分別對應(yīng)包裝類Double、Float、Long、Integer、Short、Character、Boolean。JVM支持基本類型與對應(yīng)包裝類的自動轉(zhuǎn)換,被稱為自動裝箱和拆箱。裝箱和拆箱都是需要CPU和內(nèi)存資源的,所以應(yīng)盡量避免使用自動裝箱和拆箱。

反例:





Integer sum = 0;int[] values = ...;for (int value : values) { sum = value; // 相當(dāng)于result = Integer.valueOf(result.intValue() value);}

正例:





int sum = 0;int[] values = ...;for (int value : values) {    sum  = value;}

1.4.如果變量的初值會被覆蓋,就沒有必要給變量賦初值

反例:






List<UserDO> userList = new ArrayList<>();if (isAll) { userList = userDAO.queryAll();} else { userList = userDAO.queryActive();}

正例:






List<UserDO> userList;if (isAll) {    userList = userDAO.queryAll();} else {    userList = userDAO.queryActive();}

1.5.盡量使用函數(shù)內(nèi)的基本類型臨時變量

在函數(shù)內(nèi),基本類型的參數(shù)和臨時變量都保存在棧(Stack)中,訪問速度較快;對象類型的參數(shù)和臨時變量的引用都保存在棧(Stack)中,內(nèi)容都保存在堆(Heap)中,訪問速度較慢。在類中,任何類型的成員變量都保存在堆(Heap)中,訪問速度較慢。

反例:









public final class Accumulator { private double result = 0.0D; public void addAll(@NonNull double[] values) { for(double value : values) { result = value; } } ...}

正例:











public final class Accumulator {    private double result = 0.0D;    public void addAll(@NonNull double[] values) {        double sum = 0.0D;        for(double value : values) {            sum  = value;        }        result  = sum;    }    ...}

1.6.盡量不要在循環(huán)體外定義變量

在老版JDK中,建議“盡量不要在循環(huán)體內(nèi)定義變量”,但是在新版的JDK中已經(jīng)做了優(yōu)化。通過對編譯后的字節(jié)碼分析,變量定義在循環(huán)體外和循環(huán)體內(nèi)沒有本質(zhì)的區(qū)別,運(yùn)行效率基本上是一樣的。

反而,根據(jù)“ 局部變量作用域最小化 ”原則,變量定義在循環(huán)體內(nèi)更科學(xué)更便于維護(hù),避免了延長大對象生命周期導(dǎo)致延緩回收問題 。

反例:









UserVO userVO;List<UserDO> userDOList = ...;List<UserVO> userVOList = new ArrayList<>(userDOList.size());for (UserDO userDO : userDOList) { userVO = new UserVO(); userVO.setId(userDO.getId()); ... userVOList.add(userVO);}

正例:








List<UserDO> userDOList = ...;List<UserVO> userVOList = new ArrayList<>(userDOList.size());for (UserDO userDO : userDOList) {    UserVO userVO = new UserVO();    userVO.setId(userDO.getId());    ...    userVOList.add(userVO);}

1.7.不可變的靜態(tài)常量,盡量使用非線程安全類

不可變的靜態(tài)常量,雖然需要支持多線程訪問,也可以使用非線程安全類。

反例:







public static final Map<String, Class> CLASS_MAP;static { Map<String, Class> classMap = new ConcurrentHashMap<>(16); classMap.put('VARCHAR', java.lang.String.class); ... CLASS_MAP = Collections.unmodifiableMap(classMap);}

正例:







public static final Map<String, Class> CLASS_MAP;static {    Map<String, Class> classMap = new HashMap<>(16);    classMap.put('VARCHAR', java.lang.String.class);    ...    CLASS_MAP = Collections.unmodifiableMap(classMap);}


1.8.不可變的成員變量,盡量使用非線程安全類

不可變的成員變量,雖然需要支持多線程訪問,也可以使用非線程安全類。

反例:


















@Servicepublic class StrategyFactory implements InitializingBean { @Autowired private List<Strategy> strategyList; private Map<String, Strategy> strategyMap; @Override public void afterPropertiesSet() { if (CollectionUtils.isNotEmpty(strategyList)) { int size = (int) Math.ceil(strategyList.size() * 4.0 / 3); Map<String, Strategy> map = new ConcurrentHashMap<>(size); for (Strategy strategy : strategyList) { map.put(strategy.getType(), strategy); } strategyMap = Collections.unmodifiableMap(map); } } ...}

正例:

















@Servicepublic class StrategyFactory implements InitializingBean {    @Autowired    private List<Strategy> strategyList;    private Map<String, Strategy> strategyMap;    @Override    public void afterPropertiesSet() {        if (CollectionUtils.isNotEmpty(strategyList)) {            int size = (int) Math.ceil(strategyList.size() * 4.0 / 3);            Map<String, Strategy> map = new HashMap<>(size);            for (Strategy strategy : strategyList) {                map.put(strategy.getType(), strategy);            }            strategyMap = Collections.unmodifiableMap(map);        }    }    ...


2.對象&類

2.1.禁止使用JSON轉(zhuǎn)化對象

JSON提供把對象轉(zhuǎn)化為JSON字符串、把JSON字符串轉(zhuǎn)為對象的功能,于是被某些人用來轉(zhuǎn)化對象。這種對象轉(zhuǎn)化方式,雖然在功能上沒有問題,但是在性能上卻存在問題。

反例:


List<UserDO> userDOList = ...;List<UserVO> userVOList = JSON.parseArray(JSON.toJSONString(userDOList), UserVO.class);
正例:








List<UserDO> userDOList = ...;List<UserVO> userVOList = new ArrayList<>(userDOList.size());for (UserDO userDO : userDOList) {    UserVO userVO = new UserVO();    userVO.setId(userDO.getId());    ...    userVOList.add(userVO);}

2.2.盡量不使用反射賦值對象

用反射賦值對象,主要優(yōu)點是節(jié)省了代碼量,主要缺點卻是性能有所下降。

反例:







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) {    UserVO userVO = new UserVO();    userVO.setId(userDO.getId());    ...    userVOList.add(userVO);}


2.3.采用Lambda表達(dá)式替換內(nèi)部匿名類

對于大多數(shù)剛接觸JDK8的同學(xué)來說,都會認(rèn)為Lambda表達(dá)式就是匿名內(nèi)部類的語法糖。實際上, Lambda表達(dá)式在大多數(shù)虛擬機(jī)中采用invokeDynamic指令實現(xiàn),相對于匿名內(nèi)部類在效率上會更高一些。

反例:










List<User> userList = ...;Collections.sort(userList, new Comparator<User>() { @Override public int compare(User user1, User user2) { Long userId1 = user1.getId(); Long userId2 = user2.getId(); ... return userId1.compareTo(userId2); }});

正例:







List<User> userList = ...;Collections.sort(userList, (user1, user2) -> {    Long userId1 = user1.getId();    Long userId2 = user2.getId();    ...    return userId1.compareTo(userId2);});


2.4.盡量避免定義不必要的子類

多一個類就需要多一份類加載,所以盡量避免定義不必要的子類。

反例:







public static final Map<String, Class> CLASS_MAP = Collections.unmodifiableMap(new HashMap<String, Class>(16) { private static final long serialVersionUID = 1L; { put('VARCHAR', java.lang.String.class); }});

正例:







public static final Map<String, Class> CLASS_MAP;static {    Map<String, Class> classMap = new HashMap<>(16);    classMap.put('VARCHAR', java.lang.String.class);    ...    CLASS_MAP = Collections.unmodifiableMap(classMap);}


2.5.盡量指定類的final修飾符

為類指定final修飾符,可以讓該類不可以被繼承。如果指定了一個類為final,則該類所有的方法都是final的,Java編譯器會尋找機(jī)會內(nèi)聯(lián)所有的final方法。內(nèi)聯(lián)對于提升Java運(yùn)行效率作用重大,具體可參見Java運(yùn)行期優(yōu)化,能夠使性能平均提高50%。

反例:



public class DateHelper { ...}

正例:



public final class DateHelper {    ...}

注意:使用Spring的AOP特性時,需要對Bean進(jìn)行動態(tài)代理,如果Bean類添加了final修飾,會導(dǎo)致異常。

3.方法

3.1.把跟類成員變量無關(guān)的方法聲明成靜態(tài)方法

靜態(tài)方法的好處就是不用生成類的實例就可以直接調(diào)用。靜態(tài)方法不再屬于某個對象,而是屬于它所在的類。只需要通過其類名就可以訪問,不需要再消耗資源去反復(fù)創(chuàng)建對象。即便在類內(nèi)部的私有方法,如果沒有使用到類成員變量,也應(yīng)該聲明為靜態(tài)方法。

反例:





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;}

3.2.盡量使用基本數(shù)據(jù)類型作為方法參數(shù)類型,避免不必要的裝箱、拆箱和空指針判斷


反例:






public static double sum(Double value1, Double value2) { double double1 = Objects.isNull(value1) ? 0.0D : value1; double double2 = Objects.isNull(value2) ? 0.0D : value2; return double1 double2;}double result = sum(1.0D, 2.0D);

正例:




public static double sum(double value1, double value2) {    return value1   value2;}double result = sum(1.0D, 2.0D);


3.3.盡量使用基本數(shù)據(jù)類型作為方法返回值類型,避免不必要的裝箱、拆箱和空指針判斷

在JDK類庫的方法中,很多方法返回值都采用了基本數(shù)據(jù)類型,首先是為了避免不必要的裝箱和拆箱,其次是為了避免返回值的空指針判斷。比如:Collection.isEmpty()和Map.size()。

反例:













public static Boolean isValid(UserDO user) { if (Objects.isNull(user)) { return false; } return Boolean.TRUE.equals(user.getIsValid());}
// 調(diào)用代碼UserDO user = ...;Boolean isValid = isValid(user);if (Objects.nonNull(isValid) && isValid.booleanValue()) { ...}

正例:












public static boolean isValid(UserDO user) {    if (Objects.isNull(user)) {        return false;    }  return Boolean.TRUE.equals(user.getIsValid());}
// 調(diào)用代碼UserDO user = ...;if (isValid(user)) { ...}


3.4.協(xié)議方法參數(shù)值非空,避免不必要的空指針判斷

協(xié)議編程,可以@NonNull和@Nullable標(biāo)注參數(shù),是否遵循全憑調(diào)用者自覺。

反例:






public static boolean isValid(UserDO user) { if (Objects.isNull(user)) { return false; } return Boolean.TRUE.equals(user.getIsValid());}

正例:



public static boolean isValid(@NonNull UserDO user) {  return Boolean.TRUE.equals(user.getIsValid());}


3.5.協(xié)議方法返回值非空,避免不必要的空指針判斷

協(xié)議編程,可以@NonNull和@Nullable標(biāo)注參數(shù),是否遵循全憑實現(xiàn)者自覺。

反例:












// 定義接口public interface OrderService { public List<OrderVO> queryUserOrder(Long userId);}
// 調(diào)用代碼List<OrderVO> orderList = orderService.queryUserOrder(userId);if (CollectionUtils.isNotEmpty(orderList)) { for (OrderVO order : orderList) { ... }}

正例:











// 定義接口public interface OrderService {    @NonNull    public List<OrderVO> queryUserOrder(Long userId);}
// 調(diào)用代碼List<OrderVO> orderList = orderService.queryUserOrder(userId);for (OrderVO order : orderList) { ...}


3.6.被調(diào)用方法已支持判空處理,調(diào)用方法無需再進(jìn)行判空處理

反例:




UserDO user = null;if (StringUtils.isNotBlank(value)) { user = JSON.parseObject(value, UserDO.class);}

正例:

UserDO user = JSON.parseObject(value, UserDO.class);


3.7.盡量避免不必要的函數(shù)封裝

方法調(diào)用會引起入棧和出棧,導(dǎo)致消耗更多的CPU和內(nèi)存,應(yīng)當(dāng)盡量避免不必要的函數(shù)封裝。當(dāng)然,為了使代碼更簡潔、更清晰、更易維護(hù),增加一定的方法調(diào)用所帶來的性能損耗是值得的。

反例:







// 函數(shù)封裝public static boolean isVip(Boolean isVip) { return Boolean.TRUE.equals(isVip);}
// 使用代碼boolean isVip = isVip(user.getVip());

正例:

boolean isVip = Boolean.TRUE.equals(user.getVip());

3.8.盡量指定方法的final修飾符

方法指定final修飾符,可以讓方法不可以被重寫,Java編譯器會尋找機(jī)會內(nèi)聯(lián)所有的final方法。內(nèi)聯(lián)對于提升Java運(yùn)行效率作用重大,具體可參見Java運(yùn)行期優(yōu)化,能夠使性能平均提高50%。

注意:所有的private方法會隱式地被指定final修飾符,所以無須再為其指定final修飾符。

反例:






public class Rectangle { ... public double area() { ... }}

正例:






public class Rectangle {    ...    public final double area() {        ...    }}

注意:使用Spring的AOP特性時,需要對Bean進(jìn)行動態(tài)代理,如果方法添加了final修飾,將不會被代理。

4.表達(dá)式

4.1.盡量減少方法的重復(fù)調(diào)用

反例:




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  ) {    ...}


4.2.盡量避免不必要的方法調(diào)用

反例:




List<UserDO> userList = userDAO.queryActive();if (isAll) { userList = userDAO.queryAll();}

正例:






List<UserDO> userList;if (isAll) {    userList = userDAO.queryAll();} else {    userList = userDAO.queryActive();}


4.3.盡量使用移位來代替正整數(shù)乘除

用移位操作可以極大地提高性能。對于乘除2^n(n為正整數(shù))的正整數(shù)計算,可以用移位操作來代替。

反例:


int num1 = a * 4;int num2 = a / 4;

正例:


int num1 = a << 2;int num2 = a >> 2;


4.4.提取公共表達(dá)式,避免重復(fù)計算

提取公共表達(dá)式,只計算一次值,然后重復(fù)利用值。

反例:

double distance = Math.sqrt((x2 - x1) * (x2 - x1) (y2 - y1) * (y2 - y1));
正例:





double dx = x2 - x1;double dy = y2 - y1;double distance = Math.sqrt(dx * dx   dy * dy);double distance = Math.sqrt(Math.pow(x2 - x1, 2)   Math.pow(y2 - y1, 2));


4.5.盡量不在條件表達(dá)式中用!取反

使用!取反會多一次計算,如果沒有必要則優(yōu)化掉。

反例:





if (!(a >= 10)) { ... // 條件處理1} else { ... // 條件處理2}

正例:





if (a < 10) {    ... // 條件處理1} else {    ... // 條件處理2}


4.6.對于多常量選擇分支,盡量使用switch語句而不是if-else語句

if-else語句,每個if條件語句都要加裝計算,直到if條件語句為true為止。switch語句進(jìn)行了跳轉(zhuǎn)優(yōu)化,Java中采用tableswitch或lookupswitch指令實現(xiàn),對于多常量選擇分支處理效率更高。經(jīng)過試驗證明:在每個分支出現(xiàn)概率相同的情況下,低于5個分支時if-else語句效率更高,高于5個分支時switch語句效率更高。

反例:









if (i == 1) { ...; // 分支1} else if (i == 2) { ...; // 分支2} else if (i == ...) { ...; // 分支n} else { ...; // 分支n 1}

正例:














switch (i) {    case 1 :        ... // 分支1        break;    case 2 :        ... // 分支2        break;    case ... :        ... // 分支n        break;    default :        ... // 分支n 1        break;}

備注:如果業(yè)務(wù)復(fù)雜,可以采用Map實現(xiàn)策略模式。

5.字符串

5.1.盡量不要使用正則表達(dá)式匹配

正則表達(dá)式匹配效率較低,盡量使用字符串匹配操作。

反例:



String source = 'a::1,b::2,c::3,d::4';String target = source.replaceAll('::', '=');Stringp[] targets = source.spit('::');

正例:



String source = 'a::1,b::2,c::3,d::4';String target = source.replace('::', '=');Stringp[] targets = StringUtils.split(source, '::');


5.2.盡量使用字符替換字符串

字符串的長度不確定,而字符的長度固定為1,查找和匹配的效率自然提高了。

反例:



String source = 'a:1,b:2,c:3,d:4';int index = source.indexOf(':');String target = source.replace(':', '=');

正例:



String source = 'a:1,b:2,c:3,d:4';int index = source.indexOf(':');String target = source.replace(':', '=');


5.3.盡量使用StringBuilder進(jìn)行字符串拼接

String是final類,內(nèi)容不可修改,所以每次字符串拼接都會生成一個新對象。StringBuilder在初始化時申請了一塊內(nèi)存,以后的字符串拼接都在這塊內(nèi)存中執(zhí)行,不會申請新內(nèi)存和生成新對象。

反例:







String s = '';for (int i = 0; i < 10; i ) { if (i != 0) { s = ','; } s = i;}

例:







StringBuilder sb = new StringBuilder(128);for (int i = 0; i < 10; i  ) {    if (i != 0) {        sb.append(',');    }    sb.append(i);}


5.4.不要使用'' 轉(zhuǎn)化字符串

使用'' 進(jìn)行字符串轉(zhuǎn)化,使用方便但是效率低,建議使用String.valueOf.

反例:


int i = 12345;String s = '' i;

正例:


int i = 12345;String s = String.valueOf(i);


6.數(shù)組

6.1.不要使用循環(huán)拷貝數(shù)組,盡量使用System.arraycopy拷貝數(shù)組

推薦使用System.arraycopy拷貝數(shù)組,也可以使用Arrays.copyOf拷貝數(shù)組。

反例:





int[] sources = new int[] {1, 2, 3, 4, 5};int[] targets = new int[sources.length];for (int i = 0; i < targets.length; i ) { targets[i] = sources[i];}

正例:



int[] sources = new int[] {1, 2, 3, 4, 5};int[] targets = new int[sources.length];System.arraycopy(sources, 0, targets, 0, targets.length);


6.2.集合轉(zhuǎn)化為類型T數(shù)組時,盡量傳入空數(shù)組T[0]

將集合轉(zhuǎn)換為數(shù)組有2種形式:toArray(new T[n])和toArray(new T[0])。在舊的Java版本中,建議使用toArray(new T[n]),因為創(chuàng)建數(shù)組時所需的反射調(diào)用非常慢。在OpenJDK6后,反射調(diào)用是內(nèi)在的,使得性能得以提高,toArray(new T[0])比toArray(new T[n])效率更高。此外,toArray(new T[n])比toArray(new T[0])多獲取一次列表大小,如果計算列表大小耗時過長,也會導(dǎo)致toArray(new T[n])效率降低。

反例:


List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, ...);Integer[] integers = integerList.toArray(new Integer[integerList.size()]);
正例:


List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, ...);Integer[] integers = integerList.toArray(new Integer[0]); // 勿用new Integer[]{}

建議:集合應(yīng)該提供一個toArray(Class<T> clazz)方法,避免無用的空數(shù)組初始化(new T[0])。

6.3.集合轉(zhuǎn)化為Object數(shù)組時,盡量使用toArray()方法

轉(zhuǎn)化Object數(shù)組時,沒有必要使用toArray[new Object[0]],可以直接使用toArray()。避免了類型的判斷,也避免了空數(shù)組的申請,所以效率會更高。

反例:


List<Object> objectList = Arrays.asList(1, '2', 3, '4', 5, ...);Object[] objects = objectList.toArray(new Object[0]);

正例:


List<Object> objectList = Arrays.asList(1, '2', 3, '4', 5, ...);Object[] objects = objectList.toArray();


7.集合

7.1.初始化集合時,盡量指定集合大小

Java集合初始化時都會指定一個默認(rèn)大小,當(dāng)默認(rèn)大小不再滿足數(shù)據(jù)需求時就會擴(kuò)容,每次擴(kuò)容的時間復(fù)雜度有可能是O(n)。所以,盡量指定預(yù)知的集合大小,就能避免或減少集合的擴(kuò)容次數(shù)。

反例:









List<UserDO> userDOList = ...;Set<Long> userSet = new HashSet<>();Map<Long, UserDO> userMap = new HashMap<>();List<UserVO> userList = new ArrayList<>();for (UserDO userDO : userDOList) { userSet.add(userDO.getId()); userMap.put(userDO.getId(), userDO); userList.add(transUser(userDO));}

正例:










List<UserDO> userDOList = ...;int userSize = userDOList.size();Set<Long> userSet = new HashSet<>(userSize);Map<Long, UserDO> userMap = new HashMap<>((int) Math.ceil(userSize * 4.0 / 3));List<UserVO> userList = new ArrayList<>(userSize);for (UserDO userDO : userDOList) {    userSet.add(userDO.getId());    userMap.put(userDO.getId(), userDO);    userList.add(transUser(userDO));}


7.2.不要使用循環(huán)拷貝集合,盡量使用JDK提供的方法拷貝集合

JDK提供的方法可以一步指定集合的容量,避免多次擴(kuò)容浪費時間和空間。同時,這些方法的底層也是調(diào)用System.arraycopy方法實現(xiàn),進(jìn)行數(shù)據(jù)的批量拷貝效率更高。

反例:









List<UserDO> user1List = ...;List<UserDO> user2List = ...;List<UserDO> userList = new ArrayList<>(user1List.size() user2List.size());for (UserDO user1 : user1List) { userList.add(user1);}for (UserDO user2 : user2List) { userList.add(user2);}

正例:





List<UserDO> user1List = ...;List<UserDO> user2List = ...;List<UserDO> userList = new ArrayList<>(user1List.size()   user2List.size());userList.addAll(user1List);userList.addAll(user2List);


7.3.盡量使用Arrays.asList轉(zhuǎn)化數(shù)組為列表

原理與'不要使用循環(huán)拷貝集合,盡量使用JDK提供的方法拷貝集合'類似。

反例:










List<String> typeList = new ArrayList<>(8);typeList.add('Short');typeList.add('Integer');typeList.add('Long');
String[] names = ...;List<String> nameList = ...;for (String name : names) { nameList.add(name);}

正例:





List<String> typeList = Arrays.asList('Short', 'Integer', 'Long');
String[] names = ...;List<String> nameList = ...;nameList.addAll(Arrays.asList(names));


7.4.直接迭代需要使用的集合

直接迭代需要使用的集合,無需通過其它操作獲取數(shù)據(jù)。

反例:





Map<Long, UserDO> userMap = ...;for (Long userId : userMap.keySet()) { UserDO user = userMap.get(userId); ...}

正例:






Map<Long, UserDO> userMap = ...;for (Map.Entry<Long, UserDO> userEntry : userMap.entrySet()) {    Long userId = userEntry.getKey();    UserDO user = userEntry.getValue();    ...}


7.5.不要使用size方法檢測空,必須使用isEmpty方法檢測空

使用size方法來檢測空邏輯上沒有問題,但使用isEmpty方法使得代碼更易讀,并且可以獲得更好的性能。任何isEmpty方法實現(xiàn)的時間復(fù)雜度都是O(1),但是某些size方法實現(xiàn)的時間復(fù)雜度有可能是O(n)。

反例:








List<UserDO> userList = ...;if (userList.size() == 0) { ...}Map<Long, UserDO> userMap = ...;if (userMap.size() == 0) { ...}

正例:








List<UserDO> userList = ...;if (userList.isEmpty()) {    ...}Map<Long, UserDO> userMap = ...;if (userMap.isEmpty()) {    ...}


7.6.非隨機(jī)訪問的List,盡量使用迭代代替隨機(jī)訪問

對于列表,可分為隨機(jī)訪問和非隨機(jī)訪問兩類,可以用是否實現(xiàn)RandomAccess接口判斷。隨機(jī)訪問列表,直接通過get獲取數(shù)據(jù)不影響效率。而非隨機(jī)訪問列表,通過get獲取數(shù)據(jù)效率極低。

反例:






LinkedList<UserDO> userDOList = ...;int size = userDOList.size();for (int i = 0; i < size; i ) { UserDO userDO = userDOList.get(i); ...}

正例:




LinkedList<UserDO> userDOList = ...;for (UserDO userDO : userDOList) {    ...}

其實,不管列表支不支持隨機(jī)訪問,都應(yīng)該使用迭代進(jìn)行遍歷。

7.7.盡量使用HashSet判斷值存在

在Java集合類庫中,List的contains方法普遍時間復(fù)雜度是O(n),而HashSet的時間復(fù)雜度為O(1)。如果需要頻繁調(diào)用contains方法查找數(shù)據(jù),可以先將List轉(zhuǎn)換成HashSet。

反例:








List<Long> adminIdList = ...;List<UserDO> userDOList = ...;List<UserVO> userVOList = new ArrayList<>(userDOList.size());for (UserDO userDO : userDOList) { if (adminIdList.contains(userDO.getId())) { userVOList.add(transUser(userDO)); }}

正例:








Set<Long> adminIdSet = ...;List<UserDO> userDOList = ...;List<UserVO> userVOList = new ArrayList<>(userDOList.size());for (UserDO userDO : userDOList) {    if (adminIdSet.contains(userDO.getId())) {        userVOList.add(transUser(userDO));    }}


7.8.避免先判斷存在再進(jìn)行獲取

如果需要先判斷存在再進(jìn)行獲取,可以直接獲取并判斷空,從而避免了二次查找操作。

反例:









public static UserVO transUser(UserDO user, Map<Long, RoleDO> roleMap) { UserVO userVO = new UserVO(); userVO.setId(user.getId()); ... if (roleMap.contains(user.getRoleId())) { RoleDO role = roleMap.get(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));    }}


8.異常

8.1.直接捕獲對應(yīng)的異常

直接捕獲對應(yīng)的異常,避免用instanceof判斷,效率更高代碼更簡潔。

反例:









try { saveData();} catch (Exception e) { if (e instanceof IOException) { log.error('保存數(shù)據(jù)IO異常', e); } else { log.error('保存數(shù)據(jù)其它異常', e); }}

正例:







try {    saveData();} catch (IOException e) {    log.error('保存數(shù)據(jù)IO異常', e);} catch (Exception e) {    log.error('保存數(shù)據(jù)其它異常', e);}


8.2.盡量避免在循環(huán)中捕獲異常

當(dāng)循環(huán)體拋出異常后,無需循環(huán)繼續(xù)執(zhí)行時,沒有必要在循環(huán)體中捕獲異常。因為,過多的捕獲異常會降低程序執(zhí)行效率。

反例:











public Double sum(List<String> valueList) { double sum = 0.0D; for (String value : valueList) { try { sum = Double.parseDouble(value); } catch (NumberFormatException e) { return null; } } return sum;}

正例:











public Double sum(List<String> valueList) {    double sum = 0.0D;    try {        for (String value : valueList) {            sum  = Double.parseDouble(value);        }    } catch (NumberFormatException e) {        return null;    }    return sum;}


8.3.禁止使用異??刂茦I(yè)務(wù)流程

相對于條件表達(dá)式,異常的處理效率更低。

反例:







public static boolean isValid(UserDO user) { try { return Boolean.TRUE.equals(user.getIsValid()); } catch(NullPointerException e) { return false; }}

正例:






public static boolean isValid(UserDO user) {    if (Objects.isNull(user)) {        return false;    }  return Boolean.TRUE.equals(user.getIsValid());}


9.緩沖區(qū)

9.1.初始化時盡量指定緩沖區(qū)大小

初始化時,指定緩沖區(qū)的預(yù)期容量大小,避免多次擴(kuò)容浪費時間和空間。

反例:


StringBuffer buffer = new StringBuffer();StringBuilder builder = new StringBuilder();

正例:


StringBuffer buffer = new StringBuffer(1024);StringBuilder builder = new StringBuilder(1024);


9.2.盡量重復(fù)使用同一緩沖區(qū)

針對緩沖區(qū),Java虛擬機(jī)需要花時間生成對象,還要花時間進(jìn)行垃圾回收處理。所以,盡量重復(fù)利用緩沖區(qū)。

反例:







StringBuilder builder1 = new StringBuilder(128);builder1.append('update t_user set name = '').append(userName).append('' where id = ').append(userId);statement.executeUpdate(builder1.toString());StringBuilder builder2 = new StringBuilder(128);builder2.append('select id, name from t_user where id = ').append(userId);ResultSet resultSet = statement.executeQuery(builder2.toString());...

正例:







StringBuilder builder = new StringBuilder(128);builder.append('update t_user set name = '').append(userName).append('' where id = ').append(userId);statement.executeUpdate(builder.toString());builder.setLength(0);builder.append('select id, name from t_user where id = ').append(userId);ResultSet resultSet = statement.executeQuery(builder.toString());...

其中,使用setLength方法讓緩沖區(qū)重新從0開始。

9.3.盡量設(shè)計使用同一緩沖區(qū)

為了提高程序運(yùn)行效率,在設(shè)計上盡量使用同一緩沖區(qū)。

反例:























// 轉(zhuǎn)化XML(UserDO)public static String toXml(UserDO user) { StringBuilder builder = new StringBuilder(128); builder.append('<UserDO>'); builder.append(toXml(user.getId())); builder.append(toXml(user.getName())); builder.append(toXml(user.getRole())); builder.append('</UserDO>'); return builder.toString();}// 轉(zhuǎn)化XML(Long)public static String toXml(Long value) { StringBuilder builder = new StringBuilder(128); builder.append('<Long>'); builder.append(value); builder.append('</Long>'); return builder.toString();}...
// 使用代碼UserDO user = ...;String xml = toXml(user);

正例:





















// 轉(zhuǎn)化XML(UserDO)public static void toXml(StringBuilder builder, UserDO user) {    builder.append('<UserDO>');    toXml(builder, user.getId());    toXml(builder, user.getName());    toXml(builder, user.getRole());    builder.append('</UserDO>');}// 轉(zhuǎn)化XML(Long)public static void toXml(StringBuilder builder, Long value) {    builder.append('<Long>');    builder.append(value);    builder.append('</Long>');}...
// 使用代碼UserDO user = ...;StringBuilder builder = new StringBuilder(1024);toXml(builder, user);String xml = builder.toString();

去掉每個轉(zhuǎn)化方法中的緩沖區(qū)申請,申請一個緩沖區(qū)給每個轉(zhuǎn)化方法使用。從時間上來說,節(jié)約了大量緩沖區(qū)的申請釋放時間;從空間上來說,節(jié)約了大量緩沖區(qū)的臨時存儲空間。

9.4.盡量使用緩沖流減少IO操作

使用緩沖流BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream等,可以大幅較少IO次數(shù)并提升IO速度。

反例:










try (FileInputStream input = new FileInputStream('a'); FileOutputStream output = new FileOutputStream('b')) { int size = 0; byte[] temp = new byte[1024]; while ((size = input.read(temp)) != -1) { output.write(temp, 0, size); }} catch (IOException e) { log.error('復(fù)制文件異常', e);}

正例:










try (BufferedInputStream input = new BufferedInputStream(new FileInputStream('a'));    BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream('b'))) {    int size = 0;    byte[] temp = new byte[1024];    while ((size = input.read(temp)) != -1) {        output.write(temp, 0, size);    }} catch (IOException e) {    log.error('復(fù)制文件異常', e);}
其中,可以根據(jù)實際情況手動指定緩沖流的大小,把緩沖流的緩沖作用發(fā)揮到最大。

10.線程

10.1.在單線程中,盡量使用非線程安全類

使用非線程安全類,避免了不必要的同步開銷。

反例:


StringBuffer buffer = new StringBuffer(128);buffer.append('select * from ').append(T_USER).append(' where id = ?');

正例:


StringBuilder buffer = new StringBuilder(128);buffer.append('select * from ').append(T_USER).append(' where id = ?');


10.2.在多線程中,盡量使用線程安全類

使用線程安全類,比自己實現(xiàn)的同步代碼更簡潔更高效。

反例:







private volatile int counter = 0;public void access(Long userId) { synchronized (this) { counter ; } ...}

正例:





private final AtomicInteger counter = new AtomicInteger(0);public void access(Long userId) {    counter.incrementAndGet();    ...}


10.3.盡量減少同步代碼塊范圍

在一個方法中,可能只有一小部分的邏輯是需要同步控制的,如果同步控制了整個方法會影響執(zhí)行效率。所以,盡量減少同步代碼塊的范圍,只對需要進(jìn)行同步的代碼進(jìn)行同步。

反例:





private volatile int counter = 0;public synchronized void access(Long userId) { counter ; ... // 非同步操作}

正例:







private volatile int counter = 0;public void access(Long userId) {    synchronized (this) {        counter  ;    }    ... // 非同步操作}


10.4.盡量合并為同一同步代碼塊

同步代碼塊是有性能開銷的,如果確定可以合并為同一同步代碼塊,就應(yīng)該盡量合并為同一同步代碼塊。

反例:











// 處理單一訂單public synchronized handleOrder(OrderDO order) { ...}
// 處理所有訂單public void handleOrder(List<OrderDO> orderList) { for (OrderDO order : orderList) { handleOrder(order); }}

正例:











// 處理單一訂單public handleOrder(OrderDO order) {    ...}
// 處理所有訂單public synchronized void handleOrder(List<OrderDO> orderList) { for (OrderDO order : orderList) { handleOrder(order); }}


10.5.盡量使用線程池減少線程開銷

多線程中兩個必要的開銷:線程的創(chuàng)建和上下文切換。采用線程池,可以盡量地避免這些開銷。

反例:



public void executeTask(Runnable runnable) { new Thread(runnable).start();}

正例:




private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(10);public void executeTask(Runnable runnable) {    executorService.execute(runnable);}


后記

作為一名長期奮戰(zhàn)在業(yè)務(wù)一線的'IT民工',沒有機(jī)會去研究什么高深莫測的'理論',只能專注于眼前看得見摸得著的'技術(shù)',致力于做到'干一行、愛一行、專一行、精一行'。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java 代碼精簡之道
Java單元測試技巧之JSON序列化
工作四年,分享50個讓你代碼更好的小建議
Java函數(shù)優(yōu)雅之道(下)
DO、DTO和VO分層設(shè)計的好處
如何把愛和規(guī)矩同時給孩子?
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服