类加载器
根类加载器(Bootstrap) 它用来加载 Java 的核心库。使用c++编写,程序员无法在Java代码中获得该类 获取类的加载器,如果这个类是由Bootstrap来加载的,那个getClassLoader返回的就是null
1 2 Class clazz=Class.forName("java.lang.String" ); System.out.println(clazz.getClassLoader()); //null
扩展类加载器(Extensin) 它用来加载 Java 的扩展库
系统类加载器(System) 它根据 Java 应用的类路径(classpath)来加载 Java
用户自定义的类加载器 java.lang.ClassLoader的子类,用户可以定制类的加载方式。
类加载器并不需要等到某一个类被“首次主动使用”时再加载它。
调用ClassLoader类的loadClass方法加载一个类,并不是对一个类的主动使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Test { static{ System.out.println("abc" ); } } public class Main { public static void main(String[] args) throws Exception{ ClassLoader loader=ClassLoader.getSystemClassLoader(); loader.loadClass("com.kongzhong.gw2.account.service.Test" );//并不会打印出"abc" System.out.println("----------" ); Class.forName("com.kongzhong.gw2.account.service.Test" );//反射会导致主动使用,会打印出"abc" } }
父类委托加载机制 当Java程序请求加载器loader1加载某一个类的时候,loader1首先委托自己的父加载器去加载这个类,若父加载器能加载,则由父加载器完成加载任务。否则才由加载器loader1本身加载这个类。这样做是为了安全考虑,防止有人冒充Java核心类库。
例如:用户自定义了一个类,类的全名为java.lang.String
1 2 3 4 5 6 7 8 9 10 11 12 13 package java.lang; public class String { public void sayHello (){ System.out.println("aaa" ); } } public static void main(String[] args) { String string=new String(); string.sayHello(); }
结果:运行时的结果为:Exception in thread “main” java.lang.NoSuchMethodError: java.lang.String.sayHello()V 总结:说明类加载是父类委托机制。先让父加载器去加载。运行的永远是java类库中String
定义类加载器与初始化类加载器 如果某一个类加载器能够加载一个类,那么该类加载器就被叫做定义类加载器,定义类加载器及其所有子加载器都被叫做:初始类加载器。
当生成一个自定义类加载器实例时,如果没有指定它的父加载器,那么系统类加载器就将成为该类加载器的父加载器。
命名空间 每一个类加载器都有自己的命名空间,命名空间由该加载器及其所有父加载器的类组成。在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。
运行时包 由同一个类加载器加载的属于相同包的类组成了运行时包。决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看定义类加载器是否相同。只有属于
同一个运行时包的类才能互相访问(即默认访问级别)类和类成员。这样的限制能避免用户自定义的类冒充核心类库的类。去访问核心类库的包可见成员。
假设用户自己定义了一个类java.lang.Spy,并由用户自定义的类加载器加载,由于java.lang.Spy和核心类库java.lang.*由不同的加载器加载,它们属于不同的运行时包。
所以java.lang.Spy不能访问核心类库java.lang包中的包可见成员。
创建用户自定义的类加载器 要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定的类的名字返回对应的Class对象的引用。
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 102 103 104 105 106 107 108 109 110 111 import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class MyClassLoader extends ClassLoader{ private String name;//类加载器的名字 private String path="d:\\" ;//加载类的路径 private static final String FILE_TYPE=".class" ;//class文件的扩展名 public MyClassLoader(String name){ super(); this.name=name; } public MyClassLoader(ClassLoader parent,String name){ super(parent); this.name=name; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data=this.loadClassData(name); return this.defineClass(name, data, 0, data.length); } /** * 把.class字节码转变成字节数组 * @param name * @return */ private byte[] loadClassData(String name){ InputStream is=null; byte[] data=null; ByteArrayOutputStream baos=null; try { name=name.replace("." ,"\\" ); is=new FileInputStream(new File(path+name+FILE_TYPE)); baos=new ByteArrayOutputStream(); int ch=0; while ((ch=is.read())!=-1){ baos.write(ch); } data=baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); }finally { try{ is.close(); baos.close(); }catch(Exception e){ e.printStackTrace(); } } return data; } @Override public String toString () { return this.name; } public static void main(String[] args) throws Exception{ MyClassLoader loader1=new MyClassLoader("loader1" );//loader1的父加载器是系统类加载器 loader1.setPath("c:\\myapp\\serverlib\\" );//从c:\\myapp\\serverlib\\加载类 MyClassLoader loader2=new MyClassLoader(loader1,"loader2" );//loader2的父加载器是loader1 loader2.setPath("c:\\myapp\\clientlib\\" );//从c:\\myapp\\clientlib\\加载类 MyClassLoader loader3=new MyClassLoader(null,"loader3" );//loader3的父加载器是根类加载器 loader3.setPath("c:\\myapp\\otherlib\\" );//c:\\myapp\\otherlib\\加载类 test (loader1);//如果Student.class在c:\\myapp\\serverlib\\下则打印出loader1。 test (loader2);//如果Student.class在c:\\myapp\\serverlib\\下,而c:\\myapp\\clientlib\\没有的话。那么则打印出loader1,如果两个都有Student.class那么打印出loader1 test (loader3);//打印出loader3,因为根类加载器不会加载Student.class。只有loader3加载。 } public static void test (ClassLoader loader) throws Exception{ Class clazz=loader.loadClass("Student" );//加载Student这个类 Object object=clazz.newInstance(); } public String getName () { return name; } public void set Name(String name) { this.name = name; } public String getPath () { return path; } public void set Path(String path) { this.path = path; } public static String getFileType () { return FILE_TYPE; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Student { private String name; public Student (){ System.out.println(this.getClass().getClassLoader()); System.out.println(this.getClass().getClassLoader().getParent()); } public Student(String name){ this.name=name; } public String getName () { return name; } public void set Name(String name) { this.name = name; } }