1. 程式人生 > >Java中的條件編譯

Java中的條件編譯

在程式碼中新增大量log,對於CPU和記憶體的影響如何,會不會降低效能?相信有不少人對此有疑問,本文將詳細解答該問題。

一、概述

條件編譯是指源程式的程式碼行,可以在滿足一定條件的情況下才進行編譯,而未選中的原始碼,不會生成中間碼或機器碼,即部分內容參與編譯。

條件編譯的好處:對於不同硬體平臺或者軟體平臺,或者不同功能模組的程式碼,編寫到在同一個原始檔,從而方便程式的維護和移植。

很多程式設計語言都提供條件編譯的功能,比如C/++c採用前處理器指示符來達到條件編譯。而Java語言並沒有提供直接的前處理器,那麼Java是不是就沒有條件編譯呢?先告訴大家,答案是Java存在條件編譯,在這之前先說說C/C++的條件編譯。

二、C/C++條件編譯

對於C/C++,常見的預處理指令:

#include 引入原始碼檔案
#define 巨集定義
#undef 取消已存在的巨集定義
#if 如果條件為真,則編譯後面的程式碼
#ifdef 如果巨集已定義,則編譯後面的程式碼
#ifndef 如果巨集未定義,則編譯後面的程式碼
#elif 如果前面的#if條件為假,並且當前條件為真,則編譯後面的程式碼
#endif 結束前面的#if……#else條件編譯語句塊

條件編譯常見形式:

(1) 當通過#define已定義過該 識別符號,則程式編譯階段會選擇編譯程式碼段1,否則編譯程式碼段2

#ifdef 識別符號
        程式碼段1
#else
        程式碼段2
#endif

(2) 當通過#define未定義過該 識別符號,則程式編譯階段會選擇編譯程式碼段1,否則編譯程式碼段2。功能正好與(1)相反

#ifndef 識別符號
        程式碼段1
#else
        程式碼段2
#endif

(3) 當 表示式為真,則程式編譯階段會選擇編譯程式碼段1,否則編譯程式碼段2

#if 表示式
        程式碼段1
#else
        程式碼段2
#endif

三、Java條件編譯

Java語法的條件編譯,是通過判斷條件為常量的if語句實現的。其原理是Java語言的語法糖,根據if判斷條件的真假,編譯器直接把分支為false的程式碼塊消除。通過該方式實現的條件編譯,必須在方法體內實現,而無法在正整個Java類的結構或者類的屬性上進行條件編譯,這與C/C++的條件編譯相比,確實更有侷限性。在Java語言設計之初並沒有引入條件編譯的功能,雖有侷限,但是總比沒有更強。

反編譯分析技術:

對於Debug.java檔案,執行:

javac Debug.java  //編譯後 生成Debug.class檔案
javap -c Debug.class //通過javap,反編譯class檔案

接下來,展開幾項對比分析:

3.1 final對比

該對比項是針對是否採用final變數與非final變數的對比:

原始碼:

// 空方法
public void voidMethod(){

}

//final常量
private final boolean FINAL_FLAG_FALSE = false;
public void constantFalseFlag(){
    if(FLAG_FALSE){
        System.out.println("debug log...");
    }
}

// 非final
private boolean  falseFlag= false;
public void falseFlag(){
    if(falseFlag){
        System.out.println("debug log...");
    }
}

反編譯解析後的結果如下:

// 空方法
public void voidMethod();
   Code:
      0: return

//final常量
public void constantFalseFlag();
   Code:
      0: return

// 非final
public void falseFlag();
   Code:
      0: aload_0
      1: getfield      #3 // Field falseFlag:Z
      4: ifeq          15
      7: getstatic     #5 // Field java/lang/System.out:Ljava/io/PrintStream;
     10: ldc           #6 // String debug log...
     12: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     15: return

從反編譯的Code欄位,可以看出constantFalseFlag()方法體內的內容經過編譯後,對於常量false分支,是不可達分支,則在編譯成class位元組碼檔案時剪出該分支,最終效果等價於voidMethod()。而對於falseFlag()方法,則多了5條指令。

可見,對於常量為false的if語句,由於恆為false,等同於條件編譯的功能。

另外除了反編譯的方式來對比分析,如果不瞭解反編譯後的語法,還可以簡單地對比編譯後的.Class檔案的大小,也會發現if(false){}內部的程式碼塊會自動剪除。

from: http://gityuan.com/2015/07/26/java-condition-compile/