前言
上一節我們討論過通過關鍵字synchronized實現線程同步,同時最主要了解到在Java中className.class所代表的具體含義,在博客寫完后,感覺還是有點迷糊,然后再次深入了解后,原來關於className.class在Java語言規范中定義為(Class Literal),我們翻譯為類文字好像比較生硬,還是以英文作為說明最好,本節我們再來詳細討論下Class Literal。
Class Literal
在java語言規范中有對Class Literals的定義《https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.8.2》:它是由類,接口,數組或原始類型的名稱或偽類型void組成的表達式,后面緊跟【.】和【class】。比如C.class,那么它的類型則為Class <C>,其中C是類,接口或數組類型的名稱。比如p.class的類型(其中p是基本類型的名稱)是Class <B>,其中B是裝箱轉換后的類型p的表達式的類型,也就說例如int.class,它的Class Literal實際上是Class<Integer>。而void.class,它的Class Literal是Class<Void>。最后對於類型的變量當然也就沒有Class Literal。比如我們要想獲取包裝類Integer的Class Literal,可以通過如下兩種方式來獲取:
System.out.println(Class.forName("java.lang.Integer")); System.out.println(Integer.class);
接下來我們通過定義一個類來更加深入了解,如下:
class Test { }
我們再來通過上述方法獲取其Class Literal,此時forName中參數則是類所在包空間,如下:
Class cls = Class.forName("com.company.Test"); System.out.println(cls.toString()); System.out.println(Test.class);
還記得上一節我們重點講解的就是通過關鍵字synchronized,在其方法或同步塊中的監視器或鎖定對象是className.class即Class Literal,我們也知道在類加載時機的第一階段中的第三件事情則是在JVM中生成對於對應類且只存在一次的java.lang.class的對象,該對象包含有關該類的元數據等等,也就是說該鎖定對象就是對該類生成的java.lang.class對象的引用。例如,如下例子:
class Test { public void lockMethod1() { synchronized (Test.class) { System.out.println("1"); } } public void lockMethod2() { synchronized (Test.class) { System.out.println("2"); } } }
當發生並發分別執行如上方法一和方法二,若此時執行到方法二時,但是方法一並未執行完成,通過上述對鎖定對象的詳細分析,此時必將導致方法二會被阻塞,直到方法一執行完畢,釋放線程同步鎖。到此我們講解了Class Literal在線程同步中的使用,其實在反射中使用的機會也比較多,比如創建命令行將程序進行重啟的命令,我們通過ProcessImpl類中的createCommandLine方法,創建命令行,這里我們嘗試使用反射來實現,C#中通過反射調用方法,其參數是Object數組(記得是這樣),在java中通過反射調用方法,其參數就是ClassLiteral泛型數組,所以我們必須顯式指定參數類型,這就應用到了Class Literal,如下:
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException { final String[] cmd = { "shutdown.exe", "/r", "/t 0", }; final String executablePath = new File(cmd[0]).getPath(); final Class<?> impl = ClassLoader.getSystemClassLoader().loadClass("java.lang.ProcessImpl"); final Method myMethod = impl.getDeclaredMethod( "createCommandLine", new Class[] { int.class, String.class, String[].class }); myMethod.setAccessible(true); final Object result = myMethod.invoke( null, 2, executablePath, cmd); System.out.println(result); }
總結
本節我們再一次深入並了解className.class,在java語言規范中其專有名詞為Class Literal,並對其在線程同步中的使用以及為何就保證了線程安全又進行了啰嗦式的分析,最后也通過一個反射例子作為Class Literal的使用練習而結束本文,至此關於Class Literal的學習算告一段落。下一節我們進入學習Hibernate。