什麼是類載入器: public class ClassInit { public static void main(String[] args) { ClassLoader c=ClassInit.class.getClassLoader(); } } 上面這個ClassLoader就是類載入器 打 ...
什麼是類載入器:
public class ClassInit { public static void main(String[] args) { ClassLoader c=ClassInit.class.getClassLoader(); } }
上面這個ClassLoader就是類載入器
列印c,註意到一個Launcher類:
sun.misc.Launcher$AppClassLoader@18b4aac2
進入Launcher類,註意到其中的兩段代碼:
var1 = Launcher.ExtClassLoader.getExtClassLoader(); this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
其中的ExtClassLoader和AppClassLoader有什麼關係呢?
修改成如下代碼:
public class ClassInit { public static void main(String[] args) { ClassLoader c = ClassInit.class.getClassLoader(); while (c != null) { System.out.println(c); c = c.getParent(); } } }
列印:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
於是得到了結論:AppClassLoader有一個父親是ExtClassLoader
繼續分析ClassLoader,註意到一段代碼:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e)
獨占鎖保證了同時只能載入一個類
然後檢查這個類是否被載入過,如果被載入過直接返回;
如果沒有被載入過,那麼就遞歸載入他的父親,直到沒有父親,最後執行findBootstrapClassOrNull()
往下看發現這樣一段代碼:
// return null if not found private native Class<?> findBootstrapClass(String name);
這個函數沒有函數體,被native修飾,代表調用一個本地方法
這裡調用本地方法,根據操作系統的不同,調用的本地方法介面不同,然後調用本地方法庫
得出結論:
一個類的載入順序是:BootstrapClassLoader--->ExtClassLoader--->AppClassLoader
一個類的檢查順序是:AppClassLoader--->ExtClassLoader--->BootstrapClassLoader
為什麼載入順序要從BootstrapClassLoader開始呢?
寫一個新代碼,包名和類名與java源碼的List一樣:
package java.util; public class List{ public static void main(String[] args){ xxxxxx } }
發現無法運行,報錯:在類java.util.List中找不到main方法
無法運行是非常合理的,否則一個黑客豈不是可以輕易植入病毒代碼,然後通過自定義類載入器加入到JVM中
這種機制稱為:雙親委任
目的:安全
1.父類如果可以載入,那麼不允許子類載入
2.保證一個類只載入一次
判斷兩個對象是不是相等,最重要的條件是看是不是一個類載入器:
寫一個代碼:
package org.dreamtech.cl; import java.io.IOException; import java.io.InputStream; public class ClassInit { //自定義類載入器 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { ClassLoader loader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } byte[] b = new byte[is.available()]; is.read(b); return defineClass(name, b, 0, b.length); } catch (IOException e) { throw new ClassNotFoundException(); } } }; String className = "org.dreamtech.cl.ClassInit"; Object o1 = ClassInit.class.getClassLoader().loadClass(className).newInstance(); Object o2 = loader.loadClass(className).newInstance(); System.out.println(o1 == o2); System.out.println(o1.equals(o2)); System.out.println(o1 instanceof org.dreamtech.cl.ClassInit); System.out.println(o2 instanceof org.dreamtech.cl.ClassInit); System.out.println(o1.getClass().getClassLoader()); System.out.println(o2.getClass().getClassLoader()); } }
列印如下:
false false true false sun.misc.Launcher$AppClassLoader@18b4aac2 org.dreamtech.cl.ClassInit$1@14ae5a5