java能不能自己寫一個類叫java.lang.System/String正確答案,java.lang.string

來源:互聯網
上載者:User

java能不能自己寫一個類叫java.lang.System/String正確答案,java.lang.string

最近學習了下java類載入相關的知識。然後看到網上有一道面試題是

能不能自己寫個類叫java.lang.System?

網上提供的答案:通常不可以,但可以採取另類方法達到這個需求。所謂的另類方法指自己寫個類載入器來載入java.lang.System達到目的。

首先表明下我的觀點。上述答案完全是誤導讀者,是不正確的答案。我就納悶了網上怎麼把這種完全不正確的搜尋結果排在前面,而且幾乎搜到的都是這種不正確的答案。可能很多不明真相的朋友就這麼被誤導了,所以還是希望大家對網上的內容先持懷疑態度為好。下面詳細說明為什麼。

首先,摘抄網上錯誤答案的詳細解釋

“為了不讓我們寫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。關於這三種載入器各自的作用這裡不做詳細說明,有興趣的可以自己瞭解下。

類載入器是有層次的

一般是: 自訂類載入器  >> 應用程式類載入器  >> 擴充類載入器  >> 啟動類載入器

上面的層次關係被稱為雙親委派模型(Parents Delegation Model)。除了最頂層的啟動類載入器外,其餘的類載入器都有對應的父類載入器。

再簡單說下雙親委託機制:如果一個類載入器收到了類載入的請求,它首先不會自己嘗試去載入這個類,而是把這個請求委派給父類載入器,每一個層次的類載入器都是加此,因此所有的載入請求最終到達頂層的啟動類載入器,只有當父類載入器反饋自己無法完成載入請求時(指它的搜尋範圍沒有找到所需的類),子類載入器才會嘗試自己去載入。

再回去看下解釋內容,我相信前面的部分大家應該很看懂了,也沒什麼大問題。最後的如果部分“如果我們自己的類載入器放在一個特殊的目錄,那麼系統的載入器就無法載入,也就是最終還是由我們自己的載入器載入。” 我就不明白所以了,邏輯完全不通。我想它的本意可能是,將自己的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。

那些抓取網上內容,放在自家網站上的請自覺。百度搜尋你為啥老把原創網頁排後面呢,請反思,不要只想著賺錢。



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.