天天看點

在什麼情況下調用類中static final修飾的屬性時不需要初始化類

被final修飾靜态字段在操作使用時,不會使類進行初始化,因為在編譯期已經将此常量放在常量池。

測試:

class ClassLoadTime{

  static{

    System.out.println("ClassLoadTime類初始化時就會被執行!");

  }

  public static final int MIN = 10; (防止測試類和此類不在一個包,使用public修飾符)

}

class ClassLoadDemo{

  public static void main(String[] args){

   System.out.println(ClassLoadTime.MIN);

  }

}

由于編譯器編譯優化,導緻該變量在常量池中,當你用ClassLoadTime.MIN時,隻會到常量池中查該數值,不會加載ClassLoaderTime類。

輸出:

10

————————————————

版權聲明:本文為CSDN部落客「冰淩其」的原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/xiao1_1bing/article/details/81139926

在看到上述文章的這段内容時有一個疑問,如果我通路的屬性類型是一個引用類型的話也不會初始化 ClassLoaderTime類嗎?于是用一下代碼測試了一下:

public class HungrySingletonPattern {
	static{
		System.out.println("HungrySingletonPattern類初始化時就會被執行!");
	}
	
	public static final HungrySingletonPattern instance = new HungrySingletonPattern();
	public static final String str = "調我不用初始化類!";
	
	private HungrySingletonPattern() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	public static final HungrySingletonPattern getInstance(){
		return instance;
	}
}

public class Test {
	public static void main(String[] args) throws SecurityException, NoSuchFieldException {
		HungrySingletonPattern i = HungrySingletonPattern.instance;
//		String s = HungrySingletonPattern.str;
	}
}
           

運作結果如下:

HungrySingletonPattern類初始化時就會被執行!

說明這個HungrySingletonPattern類被初始化了!為什麼上邊的例子不會初始化類,在把類中屬性換成引用類型時類就會被初始化呢?上邊的例子說是編譯器的優化導緻的,那麼我們就來看看兩段代碼生成的位元組碼有什麼差別吧:

1. main函數中調用HungrySingletonPattern.str的情況:

Code:
   stack=1, locals=2, args_size=1
      0: ldc           #21                 // String 調我不用初始化類!
      2: astore_1
      3: return
           

2. main函數中調用HungrySingletonPattern.instance的情況:

Code:
      stack=1, locals=2, args_size=1
         0: getstatic     #21                 // Field cn/edu/xidian/singleton/h
ungry/HungrySingletonPattern.instance:Lcn/edu/xidian/singleton/hungry/HungrySing
letonPattern;
         3: astore_1
         4: return
           

我們發現上述兩個例子中生成的位元組碼唯一的差別就是1中生成的指令是ldc指令,而2中生成的指令是getstatic指令。那麼這兩個指令都是什麼含義呢?

我們通過查詢java虛拟機規範發現指令的解釋如下:

0xb2   getstatic   擷取指定類的靜态域,并将其值壓入棧頂。
0x12 ldc 将 int,float 或 String 型常量值從常量池中推送至棧頂。

是以我們可以大膽的推測:當類中的基本類型或String類型在被static final修飾時,編譯器會将指令直接優化成ldc指令,進而在隻擷取該屬性的值時,不需要初始化它所在的類;而當被static final修飾的屬性為引用類型時,由于編譯器無法直接将其轉化為常量,故在擷取該類型的屬性時還是需要初始化類的。