测试用的是jdk1.6.0_16. 项目的结构图:
src:源码
classes:编译后的文件
lib:junit
urlclassloaderpath:测试过程中要从这里加载类
编译和运行(都是在classloader目录下):
编译:javac -encoding utf-8 -d classes -cp lib\junit-4.7.jar src\urlclassloader\URLClassLoaderTest.java |
运行:java -cp classes;lib\junit-4.7.jar org.junit.runner.JUnitCore urlclassloader.URLClassLoaderTest |
问题的来源,先看下面的code:
try { System.out.println("==========test2========="); URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + "/urlclassloaderpath") // new URL("file:/" + System.getProperty("user.dir") + "/urlclassloaderpath/") }; for(URL u : urls) { System.out.println(u.toString()); } URLClassLoader ul = new URLClassLoader(urls); Class<?> c = ul.loadClass("hello.HelloWorld"); System.out.println(c); c = ul.loadClass("urlclassloader.test.Test"); System.out.println(c); System.out.println("==========test2=========" + rt); } catch (Exception e) { System.out.println("test2():" + e + rt); }
上面的意思就是从urlclassloaderpath下加载hello.HelloWorld这个类, 目录下也的确存在hello包和HelloWorld类。如果使用的是:new URL("file:/" + System.getProperty("user.dir") + "/urlclassloaderpath"),那么运行的时候会出现异常:java.lang.ClassNotFoundException。如果使用的是:new URL("file:/" + System.getProperty("user.dir") + "/urlclassloaderpath/") 则是OK的。郁闷的地方来了,指定classes目录时,无论是否在最后加"/"都是没有问题的,换成urlclassloaderpath目录怎么就不行了?
可以先查看一下URLClassLoader类的API(中文的), 是这么说的:该类加载器用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。这里假定任何以 '/' 结束的 URL 都是指向目录的。如果不是以该字符结束,则认为该 URL 指向一个将根据需要打开的 JAR 文件。
不过从运行的结果来看,貌似不是很对。
再看刚刚那个程序, 下面还加载了:c = ul.loadClass("urlclassloader.test.Test"); 在URL中我们并没有指定classes目录, 但是加载urlclassloader.test.Test缺少没有任何问题的。其实这里可以这么思考下:运行的时候指定的classpath是classes,那么从这个目录下加载当然是没有问题的。这样的话就能够说的通了。
再来看下加载jar包中的类. 只是指定了目录,而没有指定具体的jar包。
try { URL[] urls = new URL[] { new URL("file:" + System.getProperty("user.dir") + "/classes/") }; for(URL u : urls) { System.out.println(u.toString()); } // 从jar文件中加载一个类 URLClassLoader ul = new URLClassLoader(urls); Class<?> c = ul.loadClass("test.classpath.jar.ClasspathJarFile"); System.out.println(c); System.out.println("==========test=========" + rt); } catch (Exception e) { System.out.println("test() : " + e + rt); }
运行上面的程序,报的是java.lang.ClassNotFoundException。 如果改成:
new URL("file:" + System.getProperty("user.dir") + "/classes/classpath.jar")
指定了具体的jar包,则是没有问题的。
其实这个和我们的命令行差不多,在我们指定-cp的时候必须要指定具体的jar包,而不能只指定目录。
完整的测试程序见附件或者https://github.com/yklovejava/yklovejava/tree/master/jvm/classloader
总结:测试完后就明白了