在 Java 中,介面定義的常量預設為 public static final。許多開發者直覺認為這些常量在程式執行期間是固定不變的。然而,若常量的初始化表達式涉及隨機數生成或其他非確定性運算,情況就大不相同了。
package Dec2016;
import java.util.Random;
public class Ques1222 {
public static void main(String[] args){
System.out.println("輸出常量:" + Test.NUM);
}
}
interface Test {
public static final float NUM = new Random().nextFloat();
}
請問:主程式輸出的 Test.NUM 值是固定不變的嗎?
答案是:不固定。 每次執行主程式時,Test.NUM 都會產生一個新的隨機浮點數。
原因在於 Java 的類別初始化機制:
Test 被首次主動使用(此處為存取 Test.NUM)時,JVM 會觸發其初始化。new Random().nextFloat()。Random 類別預設使用系統時間作為種子,因此每次建立新實例並呼叫 nextFloat() 都會得到不同結果。NUM 被宣告為 final,但 final 僅保證引用不可變(對基本型別則是值不可變),並不代表初始化過程是確定性的。static float getRandomNum(),讓調用者明確知曉每次調用都會產生新值。new Random().nextFloat() 非常數表達式,故無法內聯。若將介面改為類別,並將 NUM 定義為 static final,行為完全一致。這體現了 Java 中「常量」語義的核心:**引用不可變 ≠ 初始化確定性**。
實務開發中,若發現「常量」值在不同執行間發生變化,優先檢查其初始化邏輯是否包含隨機數、時間戳、IO 操作等非確定性行為。