類別載入器(ClassLoader)用來載入 class位元組碼到 Java 虛擬機器中。一般來說,Java 虛擬機器使用 Java 類別的方式如下:Java 原始檔在經過 Javac之後就被轉換成 Java 字節碼檔案(.class 檔案)。類別載入器負責讀取 Java 位元組程式碼,並轉換成 java.lang.Class 類別的一個實例。每一個這樣的實例用來表示一個 Java 類別。實際的情況可能更加複雜,例如 Java 位元組程式碼可能是透過工具動態產生的,也可能是透過網路下載。
類加載器雖然只用於實現類的加載動作,但它在Java程序中起到的作用卻遠遠不限於類加載階段。對於任意一個類,都需要由載入它的類別載入器和這個類別本身一同確立其在Java虛擬中的唯一性。說通俗一些,比較兩個類別是否“相等”,只有在兩個類別是由同一個類別載入器的前提之下才有意義,否則,即使這兩個類別來自同一個class文件,只要載入它的類別載入器不同,那這兩個類別必定不相等。這裡所指的「相等」包括代表類別的Class物件的equal方法、isAssignableFrom()、isInstance()方法及instance關鍵字傳回的結果。
主要分為Bootstrap ClassLoader、I
主啟動類別載入器(Bootstrap ClassLoader):
這個類別載入器使用C++語言實現,並非ClassLoader的子類別。主要負責載入存放在JAVA_HOME / jre / lib / rt.jar裡面所有的class文件,或被-Xbootclasspath參數所指定路徑中以rt.jar命名的文件。
擴充類別載入器(Extension ClassLoader):
這個載入器是由sun.misc.Launcher$ExtClassLoader實現,它負責載入AVA_HOME / lib / ext目錄中的,或是被系統變數所寫.指定的路徑中的所有類別庫。
應用程式類別載入器(Application ClassLoader):
這個載入器由sun.misc.Launcher$AppClassLoader實現,它負責載入classpath對應的jar及目錄。一般情況下這個就是程式中預設的類別載入器。
自訂類別載入器(User Defined ClassLoader):
雙親委託模型:
上圖中所展示的類別載入器之間的這種層次關係,就稱為類別載入器的雙親委託模型。雙親委託模型要求除了頂層的啟動類別載入器外,其餘的類別載入器都應當有自己的父類別載入器。這裡類別載入器之間的父子關係一般不會以繼承的關係來實現,而是使用組合關係來重複使用父載入器的程式碼。
public abstract class ClassLoader { private static native void registerNatives(); static { registerNatives(); } // The parent class loader for delegation private ClassLoader parent; // Hashtable that maps packages to certs private Hashtable package2certs = new Hashtable(11); }
双亲委托的工作过程:如果一个类加载器收到了一个类加载请求,它首先不会自己去加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成加载请求(它管理的范围之中没有这个类)时,子加载器才会尝试着自己去加载。
使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如java.lang.Object存放在rt.jar之中,无论那个类加载器要加载这个类,最终都是委托给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,相反,如果没有双亲委托模型,由各个类加载器去完成的话,如果用户自己写一个名为java.lang.Object的类,并放在classpath中,应用程序中可能会出现多个不同的Object类,java类型体系中最基本安全行为也就无法保证。
java.lang.ClassLoader 类提供的几个关键方法:
loadClass: 此方法负责加载指定名字的类,首先会从已加载的类中去寻找,如果没有找到;从parent ClassLoader[ExtClassLoader]中加载;如果没有加载到,则从Bootstrap ClassLoader中尝试加载(findBootstrapClassOrNull方法), 如果还是加载失败,则抛出异常ClassNotFoundException, 在调用自己的findClass方法进行加载。如果要改变类的加载顺序可以覆盖此方法;如果加载顺序相同,则可以通过覆盖findClass方法来做特殊处理,例如:解密,固定路径寻找等。当通过整个寻找类的过程仍然未获取Class对象,则抛出ClassNotFoundException异常。
如果类需要resolve,在调用resolveClass进行链接。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
findLoadedClass 此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native方法。
protected final Class<?> findLoadedClass(String name) { if (!checkName(name)) return null; return findLoadedClass0(name); } private native final Class findLoadedClass0(String name);
findClass 此方法直接抛出ClassNotFoundException异常,因此要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
findSystemClass 此方法是从sun.misc.Launcher$AppClassLoader中寻找类,如果未找到,则继续从BootstrapClassLoader中寻找,如果仍然未找到,返回null
protected final Class<?> findSystemClass(String name) throws ClassNotFoundException { ClassLoader system = getSystemClassLoader(); if (system == null) { if (!checkName(name)) throw new ClassNotFoundException(name); Class cls = findBootstrapClass(name); if (cls == null) { throw new ClassNotFoundException(name); } return cls; } return system.loadClass(name); }
defineClass 此方法负责将二进制字节流转换为Class对象,这个方法对于自定义类加载器而言非常重要。如果二进制的字节码的格式不符合jvm class文件格式规范,则抛出ClassFormatError异常;如果生成的类名和二进制字节码不同,则抛出NoClassDefFoundError;如果加载的class是受保护的、采用不同签名的,或者类名是以java.开头的,则抛出SecurityException异常。
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { return defineClassCond(name, b, off, len, protectionDomain, true); } // Private method w/ an extra argument for skipping class verification private final Class<?> defineClassCond(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain, boolean verify) throws ClassFormatError { protectionDomain = preDefineClass(name, protectionDomain); Class c = null; String source = defineClassSourceLocation(protectionDomain); try { c = defineClass1(name, b, off, len, protectionDomain, source, verify); } catch (ClassFormatError cfe) { c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source, verify); } postDefineClass(c, protectionDomain); return c; }
resolveClass 此方法负责完成Class对象的链接,如果链接过,则直接返回。
ClassNotFoundException This is the most common exception. The reason for this exception is that the class file was not found when loading the class in the current ClassLoader.
NoClassDefFoundError This exception is because another class referenced in the loaded class does not exist. For example, if A is to be loaded, and B is stolen from A, and B does not exist or the current ClassLoader cannot load B, this exception will be thrown.
LinkageError This exception is more likely to occur in the case of custom ClassLoader. The main reason is that this class has already been loaded in ClassLoader, and repeated loading will cause this exception.
The above is the content of Java virtual machine learning - ClassLoader. For more related content, please pay attention to the PHP Chinese website (www.php.cn) !