㈠ java類載入的過程是怎樣的
java程序在執行過程中,類,對象以及它們成員載入、初始化的順序如下: x0dx0a1、首先載入要創建對象的類及其直接與間接父類。 x0dx0a2、在類被載入的同時會將靜態成員進行載入,主要包括靜態成員變數的初始化,靜態語句塊的執行,在載入時按代碼的先後順序進行。 x0dx0a3、需要的類載入完成後,開始創建對象,首先會載入非靜態的成員,主要包括非靜態成員變數的初始化,非靜態語句塊的執行,在載入時按代碼的先後順序進行。 x0dx0a4、最後執行構造器,構造器執行完畢,對象生成。
㈡ java靜態代碼塊 類載入順序問題。
原因就是一句話:先有了父親,才會有兒子!
程序運行時,載入AB兩類,因為A是B子類,所以先載入B類,Load B,再加在A類 LoadA,調用A a = new A()代碼時,會先調用其父類B的構造方法 Create B 再調用A的構造方法 Create A
㈢ java中類載入路徑和項目根路徑獲取的幾種方式
// 第一種:獲取類載入的根路徑 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包的路徑
㈣ java中類載入的兩種方法是什麼
java類有兩種方法一種是類方法就是用static修飾的,一種是實例方法,就是沒有static修飾的方法
㈤ java中反射實例類裝載的步驟及簡要闡述
java反射和類裝載
反射機制:
Person p=new Person();
這是什麼?當然是實例化一個對象了.可是這種實例化對象的方法存在一個問題,那就是必須要知道類名才可以實例化它的對象,這樣我們在應用方面就會受到限制.那麼有沒有這樣一種方式,讓我們不知道這個類的類名就可以實例化它的對象呢?Thank Goodness!幸虧我們用的是java, java就提供了這樣的機制.
1).java程序在運行時可以獲得任何一個類的位元組碼信息,包括類的修飾符(public,static等),基類(超類,父類),實現的介面,欄位和方法等信息.
2).java程序在運行時可以根據位元組碼信息來創建該類的實例對象,改變對象的欄位內容和調用對象方法.
這樣的機制就叫反射技術.可以想像光學中的反射,就像我們照鏡子,鏡子中又出現一個自己(比喻可能不太恰當,但是足以表達清楚意思了).反射技術提供了一種通用的動態連接程序組件的方法,不必要把程序所需要的目標類硬編碼到源程序中,從而使得我們可以創建靈活的程序.
反射的實現步驟( 不問不需要答) ,
1、獲取類的常用方式有三種: a) Class.forName("包名.類名"),最常用、推薦;b) 包名.類名.class 最簡捷;c) 對象.getClass 的方式獲得。
2、對象的實例化,上面已經獲取了類,只需要調用類的實例化方法,類.newInstance()便可。
3、獲取屬性和構造等,可以參考 JavaApi 的調用,類. getDeclaredFields,類. getConstructor(..)等。
Java的反射機制是通過反射API來實現的,它允許程序在運行過程中取得任何一個已知名稱的類的內部信息.反射API位於java.lang.reflect包中.主要包括以下幾類:
1).Constructor類:用來描述一個類的構造方法
2).Field類:用來描述一個類的成員變數
3).Method類:用來描述一個類的方法.
4).Modifer類:用來描述類內各元素的修飾符
5).Array:用來對數組進行操作.
Constructor,Field,Method這三個類都是JVM(虛擬機)在程序運行時創建的,用來表示載入類中相應的成員.這三個類都實現了java.lang.reflect.Member介面,Member介面定義了獲取類成員或構造方法等信息的方法.要使用這些反射API,必須先得到要操作的對象或類的Class類的實例.通過調用Class類的newInstance方法(只能調用類的默認構造方法)可以創建類的實例.這樣有局限性,我們可以先沖類的Class實例獲取類需要的構造方法,然後在利用反射來創建類的一個實例.
類載入機制:
類的載入機制可以分為載入-鏈接-初始化三個階段,鏈接又可以分為驗證、准備、解析三個過程。
載入:通過類的載入器查找並載入二進制位元組流的過程,在堆內存中的方法區生成 一個代表這個類的 java.lang.Class 對象,作為這個類的數據請求入口。(這里可以把上面類載入器載入文件的過程描述一下(參考版本一,不作重復))。
驗證:主要是對一些詞法、語法進行規范性校驗,避免對 JVM 本身安全造成危害; 比如對文件格式,位元組碼驗證,無數據驗證等。但驗證階段是非必須的,可以通過參數 設置來進行關閉,以提高載入的時效。
准備:對類變數分配內存,並且對類變數預初始化,初始化成數據類型的原始值, 比如 static int a=11,會被初始化成成 a=0;如果是 static double a =11,則會被初始化成 a=0.0; 而成員變數只會成實例化後的堆中初始化。
解析:把常量池中的符號引用轉換為直接引用的過程。
初始化:對類的靜態變數和靜態塊中的變數進行初始化。(上面的准備階段可以作為 預初始化,初始到變數類型的原值,但如果被 final 修飾會進行真正初始化)
上面載入、鏈接、初始化的各個階段並不是彼此獨立,而是交叉進行,這點很重要 。
***class.forName和 classloader的區別
Class.forName 和 ClassLoader 都是用來裝載類的,對於類的裝載一般為分三個階段載入、鏈接、編譯,它們裝載類的方式是有區別。
首先看一下 Class.forName(..),forName(..)方法有一個重載方法 forName(className,boolean,ClassLoader),它有三個參數,第一個參數是類的包路徑,第二個參數是 boolean
類型,為 true 地表示 Loading 時會進行初始化,第三個就是指定一個載入器;當你調用class.forName(..)時,默認調用的是有三個參數的重載方法,第二個參數默認傳入 true,第三個參數默認使用的是當前類載入時用的載入器。
ClassLoader.loadClass()也有一個重載方法,從源碼中可以看出它默認調的是它的重載 方法 loadClass(name, false),當第二參數為 false 時,說明類載入時不會被鏈接。這也是兩者之間最大區別,前者在載入的時候已經初始化,後者在載入的時候還沒有鏈接。如果你需要在載入時初始化一些東西,就要用 Class.forName 了,比如我們常用的驅動載入, 實際上它的注冊動作就是在載入時的一個靜態塊中完成的。所以它不能被 ClassLoader 載入代替。
㈥ java類載入順序
記住 3 條原則:
1、父類優先於子類
2、屬性優先於代碼塊優先於構造方法
3、靜態優先於非靜態
因此,類載入順序為:
父類靜態變數->父類靜態語句塊->子類靜態變數->子類靜態語句塊->父類普通成員變數->父類動態語句塊->父類構造器->子類普通成員變數->子類動態語句塊->子類構造器
㈦ java運行顯示「找不到或無法載入主類
我的設置很簡單:
用戶變數:
JAVA_HOME:C:Program FilesJavajdk1.7.0_25
系統變數:
PATH:C:Program FilesJavajdk1.7.0_25in
㈧ java如何實現類載入
重新定義類載入器,也就是ClassLoader,覆蓋其中的一個方法findClass
例如,應用程序可以創建一個網路類載入器,從伺服器中下載類文件。示例代碼如下所示:
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
. . .
㈨ 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中很難控制