Class.forName的作用以及new和newInstance生成對象的區別

Class.forName(xxx.xx.xx) 返回的是一個類

首先你要明白在java裏面任何class都要裝載在虛擬機上才能運行。這句話就是裝載類用的(和new 不一樣,要分清楚)。

至於什麼時候用,你可以考慮一下這個問題,給你一個字符串變量,它代表一個類的包名和類名,你怎麼實例化它?只有你提到的這個方法了,不過要再加一點。
A a = (A)Class.forName(“pacage.A”).newInstance();
這和你
A a = new A();
是一樣的效果。

關於補充的問題
答案是肯定的,jvm會執行靜態代碼段,你要記住一個概念,靜態代碼是和class綁定的,class裝載成功就表示執行了你的靜態代碼了。而且以後不會再走這段靜態代碼了。

Class.forName(xxx.xx.xx) 返回的是一個類
Class.forName(xxx.xx.xx);的作用是要求JVM查找並加載指定的類,也就是說JVM會執行該類的靜態代碼段

動態加載和創建Class 對象,比如想根據用戶輸入的字符串來創建對象
String str = 用戶輸入的字符串
Class t = Class.forName(str);
t.newInstance();

在初始化一個類,生成一個實例的時候,newInstance()方法和new關鍵字除了一個是方法,一個是關鍵字外,最主要有什麼區別?它們的區別在於創建對象的方式不一樣,前者是使用類加載機制,後者是創建一個新類。那麼爲什麼會有兩種創建對象方式?這主要考慮到軟件的可伸縮、可擴展和可重用等軟件設計思想。

Java中工廠模式經常使用newInstance()方法來創建對象,因此從爲什麼要使用工廠模式上可以找到具體答案。 例如:
class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();

其中ExampleInterface是Example的接口,可以寫成如下形式:
String className = “Example”;
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

進一步可以寫成如下形式:
String className = readfromXMlConfig;//從xml 配置文件中獲得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

上面代碼已經不存在Example的類名稱,它的優點是,無論Example類怎麼變化,上述代碼不變,甚至可以更換Example的兄弟類Example2 , Example3 , Example4……,只要他們繼承ExampleInterface就可以。

從JVM的角度看,我們使用關鍵字new創建一個類的時候,這個類可以沒有被加載。但是使用newInstance()方法的時候,就必須保證:1、這個類已經加載;2、這個類已經連接了。而完成上面兩個步驟的正是Class的靜態方法forName()所完成的,這個靜態方法調用了啓動類加載器,即加載java API的那個加載器。

現在可以看出,newInstance()實際上是把new這個方式分解爲兩步,即首先調用Class加載方法加載某個類,然後實例化。 這樣分步的好處是顯而易見的。我們可以在調用class的靜態加載方法forName時獲得更好的靈活性,提供給了一種降耦的手段。

最後用最簡單的描述來區分new關鍵字和newInstance()方法的區別:
newInstance: 弱類型。低效率。只能調用無參構造。
new: 強類型。相對高效。能調用任何public構造。

返回與帶有給定字符串名的類或接口相關聯的 Class 對象。調用此方法等效於:Class.forName(className, true, currentLoader)
其中 currentLoader 表示此類的定義類加載器。
例如,以下代碼片段返回 java.lang.Thread 類的運行時 Class 描述符。
Class t = Class.forName(“java.lang.Thread”)
調用 forName(“X”) 將導致名爲 X 的類被初始化。
參數:
className - 所需類的完全限定名。
返回:
具有指定名的類的 Class 對象。
通俗的說就是:獲得字符串參數中指定的類,並初始化該類類裝載.
類裝載就是把一個類或是一個接口的字節碼文件,通過解析該字節碼來構建代表這個類或是這個接口的實例的過程。 這個字節碼文件來源可能是壓縮包、網絡、運行時編譯出的或者自動生成的class文件,jvm spec沒有規定必須從什麼地方加載。

類裝載的兩種方式:
1.Class c1 = Class.forName (“java.lang.String”);
2.ClassLoader cl = new ClassLoader();
Class cl.loadClass( String name, boolean resolve );
兩種裝載方法的區別:
不同的類裝載器
Class.forName是從指定的classloader中裝載類,如果沒有指定,也就是一個參數的時候,是從裝載當前對象實例所在的classloader中裝載類。
而ClassLoader的實例調用loadclass方法,是指從當前ClassLoader實例中調用類,而這個實例與裝載當前所在類實例的Classloader也許不是同一個.
說白了就是他們實現裝載的時候,使用的類裝載器的指定是不同的。那爲什麼使用不同的ClassLoader來裝載類呢?

其實使用多個classloader加載類的情況非常常見,比如說我們的app server都是這樣的. 在Web與EJB間, 他們的classLoader就是不同的,這樣做的目的就是爲了避免兩者間類裝載的相互干擾。

是否實例化類
Class的裝載分了三個階段,loading(裝載),linking(連接)和initializing(實例化)分別定義在The Java Language Specification的12.2,12.3和12.4。
Class.forName(className)實際上是調用Class.forName(className, true, this.getClass().getClassLoader())。注意第二個參數,是指Class被loading後是不是必須被初始化。
ClassLoader.loadClass(className)實際上調用的是ClassLoader.loadClass(name, false),第二個參數指出Class是否被link。
區別就出來了。Class.forName(className)裝載的class已經被實例化,而ClassLoader.loadClass(className)裝載的class還沒有被link,所以就更談不上實例化了。
簡單說,就是通過類名反射出類的對象 。
一般情況下,這兩個方法效果一樣,都能裝載Class。但如果程序需要Class被實例化,就必須用Class.forName(name)了。
————————————————
版權聲明:本文爲CSDN博主「jiudihanbing」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/jiudihanbing/article/details/50740533

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章