<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          class加載過(guò)程

          共 15942字,需瀏覽 32分鐘

           ·

          2021-03-20 10:11

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          76套java從入門(mén)到精通實(shí)戰(zhàn)課程分享

          1.class加載過(guò)程

          java虛擬機(jī)把描述類(lèi)的數(shù)據(jù)從class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行 校驗(yàn)/準(zhǔn)備/解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類(lèi)型,這個(gè)過(guò)程被稱(chēng)作虛擬機(jī)的類(lèi)加載機(jī)制。

          loading -> linking (verification-> preparation -> resolution )-> initializing


          loading:把class文件load到內(nèi)存中,采用雙親委派,主要是為了安全性

          verification:校驗(yàn)class文件是否符合標(biāo)準(zhǔn)

          preparation:靜態(tài)變量分配內(nèi)存并設(shè)初始值的階段(不包括實(shí)例變量)

          resolution:把符號(hào)引用轉(zhuǎn)換為直接引用

          initializing:靜態(tài)變量賦初始值


          1.1 Loading 過(guò)程

          遇到new,getstatic,putstatic或者invokestatic 這四條指令時(shí),如果類(lèi)型沒(méi)有進(jìn)行初始,則需要先觸發(fā)其初始化階段。

          能夠生成這四條指令的典型Java代碼場(chǎng)景有:

          1.使用new關(guān)鍵字實(shí)例化對(duì)象。

          2.讀取或設(shè)置一個(gè)類(lèi)型的靜態(tài)字段。(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外,但是final static int i=10;如果沒(méi)有=10還是會(huì)在準(zhǔn)備階段給它賦默認(rèn)值,在初始化的時(shí)候賦值。)

          3.調(diào)用一個(gè)類(lèi)型的靜態(tài)方法。

          4.使用java.lang.reflect包的方法對(duì)類(lèi)型進(jìn)行反射調(diào)用。

          5.虛擬機(jī)啟動(dòng)的時(shí)候被執(zhí)行的主類(lèi)必須初始化。

          6.初始化子類(lèi)時(shí),父類(lèi)首先初始化。


          1.2 Linking 過(guò)程

          Verification:驗(yàn)證文件是否符合JVM規(guī)定

          Preparation: 靜態(tài)成員變量賦默認(rèn)值

          Resolution:將類(lèi)、方法、屬性等符號(hào)引用解析為直接引用常量池中的各種符號(hào)引用解析為指針、偏移量等內(nèi)存地址的直接引用


          1.3 Initializing

          調(diào)用類(lèi)初始化代碼 ,給靜態(tài)成員變量賦初始值


          1.4 雙親委派模型

          雙親委派模型的工作過(guò)程是:如果一個(gè)類(lèi)加載器收到了類(lèi)加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這 個(gè)類(lèi),而是把這個(gè)請(qǐng)求委派給父類(lèi)加載器去完成,每一個(gè)層次的類(lèi)加載器都是如此,因此所有的加載 請(qǐng)求最終都應(yīng)該傳送到最頂層的啟動(dòng)類(lèi)加載器中,只有當(dāng)父加載器反饋?zhàn)约簾o(wú)法完成這個(gè)加載請(qǐng)求 (它的搜索范圍中沒(méi)有找到所需的類(lèi))時(shí),子加載器才會(huì)嘗試自己去完成加載。


          2.類(lèi)加載器

          類(lèi)加載器實(shí)現(xiàn)了loading這個(gè)動(dòng)作,把class文件加載到內(nèi)存。

          1.啟動(dòng)類(lèi)加載器(BootstrapClassLoader):負(fù)責(zé)加載存放在lib/rt.jar charset.jar 等目錄下的核心類(lèi),由c++實(shí)現(xiàn)。啟動(dòng)類(lèi)加載器無(wú)法被Java程序直接引用(classLoader的loadClass方法中,若parent為 null則使用啟動(dòng)類(lèi)加載器。

          2.擴(kuò)展類(lèi)加載器(ExtensionClassLoader):負(fù)責(zé)加載lib/ext目錄下的類(lèi),在類(lèi)sun.misc.Launcher E x t C l a s s L o a d e r 中 以 J a v a 代 碼 的 形 式 實(shí) 現(xiàn) 。3. 應(yīng) 用 程 序 類(lèi) 加 載 器 ( A p p l i c a t i o n C l a s s L o a d e r ) :負(fù) 責(zé) 加 載 用 戶(hù) 類(lèi) 路 徑 ( C l a s s P a t h ) 下 所 有 的 類(lèi) 庫(kù) 。由 s u n . m i s c . L a u n c h e r ExtClassLoader 中以Java代碼的形式實(shí)現(xiàn) 。3.應(yīng)用程序類(lèi)加載器(ApplicationClassLoader):負(fù)責(zé)加載用戶(hù)類(lèi)路徑(ClassPath)下所有的類(lèi)庫(kù)。由sun.misc.Launcher ExtClassLoader中以Java代碼的形式實(shí)現(xiàn)。3.應(yīng)用程序類(lèi)加載器(ApplicationClassLoader):負(fù)責(zé)加載用戶(hù)類(lèi)路徑(ClassPath)下所有的類(lèi)庫(kù)。由sun.misc.LauncherAppClassLoader實(shí)現(xiàn) ,是ClassLoader類(lèi)中的getSystemClassLoader()方法的返回值。如果應(yīng)用程序中沒(méi)有自定義過(guò)自己的類(lèi)加載器,一般情況下這個(gè)就是程序中默認(rèn)的類(lèi)加載器。

          4、自定義類(lèi)加載器:CustomClassLoader

          public class ClassLoaderScope {
               public static void main(String[] args) {
                   System.out.println("-------------------Bootstrap加載類(lèi)-----------------")
                    String property = System.getProperty("sun.boot.class.path");
                    String s = property.replaceAll(";", System.lineSeparator());
                    System.out.println(s);
            
                   System.out.println("-------------------Ext加載類(lèi)-------------------");
                   String property1 = System.getProperty("java.ext.dirs");
                   String s1 = property1.replaceAll(";", System.lineSeparator());
                   System.out.println(s1);
           
                   System.out.println("-------------------App加載類(lèi)-------------------");
                   String property2 = System.getProperty("java.class.path");
                   String s2 = property2.replaceAll(";", System.lineSeparator());
                   System.out.println(s2);
               }
           }


          2.1 自定義類(lèi)加載器

          只需繼承ClassLoader抽象類(lèi),并重寫(xiě)findClass方法(如果要打破雙親委派模型,需要重寫(xiě)loadClass方法)原因可以查看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) {
                          // 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.
                          long t1 = System.nanoTime();
                          c = findClass(name);

                          // this is the defining class loader; record the stats
                          sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                          sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                          sun.misc.PerfCounter.getFindClasses().increment();
                      }
                  }
                  if (resolve) {
                      resolveClass(c);
                  }
                  return c;
              }
          }



          這個(gè)是ClassLoader中的loadClass方法,大致流程如下:

          1)檢查類(lèi)是否已加載,如果是則不用再重新加載了;

          2)如果未加載,則通過(guò)父類(lèi)加載(依次遞歸)或者啟動(dòng)類(lèi)加載器(bootstrap)加載;

          3)如果還未找到,則調(diào)用本加載器的findClass方法;

          以上可知,類(lèi)加載器先通過(guò)父類(lèi)加載,父類(lèi)未找到時(shí),才有本加載器加載。


          因?yàn)樽远x類(lèi)加載器是繼承ClassLoader,而我們?cè)倏磃indClass方法:

          protected Class<?> findClass(String name) throws ClassNotFoundException {    
              throw new ClassNotFoundException(name);
          }


          可以看出,它直接返回ClassNotFoundException。

          因此,自定義類(lèi)加載器必須重寫(xiě)findClass方法。


          自定義類(lèi)加載器示例代碼:類(lèi)加載器HClassLoader:

          class HClassLoader extends ClassLoader {

          private String classPath;

          public HClassLoader(String classPath) {
              this.classPath = classPath;
          }

          @Override
          protected Class<?> findClass(String name) throws ClassNotFoundException {
              try {
                  byte[] data = loadByte(name);
                  return defineClass(name, data, 0, data.length);
              } catch (Exception e) {
                  e.printStackTrace();
                  throw new ClassNotFoundException();
              }

          }

          /**
           * 獲取.class的字節(jié)流
           * @param name
           * @return
           * @throws Exception
           */
          private byte[] loadByte(String name) throws Exception {
              name = name.replaceAll("\\.""/");
              FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
              int len = fis.available();
              byte[] data = new byte[len];
              fis.read(data);
              fis.close();
              // 字節(jié)流解密
              data = DESInstance.deCode("1234567890qwertyuiopasdf".getBytes(), data);
              return data;
            }
          }



          加載一個(gè)類(lèi)Car:

          public class Car {

              public Car() {
                  System.out.println("Car:" + getClass().getClassLoader());
                  System.out.println("Car Parent:" + getClass().getClassLoader().getParent());
              }
              public String print() {
                  System.out.println("Car:print()");
                  return "carPrint";
              }
           }


          測(cè)試代碼

          @Test
          public void testClassLoader() throws Exception {
              HClassLoader myClassLoader = new HClassLoader("e:/temp/a");
              Class clazz = myClassLoader.loadClass("com.ha.Car");
              Object o = clazz.newInstance();
              Method print = clazz.getDeclaredMethod("print", null);
              print.invoke(o, null);
          }


          需要注意的是:

          執(zhí)行測(cè)試代碼前,必須將Car.class文件移動(dòng)到e:/temp/a下,并且按包名建立層級(jí)目錄(這里為com/ha/)。因?yàn)槿绻灰苿?dòng)Car.class文件,那么Car類(lèi)會(huì)被AppClassLoader加載(自定義類(lèi)加載器的parent是AppClassLoader)。


          2.2 自定義類(lèi)加載器的應(yīng)用

          上面介紹了Java類(lèi)加載器的相關(guān)知識(shí)。對(duì)于自定義類(lèi)加載器,哪里可以用到呢?

          主流的Java Web服務(wù)器,比如Tomcat,都實(shí)現(xiàn)了自定義的類(lèi)加載器。因?yàn)樗鉀Q幾個(gè)問(wèn)題:

          1)Tomcat上可以部署多個(gè)不同的應(yīng)用,但是它們可以使用同一份類(lèi)庫(kù)的不同版本。這就需要自定義類(lèi)加載器,以便對(duì)加載的類(lèi)庫(kù)進(jìn)行隔離,否則會(huì)出現(xiàn)問(wèn)題;

          2)對(duì)于非.class的文件,需要轉(zhuǎn)為Java類(lèi),就需要自定義類(lèi)加載器。比如JSP文件。


          這里舉一個(gè)其它的例子:Java核心代碼的加密。

          假設(shè)我們項(xiàng)目當(dāng)中,有一些核心代碼不想讓別人反編譯看到。當(dāng)前知道有兩種方法,一種是通過(guò)代碼混淆(推薦Allatori,商用收費(fèi));一種是自己編寫(xiě)加密算法,對(duì)字節(jié)碼加密,加大反編譯難度。

          代碼混淆如果用Allatori,比較簡(jiǎn)便。注意控制自己編寫(xiě)類(lèi)的訪問(wèn)權(quán)限即可。接口用public,內(nèi)部方法用private,其他的用默認(rèn)的(即不加訪問(wèn)修飾符)或者protected。代碼混淆這里不過(guò)多說(shuō)明,這里主要介紹一下字節(jié)碼加密。

          大概流程是:

          .class加密代碼:

          @Test
          public void testEncode() {
              String classFile = "e:/temp/a/com/ha/Car.class";
              FileInputStream fis = null;
              try {
                  fis = new FileInputStream(classFile);
                  int len = fis.available();
                  byte[] data = new byte[len];
                  fis.read(data);
                  fis.close();

                  data = DESInstance.enCode("1234567890qwertyuiopasdf".getBytes(), data);

                  String outFile = "e:/temp/a/com/ha/EnCar.class";
                  FileOutputStream fos = new FileOutputStream(outFile);
                  fos.write(data);
                  fos.close();
              } catch (Exception e) {
                  e.printStackTrace();
              }

          }



          類(lèi)加載器中解密,查看上文中的:

          // 字節(jié)流解密
          data = DESInstance.deCode("1234567890qwertyuiopasdf".getBytes(), data);


          加解密工具類(lèi)

          public class DESInstance {

              private static String ALGORITHM = "DESede";

              /**
               * 加密
               *
               * @param key
               * @param src
               * @return
               */
              public static byte[] enCode(byte[] key, byte[] src) {

                  byte[] value = null;
                  SecretKey deskey = new SecretKeySpec(key, ALGORITHM);
                  try {
                      Cipher cipher = Cipher.getInstance(ALGORITHM);
                      cipher.init(Cipher.ENCRYPT_MODE, deskey);
                      value = cipher.doFinal(src);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }

                  return value;
              }

              /**
               * 解密
               *
               * @param key
               * @param src
               * @return
               */
              public static byte[] deCode(byte[] key, byte[] src) {
                  byte[] value = null;
                  SecretKey deskey = new SecretKeySpec(key, ALGORITHM);

                  try {
                      Cipher cipher = Cipher.getInstance(ALGORITHM);
                      cipher.init(Cipher.DECRYPT_MODE, deskey);
                      value = cipher.doFinal(src);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }

                  return value;
              }
          }


          注意秘鑰是24位,否則會(huì)報(bào)錯(cuò):

          java.security.InvalidKeyException: Invalid key length

          如果解密密碼錯(cuò)誤,則是如下錯(cuò)誤:

          javax.crypto.BadPaddingException: Given final block not properly padded

          當(dāng)然,這樣做還是會(huì)被反編譯破解,要加大難度,還需要其他處理的。

          ————————————————

          版權(quán)聲明:本文為CSDN博主「兢兢業(yè)業(yè)的子牙」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/qq_33449307/article/details/114950029





          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長(zhǎng)按上方微信二維碼 2 秒





          感謝點(diǎn)贊支持下哈 

          瀏覽 86
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  豆花精品在线视频 | 国产色婷婷视频在线观看 | 色蜜桃av | 夜夜撸,狠狠撸夜夜操狠狠操 | 噜噜AV|