在代碼中添加大量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){}內部的代碼塊會自動剪除。