摘要:當程序使用某個類時,如果該類還沒被初始化,加載到內(nèi)存中,則系統(tǒng)會通過加載連接初始化三個過程來對該類進行初始化。一旦一個類被加載到中之后,就不會再次載入了。它既可以從本地文件系統(tǒng)獲取二進制文件來加載類,也可以遠程主機獲取二進制文件來加載類。
當程序使用某個類時,如果該類還沒被初始化,加載到內(nèi)存中,則系統(tǒng)會通過加載、連接、初始化三個過程來對該類進行初始化。該過程就被稱為類的初始化
類加載指將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個java.lang.Class的對象
從本地文件系統(tǒng)加載的class文件
從JAR包加載class文件
從網(wǎng)絡加載class文件
把一個Java源文件動態(tài)編譯,并執(zhí)行加載
類加載器類加載器通常無須等到“首次使用”該類時才加載該類,JVM允許系統(tǒng)預先加載某些類
類加載器就是負責加載所有的類,將其載入內(nèi)存中,生成一個java.lang.Class實例。一旦一個類被加載到JVM中之后,就不會再次載入了。
根類加載器(Bootstrap ClassLoader):其負責加載Java的核心類,比如String、System這些類
拓展類加載器(Extension ClassLoader):其負責加載JRE的拓展類庫
系統(tǒng)類加載器(System ClassLoader):其負責加載CLASSPATH環(huán)境變量所指定的JAR包和類路徑
用戶類加載器:用戶自定義的加載器,以類加載器為父類
類加載器之間的父子關系并不是繼承關系,是類加載器實例之間的關系
public static void main(String[] args) throws IOException { ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); System.out.println("系統(tǒng)類加載"); Enumerationem1 = systemLoader.getResources(""); while (em1.hasMoreElements()) { System.out.println(em1.nextElement()); } ClassLoader extensionLader = systemLoader.getParent(); System.out.println("拓展類加載器" + extensionLader); System.out.println("拓展類加載器的父" + extensionLader.getParent()); }
結果
系統(tǒng)類加載 file:/E:/gaode/em/bin/ 拓展類加載器sun.misc.Launcher$ExtClassLoader@6d06d69c 拓展類加載器的父null
為什么根類加載器為NULL?
JVM類加載機制根類加載器并不是Java實現(xiàn)的,而且由于程序通常須訪問根加載器,因此訪問擴展類加載器的父類加載器時返回NULL
全盤負責,當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入
父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類
緩存機制,緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區(qū)尋找該Class,只有緩存區(qū)不存在,系統(tǒng)才會讀取該類對應的二進制數(shù)據(jù),并將其轉(zhuǎn)換成Class對象,存入緩存區(qū)。這就是為什么修改了Class后,必須重啟JVM,程序的修改才會生效
URLClassLoader類URLClassLoader為ClassLoader的一個實現(xiàn)類,該類也是系統(tǒng)類加載器和拓展類加載器的父類(繼承關系)。它既可以從本地文件系統(tǒng)獲取二進制文件來加載類,也可以遠程主機獲取二進制文件來加載類。
兩個構造器
URLClassLoader(URL[] urls):使用默認的父類加載器創(chuàng)建一個ClassLoader對象,該對象將從urls所指定的路徑來查詢并加載類
URLClassLoader(URL[] urls,ClassLoader parent):使用指定的父類加載器創(chuàng)建一個ClassLoader對象,其他功能與前一個構造器相同
import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import com.mysql.jdbc.Driver; public class GetMysql { private static Connection conn; public static Connection getConn(String url,String user,String pass) throws MalformedURLException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException{ if(conn==null){ URL[]urls={new URL("file:mysql-connector-java-5.1.18.jar")}; URLClassLoader myClassLoader=new URLClassLoader(urls); Driver driver=(Driver) myClassLoader.loadClass("com.mysql.jdbc.Driver").newInstance(); Properties pros=new Properties(); pros.setProperty("user", user); pros.setProperty("password", pass); conn=driver.connect(url, pros); } return conn; } public static method1 getConn() throws MalformedURLException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException{ URL[]urls={new URL("file:com.em")}; URLClassLoader myClassLoader=new URLClassLoader(urls); method1 driver=(method1) myClassLoader.loadClass("com.em.method1").newInstance(); return driver; } public static void main(String[] args) throws MalformedURLException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { System.out.println(getConn("jdbc:mysql://10.10.16.11:3306/auto?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true", "jiji", "jiji")); System.out.println(getConn()); } }
獲得URLClassLoader對象后,調(diào)用loanClass()方法來加載指定的類
自定義類加載器import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Method; public class CompileClassLoader extends ClassLoader { // 讀取一個文件的內(nèi)容 @SuppressWarnings("resource") private byte[] getBytes(String filename) throws IOException { File file = new File(filename); long len = file.length(); byte[] raw = new byte[(int) len]; FileInputStream fin = new FileInputStream(file); // 一次讀取class文件的全部二進制數(shù)據(jù) int r = fin.read(raw); if (r != len) throw new IOException("無法讀取全部文件" + r + "!=" + len); fin.close(); return raw; } // 定義編譯指定java文件的方法 private boolean compile(String javaFile) throws IOException { System.out.println("CompileClassLoader:正在編譯" + javaFile + "…….."); // 調(diào)用系統(tǒng)的javac命令 Process p = Runtime.getRuntime().exec("javac" + javaFile); try { // 其它線程都等待這個線程完成 p.waitFor(); } catch (InterruptedException ie) { System.out.println(ie); } // 獲取javac 的線程的退出值 int ret = p.exitValue(); // 返回編譯是否成功 return ret == 0; } // 重寫Classloader的findCLass方法 protected Class> findClass(String name) throws ClassNotFoundException { Class clazz = null; // 將包路徑中的.替換成斜線/ String fileStub = name.replace(".", "/"); String javaFilename = fileStub + ".java"; String classFilename = fileStub + ".class"; File javaFile = new File(javaFilename); File classFile = new File(classFilename); // 當指定Java源文件存在,且class文件不存在,或者Java源文件的修改時間比class文件//修改時間晚時,重新編譯 if (javaFile.exists() && (!classFile.exists()) || javaFile.lastModified() > classFile.lastModified()) { try { // 如果編譯失敗,或該Class文件不存在 if (!compile(javaFilename) || !classFile.exists()) { throw new ClassNotFoundException("ClassNotFoundException:" + javaFilename); } } catch (IOException ex) { ex.printStackTrace(); } } // 如果class文件存在,系統(tǒng)負責將該文件轉(zhuǎn)化成class對象 if (classFile.exists()) { try { // 將class文件的二進制數(shù)據(jù)讀入數(shù)組 byte[] raw = getBytes(classFilename); // 調(diào)用Classloader的defineClass方法將二進制數(shù)據(jù)轉(zhuǎn)換成class對象 clazz = defineClass(name, raw, 0, raw.length); } catch (IOException ie) { ie.printStackTrace(); } } // 如果claszz為null,表明加載失敗,則拋出異常 if (clazz == null) { throw new ClassNotFoundException(name); } return clazz; } // 定義一個主方法 public static void main(String[] args) throws Exception { // 如果運行該程序時沒有參數(shù),即沒有目標類 if (args.length < 1) { System.out.println("缺少運行的目標類,請按如下格式運行java源文件:"); System.out.println("java CompileClassLoader ClassName"); } // 第一個參數(shù)是需要運行的類 String progClass = args[0]; // 剩下的參數(shù)將作為運行目標類時的參數(shù),所以將這些參數(shù)復制到一個新數(shù)組中 String progargs[] = new String[args.length - 1]; System.arraycopy(args, 1, progargs, 0, progargs.length); CompileClassLoader cl = new CompileClassLoader(); // 加載需要運行的類 Class> clazz = cl.loadClass(progClass); // 獲取需要運行的類的主方法 Method main = clazz.getMethod("main", (new String[0]).getClass()); Object argsArray[] = { progargs }; main.invoke(null, argsArray); } }
JVM中除了根類加載器之外的所有類的加載器都是ClassLoader子類的實例,通過重寫ClassLoader中的方法,實現(xiàn)自定義的類加載器
loadClass(String name,boolean resolve):為ClassLoader的入口點,根據(jù)指定名稱來加載類,系統(tǒng)就是調(diào)用ClassLoader的該方法來獲取制定累對應的Class對象
findClass(String name):根據(jù)指定名稱來查找類
類的鏈接推薦使用findClass方法
當類被加載后,系統(tǒng)會為之生成一個Class對象,接著將會進入連接階段,鏈接階段負責把類的二進制數(shù)據(jù)合并到JRE中
三個階段
驗證:檢驗被加載的類是否有正確的內(nèi)部結構,并和其他類協(xié)調(diào)一致
準備:負責為類的類變量分配內(nèi)存。并設置默認初始值
解析:將類的二進制數(shù)據(jù)中的符號引用替換成直接引用
類的初始化JVM負責對類進行初始化,主要對類變量進行初始化
在Java中對類變量進行初始值設定有兩種方式:①聲明類變量是指定初始值②使用靜態(tài)代碼塊為類變量指定初始值
JVM初始化步驟
假如這個類還沒有被加載和連接,則程序先加載并連接該類
假如該類的直接父類還沒有被初始化,則先初始化其直接父類
假如類中有初始化語句,則系統(tǒng)依次執(zhí)行這些初始化語句
類初始化時機創(chuàng)建類實例。也就是new的方式
調(diào)用某個類的類方法
訪問某個類或接口的類變量,或為該類變量賦值
使用反射方式強制創(chuàng)建某個類或接口對應的java.lang.Class對象
初始化某個類的子類,則其父類也會被初始化
直接使用java.exe命令來運行某個主類
類加載機制(類加載過程和類加載器)
更多內(nèi)容可以關注微信公眾號,或者訪問AppZone網(wǎng)站
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/66010.html
摘要:如果需要支持類的動態(tài)加載或需要對編譯后的字節(jié)碼文件進行解密操作等,就需要與類加載器打交道了。雙親委派模型,雙親委派模型,約定類加載器的加載機制。任何之類的字節(jié)碼都無法調(diào)用方法,因為該方法只能在類加載的過程中由調(diào)用。 jvm系列 垃圾回收基礎 JVM的編譯策略 GC的三大基礎算法 GC的三大高級算法 GC策略的評價指標 JVM信息查看 GC通用日志解讀 jvm的card table數(shù)據(jù)...
摘要:實現(xiàn)這個口號的就是可以運行在不同平臺上的虛擬機和與平臺無關的字節(jié)碼。類加載過程加載加載是類加載的第一個階段,虛擬機要完成以下三個過程通過類的全限定名獲取定義此類的二進制字節(jié)流。驗證目的是確保文件字節(jié)流信息符合虛擬機的要求。 引言 我們知道java代碼編譯后生成的是字節(jié)碼,那虛擬機是如何加載這些class字節(jié)碼文件的呢?加載之后又是如何進行方法調(diào)用的呢? 一 類文件結構 無關性基石 ja...
摘要:類加載器類加載器執(zhí)行的操作就是上述加載階段做的事,通過一個類的全限定名來獲取定義這個類的二進制字節(jié)流,類加載器可以分為下列三種。應用程序類加載器,也稱為系統(tǒng)類加載器。 類加載流程: showImg(https://segmentfault.com/img/bV8SRP?w=1152&h=388);從上面這幅圖可以看出一個類從加載到卸載有7個階段,其中驗證、準備和解析這三個步驟統(tǒng)稱為連接...
閱讀 2240·2023-04-26 00:43
閱讀 2747·2021-11-22 15:22
閱讀 3927·2021-11-11 16:55
閱讀 1018·2021-11-04 16:06
閱讀 1837·2019-08-30 14:12
閱讀 1072·2019-08-30 14:02
閱讀 3441·2019-08-29 17:05
閱讀 1484·2019-08-29 12:27