導航:首頁 > 編程語言 > java載入類的方法

java載入類的方法

發布時間:2022-05-29 12:42:08

java解釋器如何載入類

類載入次序:1、靜態代碼塊或者靜態方法->2、main方法調用到的方法
對象載入次序:1、靜態代碼塊或者靜態方法->2、非靜態代碼塊或者非靜態方法->3、對象的構造方法。
但是有一段代碼沒有辦法解釋。代碼忘了,過段時間丟上來
個人感覺應該好像不大對勁,我覺得應該是:
類裝載時,1、靜態代碼塊或者靜態方法被調用
然後是程序的運行,main調用到的方法會被執行,如果是新建一個對象,則
2、非靜態代碼塊或者非靜態方法->3、對象的構造方法順序執行。

===============================================

首先我們要分析類載入原理,java中默認有三種類載入器:引導類載入器,擴展類載入器,系統類載入器(也叫應用類載入器)引導類載入器負責載入jdk中的系統類,這種類載入器都是用c語言實現的,在java程序中沒有辦法獲得這個類載入器,對於java程序是一個概念而已,基本上不用考慮它的存在,像String,Integer這樣的類都是由引導類載入器載入器的.
擴展類載入器負責載入標准擴展類,一般使用java實現,這是一個真正的java類載入器,負責載入jre/lib/ext中的類,和普通的類載入器一樣,其實這個類載入器對我們來說也不是很重要,我們可以通過java程序獲得這個類載入器。
系統類載入器,載入第一個應用類的載入器(其實這個定義並不準確,下面你將會看到),也就是執行java MainClass 時載入MainClass的載入器,這個載入器使用java實現,使用的很廣泛,負責載入classpath中指定的類。

類載入器之間有一定的關系(父子關系),我們可以認為擴展類載入器的父載入器是引導類載入器(當然不這樣認為也是可以的,因為引導類載入器表現在java中就是一個null),不過系統類載入器的父載入器一定是擴展類載入器,類載入器在載入類的時候會先給父載入器一個機會,只有父載入器無法載入時才會自己去載入。

我們無法獲得引導類載入器,因為它是使用c實現的,而且使用引導類載入器載入的類通過getClassLoader方法返回的是null.所以無法直接操作引導類載入器,但是我們可以根據Class.getClassLoader方法是否為null判斷這個類是不是引導類載入器載入的,可以通過下面的方法獲得引導類載入器載入的類路徑(每個jar包或者文件夾對應了一個URL);
sun.misc.Launcher.getBootstrapClassPath().getURLs()
你可以直接在你的main函數中輸出就可以了
System.out.println(java.util.Arrays.asList(sun.misc.Launcher.getBootstrapClassPath().getURLs()).toString());
得到的結果是:
[file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/rt.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/i18n.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/sunrsasign.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jsse.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jce.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/charsets.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/classes]

其實我們是可以指定引導類載入器的類路徑的,java提供了一個-Xbootclasspath參數,不過這個參數不是標准參數。
java -Xbootclasspath: 運行時指定引導類載入器的載入路徑(jar文件或者目錄)
java -Xbootclasspath/p:和上面的相同,不過把這個路徑放到原來的路徑前面
java -Xbootclasspath/a:這個就是在原引導類路徑後面添加類路徑。
上面我們有提過載入第一個應用類未必就是系統載入器。
如果我把這個應用類的路徑放到引導類路徑中,它將會被引導類載入器載入,大致這樣
java -Xbootclasspath/a:myjar.jar MainClass
如果MainClass在myjar.jar中,那麼這個類將會被引導類載入器載入。
如果希望看詳情,使用-verbose參數,為了看的更清楚,使用重定向,大致為(windows下):
java -verbose -Xbootclasspath/a:myjar.jar MainClass -> C:\out.txt
通過這個參數我們可以實現自己的系統類,比如替換掉java.lang.Object的實現,自己可以擴展
一些方法,不過這樣做似乎沒有好處,因為那就不是標准了。

我們最關心的還是系統類載入器,一般都認為系統類載入器是載入應用程序第一個類的載入器,
也就是java MainClass命令中載入MainClass的類載入器,這種說法雖然不是很嚴謹,但基本上還是可以這樣認為的,因為我們很少會改變引導類載入器和擴展類載入器的默認行為。應該說系統類載入器負責載入classpath路徑中的而且沒有被擴展類載入器載入的類(當然也包括引導類載入器載入的)。如果classpath中有這個類,但是這個類也在擴展類載入器的類路徑,那麼系統類載入器將沒有機會載入它。
我們很少改變擴展類載入器的行為,所以一般你自己定義的類都是系統類載入器載入器的。

獲得系統類載入器非常簡單,假設MyClass是你定義的一個類
MyClass.class.getClassLoader()返回的就是系統類載入器,當然這種方法無法保證絕對正確,我們可以使用更簡單而且一定正確的方式:
ClassLoader.getSystemClassLoader()獲得系統類載入器。我們知道ClassLoader是一個抽象類,所以系統類載入器肯定是ClassLoader的一個子類實現。我們來看看它是什麼
ClassLoader.getSystemClassLoader().getClass();
結果是class sun.misc.Lancher$AppClassLoader
可以看出這是sun的一個實現,從名字可以看出是一個內部類,目前我也沒有看到這個源代碼,似乎還不是很清晰:
我們在看看它的父類是什麼:
ClassLoader.getSystemClassLoader().getClass().getSuperclass();
結果是:class java.net.URLClassLoader
這個是j2se的標准類,它的父類是SecureClassLoader,而SecureClassLoader是繼承ClassLoader的。
現在整個關系應該很清楚,我們會看到幾乎所有的ClassLoader實現都是繼承URLClassLoader的。
因為系統類載入器是非常重要的,而且是我們可以直接控制的,所以我們後面還會介紹,不過先來看一下擴展類
載入器以及它們之間的關系。

擴展類載入器似乎是一個不起眼的角色,它負責載入java的標准擴展(jre/lib/ext目錄下的所有jar),它其實就是一個普通的載入器,看得見摸得著的。
首先的問題是怎麼知道擴展類載入器在哪裡?
的確沒有直接途徑獲得擴展類載入器,但是我們知道它是系統類載入器的父載入器,我們已經很容易的獲得系統類載入器了,所以我們可以間接的獲得擴展類載入器:
ClassLoader.getSystemClassLoader().getParent().getClass();
其實是通過系統類載入器間接的獲得了擴展類載入器,看看是什麼東西:
結果是:class sun.misc.Launcher$ExtClassLoader
這個類和系統類載入器一樣是一個內部類,而且定義在同一個類中。
同樣看看它的父類是什麼:
ClassLoader.getSystemClassLoader().getParent().getClass().getSuperclass();
可以看出結果也是class java.net.URLClassLoader
擴展類載入jre/lib/ext目錄下的所有類,包括jar,目錄下的所有類(目錄名不一定要classes).
現在可以回答上面的問題了,你寫一個HelloWorld,放到jre/lib/ext/下的某個目錄
比如 jre/lib/ext/myclass/HelloWorld.class
然後在你classpath也設置一份到這個類的路徑,結果執行java HelloWorld時,這個類是被擴展類載入器載入器的,可以這樣證明
public static void main(String[] args){
System.out.println("loaded by"+HelloWorld.class.getClassLoader().getClass());
System.out.println("Hello World");
}
結果可以得到class sun.misc.Launcher$ExtClassLoader
當然如果你把jre/lib/ext下myclass這個目錄刪除,仍然可以運行,但是這樣結果是
class sun.misc.Lancher$AppClassLoader
如果你不知道這個過程的話,假設在你擴展類路徑下有一份classpath中的拷貝,或者是比較低的版本,當你使用新的版本時會發現沒有起作用,知道這個過程你就不會覺得奇怪了。另外就是兩個不同的類載入器是可以載入一個同名的類的,也就是說雖然擴展類載入器載入了某個類,系統類載入器是可以載入自己的版本的,
但是現有的實現都沒有這樣做,ClassLoader中的方法是會請求父類載入器先載入的,如果你自己定義類載入器完全可以修改這種默認行為,甚至可以讓他沒有父載入器。

這里給出一個方法如何獲得擴展類載入器載入的路徑:
String path=System.getProperty("java.ext.dirs");
File dir=new File(path);
if(!dir.exists()||!dir.isDirectory()){
return Collections.EMPTY_LIST;
}
File[] jars=dir.listFiles();
URL[] urls=new URL[jars.length];
for(int i=0;i<jars.length;i++){
urls[i]=sun.misc.URLClassPath.pathToURLs(jars[i].getAbsolutePath())[0];
}
return Arrays.asList(urls);

對於擴展類載入器我們基本上不會去關心,也很少把你自己的jar放到擴展路徑,大部分情況下我們都感覺不到它的存在,當然如果你一定要放到這個目錄下,一定要知道這個過程,它會優先於classpath中的類。

現在我們應該很清楚知道某個類是哪個載入器載入的,並且知道為什麼是它載入的,如果要在運行時獲得某個類的類載入器,直接使用Class的getClassLoader()方法就可以了。

用戶定義的類一般都是系統類載入器載入的,我們很少直接使用類載入器載入類,我們甚至很少自己載入類。
因為類在使用時會被自動載入,我們用到某個類時該類會被自動載入,比如new A()會導致類A自動被載入,不過這種載入只發生一次。
我們也可以使用系統類載入器手動載入類,ClassLoader提供了這個介面
ClassLoader.getSystemClassLoader().loadClass("classFullName");
這就很明確的指定了使用系統類載入器載入指定的類,但是如果該類能夠被擴展類載入器載入,系統類載入器還是不會有機會的。
我們最常用的還是使用Class.forName載入使用的類,這種方式沒有指定某個特定的ClassLoader,會使用調用類的ClassLoader。
也就是說調用這個方法的類的類載入器將會用於載入這個類。比如在類A中使用Class.forName載入類B,那麼載入類A的類載入器將會用於載入類B,這樣兩個類的類載入器是同一個。

最後討論一下如何獲得某個類載入器載入了哪些類,這個似乎有一定的使用價值,可以看出哪些類被載入了。其實這個也不是很難,因為ClassLoader中有一個classes成員變數就是用來保存類載入器載入的類列表,而且有一個方法
void addClass(Class c) { classes.addElement(c);}
這個方法被JVM調用。
我們只要利用反射獲得classes這個值就可以了,不過classes聲明為private的,我們需要修改它的訪問許可權(沒有安全管理器時很容易做到)
classes = ClassLoader.class.getDeclaredField("classes");
classes.setAccessible(true);
List ret=(List) classes.get(cl); //classes是一個Vector
可惜的是對於引導類載入器沒有辦法獲得載入的類,因為它是c實現的,在java中很難控制

⑵ 關於Java中類的載入的疑問

當創建一個類的對象時(new 類名)和執行某類的靜態方法時(類名.方法名),該類的.class文件會被載入到一段內存中

這句話倒是沒錯,但當創建一個類的對象時(new)
1.先將該類的.class文件會被載入到一段內存,初始化static屬性和方法,為他們分配空間,以後一直在內存中(這就是不建議使用static屬性和方法的原因之一)
2.其中非static屬性和方法,為他們初始化,分配空間,然後才可以使用,因為非static的屬性和方法是跟隨對象的,所以當對象被回收時,屬於該對象的屬性和方法也被回收,再new的時候再生成。

static還有一點不好的就是所有對象共用,這樣可能導致數據的不安全性(比如我在用的時候,你突然給改變了,我再用的時候值可能就不連續了)
所以建議使用非static

⑶ java怎麼獲取類載入

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class MyUrlDemo {

public static void main(String[] args) {
MyUrlDemo muDemo = new MyUrlDemo();
try {
muDemo.showURL();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void showURL() throws IOException {

// 第一種:獲取類載入的根路徑 D:\git\tie\tie\target\classes
File f = new File(this.getClass().getResource("/").getPath());
System.out.println(f);

// 獲取當前類的所在工程路徑; 如果不加「/」 獲取當前類的載入目錄 D:\git\tie\tie\target\classes\my
File f2 = new File(this.getClass().getResource("").getPath());
System.out.println(f2);

// 第二種:獲取項目路徑 D:\git\tie\tie
File directory = new File("");// 參數為空
String courseFile = directory.getCanonicalPath();
System.out.println(courseFile);

// 第三種: file:/D:/git/tie/tie/target/classes/
URL xmlpath = this.getClass().getClassLoader().getResource("");
System.out.println(xmlpath);

// 第四種: D:\git\tie\tie
System.out.println(System.getProperty("user.dir"));
/*
* 結果: C:\Documents and Settings\Administrator\workspace\projectName
* 獲取當前工程路徑
*/

// 第五種: 獲取所有的類路徑 包括jar包的路徑
System.out.println(System.getProperty("java.class.path"));

}
}

⑷ java如何實現類載入

重新定義類載入器,也就是ClassLoader,覆蓋其中的一個方法findClass
例如,應用程序可以創建一個網路類載入器,從伺服器中下載類文件。示例代碼如下所示:

ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
. . .

⑸ 關於JAVA中的初始化及類的載入

你不太可能完全用代碼證明類載入的動作,因為在你用這個類之前(也就是你想要開始證明一個類載入的過程時)她已經載入了。

現在來說,靜態成員會和類載入的時候一起初始化,所以最多最多也只能通過靜態成員來證明類的載入。

那麼問題就是如何在靜態成員被初始化的時候提示消息,兩種方法:

第一種是使用靜態的欄位成員,此成員指定為一個對象初始化,在這個初始化對象的構造函數做通知。代碼:
public class StaticLoad{
public static LoadNotify = new LoadNotify();
}

public class LoadNotify{
public LoadNotify(){
System.out.println("Class is Loaded");
}
}
那麼使用StaticLoad類的時候,會打出Class is Loaded證明類載入的時候初始化了static成員,調用多次也僅會打出一次。

第二種是使用靜態塊,代碼:
public class StaticLoad{
public static LoadNotify = new LoadNotify();
static {
System.out.println("Class is Loaded");
}
}
此時類載入時也會執行靜態塊中的代碼。

這是進行當類載入的時候進行通知的方法,你可以自己寫一寫代碼,他們僅會打出一次。

對於類的載入順序,網上有詳細的,我這里只說一些簡單的:
首先需要用到一個類的時候,虛擬機會從classpath讀取此類的代碼,到內存的類的代碼存儲區,然後對於每一個靜態欄位開辟一個存儲區,再存儲方法的代碼。接著初始化類,所有的靜態成員按照順序初始化(按代碼聲明順序從上到下開始),當需要載入其他類的時候載入其他的類。然後完成初始化供虛擬機使用。
當實例化一個類的時候,如果需要載入的話,會按照上面的順序載入,然後在內存中分配所有實例欄位的空間,接著調用構造函數,其中構造函數會(顯示或隱式的)調用父類構造函數(此過程向上遞歸),然後執行剩下的構造代碼。最後返回實例化的對象的引用返回構造函數。

⑹ 描述java虛擬機載入類的過程,要詳盡!

先將class文件讀到JVM,找到static
的變數並初始化,在找到static代碼段執行,在初始化普通變數,然後是構造方法。最後載入其他方法。

⑺ java類的載入過程是怎麼樣的

類載入到jvm需要經歷如下幾個過程:

一.載入
通過類的全限定名轉換為二進制位元組流,在jvm堆中生成代表這個Class的對象,作為方法區域的方法入口.

二.連接
1.驗證:驗證class的位元組流是否對jvm虛擬機造成傷害,是否符合jvm的規范,這里包含幾個驗證.
2.准備:為類的static變數賦初始值,其中不包含類其它實例成員的初始化.
3.解析:將常量池內的符號引用替換成直接引用
三.初始化
這里是static{}塊,構造函數,代碼塊{}的執行過程.
四.使用
對象的屬性,方法等調用操作.
五.銷毀
jvm通過確定對象沒有引用後進行gc操作.

⑻ java程序什麼是類載入

你可以了解一下
深入理解java虛擬機,
java類載入
是因為在運行時當它需要引用某個類的時候,會先去載入這個類,就是去讀這個class文件到內存裡面來,現在還沒有創建這個類的實例,
你可以理解為
你寫的java代碼在編譯成class文件後,並沒有去執行,或者去引用他,只有程序運行時它將要使用這個類的時候才會去讀取這個class文件,這時候叫做類載入,當緩存了這個類文件之後,後面的創建類的時候都會去引用它,因為類載入是運行是載入,所以java代碼在運行時是有辦法修改的,同樣java類載入也允許載入一個遠程的class文件,也代表它是支持程序不停止時更新程序代碼的。

⑼ java中類載入的兩種方法是什麼

java類有兩種方法一種是類方法就是用static修飾的,一種是實例方法,就是沒有static修飾的方法

⑽ Java中用import導入類和用Class方法載入類有什麼區別

import僅僅包含導入操作,並不包含將位元組碼文件載入進內存這一動作,將位元組碼文件載入進內存是後續的實例化操作完成的。例如通過import導入了一堆包和類,但是後續什麼都沒用(沒用實例化),那麼導入的東西是不會被載入進內存的。而且import是編譯期的,如果你在後續代碼中沒有使用到你導入的內容,那麼import語句甚至不會編譯和執行。查看位元組碼文件可以看出,import的作用就是對你程序中要用到(實例)的東西進行署名(signature),當程序運行的時候好知道你實例化的對象的類的位元組碼文件去哪裡找。
而Class.forName方法包含的動作是:根據給出的全類名(方法的參數)找到對應的位元組碼文件,並將位元組碼文件通過ClassLoader載入進內存中生成Class類對象(方法的返回值就是Class類對象)。
這些就是二者的區別了。

閱讀全文

與java載入類的方法相關的資料

熱點內容
82一56的筒便演算法 瀏覽:404
數控機床fanuc編程 瀏覽:607
天刀mode不是內部或外部命令 瀏覽:854
長城c30壓縮機價格 瀏覽:1000
java打開圖片文件 瀏覽:409
跟程序員聊天聊到半夜 瀏覽:411
自己怎麼做app代碼 瀏覽:915
win7旗艦版進不去帶命令符 瀏覽:799
單片機溫度檢測電路 瀏覽:802
拼圖軟體不壓縮 瀏覽:656
紅袖添香小說源碼 瀏覽:624
erp加密工具在哪裡買 瀏覽:516
怎麼給qq群里的文件加密 瀏覽:762
androidsetbitmap 瀏覽:597
mt4反向編譯 瀏覽:201
sun伺服器命令 瀏覽:827
程序員同乘電梯 瀏覽:617
49乘以235的簡便演算法 瀏覽:673
新概念51單片機c語言教程光碟 瀏覽:262
伺服器分區如何選擇 瀏覽:354