class装载验证流程
加载,链接(验证,准备,解析),初始化
一,加载:首先取得二进制流(网络,文件等等),然后转为方法区的数据结构,并在堆中生成对应的java.lang.Class对象
二,链接:
1.验证(保证文件格式,等)格式的正确性
2.准备 分配内存,并为类设置初始值(方法区中)
方法区-(类信息,常量池,静态字段,方法)加载进内存以后
敲黑板~
在准备阶段会为方法区属性设置初始值(为静态变量)
例如public static int i = 1;
此时准备阶段 i值为0;
当初始化阶段中会被设置为1;
当静态变量使用final修饰时,那么准备阶段就会被附上正确的值。
public static final int i = 1;
准备阶段 i值为1;
3.解析 符号引用替换为直接引用
符号引用相当于在 常量池里头的"java.lang.Object";
直接引用指针或地址偏移量,引用对象一定存在内存。
三,初始化
初始化阶段,会执行类的构造器
会为static变量正确的值,会执行static{}中的语句
并且是线程安全的,执行时其他线程暂停。
java.lang.NoSuchFieldError错误在什么阶段抛出?
首先,加载阶段是不会的,它只是读取字节流
我认为应该在链接的认证阶段,当发现(存取或改变数据域所产生的错误)就抛出了。
出现了 NoSuchMethodError 或者 NoSuchFieldError ,这时一般是应用的classpath下包含了多个包含了想同类的jar包,而很不幸的加载到了 不正确 的jar包。
ClassLoader
它是一个抽象类,它的实例把JAVA字节码加载入JVM虚拟机,只负责了类装载过程的加载阶段。
ClassLoader的一些方法:
public Class<?> loadClass(String name) throws ClassNotFoundException
载入并返回一个Class对象
protected final Class<?> defineClass(byte[] b, int off, int len)
定义一个类,不公开调用,参数: class字节码,位置等
protected Class<?> findClass(String name) throws ClassNotFoundException
loadClass回调该方法,自定义ClassLoader的推荐做法 ,寻找class
protected final Class<?> findLoadedClass(String name)
寻找已经加载的类
基于双亲委派模型,自顶向上检查类是否已经加载,自顶向下尝试加载类。
ClassLoder的默认设计模式
在本加载器中先寻找,没找到,查看parent是否有,有的话让父加载器尝试加载。
强制在App ClassLoader中加载,(前提是:-Xbootclasspath/a:D:/tmp/clz 这个类在此目录 bootClassLoader会加载这个类)
public static void main(String args[]) throws Exception {
ClassLoader cl=FindClassOrder2.class.getClassLoader();
byte[] bHelloLoader=loadClassBytes("geym.jvm.ch6.findorder.HelloLoader");
Method md_defineClass=ClassLoader.class.getDeclaredMethod("defineClass", byte[].class,int.class,int.class);//得到App ClassLoder的 defineClass方法
md_defineClass.setAccessible(true);//原本是受保护的
md_defineClass.invoke(cl,bHelloLoader,0,bHelloLoader.length);//执行defineClass方法
md_defineClass.setAccessible(false);
HelloLoader loader = new HelloLoader();
System.out.println(loader.getClass().getClassLoader());
loader.print();
}
//这样就会在App ClassLoader中加载
能否只用反射,将类注入到bootClassLoader?
我觉得应该不可以,虽然知道获取到当前appClassLoader的父类的父类即可。但boot是null。没办法的。
双亲委派模式的缺点?
顶层的ClassLoader无法加载底层的ClassLoader。底层加载的类,顶层不能获取。
javax.xml.parsers包中定义了xml解析的类接口
Service Provider Interface SPI 位于rt.jar
即接口在启动ClassLoader中。
而SPI的实现类,在AppLoader。
这时如何调用呢?如何让顶层访问底层呢?
使用上下文加载器。
Thread.setContextClassLoader();
这只是一个角色,任何加载器都能扮演这个角色(上下文加载器)。
这样,在顶层的ClassLoader中可以传入底层的ClassLoader。
static private Class getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
try {
if (cl == null) {
if (useBSClsLoader) {
return Class.forName(className, true, FactoryFinder.class.getClassLoader());
} else {
cl = ss.getContextClassLoader();
if (cl == null) {
throw new ClassNotFoundException();
}
else {
//-------------------------注意
return cl.loadClass(className); //使用上下文ClassLoader cl就是
//--------------------------
}
}
}
else {
return cl.loadClass(className);
}
}
catch (ClassNotFoundException e1) {
if (doFallback) {
// Use current class loader - should always be bootstrap CL
return Class.forName(className, true, FactoryFinder.class.getClassLoader());
}
Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent
OSGi的ClassLoader形成网状结构,根据需要自由加载Class
例子:OrderClassLoader的部分实先
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// First, check if the class has already been loaded
//找,并加载
Class re=findClass(name);
if(re==null){
System.out.println(“无法载入类:”+name+“ 需要请求父加载器");
return super.loadClass(name,resolve);
}
return re;
}
protected Class<?> findClass(String className) throws ClassNotFoundException {
//寻找····················
Class clazz = this.findLoadedClass(className);
if (null == clazz) {
try {
String classFile = getClassFile(className);
FileInputStream fis = new FileInputStream(classFile);
FileChannel fileC = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel outC = Channels.newChannel(baos);
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
省略部分代码
fis.close();
byte[] bytes = baos.toByteArray();
//加载·························
clazz = defineClass(className, bytes, 0, bytes.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return clazz;
}