4.自定义类加载器
public class MyClassLoader extends ClassLoader{
private String rootPath;
public MyClassLoader(String rootPath){
this.rootPath = rootPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//check if the class have been loaded
Class<?> c = findLoadedClass(name);
if(c!=null){
return c;
}
//load the class
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}
else{
c = defineClass(name,classData, 0, classData.length);
return c;
}
}
private byte[] getClassData(String className){
String path = rootPath+"/"+className.replace('.', '/')+".class";
InputStream is = null;
ByteArrayOutputStream bos = null;
try {
is = new FileInputStream(path);
bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int temp = 0;
while((temp = is.read(buffer))!=-1){
bos.write(buffer,0,temp);
}
return bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
is.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
  测试自定义的类加载器
  创建一个测试类HelloWorld
  package testOthers;
  public class HelloWorld {
  }
  在D盘根目录创建一个testOthers文件夹,编译HelloWorld.java,将得到的class文件放到testOthers文件夹下。
  利用如下代码进行测试
  public class testMyClassLoader {
  @Test
  public void test() throws Exception{
  MyClassLoader loader = new MyClassLoader("D:");
  Class<?> c = loader.loadClass("testOthers.HelloWorld");
  System.out.println(c.getClassLoader());
  }
  }
  输出:
  说明HelloWorld类是被我们的自定义类加载器MyClassLoader加载的
  5.类加载过程详解
  JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)

  1) 装载:
  查找并加载类的二进制数据;
  2)链接:
  验证:确保被加载类信息符合JVM规范、没有安全方面的问题。
  准备:为类的静态变量分配内存,并将其初始化为默认值。
  解析:把虚拟机常量池中的符号引用转换为直接引用。
  3)初始化:
  为类的静态变量赋予正确的初始值。
  ps:解析部分需要说明一下,Java 中,虚拟机会为每个加载的类维护一个常量池【不同于字符串常量池,这个常量池只是该类的字面值(例如类名、方法名)和符号引用的有序集合。 而字符串常量池,是整个JVM共享的】这些符号(如int a = 5;中的a)是符号引用,而解析过程是把它转换成指向堆中的对象地址的相对地址。
  类的初始化步骤:
  1)如果这个类还没有被加载和链接,那先进行加载和链接
  2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那初始化直接的父类(不适用于接口)
  3)如果类中存在static标识的块,那依次执行这些初始化语句。