最近在整理一下日記資料。
html
日記記錄到比不上網上這篇全,轉過來了,摘自:http://www.bkjia.com/ASPjc/943121.htmljava
網上提供的答案:一般不能夠,但能夠採起另類方法達到這個需求。所謂的另類方法指本身寫個類加載器來加載java.lang.System達到目的。ide
首先代表下個人觀點。上述答案徹底是誤導讀者,是不正確的答案。我就納悶了網上怎麼把這種徹底不正確的搜索結果排在前面,並且幾乎搜到的都是這種不正確的答案。可能不少不明真相的朋友就這麼被誤導了,因此仍是但願你們對網上的內容先持懷疑態度爲好。下面詳細說明爲何。源碼分析
「爲了避免讓咱們寫System類,類加載採用委託機制,這樣能夠保證爸爸們優先,爸爸們能找到的類,兒子就沒有機會加載。而System類是Bootstrap加載器加載的,就算本身重寫,也老是使用Java系統提供的System,本身寫的System類根本沒有機會獲得加載。
可是,咱們能夠本身定義一個類加載器來達到這個目的,爲了不雙親委託機制,這個類加載器也必須是特殊的。因爲系統自帶的三個類加載器都加載特定目錄下的類,若是咱們本身的類加載器放在一個特殊的目錄,那麼系統的加載器就沒法加載,也就是最終仍是由咱們本身的加載器加載。」
測試
類加載器可分爲兩類:一是啓動類加載器(Bootstrap ClassLoader),是C++實現的,是JVM的一部分;另外一種是其它的類加載器,是Java實現的,獨立於JVM,所有都繼承自抽象類java.lang.ClassLoader。jdk自帶了三種類加載器,分別是啓動類加載器(Bootstrap ClassLoader),擴展類加載器(Extension ClassLoader),應用程序類加載器(Application ClassLoader)。後兩種加載器是繼承自抽象類java.lang.ClassLoader。關於這三種加載器各自的做用這裏不作詳細說明,有興趣的能夠本身瞭解下。網站
類加載器是有層次的spa
通常是: 自定義類加載器 >> 應用程序類加載器 >> 擴展類加載器 >> 啓動類加載器.net
上面的層次關係被稱爲雙親委派模型(Parents Delegation Model)。除了最頂層的啓動類加載器外,其他的類加載器都有對應的父類加載器。code
再簡單說下雙親委託機制:若是一個類加載器收到了類加載的請求,它首先不會本身嘗試去加載這個類,而是把這個請求委派給父類加載器,每個層次的類加載器都是加此,所以全部的加載請求最終到達頂層的啓動類加載器,只有當父類加載器反饋本身沒法完成加載請求時(指它的搜索範圍沒有找到所需的類),子類加載器纔會嘗試本身去加載。orm
再回去看下解釋內容,我相信前面的部分你們應該很看懂了,也沒什麼大問題。最後的若是部分「若是咱們本身的類加載器放在一個特殊的目錄,那麼系統的加載器就沒法加載,也就是最終仍是由咱們本身的加載器加載。」 我就不明白因此了,邏輯徹底不通。我想它的本意多是,將本身的java.lang.System類放置在特殊目錄,而後系統自帶的加載器沒法加載,這樣最終仍是由咱們本身的加載器加載(由於咱們本身的加載器知道其所在的特殊目錄)。這種說法好像邏輯上沒有問題,那麼咱們就來實驗下了。
測試類結構及內容以下:
public class MyClassLoader extends ClassLoader{ public MyClassLoader() { super(null); } @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try{ String className = null; if(name.startsWith("java.lang")){ className = "/" + name.replace('.', '/') + ".class"; }else{ className = name.substring(name.lastIndexOf('.') + 1) + ".class"; } System.out.println(className); InputStream is = getClass().getResourceAsStream(className); System.out.println(is); if(is == null) return super.loadClass(name); byte[] b = new byte[is.available()]; is.read(b); return defineClass(name, b, 0, b.length); }catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); } } }
public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader myLoader = new MyClassLoader(); Object obj = myLoader.loadClass("java.lang.Math").newInstance(); System.out.println(obj); } }
public final class Math { public static void main(String[] args) { System.out.println("hello world"); } }
public class MyMath { public static void main(String[] args) { System.out.println("hello world"); } }
上面的測試代碼沒用自定義java.lang.System類,由於測試代碼用到了JDK自帶的System類進行輸出打印,會衝突,因此改用爲自定義的java.lang.Math類。若是自定義的Math類能加載,那麼自定義的System類一樣能加載。
咱們先直接運行下Math類,輸出以下:
java.lang.NoSuchMethodError: main
Exception in thread "main"
提示Math類沒有main方法。首先你們要明白一個概念,當類首次主動使用時,必須進行類的加載,這部分工做是由類加載器來完成的。根據雙親委託原則,Math類首先由啓動類加載器去嘗試加載,很顯然,它找到rt.jar中的java.lang.Math類並加載進內存(並不會加載咱們自定義的Math類),而後執行main方法時,發現不存在該方法,因此報方法不存在錯誤。也就是說,默認狀況下JVM不會加載咱們自定義的Math類。
再直接運行MyMath類,輸出以下:
java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:479)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:625)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
Exception in thread "main"
注意紅色部分的內容。由堆棧異常信息可知道,當應用程序類加載器類(AppClassLoader)嘗試加載MyMath類時,ClassLoader.java的479行拋出了SecurityException
禁止使用包名:java.lang。
直接查看抽象類java.lang.ClassLoader的preDefineClass方法代碼,摘抄以下:
private ProtectionDomain preDefineClass(String name, ProtectionDomain protectionDomain) { if (!checkName(name)) throw new NoClassDefFoundError("IllegalName: " + name); if ((name != null) && name.startsWith("java.")) { throw new SecurityException("Prohibited package name: " + name.substring(0, name.lastIndexOf('.'))); } if (protectionDomain == null) { protectionDomain = getDefaultDomain(); } if (name != null) checkCerts(name, protectionDomain.getCodeSource()); return protectionDomain; }
能夠看到若是加載的類全名稱以「java.」開頭時,將會拋出SecurityException,這也是爲何直接執行MyMath類會出現SecurityException。
照這樣,咱們自定義的類加載器必須繼承自ClassLoader,其loadClass()方法裏調用了父類的defineClass()方法,並最終調到preDefineClass()方法,所以咱們自定義的類加載器也是不能加載以「java.」開頭的java類的。咱們繼續運行下ClassLoaderTest類,輸出以下:
/java/lang/Math.class
sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@a981ca
java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:479)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:625)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.lang.ClassLoader.defineClass(ClassLoader.java:465)
at com.tq.MyClassLoader.loadClass(MyClassLoader.java:28)
at com.tq.ClassLoaderTest.main(ClassLoaderTest.java:8)
Exception in thread "main" java.lang.ClassNotFoundException
at com.tq.MyClassLoader.loadClass(MyClassLoader.java:31)
at com.tq.ClassLoaderTest.main(ClassLoaderTest.java:8)
紅色部分清楚代表,也是在preDefineClass方法中拋出的SecurityException。
經過代碼實例及源碼分析能夠看到,對於自定義的類加載器,強行用defineClass()方法去加載一個以"java."開頭的類也是會拋出異常的。
不能本身寫以"java."開頭的類,其要麼不能加載進內存,要麼即便你用自定義的類加載器去強行加載,也會收到一個SecurityException。
碼字不易,源頭來自http://blog.csdn.net/tang9140。
那些抓取網上內容,放在自家網站上的請自覺。百度搜索你爲啥老把原創網頁排後面呢,請反思,不要只想着賺錢。