qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問 http://qaseven.github.io/

          Java基礎(chǔ)加強(qiáng)之類加載器

           學(xué)習(xí)概述:本模塊深入講解了Java類加載方面的知識(shí),Java類加載器和類加載機(jī)制以及類加載原理
            學(xué)習(xí)目標(biāo):掌握類加載機(jī)制和原理,能夠獨(dú)立開發(fā)自己的類加載器。
            1.類的加載
            什么是類加載? 類加載是指將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)Java.lang.Class對(duì)象,也就是說當(dāng)程序中使用任何類時(shí),系統(tǒng)都會(huì)為之建立一個(gè)java.lang.Class對(duì)象。
            類加載器負(fù)責(zé)加載所有類,系統(tǒng)為所有被載入內(nèi)存中的類生成一個(gè)java.lang.Class實(shí)例。一旦一個(gè)類被載入JVM中,同一個(gè)類不會(huì)再被再次載入。
            思考問題:怎么樣才算同一個(gè)類?
            當(dāng)JVM啟動(dòng)時(shí),會(huì)形成三個(gè)類加載器組成的原始類加載器層次結(jié)構(gòu):
            【BootStrap ClassLoader】根類加載器 這是一個(gè)特殊的加載器,他并不是有Java編寫,而是JVM自身實(shí)現(xiàn)的
            【Extension Classloader】擴(kuò)展類加載器
            【System Classloader】系統(tǒng)類加載器
            類加載器的父子關(guān)系:
            實(shí)驗(yàn)獲得類加載器以及了解類加載器的層次結(jié)構(gòu):
          public  class ClassloaderDemo{
          public static  void main(String[] args){
          System.out.printlb(ClassLoaderDemo.class.getClassLoader().getName());
          System.out.println(System.class.getClassloader());
          ClassLoader classloader = ClassLoaderDemo.class.getClassLoader());
          while(loader!=null){
          System.out.println(loader.getClass().getName());
          loader=loader.getParent();
          }
          }
            注意:程序會(huì)拋出異常,因?yàn)镴VM根類加載器不是Java類。
            2.類的加載機(jī)制,如圖所示:
            <1>全盤負(fù)責(zé):所謂全盤負(fù)責(zé),就是說當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class的時(shí)候,該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入,除非顯式使用另外一個(gè)                            類加載器來實(shí)現(xiàn)載入。
            <2>父類委托:意思是先讓父類加載器試圖加載該Class,只有父類加載器無法加載該類是才嘗試從自己的路徑中加載該類。
            <3>緩存機(jī)制:緩存機(jī)制將會(huì)保證所有被加載過的Class都會(huì)被緩存,當(dāng)程序中需要使用某個(gè)Class時(shí),類加載器先從緩存中搜索該Class,只有當(dāng)緩存中不存在該Class對(duì)象              時(shí),系統(tǒng)才重新讀取該類對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)。這就是為什么我們修改Class后,JVM必須重新啟動(dòng),修改才生效的原因。
            類加載器的父子關(guān)系:用戶類加載器—>系統(tǒng)類加載器—>擴(kuò)展類加載器—>根類加載器
            類加載機(jī)制:
            <1>當(dāng)JVM需要加載一個(gè)類是,到底指派哪個(gè)類加載器去加載呢?
            首先當(dāng)前線程的類加載器去加載線程中的第一個(gè)類,如果A類中引用了B類,JVM將使用加載A類的加載器來加載B類,最后還可以調(diào)用ClassLoader。loadeClass方法指定        某個(gè)類加載器去加載某個(gè)類。
            <2>每個(gè)類加載器在加載類時(shí),先委托給其上級(jí)加載器。
            注意兩點(diǎn):
            當(dāng)所有的祖宗類加載器都沒有加載到類,回到發(fā)起類加載器,還加載不了,那么程序?qū)伋鯟lassNotFoundExcetpion,而不是去找發(fā)起類加載器的兒子,因?yàn)闆]有                     getChild ()方法,即使有,那么選擇哪一個(gè)兒子加載器呢?
            面試題:能不能自己寫一個(gè)類叫Java.lang.System?
            答案:可以寫,但是因?yàn)镴VM委托機(jī)制的存在,會(huì)先找到JVM根類加載器,我自己寫也可以,那么我要拋開委托加載機(jī)制,我自己指定一個(gè)ClassLoader。


           3.自定義類加載器
            JVM中除了根類加載器之外的所有類加載器都是classloader的子類實(shí)例,我們完全可以通過擴(kuò)展ClassLoader的子類,并重寫ClassLoader所包含的的方法來實(shí)現(xiàn)自定義類       加載器,ClassLoader有兩個(gè)關(guān)鍵的方法:loadClass(),findClass()。
            不過我們一般推薦重寫findClass()方法,而不是loadClass()方法,因?yàn)橹貙慺indClass()可以避免覆蓋默認(rèn)類加載器的父類委托,緩存機(jī)制兩種策略。
            下面是我自己編寫的一個(gè)類加載器:
          package snippet;
          import java.io.File;
          import java.io.FileInputStream;
          import java.io.FileNotFoundException;
          import java.io.IOException;
          import java.io.InputStream;
          /**
          *
          * @author Administrator
          *自定義類加載器
          */
          public class MyClassLoader extends ClassLoader {
          // 獲取java源文件的二進(jìn)制碼
          public byte[] getBytes(String filename){
          File file = new File(filename);
          InputStream ips=null;
          byte[] b = new byte[(int) file.length()];
          try {
          ips = new FileInputStream(file);
          int raw =ips.read(b);
          if(raw!=file.length()){
          throw new IOException("無法完整讀取文件");
          }
          } catch (FileNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          finally{
          if(ips!=null){
          try {
          ips.close();
          } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          }
          }
          return b;
          }
          public boolean compile(String javaFile){
          System.out.println("正在編譯");
          Process p=null;
          try {
          //調(diào)用系統(tǒng)javac命令
          p=Runtime.getRuntime().exec("javac" + javaFile);
          try {
          p.waitFor();
          } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          int ret = p.exitValue();
          return ret==0;
          }
          @Override
          protected Class<?> findClass(String name) {
          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文件修改的時(shí)間晚
          if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){
          if(!compile(javaFileName)||!classFile.exists()){
          try {
          throw new ClassNotFoundException("未發(fā)現(xiàn)class文件");
          } catch (ClassNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          }
          //如果class文件已經(jīng)存在,那么直接生成字節(jié)碼
          if(classFile.exists()){
          byte[] b = getBytes(classFileName);
          clazz = defineClass(name, b, 0, b.length);
          }
          //如果為空,標(biāo)明加載失敗
          if(clazz==null){
          try {
          throw new ClassNotFoundException(name);
          } catch (ClassNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          }
          }
          return clazz;
          }
          }
            上面代碼重寫了findClass方法,通過重寫該方法就可以實(shí)現(xiàn)自定義的類加載機(jī)制。
            學(xué)習(xí)總結(jié):1.了解了JVM三種類加載器(根類加載器,系統(tǒng)類加載器,擴(kuò)展類加載器),明白了三種類加載器的作用和范圍
            2.學(xué)習(xí)了JVM三種類加載機(jī)制(父類委托,緩存,全盤負(fù)責(zé))
            3.學(xué)習(xí)了如何自定義類加載器,通過繼承ClassLoader類,特別要注意兩個(gè)關(guān)鍵方法:loadClass()和findClass()兩種方法的機(jī)制和不同。

          posted on 2013-12-24 11:50 順其自然EVO 閱讀(181) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2013年12月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 嘉黎县| 滕州市| 永寿县| 甘谷县| 长春市| 兖州市| 兰溪市| 富蕴县| 南漳县| 韩城市| 河东区| 商河县| 土默特左旗| 昌邑市| 泌阳县| 叙永县| 沅江市| 桂东县| 仙游县| 牡丹江市| 威海市| 繁昌县| 体育| 井研县| 新余市| 灌阳县| 合山市| 巨野县| 霍州市| 依安县| 灵武市| 金门县| 长治县| 固始县| 瑞安市| 榕江县| 景宁| 苍山县| 罗山县| 怀化市| 广东省|