<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      java寶典

       

      Java的類加載器(ClassLoader)簡介

      ClassLoader是Java的類加載器,用于把class文件加載到JVM中,下面大概了解一下Java類加載器的概況。

      一,java提供的加載器

      Java提供了三個ClassLoader:

      1,BootstrapClassLoader

      用于加載JAVA核心類庫,也就是環境變量的%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar等。

      在JVM啟動時加入-Xbootclasspath參數,可以把對應路徑也加載到Bootstrap的路徑列表中來,這個參數有兩種用法:

      1),-Xbootclasspath/a:{人工指定路徑},把對應路徑加載到Bootstrap默認路徑后面,也就是說,如果有class重復,以Bootstrap默認路徑下的類為準(因為是按照路徑列表順序加載的),舉例:

      java -Xbootclasspath/a:D:\test\Test.jar

      2),-Xbootclasspath/p: {人工指定路徑},把對應路徑加載到Bootstrap默認路徑后面,也就是說,如果有class重復,以指定路徑下的類為準,舉例:

      java -Xbootclasspath/p:D:\test\Test.jar

      2,Extention ClassLoader

      擴展類加載器,加載環境變量%JRE_HOME%\lib\ext目錄下的class文件

      這個加載器也可以在JVM啟動時使用參數改變加載的行為,參數是-D java.ext.dirs=,作用是替換Java擴展類加載器所加載的文件目錄。

      注意,該參數是替換而不是追加,因為這個加載器的加載路徑只有一個,也就是說,%JRE_HOME%\lib\ext是擴展類加載器的默認路徑,如果我們在啟動時使用-Djava.ext.dirs=d:/test,那么java就不再加載%JRE_HOME%\lib\ext路徑下的文件。

      3,AppclassLoader

      加載classpath中的class類,通過在JVM啟動命令中的-classpath參數指定路徑,可以指定絕對路徑、相對路徑、環境變量等,舉例:

      java –classpath %CLASSPATH%

      二,各種加載器之間的關系

      從加載關系來說:

      1,BootstrapClassLoader是Extention ClassLoader的父加載器。

      2,ExtentionClassLoader是AppclassLoader的父加載器。

      注意,這里的父加載器并不是java語言里的父類,只是邏輯上的。

      從Java語言的角度來說:

      1,ExtentionClassLoader對應的java類是ExtClassLoader,他的父類是java.net.URLClassLoader。

      2,AppclassLoader對應的java類是AppClassLoader,他的父類也是java.net.URLClassLoader,沒錯,和ExtClassLoader一樣。

      3,BootstrapClassLoader是C++編寫的,壓根沒有對應的java類,當然也成不了別人的父類。

      ClassLoader類有getParent()方法,可以得到父加載器,一個加載器的父加載器是在他初始化的時候指定的。

      AppclassLoader用getParent()方法得到的是ExtClassLoader。

      ExtClassLoader用getParent()方法得到的是null。

      如果我們自定義一個加載器,往往要繼承ClassLoader類,此時默認的父加載器是AppClassLoader。

      三,加載器的加載順序

      加載器在JVM啟動時的加載順序是:

      1,BootstrapClassLoader

      2,ExtentionClassLoader

      3,AppclassLoader

      關于這個加載順序可以參考sun.misc.Launcher類,這個類在JVM啟動時初始化了各個加載器,代碼如下:

      /**
      
       *This class is used by the system to launch the main application.
      
      Launcher */
      
      public class Launcher {
      
         private static URLStreamHandlerFactory factory = new Factory();
      
         private static Launcher launcher = new Launcher();
      
         private static String bootClassPath =
      
             System.getProperty("sun.boot.class.path");
      
       
      
         public static Launcher getLauncher() {
      
             return launcher;
      
          }
      
       
      
         private ClassLoader loader;
      
       
      
         public Launcher() {
      
             // Create the extension class loader
      
             ClassLoader extcl;
      
             try {
      
                 extcl = ExtClassLoader.getExtClassLoader();
      
             } catch (IOException e) {
      
                 throw new InternalError(
      
                      "Could not createextension class loader", e);
      
             }
      
       
      
             // Now create the class loader to use to launch the application
      
             try {
      
                 loader = AppClassLoader.getAppClassLoader(extcl);
      
             } catch (IOException e) {
      
                 throw new InternalError(
      
                      "Could not create applicationclass loader", e);
      
             }
      
       
      
             // Also set the context class loader for the primordial thread.
      
             Thread.currentThread().setContextClassLoader(loader);
      
       
      
             // Finally, install a security manager if requested
      
             String s = System.getProperty("java.security.manager");
      
             if (s != null) {
      
                 SecurityManager sm = null;
      
                 if ("".equals(s) || "default".equals(s)) {
      
                      sm = newjava.lang.SecurityManager();
      
                 } else {
      
                      try {
      
                          sm =(SecurityManager)loader.loadClass(s).newInstance();
      
                      } catch (IllegalAccessExceptione) {
      
                      } catch (InstantiationExceptione) {
      
                      } catch (ClassNotFoundExceptione) {
      
                      } catch (ClassCastException e){
      
                      }
      
                 }
      
                 if (sm != null) {
      
                      System.setSecurityManager(sm);
      
                 } else {
      
                      throw new InternalError(
      
                          "Could not createSecurityManager: " + s);
      
                 }
      
             }
      
      }
      
      ……后面還有很多
      
      }
      

      可以看到,在Launcher的無參構造中,先是初始化了ExtClassLoader,然后初始化AppClassLoader,其實Bootstrap ClassLoader在這之前就加載完了,類中有這樣一個屬性:

      private static String bootClassPath =
      
             System.getProperty("sun.boot.class.path");
      

      這個就是Bootstrap ClassLoader類加載的路徑,可以自己寫一段代碼看看這個路徑是什么:

      System.out.println(System.getProperty("sun.boot.class.path"));
      

      輸出結果:

      C:\ProgramFiles\Java\jre7\lib\resources.jar;

      C:\Program Files\Java\jre7\lib\rt.jar;

      C:\ProgramFiles\Java\jre7\lib\sunrsasign.jar;

      C:\Program Files\Java\jre7\lib\jsse.jar;

      C:\Program Files\Java\jre7\lib\jce.jar;

      C:\ProgramFiles\Java\jre7\lib\charsets.jar;

      C:\Program Files\Java\jre7\lib\jfr.jar;

      C:\Program Files\Java\jre7\classes

      實際輸出結果是沒有換行的,我在分號處加了換行。

      Launcher類中加載的ExtClassLoader和AppClassLoader這兩個類的定義也是在Launcher中,源碼也可以看一下

      ExtClassLoader類的定義:

      /*
           * The class loader used for loading installed extensions.
           */
          static class ExtClassLoader extends URLClassLoader {
      
              static {
                  ClassLoader.registerAsParallelCapable();
              }
      
              /**
               * create an ExtClassLoader. The ExtClassLoader is created
               * within a context that limits which files it can read
               */
              public static ExtClassLoader getExtClassLoader() throws IOException
              {
                  final File[] dirs = getExtDirs();
      
                  try {
                      // Prior implementations of this doPrivileged() block supplied
                      // aa synthesized ACC via a call to the private method
                      // ExtClassLoader.getContext().
      
                      return AccessController.doPrivileged(
                          new PrivilegedExceptionAction<ExtClassLoader>() {
                              public ExtClassLoader run() throws IOException {
                                  int len = dirs.length;
                                  for (int i = 0; i < len; i++) {
                                      MetaIndex.registerDirectory(dirs[i]);
                                  }
                                  return new ExtClassLoader(dirs);
                              }
                          });
                  } catch (java.security.PrivilegedActionException e) {
                      throw (IOException) e.getException();
                  }
              }
      
              void addExtURL(URL url) {
                  super.addURL(url);
              }
      
              /*
               * Creates a new ExtClassLoader for the specified directories.
               */
              public ExtClassLoader(File[] dirs) throws IOException {
                  super(getExtURLs(dirs), null, factory);
                  SharedSecrets.getJavaNetAccess().
                      getURLClassPath(this).initLookupCache(this);
              }
      
              private static File[] getExtDirs() {
                  String s = System.getProperty("java.ext.dirs");
                  File[] dirs;
                  if (s != null) {
                      StringTokenizer st =
                          new StringTokenizer(s, File.pathSeparator);
                      int count = st.countTokens();
                      dirs = new File[count];
                      for (int i = 0; i < count; i++) {
                          dirs[i] = new File(st.nextToken());
                      }
                  } else {
                      dirs = new File[0];
                  }
                  return dirs;
              }
      
              private static URL[] getExtURLs(File[] dirs) throws IOException {
                  Vector<URL> urls = new Vector<URL>();
                  for (int i = 0; i < dirs.length; i++) {
                      String[] files = dirs[i].list();
                      if (files != null) {
                          for (int j = 0; j < files.length; j++) {
                              if (!files[j].equals("meta-index")) {
                                  File f = new File(dirs[i], files[j]);
                                  urls.add(getFileURL(f));
                              }
                          }
                      }
                  }
                  URL[] ua = new URL[urls.size()];
                  urls.copyInto(ua);
                  return ua;
              }
      
              /*
               * Searches the installed extension directories for the specified
               * library name. For each extension directory, we first look for
               * the native library in the subdirectory whose name is the value
               * of the system property <code>os.arch</code>. Failing that, we
               * look in the extension directory itself.
               */
              public String findLibrary(String name) {
                  name = System.mapLibraryName(name);
                  URL[] urls = super.getURLs();
                  File prevDir = null;
                  for (int i = 0; i < urls.length; i++) {
                      // Get the ext directory from the URL
                      File dir = new File(urls[i].getPath()).getParentFile();
                      if (dir != null && !dir.equals(prevDir)) {
                          // Look in architecture-specific subdirectory first
                          // Read from the saved system properties to avoid deadlock
                          String arch = VM.getSavedProperty("os.arch");
                          if (arch != null) {
                              File file = new File(new File(dir, arch), name);
                              if (file.exists()) {
                                  return file.getAbsolutePath();
                              }
                          }
                          // Then check the extension directory
                          File file = new File(dir, name);
                          if (file.exists()) {
                              return file.getAbsolutePath();
                          }
                      }
                      prevDir = dir;
                  }
                  return null;
              }
      
              private static AccessControlContext getContext(File[] dirs)
                  throws IOException
              {
                  PathPermissions perms =
                      new PathPermissions(dirs);
      
                  ProtectionDomain domain = new ProtectionDomain(
                      new CodeSource(perms.getCodeBase(),
                          (java.security.cert.Certificate[]) null),
                      perms);
      
                  AccessControlContext acc =
                      new AccessControlContext(new ProtectionDomain[] { domain });
      
                  return acc;
              }
          }
      

      可以看到里面的getExtDirs()方法中,獲得了java.ext.dirs參數的內容,這個地址也可以打印出來看看:

      System.out.println(System.getProperty("java.ext.dirs"));
      

      輸出的結果:

      C:\Program Files\Java\jre7\lib\ext;

      C:\Windows\Sun\Java\lib\ext

      AppClassLoader類的定義:

      /**
           * The class loader used for loading from java.class.path.
           * runs in a restricted security context.
           */
          static class AppClassLoader extends URLClassLoader {
      
              static {
                  ClassLoader.registerAsParallelCapable();
              }
      
              public static ClassLoader getAppClassLoader(final ClassLoader extcl)
                  throws IOException
              {
                  final String s = System.getProperty("java.class.path");
                  final File[] path = (s == null) ? new File[0] : getClassPath(s);
      
                  // Note: on bugid 4256530
                  // Prior implementations of this doPrivileged() block supplied
                  // a rather restrictive ACC via a call to the private method
                  // AppClassLoader.getContext(). This proved overly restrictive
                  // when loading  classes. Specifically it prevent
                  // accessClassInPackage.sun.* grants from being honored.
                  //
                  return AccessController.doPrivileged(
                      new PrivilegedAction<AppClassLoader>() {
                          public AppClassLoader run() {
                          URL[] urls =
                              (s == null) ? new URL[0] : pathToURLs(path);
                          return new AppClassLoader(urls, extcl);
                      }
                  });
              }
      
              final URLClassPath ucp;
      
              /*
               * Creates a new AppClassLoader
               */
              AppClassLoader(URL[] urls, ClassLoader parent) {
                  super(urls, parent, factory);
                  ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
                  ucp.initLookupCache(this);
              }
      
              /**
               * Override loadClass so we can checkPackageAccess.
               */
              public Class<?> loadClass(String name, boolean resolve)
                  throws ClassNotFoundException
              {
                  int i = name.lastIndexOf('.');
                  if (i != -1) {
                      SecurityManager sm = System.getSecurityManager();
                      if (sm != null) {
                          sm.checkPackageAccess(name.substring(0, i));
                      }
                  }
      
                  if (ucp.knownToNotExist(name)) {
                      // The class of the given name is not found in the parent
                      // class loader as well as its local URLClassPath.
                      // Check if this class has already been defined dynamically;
                      // if so, return the loaded class; otherwise, skip the parent
                      // delegation and findClass.
                      Class<?> c = findLoadedClass(name);
                      if (c != null) {
                          if (resolve) {
                              resolveClass(c);
                          }
                          return c;
                      }
                      throw new ClassNotFoundException(name);
                  }
      
                  return (super.loadClass(name, resolve));
              }
      
              /**
               * allow any classes loaded from classpath to exit the VM.
               */
              protected PermissionCollection getPermissions(CodeSource codesource)
              {
                  PermissionCollection perms = super.getPermissions(codesource);
                  perms.add(new RuntimePermission("exitVM"));
                  return perms;
              }
      
              /**
               * This class loader supports dynamic additions to the class path
               * at runtime.
               *
               * @see java.lang.instrument.Instrumentation#appendToSystemClassPathSearch
               */
              private void appendToClassPathForInstrumentation(String path) {
                  assert(Thread.holdsLock(this));
      
                  // addURL is a no-op if path already contains the URL
                  super.addURL( getFileURL(new File(path)) );
              }
      
              /**
               * create a context that can read any directories (recursively)
               * mentioned in the class path. In the case of a jar, it has to
               * be the directory containing the jar, not just the jar, as jar
               * files might refer to other jar files.
               */
      
              private static AccessControlContext getContext(File[] cp)
                  throws java.net.MalformedURLException
              {
                  PathPermissions perms =
                      new PathPermissions(cp);
      
                  ProtectionDomain domain =
                      new ProtectionDomain(new CodeSource(perms.getCodeBase(),
                          (java.security.cert.Certificate[]) null),
                      perms);
      
                  AccessControlContext acc =
                      new AccessControlContext(new ProtectionDomain[] { domain });
      
                  return acc;
              }
          }
      

      這個類的getAppClassLoader()方法中,獲得了java.class.path參數,可以打印出來:

      System.out.println(System.getProperty("java.class.path"));
      

      輸出結果:

      D:\workspace\test\bin;

      C:\Users\lk\Downloads\asm-4.2.jar;

      C:\Users\lk\Desktop\dubbo-2.8.3.2.jar;

      C:\Users\lk\Downloads\cglib-2.2.jar;

      C:\Users\lk\Downloads\netty-3.2.5.Final.jar

      都是我在classpath里面配的目錄和jar包。

      四,查找class和雙親委托

      java的加載器在查找或加載class時,需要確認這個class是否已經被加載了,如果已經被加載了自己就不再重復加載。

      類加載器查找class的方式叫做雙親委托模式,基本方法是:

      1,自己先查緩存,驗證類是否已加載,如果緩存中沒有則向上委托父加載器查詢。

      2,父加載器接到委托也是查自己的緩存,如果沒有再向上委托。

      3,直到最頂級的BootstrapClassLoader也沒在緩存中找到該類,則Bootstrap ClassLoader從他自己的加載路徑中查找該類,如果找不到則返回下一級加載器。

      4,下一級加載器也從他自己的加載路徑中查找該類,如果找不到則返回下一級加載器,直到返回最開始的加載器。

      簡單來說,就是從下往上查緩存,然后從上往下掃描路徑。如果在其中任何一步發現已經加載了該類,都會立刻返回,不再進行后面的查找。

      畫個圖來表示這個流程的話,應該是這樣的:

      圖1

      ThirdPartyImage_f5d0de06.png

      查找的順序就是圖上從①到⑥

      五,自定義ClassLoader

      自定義的ClassLoader類,一般需要滿足以下條件:

      1,繼承java.lang.ClassLoader類,或者繼承他的子類比如java.net.URLClassLoader。

      2,重寫findClass()方法或者重寫loadClass()方法,findClass()會在調用加載器的loadClass()方法時調用。

      3,在findClass()中使用defineClass()方法,這個方法不需要自己實現,是父類ClassLoader的方法。這個方法的參數在后面的例子中再詳解。

      使用自定義的ClassLoader類,一般需要以下步驟:

      1,初始化ClassLoader。

      2,調用ClassLoader的loadClass()方法加載目標class,參數是String類型,內容包括目標類的包名和類名,不包括”.class”,這個方法是父類ClassLoader的方法,不需要自己定義。在調用這個方法時,就會調用自己在加載器中寫的findClass()方法。

      3,使用加載好的類的class.newInstance()就可以得到目標類的對象。

      下面舉個例子

      首先是目標Class,簡單寫一個:

      package classloader;
      
      public class Test {
      
          public void hello() {
              System.out.println("hello world");
          }
      
      }
      

      下面是重點,自定義ClassLoader:

      package classloader;
      
      import java.io.ByteArrayOutputStream;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class MyClassLoader extends ClassLoader {
      
          private String myClassPath;
      
          public MyClassLoader(String path) {
              myClassPath = path;
          }
      
          @Override
          protected Class<?> findClass(String name) throws ClassNotFoundException {
      
              File file = new File(myClassPath, name+".class");
      
              try {
                  FileInputStream is = new FileInputStream(file);
      
                  ByteArrayOutputStream bos = new ByteArrayOutputStream();
                  int len = 0;
                  try {
                      while ((len = is.read()) != -1) {
                          bos.write(len);
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
      
                  byte[] data = bos.toByteArray();
                  is.close();
                  bos.close();
      
                  return defineClass(name, data, 0, data.length, null);
      
              } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
      
              return super.findClass(name);
          }
      
      }
      

      自定義的ClassLoader可以繼承ClassLoader類,并重寫findClass(String name)方法,這個方法中的內容,基本就是為了最后的調用defineClass()方法做準備。

      defineClass()方法在父類中有多個重載的方法,他們最終調用的是一個5個參數的defineClass()方法,這5個參數分別是:

      1,文件名(帶”.class”)

      2,class文件內容的二進制數組

      3,二進制數組中表示class數據開始的下標

      4,class二進制數據的長度

      5,protectionDomain,標識了類的封裝域的權限信息,可以沒有(本例就沒有),詳解可參考JDK文檔。

      從這個方法的定義來看,似乎是可以支持一長串二進制數組,開發者只需要指定數組中代表目標Class的開始下標和長度,java就可以從中截取出目標Class的信息并裝載,但我沒想到有什么場景可以用到這個設定。(本例中二進制數組來源于完整的class文件,所以開始下標是0,并且java需要讀取整個數組)

      最后寫一個類使用一下我們自定義的加載器:

      package classloader;
      
      import java.lang.reflect.Method;
      
      public class ClassLoaderTest {
      
          public static void main(String[] args) {
      
              try {
      
                  // 初始化加載器
                  MyClassLoader myLoader = new MyClassLoader("D:\\workspace\\test\\bin");
      
                  // 加載class
                  Class c = myLoader.loadClass("classloader.Test");
      
                  // 驗證
                  Object obj = c.newInstance();
                  Method method = c.getDeclaredMethod("hello", null);
                  method.invoke(obj, null);
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
          }
      
      }
      

      運行這個類,控制臺輸出hello world,說明目標類加載成功。

      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;
              }
          }
      

      可以看到,方法首先檢查這個類有沒有被加載,如果沒有被加載,則先去父加載器加載。如果沒有父加載器,則使用Bootstrap ClassLoader加載。如果父加載器沒能加載這個類,則調用findClass()方法加載。

      六,重新加載class,熱替換

      首先是基礎知識:

      1,java目前沒有專門的API,用來卸載JVM中加載的類。

      2,要卸載JVM中的類,需要該類的對象都被回收,加載該類的ClassLoader也被回收,使用該類的線程結束等條件,比較嚴格。

      3,在java中,不同的ClassLoader可以加載同一個類,即使class文件是同一個也可以被加載。但是同一個ClassLoader不能重復加載一個類,重復加載會報錯。

      總的來說,在不停服務的情況下熱替換class不是很靠譜,現在的java版本也根本沒打算讓開發者這么做。

      雖然可以通過新建ClassLoader實例的方法來改變新加載的Class內容,但之前ClassLoader加載的類和對象不會被修改,什么時候能被GC回收也很不可控,玩玩可以,線上環境慎用,后續的坑很多。

      還是老老實實重啟比較靠譜。

      不過我們依然可以做出一個山寨版的熱替換功能,方案就是之前提到的新建ClassLoader實例。

      首先我準備了兩個目標類,分別編譯成class文件

      第一個:

      package classloader;
      
      public class Test {
      
          public void hello() {
              System.out.println("hello world");
      //        System.out.println("Are you OK");
              
          }
      
      }
      

      第二個:

      package classloader;
      
      public class Test {
      
          public void hello() {
      //        System.out.println("hello world");
              System.out.println("Are you OK");
              
          }
      
      }
      

      我把第二個類編譯出的class文件單獨保存,將來要替換第一個class。

      然后是自己定義的ClassLoader類:

      package classloader;
      
      import java.io.ByteArrayOutputStream;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class MyClassLoader2 extends ClassLoader {
      
          private String myClassPath;
          
          public MyClassLoader2(String path) {
              myClassPath = path;
          }
      
          public Class<?> loadMyClass(String name){
              
              System.out.println("重新加載:"+name);
              
              File file = new File(myClassPath+File.separator+name.substring(0,name.indexOf(".")), name.substring(name.indexOf(".")+1,name.length())+".class");
              if(!file.exists()){
                  return null;
              }
              
              try {
                  
                  FileInputStream is = new FileInputStream(file);
                  ByteArrayOutputStream bos = new ByteArrayOutputStream();
                  int len = 0;
                  try {
                      while ((len = is.read()) != -1) {
                          bos.write(len);
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
      
                  byte[] data = bos.toByteArray();
                  is.close();
                  bos.close();
      
                  return defineClass(name, data, 0, data.length, null);
      
              } catch (Exception e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
              
              return null;
              
          }
          
          @Override
          public Class<?> loadClass(String name) throws ClassNotFoundException {
              System.out.println("loadClass():"+name);
              Class<?> cls = null;
              if (cls == null){
                  cls=loadMyClass(name);
              }
              if(cls==null){
                  cls = getSystemClassLoader().loadClass(name); 
                  System.out.println("getSystemClassLoader():"+ getSystemClassLoader());
              }
              if (cls == null){
                  throw new ClassNotFoundException(name);  
              }
              return cls;  
          }
      
      }
      

      這個自定義的加載器重寫了ClassLoader類的loadClass()方法。注意,對于目標路徑下的類,每次都會重新加載,沒有判斷重復。

      在classLoader()方法中先過自己的加載器,自己的加載器必須在特定目錄中存在class文件才可以加載,否則就用系統定義的加載器加載,因為重寫loaderClass()方法之后,目標類所有的相關類也會用這個方法加載(比如目標類的父類java.lang.Object)。

      最后是測試類:

      package classloader;
      
      import java.lang.reflect.Method;
      
      public class ClassLoaderTest {
      
          public static void main(String[] args) {
              
      //        loadClass();
              loadClass2();
              
          }
          
          public static void loadClass(){
              
              try {
                  // 初始化加載器
                  MyClassLoader myLoader = new MyClassLoader("D:\\workspace\\test\\bin");
      
                  // 加載class
                  Class c = myLoader.loadClass("classloader.Test");
      
                  // 驗證
                  Object obj = c.newInstance();
                  Method method = c.getDeclaredMethod("hello", null);
                  method.invoke(obj, null);
                  
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
          
          public static void loadClass2(){
              
              try {
                  
                  while(true){
                      
                      // 初始化加載器
                      MyClassLoader2 myLoader = new MyClassLoader2("D:\\workspace\\test\\bin");
                      
                      // 加載class
                      Class c = myLoader.loadClass("classloader.Test");
                      
                      System.out.println(c.getClassLoader());
                      System.out.println(Class.forName("classloader.Test").getClassLoader().toString());
                      System.out.println();
                      
                      // 驗證
                      Object obj = c.newInstance();
                      Method method = c.getDeclaredMethod("hello", null);
                      method.invoke(obj, null);
                      
                      Thread.sleep(1000);
                  }
                  
                  
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
      }
      

      測試類實際上是每隔一秒鐘新建一個ClassLoader的實例,并用新ClassLoader加載目標類。

      在程序啟動之前,編譯路徑下是第一個目標類的Class文件(hello world),在程序啟動之后把第二個Class文件(Are you OK)替換第一個,新加載的目標類就可以調用第二個目標類的方法。

      運行后輸出的結果如下:

      loadClass():classloader.Test
      
      重新加載:classloader.Test
      
      loadClass():java.lang.Object
      
      重新加載:java.lang.Object
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      classloader.MyClassLoader2@616affac
      
      sun.misc.Launcher$AppClassLoader@1ddd40f3
      
       
      
      loadClass():java.lang.System
      
      重新加載:java.lang.System
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      loadClass():java.io.PrintStream
      
      重新加載:java.io.PrintStream
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      hello world
      
      loadClass():classloader.Test
      
      重新加載:classloader.Test
      
      loadClass():java.lang.Object
      
      重新加載:java.lang.Object
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      classloader.MyClassLoader2@170a6001
      
      sun.misc.Launcher$AppClassLoader@1ddd40f3
      
       
      
      loadClass():java.lang.System
      
      重新加載:java.lang.System
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      loadClass():java.io.PrintStream
      
      重新加載:java.io.PrintStream
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      Are you OK
      
      loadClass():classloader.Test
      
      重新加載:classloader.Test
      
      loadClass():java.lang.Object
      
      重新加載:java.lang.Object
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      classloader.MyClassLoader2@6ef82fe7
      
      sun.misc.Launcher$AppClassLoader@1ddd40f3
      
       
      
      loadClass():java.lang.System
      
      重新加載:java.lang.System
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      loadClass():java.io.PrintStream
      
      重新加載:java.io.PrintStream
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      Are you OK
      
      loadClass():classloader.Test
      
      重新加載:classloader.Test
      
      loadClass():java.lang.Object
      
      重新加載:java.lang.Object
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      classloader.MyClassLoader2@28a2f6b
      
      sun.misc.Launcher$AppClassLoader@1ddd40f3
      
       
      
      loadClass():java.lang.System
      
      重新加載:java.lang.System
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      loadClass():java.io.PrintStream
      
      重新加載:java.io.PrintStream
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      Are you OK
      
      loadClass():classloader.Test
      
      重新加載:classloader.Test
      
      loadClass():java.lang.Object
      
      重新加載:java.lang.Object
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@1ddd40f3
      
      classloader.MyClassLoader2@6665e41
      
      sun.misc.Launcher$AppClassLoader@1ddd40f3
      

      從輸出的結果可以看到,重寫的loadClass()方法不但需要加載目標Test類,還要加載java.lang.Object,java.lang.System等類。

      通過loadClass()方法得到的Class,調用class.getClassLoader()方法得到的加載器,就是自己定義的MyClassLoader2,每次的實例都不一樣,而通過Class.forName().getClassLoader()方法得到的加載器,是AppClassLoader,每次的實例都一樣。

      另外,如果在測試類中只使用一個ClassLoader的實例,在循環中多次加載目標類,則會報錯,代碼是這樣:

      package classloader;
      
      import java.lang.reflect.Method;
      
      public class ClassLoaderTest {
      
          public static void main(String[] args) {
              
      //        loadClass();
              loadClass2();
              
          }
          
          public static void loadClass(){
              
              try {
                  // 初始化加載器
                  MyClassLoader myLoader = new MyClassLoader("D:\\workspace\\test\\bin");
      
                  // 加載class
                  Class c = myLoader.loadClass("classloader.Test");
      
                  // 驗證
                  Object obj = c.newInstance();
                  Method method = c.getDeclaredMethod("hello", null);
                  method.invoke(obj, null);
                  
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
          
          public static void loadClass2(){
              
              try {
                  
                  // 初始化加載器
                  MyClassLoader2 myLoader = new MyClassLoader2("D:\\workspace\\test\\bin");
                  
                  while(true){
                      
                      // 加載class
                      Class c = myLoader.loadClass("classloader.Test");
                      
                      System.out.println(c.getClassLoader());
                      System.out.println(Class.forName("classloader.Test").getClassLoader().toString());
                      System.out.println();
                      
                      // 驗證
                      Object obj = c.newInstance();
                      Method method = c.getDeclaredMethod("hello", null);
                      method.invoke(obj, null);
                      
                      Thread.sleep(1000);
                  }
                  
                  
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
      }
      

      程序啟動后的輸出是這樣:

      loadClass():classloader.Test
      
      重新加載:classloader.Test
      
      loadClass():java.lang.Object
      
      重新加載:java.lang.Object
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@28d320d6
      
      classloader.MyClassLoader2@37b7a72b
      
      sun.misc.Launcher$AppClassLoader@28d320d6
      
       
      
      loadClass():java.lang.System
      
      重新加載:java.lang.System
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@28d320d6
      
      loadClass():java.io.PrintStream
      
      重新加載:java.io.PrintStream
      
      getSystemClassLoader():sun.misc.Launcher$AppClassLoader@28d320d6
      
      Are you OK
      
      loadClass():classloader.Test
      
      重新加載:classloader.Test
      
      Exception in thread "main"java.lang.LinkageError: loader (instance of classloader/MyClassLoader2): attempted duplicate class definition for name: "classloader/Test"
      
               atjava.lang.ClassLoader.defineClass1(Native Method)
      
               atjava.lang.ClassLoader.defineClass(Unknown Source)
      
               atclassloader.MyClassLoader2.loadMyClass(MyClassLoader2.java:42)
      
               atclassloader.MyClassLoader2.loadClass(MyClassLoader2.java:58)
      
               atclassloader.ClassLoaderTest.loadClass2(ClassLoaderTest.java:43)
      
               atclassloader.ClassLoaderTest.main(ClassLoaderTest.java:10)
      

      也就是說,第二次加載Test類時報錯。

      關注公眾號:java寶典

      a

      posted on 2021-03-03 14:01  java寶典  閱讀(1401)  評論(0)    收藏  舉報

      導航

      主站蜘蛛池模板: 最近免费中文字幕mv在线视频3| 日韩熟女精品一区二区三区| 国产精品户外野外| 国产成人无码免费视频在线 | 国语精品国内自产视频| 国产一级av在线播放| 欧美人人妻人人澡人人尤物| 加勒比亚洲视频在线播放| 成人拍拍拍无遮挡免费视频| 骚虎视频在线观看| 中日韩精品视频一区二区三区 | 无遮挡午夜男女xx00动态| 日本一区二区在线高清观看| 四虎影视www在线播放| 午夜精品区| 亚洲欧美日韩综合在线丁香| 国产成人午夜精品福利| 1区2区3区4区产品不卡码网站| ww污污污网站在线看com| 欧洲码亚洲码的区别入口| 中国熟女仑乱hd| 日韩欧美亚洲综合久久| 亚洲欧美日韩综合一区二区| 亚洲国产中文字幕精品| 亚洲 中文 欧美 日韩 在线| 91亚洲国产三上悠亚在线播放| 五月天国产成人AV免费观看| 亚洲国产精品综合久久2007| 亚洲最大成人av在线天堂网| 91无码人妻精品一区二区蜜桃 | 好屌草这里只有精品| 国产精品一区二区黄色片| 99久久精品国产一区二区蜜芽| 清纯唯美人妻少妇第一页| 中文字幕在线视频不卡| 人妻夜夜爽天天爽三区麻豆av| 亚洲的天堂在线中文字幕| 国产suv精品一区二区883| 欧美激情一区二区久久久| 亚洲精品日韩久久精品| 风流少妇bbwbbw69视频|