1. 程式人生 > 實用技巧 >Java開發最容易踩的十個大坑,每條都是血的教訓!

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 個工具定位問題根因

  • .........