JVM(5)——类加载机制

网友投稿 857 2025-03-31

文章目录

1、类加载机制

1.1、类加载运行全过程

1.2、类的加载时机

1.3、不会初始化(可能会加载)

1.4、类加载器和双亲委派机制

1.4.1、类加载器特点

1.4.2、类加载器初始化过程

1.4.3、全盘加载机制

1.4.4、自定义类加载器

1.4.5、打破双亲加载机制

1.4.6、扩展:tomcat如何打破双亲加载机制

1.5、显示当前ClassLoader加载了哪些Jar

1.6、添加引用类的几种方式

1、类加载机制

1.1、类加载运行全过程

当我们启动一个Java文件的时候,比如

点击main方法时,首先需要通过类加载器把主类加载到JVM,具体如下

粗略地:

地址:https://www.processon.com/view/link/613df3f6e401fd7aedfe372d

详细地:

细分:

加载

在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的 main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的 java.lang.Class对象,作为方法区这个类的各种数据的访问入口 (找 Class 文件)

验证

校验字节码文件的正确性(验证格式、依赖)

准备

给类的静态变量分配内存,并赋予默认值(静态字段、方法表)

解析

将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如 main()方法)替换为指向数据存内存的指针或句柄等(直接引用),这是所谓的静态链接过 程(类加载期间完成),动态链接(调方法)是在程序运行间完成的将符号引用替换为直接引用(符号解析为引用)

初始化

对类的静态变量初始化为指定的值,执行静态代码块(构造器、静态变量赋值、静态代码块)

使用

卸载

链接:https://www.processon.com/view/link/613e05d8e401fd7aedfe52f3

类被加载到方法区中后后主要包含 运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应class实例的引用等信息。

**类加载器的引用:**这个类到类加载器实例的引用

**对应class实例的引用:**类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的对象实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点

**注意:**主类在运行过程中如果使用到其它类,会逐步加载这些类。 jar包或war包里的类不是一次性全部加载的,是使用到时才加载。

package com.zhz; /** * @author zhouhengzhe * @Description: 测试动态加载 * @date 2021/9/12下午9:55 * @since */ public class TestDynamicLoad { static { System.out.println("*************load TestDynamicLoad************"); } public static void main(String[] args) { new A(); System.out.println("*************load test************"); B b = null; //B不会加载,除非这里执行new B() } } class A { static { System.out.println("*************load A************"); } public A() { System.out.println("*************initial A************"); } } class B { static { System.out.println("*************load B************"); } public B() { System.out.println("*************initial B************"); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

![image.png](https://img-blog.csdnimg.cn/img_convert/56f3b752014b3b7ada2ef571f541cf11.png#clientId=u3b241770-215f-4&from=paste&height=153&id=kCICl&margin=[object Object]&name=image.png&originHeight=306&originWidth=1262&originalType=binary&ratio=1&size=74590&status=done&style=none&taskId=u93065b49-3993-4fd5-8715-f208e061184&width=631)

1.2、类的加载时机

虚拟机启动时,初始化用户指定的主类,就是启动执行的 main 方法所在的类;

当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类,就是 new 一个类的时候要初始化;

当遇到调用静态方法的指令时,初始化该静态方法所在的类;

当遇到访问静态字段的指令时,初始化该静态字段所在的类;

子类的初始化会触发父类的初始化;

如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;

使用反射 API 对某个类进行反射调用时,初始化这个类,其实跟前面一样,反射调用要么是已经有实例了,要么是静态方法,都需要初始化;

当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。

1.3、不会初始化(可能会加载)

通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。

定义对象数组,不会触发该类的初始化。

常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。

通过类名获取 Class 对象,不会触发类的初始化,Hello.class 不会让 Hello 类初始化。

通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。(Class.forName(“jvm.Hello”))默认会加载 Hello 类。

通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是不初始化)。

1.4、类加载器和双亲委派机制

引导类加载器(BootstrapClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等

扩展类加载器(ExtClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包

应用程序类加载器(AppClassLoader):负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类

自定义加载器:负责加载用户自定义路径下的类包

双亲委派机制是什么?

1、我们写的类会由AppClassLoader先去加载,然后AppClassLoader会委托ExtClassLoader去加载,ExtClassLoader会委托BootstrapClassLoader去加载

2、如果BootstrapClassLoader加载找不到目标类,就会回退给ExtClassLoader,让ExtClassLoader去加载,ExtClassLoader加载找不到目标类,就由AppClassLoader自己加载。

源码(证据):

//ClassLoader的loadClass方法,里面实现了双亲委派机制 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(); //都会调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类 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; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

解释:

1、检查指定名称的类是否已经加载过,如果已经加载过,就直接返回。

2、如果没加载过,就判断一下是否有父加载器,如果有父加载器,由父加载器加载(即调用parent.loadClass(name, false)),如果没有父加载器就调用BootstrapClassLoader->findBootstrapClassOrNull(name);来加载。

3、如果父加载器及BootstrapClassLoader都没有找到指定的类,那么调用当前类加载器的 findClass方法来完成类加载。

1.4.1、类加载器特点

1、双亲委托

2、防止重复加载:当父类加载器加载过后,子类加载器就不需要再次加载了,保证被加载类的唯一性

3、负责依赖

4、缓存加载

5、沙箱安全机制:防止Java核心类被随意篡改

package java.lang; /** * @author zhouhengzhe * @Description: 沙箱安全机制 * @date 2021/9/18上午2:34 * @since */ public class String { public static void main(String[] args) { System.out.println("aaa"); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

1.4.2、类加载器初始化过程

//Launcher的构造方法 public Launcher() { Launcher.ExtClassLoader var1; try { //构造扩展类加载器,在构造的过程中将其父加载器设置为null var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { //构造应用类加载器,在构造的过程中将其父加载器设置为ExtClassLoader, //Launcher的loader属性值是AppClassLoader,我们一般都是用这个类加载器来加载我们自己写的应用程序 this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } Thread.currentThread().setContextClassLoader(this.loader); String var2 = System.getProperty("java.security.manager"); if (var2 != null) { SecurityManager var3 = null; if (!"".equals(var2) && !"default".equals(var2)) { try { var3 = (SecurityManager)this.loader.loadClass(var2).newInstance(); } catch (IllegalAccessException var5) { } catch (InstantiationException var6) { } catch (ClassNotFoundException var7) { } catch (ClassCastException var8) { } } else { var3 = new SecurityManager(); } if (var3 == null) { throw new InternalError("Could not create SecurityManager: " + var2); } System.setSecurityManager(var3); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

1.4.3、全盘加载机制

显示指定某一个类加载器加载类

1.4.4、自定义类加载器

package com.zhz.bytecode; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author zhouhengzhe * @Description: 自定义类加载器 * @date 2021/9/22上午11:29 * @since */ public class CustomClassLoaderTest { static class CustomClassLoader extends ClassLoader { private String classpath; public CustomClassLoader(String classpath) { this.classpath = classpath; } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。 return defineClass(name,data,0,data.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } private byte[] loadByte(String name) throws IOException { // name = name.replaceAll("\.", "/"); FileInputStream fis = new FileInputStream(classpath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader CustomClassLoader customClassLoader=new CustomClassLoader("/Users/mac/Documents/ideaproject/Java/Java基础/jvm-learn-demo/src/main/java/"); //D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录 Class clazz = customClassLoader.loadClass("com.zhz.bytecode.Hello"); Object instance = clazz.newInstance(); Method method = clazz.getDeclaredMethod("hello", null); method.invoke(instance,null); System.out.println(clazz.getClassLoader().getClass().getName()); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

1.4.5、打破双亲加载机制

再来一个沙箱安全机制示例,尝试打破双亲委派机制,用自定义类加载器加载我们自己实现的 java.lang.String.class(失败,Java不给改核心类)

示例:

package com.zhz.bytecode; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author zhouhengzhe * @Description: 打破双亲加载机制 * @date 2021/9/22下午12:48 * @since */ public class BreakParentLoadingMechanism { static class CustomClassLoader extends ClassLoader { private String classpath; public CustomClassLoader(String classpath) { this.classpath = classpath; } /** * 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载 * @param name * @return: Class * @author: zhouhengzhe * @date: 2021/9/22 */ @Override public 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) { // 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.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } if (resolve) { resolveClass(c); } return c; } } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。 return defineClass(name,data,0,data.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } private byte[] loadByte(String name) throws IOException { // name = name.replaceAll("\.", "/"); FileInputStream fis = new FileInputStream(classpath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader CustomClassLoaderTest.CustomClassLoader customClassLoader=new CustomClassLoaderTest.CustomClassLoader("/Users/mac/Documents/ideaproject/Java/Java基础/jvm-learn-demo/src/main/java/"); //D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录 Class clazz = customClassLoader.loadClass("java.lang.String"); Object instance = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sout", null); method.invoke(instance,null); System.out.println(clazz.getClassLoader().getClass().getName()); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

![image.png](https://img-blog.csdnimg.cn/img_convert/6d660ef9dd0bb8c7edd87c0f78f401dd.png#clientId=ue853169d-3846-4&from=paste&height=118&id=u75c6f481&margin=[object Object]&name=image.png&originHeight=235&originWidth=900&originalType=binary&ratio=1&size=118165&status=done&style=none&taskId=u48d3a5f3-80fb-4329-9210-fa13d2124aa&width=450)

1.4.6、扩展:tomcat如何打破双亲加载机制

Tomcat类加载机制详解

解释:

CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用, 从而实现了公有类库的共用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则 与对方相互隔离。

WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader 实例之间相互隔离。

而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的 就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例, 并通过再建立一个新的Jsp类加载器来实现JSP文件的热加载功能。

tomcat主要类加载器:

commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;

catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;

sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有 Webapp可见,但是对于Tomcat容器不可见;

WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前 Webapp可见,比如加载war包里相关的类,每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本, 这样实现就能加载各自的spring版本;

注意:

tomcat类加载机制违背了Java推荐的双亲加载机制。

为了实现隔离性,没有遵守Java推荐的双亲加载机制,,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器,打破了双亲委派机制。

模拟实现—>模拟实现Tomcat的webappClassLoader加载自己war包应用内不同版本类实现相互共存与隔离:

代码

package com.zhz.bytecode; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author zhouhengzhe * @Description: 模拟实现Tomcat的webappClassLoader加载自己war包应用内不同版本类实现相互共存与隔 离 * @date 2021/9/22下午1:12 * @since */ public class SimulateTomcatMultipleVersionIsolation { static class CustomClassLoader extends ClassLoader { private String classpath; public CustomClassLoader(String classpath) { this.classpath = classpath; } /** * 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载 * @param name * @return: Class * @author: zhouhengzhe * @date: 2021/9/22 */ @Override public 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) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //非自定义的类还是走双亲委派加载---->重要 if (!name.startsWith("com.zhz.jvm")){ c=this.getParent().loadClass(name); }else { c = findClass(name); } // this is the defining class loader; record the stats sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } if (resolve) { resolveClass(c); } return c; } } @Override protected Class findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。 return defineClass(name,data,0,data.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } private byte[] loadByte(String name) throws IOException { //name = name.replaceAll("\.", "/"); FileInputStream fis = new FileInputStream(classpath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader CustomClassLoaderTest.CustomClassLoader customClassLoader=new CustomClassLoaderTest.CustomClassLoader("D:/test"); //D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录 Class clazz = customClassLoader.loadClass("com.zhz.jvm.User1"); Object instance = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sout", null); method.invoke(instance,null); System.out.println(clazz.getClassLoader().getClass().getName()); //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader CustomClassLoaderTest.CustomClassLoader customClassLoader1=new CustomClassLoaderTest.CustomClassLoader("D:/test1"); //D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录 Class clazz1 = customClassLoader1.loadClass("com.zhz.jvm.User1"); Object instance1 = clazz1.newInstance(); Method method1 = clazz1.getDeclaredMethod("sout", null); method1.invoke(instance1,null); System.out.println(clazz1.getClassLoader().getClass().getName()); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

JVM(5)——类加载机制

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

注意:同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一 样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。

1.5、显示当前ClassLoader加载了哪些Jar

方法一:

package com.zhz; import sun.misc.Launcher; import java.net.URL; /** * @author zhouhengzhe * @Description: 方法一:显示当前ClassLoader加载了哪些Jar * @date 2021/9/12下午10:06 * @since */ public class TestJdkClassLoader { public static void main(String[] args) { System.out.println(String.class.getClassLoader()); System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName()); System.out.println(TestJdkClassLoader.class.getClassLoader().getClass().getName()); System.out.println(); ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); ClassLoader extClassloader = appClassLoader.getParent(); ClassLoader bootstrapLoader = extClassloader.getParent(); System.out.println("the bootstrapLoader : " + bootstrapLoader); System.out.println("the extClassloader : " + extClassloader); System.out.println("the appClassLoader : " + appClassLoader); System.out.println(); System.out.println("bootstrapLoader加载以下文件:"); URL[] urls = Launcher.getBootstrapClassPath().getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(urls[i]); } System.out.println(); System.out.println("extClassloader加载以下文件:"); System.out.println(System.getProperty("java.ext.dirs")); System.out.println(); System.out.println("appClassLoader加载以下文件:"); System.out.println(System.getProperty("java.class.path")); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

方法二:

package com.zhz.bytecode; import sun.misc.Launcher; import java.lang.reflect.Field; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Objects; /** * @author zhouhengzhe * @Description: 方法二:显示当前ClassLoader加载了哪些Jar * @date 2021/9/18上午12:20 * @since */ public class JvmClassLoaderPrintPath { public static void main(String[] args) { //启动类加载器 URL[] urLs = Launcher.getBootstrapClassPath().getURLs(); System.out.println("启动类加载器"); for (URL urL : urLs) { System.out.println("==>"+ urL.toExternalForm()); } //扩展类加载器 printClassLoad("扩展类加载器",JvmClassLoaderPrintPath.class.getClassLoader()); //应用类加载器 printClassLoad("应用类加载器",JvmClassLoaderPrintPath.class.getClassLoader()); } private static void printClassLoad(String name, ClassLoader classLoader) { if (Objects.nonNull(classLoader)){ System.out.println(name+" ClassLoader -> "+classLoader.toString()); printURLForClassLoader(classLoader); return; } System.out.println(name+" ClassLoader -> null"); } private static void printURLForClassLoader(ClassLoader classLoader) { Object ucp = insightField(classLoader, "ucp"); Object path = insightField(ucp, "path"); ArrayList ps = (ArrayList) path; for (Object p : ps) { System.out.println(" ==> "+p.toString()); } } private static Object insightField(Object obj, String name){ try { Field field=null; if (obj instanceof URLClassLoader){ field=URLClassLoader.class.getDeclaredField(name); }else { field=obj.getClass().getDeclaredField(name); } field.setAccessible(true); return field.get(obj); }catch (Exception e){ e.printStackTrace(); return null; } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

结果

1.6、添加引用类的几种方式

1、放到 JDK 的 lib/ext 下,或者 -Djava.ext.dirs

2、java-cp/classpath 或者 class 文件放到当前路径

3、自定义 ClassLoader 加载

4、拿到当前执行类的 ClassLoader,反射调用 addUrl 方法添加 Jar 或路径(JDK9 无效)

Java JVM 容器

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Excel做拼音田字格的具体操作方法
下一篇:如何在Excel中的图表中显示隐藏数据
相关文章