溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Java中Class.forName()怎么使用

發(fā)布時(shí)間:2022-11-25 09:34:10 來(lái)源:億速云 閱讀:128 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“Java中Class.forName()怎么使用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

1 什么是class對(duì)象

類(lèi)是程序的一部分,每個(gè)類(lèi)都有一個(gè)class對(duì)象。

換言之,每當(dāng)編寫(xiě)并且編譯了一個(gè)新類(lèi),就會(huì)產(chǎn)生一個(gè)class對(duì)象(更恰當(dāng)?shù)恼f(shuō),是被保存在一個(gè)同名的class文件中)。

為了生成這個(gè)類(lèi)的對(duì)象,運(yùn)行這個(gè)程序的Java虛擬機(jī)(jvm)將使用被稱(chēng)為“類(lèi)加載器”的子系統(tǒng)。

所有的類(lèi)都是在對(duì)其第一次使用的時(shí)候被加載到JVM中。如當(dāng)程序創(chuàng)建對(duì)第一個(gè)靜態(tài)成員的引用時(shí),就會(huì)加載這個(gè)類(lèi)?;蛘呤褂胣ew關(guān)鍵字創(chuàng)建新的對(duì)象的時(shí)候。

因此java程序在它運(yùn)行之前并非完全加載,其各個(gè)部分是在必須的時(shí)候才加載的。類(lèi)加載器首先檢查這個(gè)類(lèi)的class對(duì)象是否已經(jīng)加載。如果尚未加載,默認(rèn)的類(lèi)加載器就會(huì)根據(jù)類(lèi)名查找.class文件。

實(shí)際上在Java中每個(gè)類(lèi)都有且只有一個(gè)Class對(duì)象。

Class 沒(méi)有公共構(gòu)造方法,因此不能顯式地聲明一個(gè)Class對(duì)象,Class 對(duì)象是在載入類(lèi)時(shí)由Java 虛擬機(jī)以及通過(guò)調(diào)用類(lèi)載入器中的 defineClass 方法自己主動(dòng)構(gòu)造的。

Class類(lèi)被創(chuàng)建后的對(duì)象就是Class對(duì)象,注意,Class對(duì)象表示的是自己手動(dòng)編寫(xiě)類(lèi)的類(lèi)型信息,比如創(chuàng)建一個(gè)Shapes類(lèi),那么,JVM就會(huì)創(chuàng)建一個(gè)Shapes對(duì)應(yīng)Class類(lèi)的Class對(duì)象,該Class對(duì)象保存了Shapes類(lèi)相關(guān)的類(lèi)型信息。

實(shí)際上在Java中每個(gè)類(lèi)都有一個(gè)Class對(duì)象,每當(dāng)我們編寫(xiě)并且編譯一個(gè)新創(chuàng)建的類(lèi)就會(huì)產(chǎn)生一個(gè)對(duì)應(yīng)Class對(duì)象并且這個(gè)Class對(duì)象會(huì)被保存在同名.class文件里(編譯后的字節(jié)碼文件保存的就是Class對(duì)象),那為什么需要這樣一個(gè)Class對(duì)象呢?

是這樣的,當(dāng)我們new一個(gè)新對(duì)象或者引用靜態(tài)成員變量時(shí),Java虛擬機(jī)(JVM)中的類(lèi)加載器子系統(tǒng)會(huì)將對(duì)應(yīng)Class對(duì)象加載到JVM中,然后JVM再根據(jù)這個(gè)類(lèi)型信息相關(guān)的Class對(duì)象創(chuàng)建我們需要實(shí)例對(duì)象或者提供靜態(tài)變量的引用值。

需要特別注意的是,手動(dòng)編寫(xiě)的每個(gè)class類(lèi),無(wú)論創(chuàng)建多少個(gè)實(shí)例對(duì)象,在JVM中都只有一個(gè)Class對(duì)象,即在內(nèi)存中每個(gè)類(lèi)有且只有一個(gè)相對(duì)應(yīng)的Class對(duì)象,挺拗口,通過(guò)下圖理解(內(nèi)存中的簡(jiǎn)易現(xiàn)象圖):

Java中Class.forName()怎么使用

到這我們也就可以得出以下幾點(diǎn)信息:

  • Class類(lèi)也是類(lèi)的一種,與class關(guān)鍵字是不一樣的。

  • 手動(dòng)編寫(xiě)的類(lèi)被編譯后會(huì)產(chǎn)生一個(gè)Class對(duì)象,其表示的是創(chuàng)建的類(lèi)的類(lèi)型信息,而且這個(gè)Class對(duì)象保存在同名.class的文件中(字節(jié)碼文件),比如創(chuàng)建一個(gè)Shapes類(lèi),編譯Shapes類(lèi)后就會(huì)創(chuàng)建其包含Shapes類(lèi)相關(guān)類(lèi)型信息的Class對(duì)象,并保存在Shapes.class字節(jié)碼文件中。

  • 每個(gè)通過(guò)關(guān)鍵字class標(biāo)識(shí)的類(lèi),在內(nèi)存中有且只有一個(gè)與之對(duì)應(yīng)的Class對(duì)象來(lái)描述其類(lèi)型信息,無(wú)論創(chuàng)建多少個(gè)實(shí)例對(duì)象,其依據(jù)的都是用一個(gè)Class對(duì)象。

  • Class類(lèi)只存私有構(gòu)造函數(shù),因此對(duì)應(yīng)Class對(duì)象只能有JVM創(chuàng)建和加載

  • Class類(lèi)的對(duì)象作用是運(yùn)行時(shí)提供或獲得某個(gè)對(duì)象的類(lèi)型信息,這點(diǎn)對(duì)于反射技術(shù)很重要(關(guān)于反射稍后分析)。

2 獲得class對(duì)象的三種方法

1、調(diào)用Object類(lèi)的getClass()方法來(lái)得到Class對(duì)象,這也是最常見(jiàn)的產(chǎn)生Class對(duì)象的方法。

比如:

MyObject x;
Class c1 = x.getClass();
  • .Object.getClass(); Object中自帶的方法,getclass(),返回一個(gè)class對(duì)象。

2、使用Class類(lèi)的中靜態(tài)forName()方法獲得與字符串相應(yīng)的Class對(duì)象。

比如: 

Class c2=Class.forName("MyObject")

MyObject必須是接口或者類(lèi)的名字。

  • class.forname()

Class c=Class.forName("類(lèi)的全限定名")

傳入string類(lèi)型參數(shù),要求jvm查找并加載指定的類(lèi),返回的是一個(gè)class對(duì)象的引用。

3、獲取Class類(lèi)型對(duì)象的第三個(gè)方法很easy。假設(shè)T是一個(gè)Java類(lèi)型。那么T.class就代表了匹配的類(lèi)對(duì)象。

比如

Class cl1 = Manager.class;
Class cl2 = int.class;
Class cl3 = Double[].class;

注意:Class對(duì)象實(shí)際上描寫(xiě)敘述的僅僅是類(lèi)型。而這類(lèi)型未必是類(lèi)或者接口。

比如上面的int.class是一個(gè)Class類(lèi)型的對(duì)象。

因?yàn)闅v史原因。數(shù)組類(lèi)型的getName方法會(huì)返回奇怪的名字。

3 class的作用和方法

  • getname():以string類(lèi)型返回class對(duì)象表示的實(shí)體(類(lèi),接口,數(shù)組,基本類(lèi)型,void等)名稱(chēng)

  • newInstance():創(chuàng)建一個(gè)實(shí)例,只能調(diào)用默認(rèn)構(gòu)造器。

  • getsuperclass():返回class表示的實(shí)體超類(lèi)的名稱(chēng)

  • getSimpleName():不辦含包名的類(lèi)名。

  • isInterfence:告訴你這個(gè)class對(duì)象是否表示某個(gè)接口。

1、getName() 

一個(gè)Class對(duì)象描寫(xiě)敘述了一個(gè)特定類(lèi)的屬性,Class類(lèi)中最經(jīng)常使用的方法getName以 String 的形式返回此 Class 對(duì)象所表示的實(shí)體(類(lèi)、接口、數(shù)組類(lèi)、基本類(lèi)型或 void)名稱(chēng)。

2、newInstance()

Class另一個(gè)實(shí)用的方法能夠?yàn)轭?lèi)創(chuàng)建一個(gè)實(shí)例,這種方法叫做newInstance()。

比如:

x.getClass.newInstance()

創(chuàng)建了一個(gè)同x一樣類(lèi)型的新實(shí)例。newInstance()方法調(diào)用默認(rèn)構(gòu)造器(無(wú)參數(shù)構(gòu)造器)初始化新建對(duì)象。

3、getClassLoader() 

返回該類(lèi)的類(lèi)載入器。

4、getComponentType() 

返回表示數(shù)組組件類(lèi)型的 Class。

5、getSuperclass() 

返回表示此 Class 所表示的實(shí)體(類(lèi)、接口、基本類(lèi)型或 void)的超類(lèi)的 Class。

6、isArray() 

判定此 Class 對(duì)象是否表示一個(gè)數(shù)組類(lèi)。

1、forName和newInstance結(jié)合起來(lái)使用,能夠依據(jù)存儲(chǔ)在字符串中的類(lèi)名創(chuàng)建對(duì)象。比如

Object obj = Class.forName(s).newInstance();

2、虛擬機(jī)為每種類(lèi)型管理一個(gè)獨(dú)一無(wú)二的Class對(duì)象。因此能夠使用==操作符來(lái)比較類(lèi)對(duì)象。比如:

if(e.getClass() == Employee.class)...

4 Class.forName()用法

主要功能

  • Class.forName(xxx.xx.xx)返回的是一個(gè)類(lèi)。

  • Class.forName(xxx.xx.xx)的作用是要求JVM查找并加載指定的類(lèi),也就是說(shuō)JVM會(huì)執(zhí)行該類(lèi)的靜態(tài)代碼段。

Class.forName是一個(gè)靜態(tài)方法,相同能夠用來(lái)載入類(lèi)。

該方法有兩種形式:Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)。

第一種形式的參數(shù) name表示的是類(lèi)的全名;initialize表示是否初始化類(lèi)。loader表示載入時(shí)使用的類(lèi)載入器。

另外一種形式則相當(dāng)于設(shè)置了參數(shù) initialize的值為 true。loader的值為當(dāng)前類(lèi)的類(lèi)載入器

4.1 什么時(shí)候用Class.forName()?

先來(lái)個(gè)熱身,給你一個(gè)字符串變量,它代表一個(gè)類(lèi)的包名和類(lèi)名,你怎么實(shí)例化它?你第一想到的肯定是new,但是注意一點(diǎn):

A a = (A)Class.forName(“pacage.A”).newInstance();

這和你 A a = new A(); 是一樣的效果。

現(xiàn)在言歸正傳。

動(dòng)態(tài)加載和創(chuàng)建Class 對(duì)象,比如想根據(jù)用戶(hù)輸入的字符串來(lái)創(chuàng)建對(duì)象時(shí)需要用到:

String str = “用戶(hù)輸入的字符串” ;
Class t = Class.forName(str);
t.newInstance();

在初始化一個(gè)類(lèi),生成一個(gè)實(shí)例的時(shí)候,newInstance()方法和new關(guān)鍵字除了一個(gè)是方法,一個(gè)是關(guān)鍵字外,最主要有什么區(qū)別?

它們的區(qū)別在于創(chuàng)建對(duì)象的方式不一樣,前者是使用類(lèi)加載機(jī)制,后者是創(chuàng)建一個(gè)新類(lèi)。

4.2 newInstance和new關(guān)鍵字的區(qū)別

Java中工廠(chǎng)模式經(jīng)常使用newInstance()方法來(lái)創(chuàng)建對(duì)象,因此從為什么要使用工廠(chǎng)模式上可以找到具體答案。 例如:

class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();

其中ExampleInterface是Example的接口,可以寫(xiě)成如下形式:

String className = “Example”;
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

進(jìn)一步可以寫(xiě)成如下形式:

String className = readfromXMlConfig;//從xml 配置文件中獲得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

上面代碼已經(jīng)不存在Example的類(lèi)名稱(chēng),它的優(yōu)點(diǎn)是,無(wú)論Example類(lèi)怎么變化,上述代碼不變,甚至可以更換Example的兄弟類(lèi)Example2 , Example3 , Example4……,只要他們繼承ExampleInterface就可以。

從JVM的角度看,我們使用關(guān)鍵字new創(chuàng)建一個(gè)類(lèi)的時(shí)候,這個(gè)類(lèi)可以沒(méi)有被加載。但是使用newInstance()方法的時(shí)候,就必須保證:

1、這個(gè)類(lèi)已經(jīng)加載;

2、這個(gè)類(lèi)已經(jīng)連接了。

而完成上面兩個(gè)步驟的正是Class的靜態(tài)方法forName()所完成的,這個(gè)靜態(tài)方法調(diào)用了啟動(dòng)類(lèi)加載器,即加載 java API的那個(gè)加載器。

現(xiàn)在可以看出,newInstance()實(shí)際上是把new這個(gè)方式分解為兩步,即首先調(diào)用Class加載方法加載某個(gè)類(lèi),然后實(shí)例化。

這樣分步的好處是顯而易見(jiàn)的。我們可以在調(diào)用class的靜態(tài)加載方法forName時(shí)獲得更好的靈活性,提供給了一種降耦的手段。

最后用最簡(jiǎn)單的描述來(lái)區(qū)分new關(guān)鍵字和newInstance()方法的區(qū)別: 

  • newInstance: 弱類(lèi)型。低效率。只能調(diào)用無(wú)參構(gòu)造。 

  • new: 強(qiáng)類(lèi)型。相對(duì)高效。能調(diào)用任何public構(gòu)造。

  • Class.forName(“”)返回的是類(lèi)。

  • Class.forName(“”).newInstance()返回的是object

5 應(yīng)用問(wèn)題解析

情景一:載入數(shù)據(jù)庫(kù)驅(qū)動(dòng)的時(shí)候

Class.forName的一個(gè)非經(jīng)常見(jiàn)的使用方法是在載入數(shù)據(jù)庫(kù)驅(qū)動(dòng)的時(shí)候

Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con=DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName==JSP","jph","jph");

為什么在我們載入數(shù)據(jù)庫(kù)驅(qū)動(dòng)包的時(shí)候有的卻沒(méi)有調(diào)用newInstance( )方法呢?

即有的jdbc連接數(shù)據(jù)庫(kù)的寫(xiě)法里是Class.forName(xxx.xx.xx);而有一些:Class.forName(xxx.xx.xx).newInstance()。為什么會(huì)有這兩種寫(xiě)法呢? 

 通過(guò)查詢(xún)Java Documentation我們會(huì)發(fā)現(xiàn)使用Class.forName( )靜態(tài)方法的目的是為了動(dòng)態(tài)加載類(lèi)。通常編碼過(guò)程中,在加載完成后,一般還要調(diào)用Class下的newInstance( )靜態(tài)方法來(lái)實(shí)例化對(duì)象以便操作。因此,單使用Class.forName( )是動(dòng)態(tài)加載類(lèi)是沒(méi)有用的,其最終目的是為了實(shí)例化對(duì)象。

剛才提到,Class.forName("");的作用是要求JVM查找并加載指定的類(lèi),如果在類(lèi)中有靜態(tài)初始化器的話(huà),JVM必然會(huì)執(zhí)行該類(lèi)的靜態(tài)代碼 段。而在JDBC規(guī)范中明確要求這個(gè)Driver類(lèi)必須向DriverManager注冊(cè)自己,即任何一個(gè)JDBC Driver的 Driver類(lèi)的代碼都必須類(lèi)似如下:

public class MyJDBCDriver implements Driver {
   static {
     DriverManager.registerDriver(new MyJDBCDriver());
  }
  }

既然在靜態(tài)初始化器的中已經(jīng)進(jìn)行了注冊(cè),所以我們?cè)谑褂肑DBC時(shí)只需要Class.forName(XXX.XXX);就可以了。

既然在靜態(tài)初始化器的中已經(jīng)進(jìn)行了注冊(cè),所以我們?cè)谑褂肑DBC時(shí)只需要Class.forName(XXX.XXX);就可以了。

public class ProxoolDriver implements Driver {
 
    private static final Log LOG = LogFactory.getLog(ProxoolDriver.class);
 
    static {
        try {
            DriverManager.registerDriver(new ProxoolDriver());
        } catch (SQLException e) {
            System.out.println(e.toString());
        }
    }
 
}

情景二:使用AIDL與電話(huà)管理Servic進(jìn)行通信

Method method =Class.forName("android.os.ServiceManager")
 
         .getMethod("getService",String.class);
 
// 獲取遠(yuǎn)程TELEPHONY_SERVICE的IBinder對(duì)象的代理
 
IBinder binder =(IBinder) method.invoke(null, new Object[] { TELEPHONY_SERVICE});
 
// 將IBinder對(duì)象的代理轉(zhuǎn)換為ITelephony對(duì)象
 
ITelephonytelephony = ITelephony.Stub.asInterface(binder);
 
// 掛斷電話(huà)
 
telephony.endCall();

“Java中Class.forName()怎么使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI