一个面试题:classpath中同时存在不同版本jar包(如slf4j-log4j12-1.5.8.jar, slf4j-log4j12-1.7.12.jar), 会有冲突吗?
创建一个工程Test1, 其中有一个类: com.test.Test
创建另一个工程Test2, 其中也有同名类: com.test.Test
而两个工程中的com.test.Test的内容都是一样的(除了sysout中的工程名外),如下所示:
public class Test {
public void doSomething(){
System.out.println("do something in test2 project");
System.out.println("classload is: "+this.getClass().getClassLoader());
}
}
将它们两个打成jar包,均导入到第三个工程Test3中(模拟版本冲突的情况),如下所示:
在这第三个工程中,有这么一个测试类,如下所示:
public class ClassLoadTest {
public static void main(String[] args) {
Test test = new Test();
test.doSomething();
}
}
main方法中引用了test1.jar和test2.jar中都存在的com.test.Test, 编译没有报错, 运行会报错吗? 如提示存在多个com.test.Test.
运行结果如下:
$ java ClassLoadTest
do something in test2 project
classload is: sun.misc.Launcher$AppClassLoader@7451b0a
一切正常,并没有报错。即类路径中存在不同版本的jar包并不会导致冲突。看一下运行该main方法时的完整类加载过程,
$ java -XX:+TraceClassLoading ClassLoadTest
[Opened /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rt.jar]
[Loaded java.lang.Object from /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rt.jar]
......
[Loaded com.test.Test from file:/home/zhuguowei/workspace/temp-use/lib/test2.jar]
...
似乎会随机加载一个存在com.test.Test类的jar包。
又经过实验发现类路径中谁在前, 就会加载哪个,如下所示:
#终端一
$ export CLASSPATH=$CLASSPATH:/lib/test1.jar:/lib/test2.jar
$ java com.zhugw.study.ClassLoadTest
do something in test1 project
classload is: sun.misc.Launcher$AppClassLoader@ecfeb11
#开启一个新的终端
$ export CLASSPATH=$CLASSPATH:/lib/test2.jar:/lib/test1.jar
$ java com.zhugw.study.ClassLoadTest
do something in test2 project
classload is: sun.misc.Launcher$AppClassLoader@6a3449a8
但若同样全限定名的类中的某些方法参数不同的话,或其中一个多了一些或少了一些方法的话, 都会产生一些问题, 如Test1工程中的com.test.Test中,存在如下的一个方法:
public void doAnotherthing(String foo){
System.out.println("do another thing in test1 project");
}
而在Test2工程中的com.test.Test中同样名称的方法多了一个参数,
public void doAnotherthing(String foo, String bar){
System.out.println("do another thing in test2 project");
}
这时Test3中的测试类的代码如下所示:
Test test = new Test();
test.doSomething();
test.doAnotherthing("foo"); //注:调用的是test1.jar中单参的doAnotherthing
没有编译错误,但运行时就有问题了,如下所示:
$ export CLASSPATH=$CLASSPATH:/lib/*
$ java ClassLoadTest
do something in test2 project
classload is: sun.misc.Launcher$AppClassLoader@7451b0af
Exception in thread "main" java.lang.NoSuchMethodError: com.test.Test.doAnotherthing(Ljava/lang/String;)V
at com.zhugw.study.ClassLoadTest.main(ClassLoadTest.java:14)
因为此时加载的是test2.jar。但是通过如下方式引入jar包就没有问题:
$ export CLASSPATH=$CLASSPATH:/lib/test1.jar:/lib/test2.jar
$ java com.zhugw.study.ClassLoadTest
do something in test1 project
classload is: sun.misc.Launcher$AppClassLoader@6a3449a8
do another thing in test1 project
此时加载的是test1.jar。