Java開發最容易踩的十個大坑,每條都是血的教訓!
本文為Java程式設計師們準備了一系列廣為流傳的Java最佳程式設計實踐,請認真讀幾遍(尤其是小白)。
認真看完後,必有收穫,日後開發可以少踩坑!
文末乾貨:Java業務開發常見錯誤100例
1.優先返回空物件而非null
如果程式要返回一個物件,確保返回的物件有值而不是null。這能節省大量的"if else"檢查。
public String getLocationName { return (null == cityName ? "": cityName);}
2.謹慎操作字串
如果兩個字串在for迴圈中使用+操作符進行拼接,那麼每次迴圈都會產生一個新的字串物件。這不僅浪費記憶體空間同時還會影響效能。類似的,如果初始化字串物件,儘量不要使用構造方法,而應該直接初始化。比方說:
//Slower InstantiationString java = new String("百度搜 程式原始碼論壇 獲精品"); //Faster InstantiationString soGood = "獲更多最新精品資源"
3.避免無用物件
建立物件是Java中最昂貴的操作之一。因此最好在有需要的時候再進行物件的建立/初始化。如下:
import java.util.ArrayList;import java.util.List;public class Employees { private List Employees; public List getEmployees() { //initialize only when required if(null == Employees) { Employees = new ArrayList(); } return Employees; }}
4.陣列與ArrayList之爭
開發人員經常會發現很難在陣列和ArrayList間做選擇。它們二者互有優劣。如何選擇應該視情況而定。
- 陣列是定長的,而ArrayList是變長的。由於陣列長度是固定的,因此在宣告陣列時就已經分配好記憶體了。而陣列的操作則會更快一些。另一方面,如果我們不知道資料的大小,那麼過多的資料便會導致- - ArrayOutOfBoundException,而少了又會浪費儲存空間。
- ArrayList在增刪元素方面要比陣列簡單。
- 陣列可以是多維的,但ArrayList只能是一維的。
import java.util.ArrayList;public class arrayVsArrayList { public static void main(String[] args) { int[] myArray = new int[6]; myArray[7]= 10; // ArraysOutOfBoundException //Declaration of ArrayList. Add and Remove of elements is easy. ArrayList<Integer> myArrayList = new ArrayList<>(); myArrayList.add(1); myArrayList.add(2); myArrayList.add(3); myArrayList.add(4); myArrayList.add(5); myArrayList.remove(0); for(int i = 0; i < myArrayList.size(); i++) { System.out.println("Element: " + myArrayList.get(i)); } //Multi-dimensional Array int[][][] multiArray = new int [3][3][3]; }}
5.try塊的finally塊沒有被執行
看下下面這段程式碼:
public class shutDownHooksDemo { public static void main(String[] args) { for(int i=0;i<5;i++) { try { if(i==4) { System.out.println("程式原始碼論壇 www.cx1314.cn"); System.exit(0); } } finally { System.out.println("最流行最優質的IT資源社群!"); } } }}
從程式碼來看,貌似finally塊中的println語句應該會被執行5次。但當程式執行後,你會發現finally塊只執行了4次。第5次迭代的時候會觸發exit函式的呼叫,於是這第5次的finally便永遠也觸發不到了。原因便是——System.exit會掛起所有執行緒的執行,包括當前執行緒。即便是try語句後的finally塊,只要是執行了exit,便也無力迴天了。
在呼叫System.exit時,JVM會在關閉前執行兩個結束任務:
首先,它會執行完所有通過Runtime.addShutdownHook註冊進來的終止的鉤子程式。這一點很關鍵,因為它會釋放JVM外部的資源。
接下來的便是Finalizer了。可能是System.runFinalizersOnExit也可能是Runtime.runFinalizersOnExit。finalizer的使用已經被廢棄有很長一段時間了。finalizer可以在存活物件上進行呼叫,即便是這些物件仍在被其它執行緒所使用。而這會導致不可預期的結果甚至是死鎖。
public class shutDownHooksDemo { public static void main(String[] args) { for(int i=0;i<5;i++) { final int final_i = i; try { Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { if(final_i==4) { System.out.println("Inside Try Block.Exiting without executing Finally block."); System.exit(0); } } }); } finally { System.out.println("Inside Finally Block."); } } }}
6.奇數判斷
看下這幾行程式碼,看看它們是否能用來準確地判斷一個數是奇數?
public boolean oddOrNot(int num) { return num % 2 == 1;}
看似是對的,但是每執行四便會有一個錯誤的結果(用資料說話)。考慮到負奇數的情況,它除以2的結果就不會是1。因此,返回值是false,而這樣是不對的。
程式碼可以修改成這樣:
public boolean oddOrNot(int num) { return (num & 1) != 0;}
這麼寫不光是負奇數的問題解決了,並且還是經過充分優化過的。因為算術運算和邏輯執行要比乘除運算更高效,計算的結果也會更快。
7.單引號與雙引號的區別
public class Haha { public static void main(String args[]) { System.out.print("H" + "a"); System.out.print('H' + 'a'); }}
看起來這段程式碼會返回"Haha",但實際返回的是Ha169。原因就是用了雙引號的時候,字元會被當作字串處理,而如果是單引號的話,字元值會通過一個叫做基礎型別拓寬的操作來轉換成整型值。然後再將值相加得到169。
8.一些防止記憶體洩露的小技巧
記憶體洩露會導致軟體的效能降級。由於Java是自動管理記憶體的,因此開發人員並沒有太多辦法介入。不過還是有一些方法能夠用來防止記憶體洩露的。
- 查詢完資料後立即釋放資料庫連線
- 儘可能使用finally塊
- 釋放靜態變數中的例項
9.避免死鎖
死鎖出現的原因有很多。避免死鎖不是一句話就能解決的。通常來說,當某個同步物件在等待另一個同步物件所擁有的資源上的鎖時,便會產生死鎖。
試著執行下下面的程式。它會告訴你什麼是死鎖。這個死鎖是由於兩個執行緒都在等待對方所擁有的資源,因此會產生死鎖。它們會一直等待,沒有誰會先放手。
public class DeadlockDemo { public static Object addLock = new Object(); public static Object subLock = new Object(); public static void main(String args[]) { MyAdditionThread add = new MyAdditionThread(); MySubtractionThread sub = new MySubtractionThread(); add.start(); sub.start(); // 更多最新精品資源 www.cx1314.cn }private static class MyAdditionThread extends Thread { public void run() { synchronized (addLock) { int a = 10, b = 3; int c = a + b; System.out.println("Addition Thread: " + c); System.out.println("Holding First Lock..."); try { Thread.sleep(10); } catch (InterruptedException e) {} System.out.println("Addition Thread: Waiting for AddLock..."); synchronized (subLock) { System.out.println("Threads: Holding Add and Sub Locks..."); } } } } private static class MySubtractionThread extends Thread { public void run() { synchronized (subLock) { int a = 10, b = 3; int c = a - b; System.out.println("Subtraction Thread: " + c); System.out.println("Holding Second Lock..."); try { Thread.sleep(10); } catch (InterruptedException e) {} System.out.println("Subtraction Thread: Waiting for SubLock..."); synchronized (addLock) { System.out.println("Threads: Holding Add and Sub Locks..."); } } } }}
輸出:
❝Addition Thread: 13
Subtraction Thread: 7
Holding First Lock...
Holding Second Lock...
Addition Thread: Waiting for AddLock...
Subtraction Thread: Waiting for SubLock...
❞
但如果呼叫的順序變一下的話,死鎖的問題就解決了。
public class DeadlockSolutionDemo { public static Object addLock = new Object(); public static Object subLock = new Object(); public static void main(String args[]) { MyAdditionThread add = new MyAdditionThread(); MySubtractionThread sub = new MySubtractionThread(); add.start(); sub.start(); }private static class MyAdditionThread extends Thread { public void run() { synchronized (addLock) { int a = 10, b = 3; int c = a + b; System.out.println("Addition Thread: " + c); System.out.println("Holding First Lock..."); try { Thread.sleep(10); } catch (InterruptedException e) {} System.out.println("Addition Thread: Waiting for AddLock..."); synchronized (subLock) { System.out.println("Threads: Holding Add and Sub Locks..."); } } // 更多最新精品資源 www.cx1314.cn } } private static class MySubtractionThread extends Thread { public void run() { synchronized (addLock) { int a = 10, b = 3; int c = a - b; System.out.println("Subtraction Thread: " + c); System.out.println("Holding Second Lock..."); try { Thread.sleep(10); } catch (InterruptedException e) {} System.out.println("Subtraction Thread: Waiting for SubLock..."); synchronized (subLock) { System.out.println("Threads: Holding Add and Sub Locks..."); } } } }}
輸出:
❝Addition Thread: 13
Holding First Lock...
Addition Thread: Waiting for AddLock...
Threads: Holding Add and Sub Locks...
Subtraction Thread: 7
Holding Second Lock...
Subtraction Thread: Waiting for SubLock...
Threads: Holding Add and Sub Locks...
❞
10.替Java省點記憶體
某些Java程式是CPU密集型的,但它們會需要大量的記憶體。這類程式通常執行得很緩慢,因為它們對記憶體的需求很大。為了能提升這類應用的效能,可得給它們多留點記憶體。因此,假設我們有一臺擁有10G記憶體的Tomcat伺服器。在這臺機器上,我們可以用如下的這條命令來分配記憶體:
❝export JAVA_OPTS="$JAVA_OPTS -Xms5000m -Xmx6000m -XX:PermSize=1024m -XX:MaxPermSize=2048m"
❞
- Xms = 最小記憶體分配
- Xmx = 最大記憶體分配
- XX:PermSize = JVM啟動時的初始大小
-
XX:MaxPermSize = JVM啟動後可分配的最大空間
分享學習資料:Java業務開發常見錯誤合集。
下載學習:http://www.cx1314.cn/thread-3182-1-1.html
讓你Get如下技能:
-
130 個程式碼坑點及其解決方案
-
100 個場景化案例解讀
-
25 次原始碼深度解析使用
-
10 個工具定位問題根因
-
.........