您的位置:首页 > 编程语言 > Java开发

java 的类加载机制(classloader)

2012-10-07 18:49 633 查看
JVM在加载类的时候,都是通过ClassLoader的loadClass()方法来加载class的,loadClass(String name)方法:

使用的是双亲委托模式:

jvm启动时,会启动jre/rt.jar里的类加载器:bootstrap classloader,用来加载java核心api;然后启动扩展类加载器ExtClassLoader加载扩展类,并加载用户程序加载器AppClassLoader,并指定ExtClassLoader为他的父类;

当类被加载时,会先检查在内存中是否已经被加载,如果是,则不再加载,如果没有,再由AppClassLoader来加载,先从jar包里找,没有再从classpath里找;

如果自定义loader类,就会存在这命名空间的情况,不同的加载器加载同一个类时,产生的实例其实是不同的;

Java代码

public Class<?> loadClass(String name) throws ClassNotFoundException {   
return loadClass(name, false);   
   }  

public Class<?> loadClass(String name) throws ClassNotFoundException { 
return loadClass(name, false); 
    }


loadClass(String name)方法再调用loadClass(String name, boolean resolve)方法:

- name - 类的二进制名称

- resolve - 如果该参数为 true,则分析这个类

Java代码

protected synchronized Class<?> loadClass(String name, boolean resolve)   
    throws ClassNotFoundException   
    {   
    // First, check if the class has already been loaded   
    //JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,如果一个Class已经被加载过,则直接从缓存中获取   
    Class c = findLoadedClass(name);   
    if (c == null) {   
        try {   
        if (parent != null) {   
            c = parent.loadClass(name, false);   
        } else {   
            c = findBootstrapClass0(name);   
        }   
        } catch (ClassNotFoundException e) {   
            // If still not found, then invoke findClass in order   
            // to find the class.   
            c = findClass(name);   
        }   
    }   
    if (resolve) {   
        resolveClass(c);   
    }   
    return c;   
}  

protected synchronized Class<?> loadClass(String name, boolean resolve) 
throws ClassNotFoundException 
    { 
// First, check if the class has already been loaded 
//JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,如果一个Class已经被加载过,则直接从缓存中获取 
Class c = findLoadedClass(name); 
if (c == null) { 
    try { 
if (parent != null) { 
    c = parent.loadClass(name, false); 
} else { 
    c = findBootstrapClass0(name); 
} 
    } catch (ClassNotFoundException e) { 
        // If still not found, then invoke findClass in order 
        // to find the class. 
        c = findClass(name); 
    } 
} 
if (resolve) { 
    resolveClass(c); 
} 
return c; 
}


如果ClassLoader并没有加载这个class,则调用findBootstrapClass0:

Java代码

private Class findBootstrapClass0(String name)   
    throws ClassNotFoundException   
    {   
    check();   
    if (!checkName(name))   
        throw new ClassNotFoundException(name);   
    return findBootstrapClass(name);   
    }  

private Class findBootstrapClass0(String name) 
throws ClassNotFoundException 
    { 
check(); 
if (!checkName(name)) 
    throw new ClassNotFoundException(name); 
return findBootstrapClass(name); 
    }


该方法会调用check()方法来判断这个类是否已经初始化,并且通过checkName(name)来判断由name指定的这个类是否存在

最后调用findBootstrapClass(name):

Java代码

private native Class findBootstrapClass(String name)   
    throws ClassNotFoundException;  

private native Class findBootstrapClass(String name) 
throws ClassNotFoundException;


而这个findBootstrapClass方法是一个native方法,这是我们的root loader,这个载入方法并非是由J***A所写,而是C++写的,它会最终调用JVM中的原生findBootstrapClass方法来完成类的加载。

如果上面两个都找不到,则使用findClass(name)来查找指定类名的Class:

Java代码

protected Class<?> findClass(String name) throws ClassNotFoundException {   
    throw new ClassNotFoundException(name);   
}  

protected Class<?> findClass(String name) throws ClassNotFoundException { 
throw new ClassNotFoundException(name); 
}


JDK5.0中的说明:

使用指定的二进制名称查找类。此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用。默认实现抛出一个 ClassNotFoundException。

所以,我们在自定义类中,只需要重写findClass()即可。

MyClassLoader类:

Java代码

public class MyClassLoader extends ClassLoader {   
    private String fileName;   
  
    public MyClassLoader(String fileName) {   
        this.fileName = fileName;   
    }   
  
    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);   
                while (true) {   
                    int i = fileC.read(buffer);   
                    if (i == 0 || i == -1) {   
                        break;   
                    }   
                    buffer.flip();   
                    outC.write(buffer);   
                    buffer.clear();   
                }   
                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;   
    }   
    private byte[] loadClassBytes(String className) throws  
            ClassNotFoundException {   
        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);   
            while (true) {   
                int i = fileC.read(buffer);   
                if (i == 0 || i == -1) {   
                    break;   
                }   
                buffer.flip();   
                outC.write(buffer);   
                buffer.clear();   
            }   
            fis.close();   
            return baos.toByteArray();   
        } catch (IOException fnfe) {   
            throw new ClassNotFoundException(className);   
        }   
    }   
    private String getClassFile(String name) {   
        StringBuffer sb = new StringBuffer(fileName);   
        name = name.replace('.', File.separatorChar) + ".class";   
        sb.append(File.separator + name);   
        return sb.toString();   
    }   
}  

public class MyClassLoader extends ClassLoader { 
    private String fileName; 

    public MyClassLoader(String fileName) { 
        this.fileName = fileName; 
    } 

    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); 
                while (true) { 
                    int i = fileC.read(buffer); 
                    if (i == 0 || i == -1) { 
                        break; 
                    } 
                    buffer.flip(); 
                    outC.write(buffer); 
                    buffer.clear(); 
                } 
                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; 
    } 
    private byte[] loadClassBytes(String className) throws 
            ClassNotFoundException { 
        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); 
            while (true) { 
                int i = fileC.read(buffer); 
                if (i == 0 || i == -1) { 
                    break; 
                } 
                buffer.flip(); 
                outC.write(buffer); 
                buffer.clear(); 
            } 
            fis.close(); 
            return baos.toByteArray(); 
        } catch (IOException fnfe) { 
            throw new ClassNotFoundException(className); 
        } 
    } 
    private String getClassFile(String name) { 
        StringBuffer sb = new StringBuffer(fileName); 
        name = name.replace('.', File.separatorChar) + ".class"; 
        sb.append(File.separator + name); 
        return sb.toString(); 
    } 
}


该类中通过调用defineClass(String name, byte[] b, int off, int len)方法来定义一个类:

Java代码

protected final Class<?> defineClass(String name, byte[] b, int off, int len)   
throws ClassFormatError   
   {   
return defineClass(name, b, off, len, null);   
   }  

protected final Class<?> defineClass(String name, byte[] b, int off, int len) 
throws ClassFormatError 
    { 
return defineClass(name, b, off, len, null); 
    }


注:MyClassLoader加载类时有一个局限,必需指定.class文件,而不能指定.jar文件。该类中的大部分代码是从网上搜索到的,是出自一牛人之笔,只是不知道原帖在哪,希望不会被隐藏。

MainClassLoader类:

Java代码

public class MainClassLoader {   
    public static void main(String[] args) {   
        try {   
            MyClassLoader tc = new MyClassLoader("F:\\OpenLib\\");   
            Class c = tc.findClass("Test");   
            c.newInstance();   
        } catch (ClassNotFoundException e) {   
            e.printStackTrace();    
        } catch (IllegalAccessException e) {   
            e.printStackTrace();   
        } catch (InstantiationException e) {   
            e.printStackTrace();    
        }   
    }   
}  

public class MainClassLoader { 
    public static void main(String[] args) { 
        try { 
            MyClassLoader tc = new MyClassLoader("F:\\OpenLib\\"); 
            Class c = tc.findClass("Test"); 
            c.newInstance(); 
        } catch (ClassNotFoundException e) { 
            e.printStackTrace(); 
        } catch (IllegalAccessException e) { 
            e.printStackTrace(); 
        } catch (InstantiationException e) { 
            e.printStackTrace(); 
        } 
    } 
}


最后是一个简单的Test测试类:

Java代码

public class Test   
{   
    public Test() {   
        System.out.println("Test");   
    }   
    public static void main(String[] args) {   
        System.out.println("Hello World");   
    }   
}


(转自:http://huangcanqin.iteye.com/blog/1273170
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: