「java三种类加载器」java类加载器包括几种

博主:adminadmin 2023-01-20 04:12:07 270

今天给各位分享java三种类加载器的知识,其中也会对java类加载器包括几种进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

JVM 为什么要3个类加载器?

JVM有三种类加载器:bootstrap负责加载系统类,extclassloader负责加载扩展类,appclassloader负责加载应用类。他们主要是分工不一样,各自负责不同的区域,另外也是为了实现委托模型。什么是委托模型呢,其实就是当类加载器有加载需求的时候,先请示他的父类使用父类的搜索路径来加入,如果没有找到的话,才使用自己的搜索路径来来搜索类。

当执行 java ***.class 的时候, java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰写而成,这个 Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader 。然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。这里要请大家注意的是, Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。

下面的图形可以表示三者之间的关系:

BootstrapLoader ---(Extends)----AppClassLoader ---(Extends)----ExtClassLoader

这三个加载器就构成我们的 Java 类加载体系。他们分别从以下的路径寻找程序所需要的类:

BootstrapLoader : sun.boot.class.path

ExtClassLoader: java.ext.dirs

AppClassLoader: java.class.path

这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。大家可以自己编程实现查看具体的路径。

java类加载器有几种???

Java中加载器的种类大致可以分为四种:Bootstrap ClassLoader(由C++语言写成),系统加载器(也就是内部类AppClassLoader),ExtClassLoader,以及java.net.UrlClassLoader.

当我们运行一个程序时,首先是找到JDK安装目下的jvm.dll来启动JAVA虚拟机,而后Bootstrap ClassLoader产生,接下来就是Bootstrap ClassLoader来加载ExtClassLoader,并且指定ExtClassLoader的父加载器为Bootstrap ClassLoader,但是因为Bootstrap ClassLoader用C++语言写的,所以用JAVA的观点来看,这个加载器的实例是不存在的,所以ExtClassLoader的父加载器被设置为了null,然后就是Bootstrap ClassLoader将AppClassLoader装载,并指定其父加载器为ExtClassLoader。

JAVA是按照加载器的委派模型来实现的。这种模型是JAVA安全性机制的保证。并且值得我们注意的就是这几个加载器的默认加载类的路径。对于AppCLassLoder来说,它的路径也就是我们的classpath里面的路径。而对于ExtClassLoader来说,它的路径是jre\lib\ext\classes.对于URLClassLoader来说,它的加载路径是我们指定的url。

java重新加载class文件

Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

Java的类加载器有三个,对应Java的三种类:

Bootstrap Loader // 负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类)

|

- - ExtClassLoader // 负责加载扩展类(就是继承类和实现类)

|

- - AppClassLoader // 负责加载应用类(程序员自定义的类)

三个加载器各自完成自己的工作,但它们是如何协调工作呢?哪一个类该由哪个类加载器完成呢?为了解决这个问题,Java采用了委托模型机制。

委托模型机制的工作原理很简单:当类加载器需要加载类的时候,先请示其Parent(即上一层加载器)在其搜索路径载入,如果找不到,才在自己的搜索路径搜索该类。这样的顺序其实就是加载器层次上自顶而下的搜索,因为加载器必须保证基础类的加载。之所以是这种机制,还有一个安全上的考虑:如果某人将一个恶意的基础类加载到jvm,委托模型机制会搜索其父类加载器,显然是不可能找到的,自然就不会将该类加载进来。

我们可以通过这样的代码来获取类加载器:

ClassLoader loader = ClassName.class.getClassLoader();

ClassLoader ParentLoader = loader.getParent();

注意一个很重要的问题,就是Java在逻辑上并不存在BootstrapKLoader的实体!因为它是用C++编写的,所以打印其内容将会得到null。

前面是对类加载器的简单介绍,它的原理机制非常简单,就是下面几个步骤:

1.装载:查找和导入class文件;

2.连接:

(1)检查:检查载入的class文件数据的正确性;

(2)准备:为类的静态变量分配存储空间;

(3)解析:将符号引用转换成直接引用(这一步是可选的)

3.初始化:初始化静态变量,静态代码块。

这样的过程在程序调用类的静态成员的时候开始执行,所以静态方法main()才会成为一般程序的入口方法。类的构造器也会引发该动作。

java解释器如何加载类?

类加载次序:1、静态代码块或者静态方法-2、main方法调用到的方法

对象加载次序:1、静态代码块或者静态方法-2、非静态代码块或者非静态方法-3、对象的构造方法。

但是有一段代码没有办法解释。代码忘了,过段时间丢上来

个人感觉应该好像不大对劲,我觉得应该是:

类装载时,1、静态代码块或者静态方法被调用

然后是程序的运行,main调用到的方法会被执行,如果是新建一个对象,则

2、非静态代码块或者非静态方法-3、对象的构造方法顺序执行。

===============================================

首先我们要分析类加载原理,java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)引导类加载器负责加载jdk中的系统类,这种类加载器都是用c语言实现的,在java程序中没有办法获得这个类加载器,对于java程序是一个概念而已,基本上不用考虑它的存在,像String,Integer这样的类都是由引导类加载器加载器的.

扩展类加载器负责加载标准扩展类,一般使用java实现,这是一个真正的java类加载器,负责加载jre/lib/ext中的类,和普通的类加载器一样,其实这个类加载器对我们来说也不是很重要,我们可以通过java程序获得这个类加载器。

系统类加载器,加载第一个应用类的加载器(其实这个定义并不准确,下面你将会看到),也就是执行java MainClass 时加载MainClass的加载器,这个加载器使用java实现,使用的很广泛,负责加载classpath中指定的类。

类加载器之间有一定的关系(父子关系),我们可以认为扩展类加载器的父加载器是引导类加载器(当然不这样认为也是可以的,因为引导类加载器表现在java中就是一个null),不过系统类加载器的父加载器一定是扩展类加载器,类加载器在加载类的时候会先给父加载器一个机会,只有父加载器无法加载时才会自己去加载。

我们无法获得引导类加载器,因为它是使用c实现的,而且使用引导类加载器加载的类通过getClassLoader方法返回的是null.所以无法直接操作引导类加载器,但是我们可以根据Class.getClassLoader方法是否为null判断这个类是不是引导类加载器加载的,可以通过下面的方法获得引导类加载器加载的类路径(每个jar包或者文件夹对应了一个URL);

sun.misc.Launcher.getBootstrapClassPath().getURLs()

你可以直接在你的main函数中输出就可以了

System.out.println(java.util.Arrays.asList(sun.misc.Launcher.getBootstrapClassPath().getURLs()).toString());

得到的结果是:

[file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/rt.jar,

file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/i18n.jar,

file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/sunrsasign.jar,

file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jsse.jar,

file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jce.jar,

file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/charsets.jar,

file:/C:/Program%20Files/Java/j2re1.4.2_10/classes]

其实我们是可以指定引导类加载器的类路径的,java提供了一个-Xbootclasspath参数,不过这个参数不是标准参数。

java -Xbootclasspath: 运行时指定引导类加载器的加载路径(jar文件或者目录)

java -Xbootclasspath/p:和上面的相同,不过把这个路径放到原来的路径前面

java -Xbootclasspath/a:这个就是在原引导类路径后面添加类路径。

上面我们有提过加载第一个应用类未必就是系统加载器。

如果我把这个应用类的路径放到引导类路径中,它将会被引导类加载器加载,大致这样

java -Xbootclasspath/a:myjar.jar MainClass

如果MainClass在myjar.jar中,那么这个类将会被引导类加载器加载。

如果希望看详情,使用-verbose参数,为了看的更清楚,使用重定向,大致为(windows下):

java -verbose -Xbootclasspath/a:myjar.jar MainClass - C:\out.txt

通过这个参数我们可以实现自己的系统类,比如替换掉java.lang.Object的实现,自己可以扩展

一些方法,不过这样做似乎没有好处,因为那就不是标准了。

我们最关心的还是系统类加载器,一般都认为系统类加载器是加载应用程序第一个类的加载器,

也就是java MainClass命令中加载MainClass的类加载器,这种说法虽然不是很严谨,但基本上还是可以这样认为的,因为我们很少会改变引导类加载器和扩展类加载器的默认行为。应该说系统类加载器负责加载classpath路径中的而且没有被扩展类加载器加载的类(当然也包括引导类加载器加载的)。如果classpath中有这个类,但是这个类也在扩展类加载器的类路径,那么系统类加载器将没有机会加载它。

我们很少改变扩展类加载器的行为,所以一般你自己定义的类都是系统类加载器加载器的。

获得系统类加载器非常简单,假设MyClass是你定义的一个类

MyClass.class.getClassLoader()返回的就是系统类加载器,当然这种方法无法保证绝对正确,我们可以使用更简单而且一定正确的方式:

ClassLoader.getSystemClassLoader()获得系统类加载器。我们知道ClassLoader是一个抽象类,所以系统类加载器肯定是ClassLoader的一个子类实现。我们来看看它是什么

ClassLoader.getSystemClassLoader().getClass();

结果是class sun.misc.Lancher$AppClassLoader

可以看出这是sun的一个实现,从名字可以看出是一个内部类,目前我也没有看到这个源代码,似乎还不是很清晰:

我们在看看它的父类是什么:

ClassLoader.getSystemClassLoader().getClass().getSuperclass();

结果是:class java.net.URLClassLoader

这个是j2se的标准类,它的父类是SecureClassLoader,而SecureClassLoader是继承ClassLoader的。

现在整个关系应该很清楚,我们会看到几乎所有的ClassLoader实现都是继承URLClassLoader的。

因为系统类加载器是非常重要的,而且是我们可以直接控制的,所以我们后面还会介绍,不过先来看一下扩展类

加载器以及它们之间的关系。

扩展类加载器似乎是一个不起眼的角色,它负责加载java的标准扩展(jre/lib/ext目录下的所有jar),它其实就是一个普通的加载器,看得见摸得着的。

首先的问题是怎么知道扩展类加载器在哪里?

的确没有直接途径获得扩展类加载器,但是我们知道它是系统类加载器的父加载器,我们已经很容易的获得系统类加载器了,所以我们可以间接的获得扩展类加载器:

ClassLoader.getSystemClassLoader().getParent().getClass();

其实是通过系统类加载器间接的获得了扩展类加载器,看看是什么东西:

结果是:class sun.misc.Launcher$ExtClassLoader

这个类和系统类加载器一样是一个内部类,而且定义在同一个类中。

同样看看它的父类是什么:

ClassLoader.getSystemClassLoader().getParent().getClass().getSuperclass();

可以看出结果也是class java.net.URLClassLoader

扩展类加载jre/lib/ext目录下的所有类,包括jar,目录下的所有类(目录名不一定要classes).

现在可以回答上面的问题了,你写一个HelloWorld,放到jre/lib/ext/下的某个目录

比如 jre/lib/ext/myclass/HelloWorld.class

然后在你classpath也设置一份到这个类的路径,结果执行java HelloWorld时,这个类是被扩展类加载器加载器的,可以这样证明

public static void main(String[] args){

System.out.println("loaded by"+HelloWorld.class.getClassLoader().getClass());

System.out.println("Hello World");

}

结果可以得到class sun.misc.Launcher$ExtClassLoader

当然如果你把jre/lib/ext下myclass这个目录删除,仍然可以运行,但是这样结果是

class sun.misc.Lancher$AppClassLoader

如果你不知道这个过程的话,假设在你扩展类路径下有一份classpath中的拷贝,或者是比较低的版本,当你使用新的版本时会发现没有起作用,知道这个过程你就不会觉得奇怪了。另外就是两个不同的类加载器是可以加载一个同名的类的,也就是说虽然扩展类加载器加载了某个类,系统类加载器是可以加载自己的版本的,

但是现有的实现都没有这样做,ClassLoader中的方法是会请求父类加载器先加载的,如果你自己定义类加载器完全可以修改这种默认行为,甚至可以让他没有父加载器。

这里给出一个方法如何获得扩展类加载器加载的路径:

String path=System.getProperty("java.ext.dirs");

File dir=new File(path);

if(!dir.exists()||!dir.isDirectory()){

return Collections.EMPTY_LIST;

}

File[] jars=dir.listFiles();

URL[] urls=new URL[jars.length];

for(int i=0;ijars.length;i++){

urls[i]=sun.misc.URLClassPath.pathToURLs(jars[i].getAbsolutePath())[0];

}

return Arrays.asList(urls);

对于扩展类加载器我们基本上不会去关心,也很少把你自己的jar放到扩展路径,大部分情况下我们都感觉不到它的存在,当然如果你一定要放到这个目录下,一定要知道这个过程,它会优先于classpath中的类。

现在我们应该很清楚知道某个类是哪个加载器加载的,并且知道为什么是它加载的,如果要在运行时获得某个类的类加载器,直接使用Class的getClassLoader()方法就可以了。

用户定义的类一般都是系统类加载器加载的,我们很少直接使用类加载器加载类,我们甚至很少自己加载类。

因为类在使用时会被自动加载,我们用到某个类时该类会被自动加载,比如new A()会导致类A自动被加载,不过这种加载只发生一次。

我们也可以使用系统类加载器手动加载类,ClassLoader提供了这个接口

ClassLoader.getSystemClassLoader().loadClass("classFullName");

这就很明确的指定了使用系统类加载器加载指定的类,但是如果该类能够被扩展类加载器加载,系统类加载器还是不会有机会的。

我们最常用的还是使用Class.forName加载使用的类,这种方式没有指定某个特定的ClassLoader,会使用调用类的ClassLoader。

也就是说调用这个方法的类的类加载器将会用于加载这个类。比如在类A中使用Class.forName加载类B,那么加载类A的类加载器将会用于加载类B,这样两个类的类加载器是同一个。

最后讨论一下如何获得某个类加载器加载了哪些类,这个似乎有一定的使用价值,可以看出哪些类被加载了。其实这个也不是很难,因为ClassLoader中有一个classes成员变量就是用来保存类加载器加载的类列表,而且有一个方法

void addClass(Class c) { classes.addElement(c);}

这个方法被JVM调用。

我们只要利用反射获得classes这个值就可以了,不过classes声明为private的,我们需要修改它的访问权限(没有安全管理器时很容易做到)

classes = ClassLoader.class.getDeclaredField("classes");

classes.setAccessible(true);

List ret=(List) classes.get(cl); //classes是一个Vector

可惜的是对于引导类加载器没有办法获得加载的类,因为它是c实现的,在java中很难控制

如何理解不同类加载器加载的类不可以互相调用

不同类加载器加载的类不可以互相调用

专业术语:定义类加载器、初始类加载器

在java中加载器分为4种:

1、appClassLoad 应用程序加载器

2、etcClassLoad 扩展类加载器

3、bootStrapClassLoad(root) 根加载器

4、自定义加载器

怎么理解 不同类加载器加载的类不可以互相调用?

当创建一个类Test时由appClassLoad类加载器加载,当引入变量String时,String根据双亲委派机制,一层一层往上查询,应该appClassLoad-》etcClassLoad-》bootStrapClassLoad最终由bootstrap加载,在这个过程种appClassLoad和etcClassLoad被定义位初始加载器,bootstrap被定义为定义类加载器,每个加载器当中都有一个命名空间,也就是记录表,记录着所有此类的初始类加载器,所以在Test类是以可使用到String的。

一个类通过不同的加载器,加载到方法区中,会生成两个不同的类,互相是不可以遇见的,而且在堆中也会生成不同的对象实例。

自定义类加载器是为了解决当.class文件不在classpath路径中时,寻找加载.class而存在的

java三种类加载器的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java类加载器包括几种、java三种类加载器的信息别忘了在本站进行查找喔。