JAVA 相同名称类的加载顺序

653 阅读1分钟

相同包名+类名在不同目录下或者jar内同时存在时的加载顺序

1.结论:根据classpath 顺序加载

2.源码:

java.net.URLClassLoader#findClass

protected Class<?> findClass(final String name)  throws ClassNotFoundException  
{  
    final Class<?> result;  
    try {  
        result = AccessController.doPrivileged(  
            new PrivilegedExceptionAction<Class<?>>() {  
                public Class<?> run() throws ClassNotFoundException {  
                    String path = name.replace('.', '/').concat(".class");  
                    // 调用sun.misc.URLClassPath#getResource(java.lang.String, boolean) 查找class文件
                    Resource res = ucp.getResource(path, false);  
                    if (res != null) {  
                        try {  
                            return defineClass(name, res);  
                        } catch (IOException e) {  
                            throw new ClassNotFoundException(name, e);  
                        } catch (ClassFormatError e2) {  
                            if (res.getDataError() != null) {  
                                e2.addSuppressed(res.getDataError());  
                            }  
                            throw e2;  
                        }  
                    } else {  
                        return null;  
                    }  
                }  
            }, acc);  
        } catch (java.security.PrivilegedActionException pae) {  
            throw (ClassNotFoundException) pae.getException();  
        }  
        if (result == null) {  
            throw new ClassNotFoundException(name);  
        }  
    return result;  
}

sun.misc.URLClassPath#getResource(java.lang.String, boolean)

public Resource getResource(String name, boolean check) {
    if (DEBUG) {
        System.err.println("URLClassPath.getResource(\"" + name + "\")");
    }
    //根据Loader的顺序查找 ,第一个找到就返回, loader顺序来自classpath顺序 
    Loader loader; 
    int[] cache = getLookupCache(name); 
    for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) { 
        Resource res = loader.getResource(name, check);
        if (res != null) { 
            return res;
        }
    }
    return null;
}

根据classpath创建AppClassLoader

sun.misc.Launcher.AppClassLoader#getAppClassLoader

public static ClassLoader getAppClassLoader(final ClassLoader extcl) throws IOException { 
    final String s = System.getProperty("java.class.path"); 
    // 获取classpath 
    final File[] path = (s == null) ? new File[0] : getClassPath(s);
    // Note: on bugid 4256530
    // Prior implementations of this doPrivileged() block supplied
    // a rather restrictive ACC via a call to the private method
    // AppClassLoader.getContext(). This proved overly restrictive 
    // when loading classes. Specifically it prevent 
    // accessClassInPackage.sun.* grants from being honored. 
    // 
    return AccessController.doPrivileged( 
        new PrivilegedAction<AppClassLoader>() { 
            public AppClassLoader run() { 
                URL[] urls (s == null) ? new URL[0] : pathToURLs(path);
                // 创建AppClassLoader 
                return new AppClassLoader(urls, extcl);
            }
        }
    );
}

3.示例验证:

启动命令添加 -verbose:class 打印类加载信息

1.   classes目录在前  jar在后

1.png

2.png

2. jar在 classes目录在

3.png

4.png