分享幾個工作中實用的程式碼最佳化技巧!
正文
類成員與方法的可見性最小化
舉例:如果是一個private的方法,想刪除就刪除
如果一個public的service方法,或者一個public的成員變數,刪除一下,不得思考很多。
使用位移操作替代乘除法
計算機是使用二進位制表示的,位移操作會極大地提高效能。
<< 左移相當於乘以 2;>> 右移相當於除以 2;
>>> 無符號右移相當於除以 2,但它會忽略符號位,空位都以 0 補齊。
a = val << 3;
b = val >> 1;
儘量減少對變數的重複計算
我們知道對方法的呼叫是有消耗的,包括建立棧幀、呼叫方法時保護現場,恢復現場等。
//反例
for (int i = 0; i < list.size(); i++) {
System.out.println("result");
}
//正例
for (int i = 0, length = list.size(); i < length; i++) {
System.out.println("result");
}
在list.size()
很大的時候,就減少了很多的消耗。
不要捕捉RuntimeException
RuntimeException 不應該透過 catch 語句去捕捉,而應該使用編碼手段進行規避。
如下面的程式碼,list 可能會出現陣列越界異常。
是否越界是可以透過程式碼提前判斷的,而不是等到發生異常時去捕捉。
提前判斷這種方式,程式碼會更優雅,效率也更高。
public String test1(List<String> list, int index) {
try {
return list.get(index);
} catch (IndexOutOfBoundsException ex) {
return null;
}
}
//正例
public String test2(List<String> list, int index) {
if (index >= list.size() || index < 0) {
return null;
}
return list.get(index);
}
使用區域性變數可避免在堆上分配
由於堆資源是多執行緒共享的,是垃圾回收器工作的主要區域,過多的物件會造成 GC 壓力,可以透過區域性變數的方式,將變數在棧上分配。這種方式變數會隨著方法執行的完畢而銷燬,能夠減輕 GC 的壓力。
減少變數的作用範圍
注意變數的作用範圍,儘量減少物件的建立。
如下面的程式碼,變數 s 每次進入方法都會建立,可以將它移動到 if 語句內部。
public void test(String str) {
final int s = 100;
if (!StringUtils.isEmpty(str)) {
int result = s * s;
}
}
儘量採用懶載入的策略,在需要的時候才建立
String str = "月伴飛魚";
if (name == "公眾號") {
list.add(str);
}
if (name == "公眾號") {
String str = "月伴飛魚";
list.add(str);
}
訪問靜態變數直接使用類名
使用物件訪問靜態變數,這種方式多了一步定址操作,需要先找到變數對應的類,再找到類對應的變數。
// 反例
int i = objectA.staticMethod();
// 正例
int i = ClassA.staticMethod();
字串拼接使用StringBuilder
字串拼接,使用 StringBuilder 或者 StringBuffer,不要使用 + 號。
//反例
public class StringTest {
@Test
public void testStringPlus() {
String str = "111";
str += "222";
str += "333";
System.out.println(str);
}
}
//正例
public class TestMain {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("111");
sb.append("222");
sb.append(333);
System.out.println(sb.toString());
}
}
重寫物件的HashCode,不要簡單地返回固定值
有同學在開發重寫 HashCode 和 Equals 方法時,會把 HashCode 的值返回固定的 0,而這樣做是不恰當的
當這些物件存入 HashMap 時,效能就會非常低,因為 HashMap 是透過 HashCode 定位到 Hash 槽,有衝突的時候,才會使用連結串列或者紅黑樹組織節點,固定地返回 0,相當於把 Hash 定址功能無效了。
HashMap等集合初始化的時候,指定初始值大小
這樣的物件有很多,比如 ArrayList,StringBuilder 等,透過指定初始值大小可減少擴容造成的效能損耗。
初始值大小計算可以參考《阿里巴巴開發手冊》:
迴圈內不要不斷建立物件引用
//反例
for (int i = 1; i <= size; i++) {
Object obj = new Object();
}
//正例
Object obj = null;
for (int i = 0; i <= size; i++) {
obj = new Object();
}
第一種會導致記憶體中有size個Object物件引用存在,size很大的話,就耗費記憶體了
遍歷Map 的時候,使用 EntrySet 方法
使用 EntrySet 方法,可以直接返回 set 物件,直接拿來用即可;而使用 KeySet 方法,獲得的是key 的集合,需要再進行一次 get 操作,多了一個操作步驟,所以更推薦使用 EntrySet 方式遍歷 Map。
Set<Map.Entry<String, String>> entryseSet = nmap.entrySet();
for (Map.Entry<String, String> entry : entryseSet) {
System.out.println(entry.getKey()+","+entry.getValue());
}
不要在多執行緒下使用同一個 Random
Random 類的 seed 會在併發訪問的情況下發生競爭,造成效能降低,建議在多執行緒環境下使用 ThreadLocalRandom 類。
public static void main(String[] args) {
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
Thread thread1 = new Thread(()->{
for (int i=0;i<10;i++){
System.out.println("Thread1:"+threadLocalRandom.nextInt(10));
}
});
Thread thread2 = new Thread(()->{
for (int i=0;i<10;i++){
System.out.println("Thread2:"+threadLocalRandom.nextInt(10));
}
});
thread1.start();
thread2.start();
}
自增推薦使用LongAddr
自增運算可以透過 synchronized 和 volatile 的組合來控制執行緒安全,或者也可以使用原子類(比如 AtomicLong)。
後者的速度比前者要高一些,AtomicLong 使用 CAS 進行比較替換,線上程多的情況下會造成過多無效自旋,可以使用 LongAdder 替換 AtomicLong 進行進一步的效能提升。
public class Test {
public int longAdderTest(Blackhole blackhole) throws InterruptedException {
LongAdder longAdder = new LongAdder();
for (int i = 0; i < 1024; i++) {
longAdder.add(1);
}
return longAdder.intValue();
}
}
程式中要少用反射
反射的功能很強大,但它是透過解析位元組碼實現的,效能就不是很理想。
現實中有很多對反射的最佳化方法,比如把反射執行的過程(比如 Method)快取起來,使用複用來加快反射速度。
Java 7.0 之後,加入了新的包java.lang.invoke
,同時加入了新的 JVM 位元組碼指令 invokedynamic,用來支援從 JVM 層面,直接透過字串對目標方法進行呼叫。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2924585/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 分享幾個 SpringBoot 實用的小技巧Spring Boot
- [API 開發管理] 分享幾個 eoLinker 實用操作技巧API
- 分享幾個實用的方法
- 分享6個Python程式設計非常實用的技巧!Python程式設計
- 分享幾個我工作中封裝的typeScript方法封裝TypeScript
- 24個PHP程式碼最佳化技巧PHP
- 幾個超級實用的css程式碼片段CSS
- 工作中實用的5個Excel小技巧,提升辦公效率!Excel
- 程式設計師練習演算法的幾個實用技巧程式設計師演算法
- 提高程式碼顏值的幾個小技巧
- 技術管理者的幾個實用技巧
- MySQL查詢最佳化的5個實用技巧MySql
- SAP Fiori Launchpad 應用的兩個實用技巧分享
- 分享工作中常用的一個Git指令碼Git指令碼
- 電腦實用7個小技巧分享
- 前端工程師分享幾個CSS技巧前端工程師CSS
- 10月22日雲棲精選夜讀|幾個實用的SpringBoot小技巧分享Spring Boot
- 介紹幾個程式碼實際開發中很實用的工具
- 幾個超級實用但很少人知道的 VS 技巧
- js技巧:十幾行的程式碼實現vue.watchJSVue
- 工作中5個實用的Linux命令Linux
- 分享兩個實用的shell指令碼指令碼
- 強烈推介的幾個微信小程式開發小技巧,簡單又實用微信小程式
- 幾個超級實用但很少人知道的 VS 技巧[更新]
- 分享幾個掘金專欄文章佈局的小技巧
- 【知識分享】Python開發簡化程式碼的6個技巧!Python
- [譯] 不用祖傳祕方 - 寫好程式碼的幾個小技巧
- 嵌入式程式碼最佳化技巧
- 編寫Spark程式的幾個最佳化點Spark
- 分享幾個Python小技巧函式裡的4個小花招Python函式
- SEO最佳化網站診斷的幾個技巧,你知道多少?網站
- 分享幾個Java面試小技巧,建議收藏!Java面試
- 分享一個用Git裝逼的小技巧~Git
- 分享個程式碼
- 相見恨晚的幾個Excel小技巧,簡單實用又高效!Excel
- 讓開發改bug全靠催?分享6個實用技巧
- Array的幾個小技巧
- 教你三個最實用的應急類辦公技巧,讓你輕鬆化解工作中的抓狂