JVM(5)——类加载机制

网友投稿 774 2022-05-28

文章目录

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

JVM(5)——类加载机制

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

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小时内删除侵权内容。

上一篇:Java基础之文件操作(IO流)
下一篇:图文详解 HDFS 组成框架
相关文章