port java.net.URL;
import java.net.URLClassLoader;
public class MyClassLoader extends ClassLoader {
private String path;
@Override
public Class<?> findClass(String name){
byte[] data = null;
try {
data = loadClassData(path);
} catch (IOException e) {
e.printStackTrace();
}
return defineClass(name, data, 0, data.length);
}
private byte[] loadClassData(String path) throws IOException{
File f = new File(path);
FileInputStream fis = new FileInputStream(f);
byte[] data = new byte[(int) f.length()];
fis.read(data);
fis.close();
return data;
}
/*
* 定义了带两个参数的loadClass方法,为了多传递一个path参数
* 内部一定要调用父类的loadClass方法,因为该方法内实现了双亲委派模型
*/
public Class<?> loadClass(String path, String name) throws ClassNotFoundException{
this.path = path;
return super.loadClass(name);
}
public static void main(String[] args) throws ClassNotFoundException{
MyClassLoader mcl = new MyClassLoader();
/*打印当前类加载器的父加载器*/
System.out.println(mcl.getParent());
System.out.println("==========");
Class<?> cls1 = mcl.loadClass("D:/用户目录/我的文档/Eclipse/Person.class"
,"javalearning.Person");
System.out.println(cls1.getClassLoader());
System.out.println("==========");
Class<?> cls2 = mcl.loadClass(null, "java.lang.Thread");
System.out.println(cls2.getClassLoader());
System.out.println("==========");
}
}
通过代码实现可以看出,自定义类加载器的核心精髓是调用ClassLoader类中的defineClass方法。
下面是运行结果
sun.misc.Launcher$AppClassLoader@4e0e2f2a
==========
demo.MyClassLoader@2a139a55
==========
null
==========
从运行结果看出,MyClassLoader的父加载器是AppClassLoader(这是在ClassLoader的构造函数中实现的)。Person.Class由MyClassLoader加载(父类加载器都没有加载成功),而当MyClassLoader加载String.class时,委托到BootstrapClassLoader加载,发现BootstrapClassLoader已加载完毕,结果null表示String类的加载器是BootstrapClassLoader。
Person类
package javalearning;
public class Person{
public int age;
public String name;
public Person(){
name = "zx";
age = 18;
}
@Override
public String toString(){
return name +" "+ age;
}
}
2. 谈谈java.lang.Class和java.lang.Object之间的悖论
通过java的语法学习,我们知道以下三点
1)java.lang.Class类继承java.lang.Object类
2)按照语法规则,创建一个java.lang.Class对象必须先创建它的父类(java.lang.Object)的一个对象
3)按照语法规则,创建一个类的对象,必须先存在表示该类的java.lang.Class对象
但是这三点又是矛盾的。这两个对象的创建没有办法顺序实现。所以不是先创建好一个,再创建另一个,而是通过自举实现的,也就是说是两个对象同时创建好。自举程序本身不是由Java语言实现的,而是由C和C++实现的。
所有的java.lang.Class对象的创建不是通过构造函数创建的,而是通过加载器生成的。每个类都有对应的用于反射该类的Class对象,每个类有且只对应一个Class对象。
每一个类都从Object类中继承了一个getClass的实例方法,返回该类对应的Class对象。
Class.class对象有两层含义。第一,它是一个Class类的实例。第二,它又又表示是Class类本身用于反射的对象,所以该对象的getClass方法返回它本身。我们不能通过Class.class的newInstance方法产生Class类的实例,如果这么做,会抛出异常。另一个方面,假设能够产生这样的对象,我们怎么知道这个对象应该对应哪一个类呢?
3. 谈谈java.lang.Class和类加载器之间的悖论
类加载器也是一个类,也有对应的Class对象,但是Class对象又必须通过加载器的实例产生,显然这两点又是矛盾的。
三个默认的类加载器中ExtensionClassLoader和AppClassLoader是由java代码实现的,而BootstrapClassLoader是由C/C++实现的。也就是说BootstrapClassLoader没有,不需要有,也不可能有对应Class对象。ExtensionClassLoader类的实例和它对应ExtensionClassLoader.class对象都是由BootstrapClassLoader一并加载创建完成。然后再由ExtensionClassLoader对象加载AppC