java类加载器与双亲委托机制详解
先看jvm运行内存结构
1.•Java运行时编译源码(.java)成字节码,由jre运行。jre由java虚拟机(jvm)实现。Jvm分析字节码,后解释并执行
2.类加载的过程
•类加载:类加载器将class文件加载到虚拟机的内存
•加载:在硬盘上查找并通过IO读入字节码文件
•连接:执行校验、准备、解析(可选)步骤
•校验:校验字节码文件的正确性
•准备:给类的静态变量分配内存,并赋予默认值
•解析:类装载器装入类所引用的其他所有类
•初始化:对类的静态变量初始化为指定的值,执行静态代码块
3.类加载器的种类
•启动类加载器:负责加载JRE的核心类库,如jre目标下的rt.jar,charsets.jar等
•扩展类加载器:负责加载JRE扩展目录ext中JAR类包
•系统类加载器:负责加载ClassPath路径下的类包
•用户自定义加载器:负责加载用户自定义路径下的类包
•全盘负责委托机制:当一个ClassLoader加载一个类时,除非显示的使用另一个ClassLoader,该类所依赖和引用的类也由这个ClassLoader载入
•双亲委派机制:指先委托父类加载器寻找目标类,在找不到的情况下在自己的路径中查找并载入目标类
实现一个我们自定义类加载器 ,只需要继承ClassLoader即可
1.
public class MyClassLoader extends ClassLoader { private String path; private String classLoaderName; public MyClassLoader(String path,String classLoaderName) { this.path=path; this.classLoaderName=classLoaderName; } //寻找类文件 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] b = loadClassByte(name); return defineClass(name,b,0,b.length); } //加载类文件 private byte[] loadClassByte(String name){ name=path+name+".class"; InputStream in=null; ByteArrayOutputStream out=null; try{ in=new FileInputStream(new File(name)); out=new ByteArrayOutputStream(); int i=0; while((i =in.read())!= -1){ out.write(i); } }catch (Exception e){ }finally { try { in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } } return out.toByteArray();
2.
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { MyClassLoader classLoader=new MyClassLoader("E://","myClassLoader"); Class<?> c = classLoader.findClass("Demo"); //System.out.println(c.toString()); System.out.println(c.getClassLoader()); c.newInstance(); }
•双亲委派模式优势
•沙箱安全机制:自己写的String.class类不会被加载,这样便可以防止核心API库被随意篡改
•避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再 加载一次
我们可以自己定义String试试效果,看看classloader会不会给我们加载
效果很明显,我们自己定义的String类根本不能ClassLoader所加载,很好的证明了上面的二个理论.