「java类加载器正确的是」java类加载器的作用
今天给各位分享java类加载器正确的是的知识,其中也会对java类加载器的作用进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
java中类加载器是怎么工作的
JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)
链接又分为三个步骤,验证、准备、解析
1) 装载:查找并加载类的二进制数据;
2)链接:
验证:确保被加载类的正确性;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;
3)初始化:为类的静态变量赋予正确的初始值;
那为什么我要有验证这一步骤呢?首先如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。
准备阶段和初始化阶段看似有点牟盾,其实是不牟盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时
类加载器(初始化)
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
类的加载
就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
类的连接
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
在该阶段,主要就是对类变量进行初始化
假如类还未被加载和连接,则程序先加载并连接该类
假如该类的直接父类还未被初始化,则先初始化其直接父类
假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
创建类的实例
调用类的类方法
访问类或者接口的类变量,或者为该类变量赋值
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象
虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
ClassLoader:是负责加载类的对象
Java运行时具有以下内置类加载器
1)Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null
2)Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
3) System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
ClassLoader 中的两个方法
1) static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
2) ClassLoader getParent():返回父类加载器进行委派
关于java 类的加载过程,下面哪些描述是正确的
关于Java 类的加载过程,下面哪些描述是正确的(A C )
A、在Java 中,有四种类型的类加载器:BootStrapClassLoader、ExtClassLoader、AppClassLoader 以及用户自定义的 ClassLoader。
B、使用 new 关键字创建类实例时,其实就显示地包含了类的加载过程
C、在Java 中,类的实例化流程分为两个部分:类的加载和类的实例化。类的加载又分
为显式加载和隐式加载。
D、Class.forName 来加载类时,是通过ExtClassLoader进行加载的。
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类加载器正确的是和java类加载器的作用的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。