JVM效能優化系列-(4) 編寫高效Java程式
4. 編寫高效Java程式
4.1 面向物件
構造器引數太多怎麼辦?
正常情況下,如果構造器引數過多,可能會考慮重寫多個不同引數的建構函式,如下面的例子所示:
public class FoodNormal { //required private final String foodName;//名稱 private final int reilang;//熱量 //optional private final int danbz;//蛋白質 private final int dianfen;//澱粉 private final int zf;//脂肪 //全引數 public FoodNormal(String foodName, int reilang, int danbz, int dianfen, int zf, int tang, int wss) { super(); this.foodName = foodName; this.reilang = reilang; this.danbz = danbz; this.dianfen = dianfen; this.zf = zf; } //2個引數 public FoodNormal(String foodName, int reilang) { this(foodName,reilang,0,0,0,0,0); } //3....6個引數 // public static void main(String[] args) { FoodNormal fn = new FoodNormal("food1",1200,200,0,0,300,100); } }
但是問題很明顯:1.可讀性很差,特別是引數個數多,並且有多個相同型別的引數時;2.調換引數的順序,編譯器也不會報錯。
針對這個兩個問題,一種選擇是 JavaBeans 模式,在這種模式中,呼叫一個無引數的建構函式來建立物件,然後呼叫 setter 方法來設定每個必需的引數和可選引數。這種方法缺陷很明顯:排除了讓類不可變的可能性,並且需要增加工作以確保執行緒安全。
推薦的方法是使用builder模式,該模式結合了可伸縮構造方法模式的安全性和JavaBean模式的可讀性。
下面的例子中,建立了一個內部類Builder用於接受對應的引數,最後通過Builder類將引數返回。
public class FoodBuilder { //required private final String foodName; private final int reilang; //optional private int danbz; private int dianfen; private int zf; public static class Builder{ //required private final String foodName; private final int reilang; //optional private int danbz; private int dianfen; private int zf; public Builder(String foodName, int reilang) { super(); this.foodName = foodName; this.reilang = reilang; } public Builder danbz(int val) { this.danbz = val; return this; } public Builder dianfen(int val) { this.dianfen = val; return this; } public Builder zf(int val) { this.zf = val; return this; } public FoodBuilder build() { return new FoodBuilder(this); } } private FoodBuilder(Builder builder) { foodName = builder.foodName; reilang = builder.reilang; danbz = builder.danbz; dianfen = builder.danbz; zf = builder.zf; } public static void main(String[] args) { FoodBuilder foodBuilder = new FoodBuilder.Builder("food2", 1000) .danbz(100).dianfen(100).zf(100).build(); } }
Builder模式更進一步
標準的Builder模式,包含以下4個部分:
- 抽象建造者:一般來說是個介面,1)建造方法,建造部件的方法(不止一個);2)返回產品的方法
- 具體建造者:繼承抽象建造者,並且實現相應的建造方法。
- 導演者:呼叫具體的建造者,建立產品物件。
- 產品:需要建造的複雜物件。
下面的例子中,man和woman是產品;personBuilder是抽象建造者;manBuilder和womanBuilder繼承了personBuilder並實現了相應方法,是具體建造者;NvWa是導演者,呼叫建造者方法建造產品。
產品類
public abstract class Person { protected String head; protected String body; protected String foot; public String getHead() { return head; } public void setHead(String head) { this.head = head; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getFoot() { return foot; } public void setFoot(String foot) { this.foot = foot; } } // 具體的產品 public class Man extends Person { public Man() { System.out.println("create a man"); } @Override public String toString() { return "Man{}"; } } public class Woman extends Person { public Woman() { System.out.println("create a Woman"); } @Override public String toString() { return "Woman{}"; } }
抽象建造類
public abstract class PersonBuilder {
//建造部件
public abstract void buildHead();
public abstract void buildBody();
public abstract void buildFoot();
public abstract Person createPerson();
}
具體建造者
public class ManBuilder extends PersonBuilder {
private Person person;
public ManBuilder() {
this.person = new Man();
}
@Override
public void buildHead() {
person.setHead("Brave Head");
}
@Override
public void buildBody() {
person.setBody("Strong body");
}
@Override
public void buildFoot() {
person.setFoot("powful foot");
}
@Override
public Person createPerson() {
return person;
}
}
public class WomanBuilder extends PersonBuilder {
private Person person;
public WomanBuilder() {
this.person = new Woman();
}
@Override
public void buildHead() {
person.setHead("Pretty Head");
}
@Override
public void buildBody() {
person.setBody("soft body");
}
@Override
public void buildFoot() {
person.setFoot("long white foot");
}
@Override
public Person createPerson() {
return person;
}
}
導演者
public class NvWa {
public Person buildPerson(PersonBuilder pb) {
pb.buildHead();
pb.buildBody();
pb.buildFoot();
return pb.createPerson();
}
}
下面是測試程式:
public class Mingyun {
public static void main(String[] args) {
System.out.println("create NvWa");
NvWa nvwa = new NvWa();
nvwa.buildPerson(new ManBuilder());
nvwa.buildPerson(new WomanBuilder());
}
}
不需要例項化的類應該構造器私有
工程中的工具類,為了防止例項化,可以將構造器私有化。
不要建立不必要的物件
1. 自動裝箱和拆箱等隱式轉換。
自動裝箱就是Java自動將原始型別值轉換成對應的物件,比如將int的變數轉換成Integer物件,這個過程叫做裝箱,反之將Integer物件轉換成int型別值,這個過程叫做拆箱。因為這裡的裝箱和拆箱是自動進行的非人為轉換,所以就稱作為自動裝箱和拆箱。
自動裝箱和拆箱在Java中很常見,比如我們有一個方法,接受一個物件型別的引數,如果我們傳遞一個原始型別值,那麼Java會自動將這個原始型別值轉換成與之對應的物件。
自動裝箱的弊端:
Integer sum = 0;
for(int i=1000; i<5000; i++){
sum+=i;
}
上面的例子中,首先sum進行自動拆箱操作,進行數值相加操作,最後發生自動裝箱操作轉換成Integer物件。上面的迴圈中會建立將近4000個無用的Integer物件,在這樣龐大的迴圈中,會降低程式的效能並且加重了垃圾回收的工作量。
2. 例項共用,宣告為static
多個共用的情況下,宣告為static或者採用單例模式,以免生成多個物件影響程式效能。
避免使用終結方法
finalize方法,因為虛擬機器不保證這個方法被執行,所以釋放資源時,不能保證。
為了合理的釋放資源,推薦下面兩種方法:
- try resource
Java 1.7中引入了try-with-resource語法糖來開啟資源,而無需自己書寫資源來關閉程式碼。例子如下:
public class TryWithResource {
public static void main(String[] args) {
try (Connection conn = new Connection()) {
conn.sendData();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
為了能夠配合try-with-resource,資源必須實現AutoClosable
介面。該介面的實現類需要重寫close
方法:
public class Connection implements AutoCloseable {
public void sendData() {
System.out.println("正在傳送資料");
}
@Override
public void close() throws Exception {
System.out.println("正在關閉連線");
}
}
- try finally
在finally語句塊中釋放資源,保證資源永遠能夠被正常釋放。
使類和成員的可訪問性最小化
可以有效的解除系統中各個模組的耦合度、實現每個模組的獨立開發、使得系統更加的可維護,更加的健壯。
如何最小化類和介面的可訪問性?
能將類和介面做成包級私有就一定要做成包級私有的。
如果一個類或者介面,只被另外的一個類應用,那麼最好將這個類或者介面做成其內部的私有類或者介面。
如何最小化一個了類中的成員的可訪問性?
首先設計出該類需要暴露出來的api,然後將剩下的成員的設計成private型別。然後再其他類需要訪問某些private型別的成員時,在刪掉private,使其變成包級私有。如果你發現你需要經常這樣做,那麼就請你重新設計一下這個類的api。
對於protected型別的成員,作用域是整個系統,所以,能用包訪問型別的成員的話就儘量不要使用保護行的成員。
不能為了測試而將包中的類或者成員變為public型別的,最多隻能設定成包級私有型別。
例項域絕對不能是public型別的.
使可變性最小化
不可變類只是例項不能被修改的類。每個例項中包含的所有資訊都必須在建立該例項的時候就提供,並在物件的整個生命週期(lifetime)內固定不變。
Java平臺類庫中包含許多不可變的類,其中有String、基本型別的包裝類、BigInteger和BigDecimal。
存在不可變的類有許多理由:不可變的類比可變的類更加易於設計、實現和使用。不容易出錯,且更加安全。
為了使類成為不可變,要遵循下面五條規則:
1、不要提供任何會修改物件狀態的方法(也稱為mutator),即改變物件屬性的方法。
2、保證類不會被擴充套件。這樣可以防止粗心或者惡意的子類假裝物件的狀態已經改變,從而破壞該類的不可變行為。為了防止子類化,一般做法是使這個類成為final的。
3、使所有的域都是final的。通過系統的強制方式,這可以清楚地表明你的意圖。而且,如果一個指向新建立例項的引用在缺乏同步機制的情況下,從一個執行緒被傳遞到另一個 執行緒,就必須確保正確的行為。
4、使所有的域都成為私有的。這樣可以防止客戶端獲得訪問被域引用的可變物件的許可權,並防止客戶端直接修改這些物件。雖然從技術上講,允許不可變的類具有公有的final 域,只要這些域包含基本型別的值或者指向不可變物件的引用,但是不建議這樣做,因為這樣會使得在以後的版本中無法再改變內部的表示法。
5、確保對於任何可變元件的互斥訪問。如果類具有指向可變物件的域,則必須確保該類的客戶端無法獲得指向這些物件的引用。並且,永遠不要用客戶端提供的物件引用初始化這樣的域,也不要從任何訪問方法(accessor)中返回該物件引用。在構造器、訪問方法和readObject中請使用保護性拷貝(defensive copy)技術。
優先使用複合
- 繼承:會打破封裝性
- 組合:在內部持有一個父類作為成員變數。
使用繼承擴充套件一個類很危險,父類的具體實現很容易影響子類的正確性。而複合優先於繼承告訴我們,不用擴充套件現有的類,而是在新類中增加一個私有域,讓它引用現有類的一個例項。這種設計稱為複合(Composition)。
只有當子類和超類之間確實存在父子關係時,才可以考慮使用繼承。否則都應該用複合,包裝類不僅比子類更加健壯,而且功能也更加強大。
介面優於抽象類
介面和抽象類
- 抽象類允許某些方法的實現,但是介面不允許(JDK 1.8開始已經可以了)
- 現有類必須成為抽象類的子類,但是隻能單繼承,介面可以多繼承
介面優點
- 現有類可以很容易被更新,以實現新的介面。
- 介面允許我們構造非層次結構的型別框架,介面可以多繼承。
- 骨架實現類,下面對骨架類進行詳細介紹
假定有Interface A
, 可以宣告abstarct class B implements A
,接著在真正的實現類C中 class C extends B implements A
。B就是所謂的骨架類,骨架類中對A中的一些基礎通用方法進行了實現,使得C可以直接使用骨架類中的實現,無需再次實現,或者呼叫骨架類中的實現進行進一步的定製與優化。C只需要實現B中未實現的方法或者新增新的方法。
骨架實現類的優點在於,它們提供抽象類的所有實現的幫助,而不會強加抽象類作為型別定義時的嚴格約束。對於具有骨架實現類的介面的大多數實現者來說,繼承這個類是顯而易見的選擇,但它不是必需的。如果一個類不能繼承骨架的實現,這個類可以直接實現介面。該類仍然受益於介面本身的任何預設方法。此外,骨架實現類仍然可以協助介面的實現。實現介面的類可以將介面方法的呼叫轉發給繼承骨架實現的私有內部類的包含例項。這種被稱為模擬多重繼承的技術,它提供了多重繼承的許多好處,同時避免了缺陷。
JDK的實現中,使用了大量的骨架類,按照慣例,骨架實現類被稱為AbstractInterface,其中Interface是它們實現的介面的名稱。 例如,集合框架( Collections Framework)提供了一個框架實現以配合每個主要集合介面:AbstractCollection,AbstractSet,AbstractList和AbstractMap。
4.2 方法
可變引數要謹慎使用
從Java 1.5開始就增加了可變引數(varargs)方法,又稱作variable arity method。可變引數方法接受0個或多個指定型別的引數。它的機制是先建立一個數組,陣列的大小為呼叫位置所傳遞的引數數量,然後將值傳到陣列中,最後將陣列傳遞到方法。
例如下面的例子,返回多個引數的和:
// Simple use of varargs - Page 197
static int sum(int... args) {
int sum = 0;
for (int arg : args)
sum += arg;
return sum;
}
但是這種方法也接受0個引數,所以一般需要對引數進行檢查。通常為了規避這種情況,就是宣告該方法有兩個引數,一個是指定型別的正常引數,另一個是這種型別的varargs引數。這個方法彌補了上面的不足(不需要再檢查引數的數量了,因為至少要傳遞一個引數,否則不能通過編譯):
static int min(int firstArg, int... remainingArgs) {
int min = firstArg;
for (int arg : remainingArgs)
if (arg < min)
min = arg;
return min;
}
需要注意的是,在重視效能的情況下,使用可變引數機制要特別小心。可變引數方法每次呼叫都會導致進行一次陣列分配和初始化。
返回零長度的陣列或集合,不要返回null
要求呼叫方單獨處理null的情況。對於list的情況,可以直接返回jdk內建的Collections.emptyList()。
優先使用標準的異常
- 可讀性。
- 追求程式碼的重用。
- 在類裝載的效能上考慮,也提倡使用標準異常。
常用異常:
illegalArgumentException --- 呼叫者傳遞的引數不合適
illegalStateException --- 接收狀態異常
NullPointException --- 空指標
UnSupportOperationException -- 操作不支援
4.3 通用程式設計
用列舉代替int常量
在列舉型別出現之前,一般都常常使用int常量或者String常量表示列舉相關事物。如:
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
針對int常量以下不足:
- 在型別安全方面,如果你想使用的是ORANGE_NAVEL,但是傳遞的是APPLE_FUJI,編譯器並不能檢測出錯誤;
- 因為int常量是編譯時常量,被編譯到使用它們的客戶端中。若與列舉常量關聯的int發生了變化,客戶端需重新編譯,否則它們的行為就不確定;
- 沒有便利方法將int常量翻譯成可列印的字串。這裡的意思應該是比如你想呼叫的是ORANGE_NAVEL,debug的時候顯示的是0,但你不能確定是APPLE_FUJI還是ORANGE_NAVEL
1. 預設列舉
上面的例子可以使用下面的enum重寫:
public enum Apple {
APPLE_FUJI,
APPLE_PIPPIN,
APPLE_GRANNY_SMITH;
}
在呼叫的時候,直接使用enum型別,在編譯的時候可以直接指定型別,否則編譯不通過;並且debug的時候,顯示的是enum中的常量(APPLE_FUJI這樣的),可以一眼看出是否用錯;最後由於列舉匯出的常量域(APPLE_FUJI等)與客戶端之間是通過列舉來引用的,再增加或者重排序列舉型別中的常量後,並不需要重新編譯客戶端程式碼。
2. 帶行為的列舉
首先必須明白,java裡的列舉就是一個類,列舉中的每個物件,是這個列舉類的一個例項。
因此我們可以編寫下面的列舉類,並且提供相應的計算方法。
public enum DepotEnum {
UNPAY(0,"未支付"),PAID(1,"已支付"),TIMOUT(-1,"超時");
private int status;
private String desc;
private String dbInfo;//其他屬性
private DepotEnum(int status, String desc) {
this.status = status;
this.desc = desc;
}
public int getStatus() {
return status;
}
public String getDesc() {
return desc;
}
public String getDbInfo() {
return dbInfo;
}
public int calcStatus(int params) {
return status+params;
}
public static void main(String[] args) {
for(DepotEnum e:DepotEnum.values()) {
System.out.println(e+":"+e.calcStatus(14));
}
}
}
下面是比較複雜的列舉,這裡在類裡面定義了列舉BetterActive列舉類,進行計算加減乘除的操作,為了保證每增加一個列舉類後,都增加對應的計算方法,這裡將計算方法oper定義為抽象方法,保證了在增加列舉變數時,一定增加對應的oper方法。
public class ActiveEnum {
public enum BetterActive{
PLUS {
@Override
double oper(double x, double y) {
return x+y;
}
},MINUS {
@Override
double oper(double x, double y) {
return x-y;
}
},MUL {
@Override
double oper(double x, double y) {
return x*y;
}
},DIV {
@Override
double oper(double x, double y) {
return x/y;
}
};
abstract double oper(double x,double y);
}
public static void main(String[] args) {
System.out.println(BetterActive.PLUS.oper(0.1, 0.2));
}
}
3. 策略列舉
主要是為了優化在多個列舉變數的情況下,儘量減少重複程式碼。下面以不同的日期,薪水的支付方式不同為例,進行說明,當增加了一個新的日期後,我們只需要在外層列舉類中進行修改,無需修改其他計算方法。
public enum BetterPayDay {
MONDAY(PayType.WORK), TUESDAY(PayType.WORK), WEDNESDAY(
PayType.WORK), THURSDAY(PayType.WORK), FRIDAY(PayType.WORK),
SATURDAY(PayType.REST), SUNDAY(PayType.REST),WUYI(PayType.REST);
private final PayType payType;//成員變數
BetterPayDay(PayType payType) {
this.payType = payType;
}
double pay(double hoursOvertime) {
return payType.pay(hoursOvertime);
}
//策略列舉
private enum PayType {
WORK {
double pay(double hoursOvertime) {
return hoursOvertime*HOURS_WORK;
}
},
REST {
double pay(double hoursOvertime) {
return hoursOvertime*HOURS_REST;
}
};
private static final int HOURS_WORK = 2;
private static final int HOURS_REST = 3;
abstract double pay(double hoursOvertime);//抽象計算加班費的方法
}
public static void main(String[] args) {
System.out.println(BetterPayDay.MONDAY.pay(7.5));
}
}
將區域性變數的作用域最小化
- 要使區域性變數的作用域最小化,最有力的方法就是在第一次使用它的地方宣告。
- 幾乎每個區域性變數的宣告都應該包含一個初始化表示式。如果沒有足夠資訊來對一個變數進行有意義的初始化,就應該推遲這個宣告,直到可以初始化為止。
- 儘量保證方法小而集中。
- 僅在某一程式碼塊中使用的區域性變數,那麼就在該程式碼塊中宣告。
精確計算,避免使用float和double
float和double型別不能用於精確計算,其主要目的是為了科學計算和工程計算,它們執行二進位制浮點運算。
轉成int或者long,推薦使用bigDecimal。
當心字串連線的效能
String是不可變的,每一次拼接都會產生字串的複製。
StringBuilder和StringBuffer
- 都是可變的類。
- StringBuffer執行緒安全,可以在多執行緒下使用;StrngBuilder非執行緒安全,速度比StringBuffer快。
控制方法的大小
這個好理解,主要是從解耦和可維護性角度考慮。
在Unix philosophy中也提到,編寫程式碼時注意Do One Thing and Do It Well
。
本文由『後端精進之路』原創,首發於部落格 http://teckee.github.io/ , 轉載請註明出處
搜尋『後端精進之路』關注公眾號,立刻獲取最新文章和價值2000元的BATJ精品面試課程。
相關推薦
JVM效能優化系列-(4) 編寫高效Java程式
4. 編寫高效Java程式 4.1 面向物件 構造器引數太多怎麼辦? 正常情況下,如果構造器引數過多,可能會考慮重寫多個不同引數的建構函式,如下面的例子所示: public class FoodNormal { //required private final String foodNa
提交訂單效能優化系列之011-放棄java同步,引入資料庫修改行數來驗證庫存
概括總結 既然Java同步之後,效能這麼差,那麼有沒有辦法可以不使用Java同步呢?有的,那就是利用資料庫修改的行數來驗證庫存。另外,假設現在庫存是10,需要減少1,推薦的做法是update Goods set stock=stock-1,而不是update
JVM效能優化, Part 5:Java的伸縮性
感謝朋友【吳傑】投遞本文。 JVM效能優化系列文章由Eva Andearsson在javaworld上發表共計5篇文章,ImportNew上有前4篇譯文。本文(第5篇)由吳傑翻譯自:javaworld 。 很多程式設計師在解決JVM效能問題的時候,花開了很多時間去調優應用程式級別的效能瓶頸,當
JVM效能優化系列-(2) 垃圾收集器與記憶體分配策略
目前已經更新完《Java併發程式設計》和《Docker教程》,歡迎關注【後端精進之路】,輕鬆閱讀全部文章。 Java併發程式設計: Java併發程式設計系列-(1) 併發程式設計基礎 Java併發程式設計系列-(2) 執行緒的併發工具類 Java併發程式設計系列-(3) 原子操作與CAS Java
JVM效能優化系列-(3) 虛擬機器執行子系統
3. 虛擬機器執行子系統 3.1 Java跨平臺的基礎 Java剛誕生的宣傳口號:一次編寫,到處執行(Write Once, Run Anywhere),其中位元組碼是構成平臺無關的基石,也是語言無關性的基礎。 Java虛擬機器不和包括Java在內的任何語言繫結,它只與Class檔案這種特定的二進位制檔案
JVM效能優化系列-(5) 早期編譯優化
5. 早期編譯優化 早起編譯優化主要指編譯期進行的優化。 java的編譯期可能指的以下三種: 前端編譯器:將.java檔案變成.class檔案,例如Sun的Javac、Eclipse JDT中的增量式編譯器(ECJ) JIT編譯器(Just In Time Compiler):將位元組碼變成機器碼,例如
最新Java程式效能優化,讓你的Java程式更快、更穩定
Eureka Eureka(原來以為是縮寫,原來就是一個單詞,翻譯為:我發現了,我找到了!0.0)是Netflix開源的一款提供服務註冊和發現的產品,它提供了完整的Service Registry和Service Discovery實現。也是springcloud體系中最重要最核心的
Java效能優化系列二(jvm記憶體調優)
首先需要注意的是在對JVM記憶體調優的時候不能只看作業系統級別Java程序所佔用的記憶體,這個數值不能準確的反應堆記憶體的真實佔用情況,因為GC過後這個值是不會變化的,因此記憶體調優的時候要更多地使用JDK提供的記憶體檢視工具,比如JConsole和Java VisualVM(jvisua
JAVA筆記 —— JVM 效能優化
JVM 引數檢視 java四類八種基本資料型別 第一類:整型 byte short int long 第二類:浮點型 float double 第三類:邏輯型 boolean(它只有兩個值可取true false) 第四類:字元型 char 在棧中可以直接分配記憶體的資料是基本資料型別。 引
提交訂單效能優化系列之002-引入自己編寫的資料庫連線池
資料庫連線池對效能的影響 總是在各種地方看到類似的說明:“資料庫連線池是非常昂貴的資源。” 那麼請問“非常昂貴”到底是有多昂貴呢? 在我的機器上的測試結果是:從資料庫獲取一個連線的平均耗時大
Java效能優化系列集錦
Java效能問題一直困擾著廣大程式設計師,由於平臺複雜性,要定位問題,找出其根源確實很難。隨著10多年Java平臺的改進以及新出現的多核多處理器,Java軟體的效能和擴充套件性已經今非昔比了。現代JVM持續演進,內建了更為成熟的優化技術、執行時技術和垃圾收集器。與此同時,底層
JAVA筆記 —— JVM 效能優化
JVM 引數檢視 java四類八種基本資料型別 第一類:整型 byte short int long 第二類:浮點型 float double 第三類:邏輯型 boolean(它只有兩個值可取true
深入理解Java虛擬機器——JVM效能優化
一、效能監控 當開發或執行一個Java應用的時候,對JVM的效能進行監控是很重要的。配置JVM不是一次配置就萬事大吉的,特別是你要應對的是Java伺服器應用的情況。你必須持續的檢查堆記憶體和非堆記憶體的分配和使用情況,執行緒數的建立情況和記憶體中載入的類的資料
提交訂單效能優化系列之003-測試阿里巴巴的druid資料來源
概括總結 使用druid資料來源之後,相對於第002版自己隨手寫的資料庫,效能反而下降了8.49%。原因在於,002版是“隨手”寫的,因此功能非常簡陋,要什麼沒什麼,只能從記憶體中獲取連線,因此很快。
提交訂單效能優化系列之004-測試hikari資料來源
概括總結 使用hikari資料來源之後,相對於第003版的druid資料來源,從提交訂單這個複雜的操作上來說,效能提升了17.79%。而從獲取資料庫連線這一簡單的操作上來說,hikari比druid優秀幾百倍。 004版本更新說明 pom.xml檔案中
提交訂單效能優化系列之006-普通的Thread多執行緒改為Java8的parallelStream併發流
概括總結 Java8的parallelStream併發流能達到跟多執行緒類似的效果,但它也不是什麼善茬,為了得到跟上一版本的多執行緒類似的效果,一改再改,雖然最後改出來了,但是還是存在理解不了的地方。
提交訂單效能優化系列之009-對比整個方法同步與方法中的部分程式碼同步
概括總結 在用到synchronized關鍵字的時候,憑直覺就會加在方法上,比如public static synchronized void test(){},但是這種直覺不見得是對的,估計大部分時候是出圖方便,想偷懶,才直接加到方法上的。推薦的做法是:僅僅
提交訂單效能優化系列之012-引入FutureTask
概括總結 引入FutureTask能提高併發度,相應就可以提升效能,這次測試的結是,提升了38.93%(參考值)。它的缺點也很明顯,就是增加了程式碼的複雜度,不方便閱讀了,且對異常也要額外處理,而且大
提交訂單效能優化系列之013-測試SQL語句中少查詢幾個欄位(包括大欄位)
概括總結 這一版本寫了兩個測試類,一個測試類中查詢全部欄位,另一個測試類中只查詢必要的欄位,然後對比效能。結論是:根據是減少的欄位的長度不同,效能會不同。具體請檢視下面的測試結果。 013版本更新說
提交訂單效能優化系列之015-收貨地址由根據ID查詢改為傳參
概括總結 在電商下單時,一般都需要選擇收貨地址。這時候,在頁面顯示的資訊已經包含手機號、姓名、詳細地址、地址ID等資訊了。那麼這時候可以有兩種選擇,1、只把地址ID作為引數傳過去,後臺根據ID重新查詢