一个关于类加载器加载顺序的经典例子

问题: 如果自己定义一个java.lang.String并运行会出现什么情况?

我们看看下面的代码:

1 package java.lang;
2 public class String{
3     public static void main(String[] args ){    
4     }
5 }

 

       大家发现什么不同了吗?对了,我们写了一个与JDK中String一模一样的类,连包java.lang都一样,唯一不同的是我们自定义的String类有一个main函数。我们来运行一下:

 java.lang.NoSuchMethodError: main
 Exception in thread "main"

这是为什么? 我们的String类不是明明有main方法吗?

其实联系到jvm类加载的双亲委托模型,我们就能解释这个问题了。

      运行这段代码,AppClassLoader会尝试加载java.lang.String这个类,但是根据双亲委托模型AppClassLoader会将加载java.lang.String的请求委托给ExtClassLoader,而 ExtClassLoader又会委托给最后的启动类加载器BootstrapLoader。

      启动类加载器BootstrapLoader只能加载JAVA_HOME\jre\lib中的class类(即J2SE API),问题是标准API中确实有一个java.lang.String(注意,这个类和我们自定义的类是完全两个类)。BootstrapLoader以为找到了这个类,毫不犹豫的加载了j2se api中的java.lang.String。

      最后出现上面的加载错误(注意不是异常,是错误,JVM退出),因为API中的String类是没有main方法的。

结论:我们当然可以自定义一个和API完全一样的类,但是由于双亲委托模型,使得我们不可能加载上我们自定义的这样一个类。所以J2SE规范中希望我们自定义的包有自己唯一的特色(网络域名)。还有一点,这种加载器原理使得JVM更加安全的运行程序,因为黑客很难随意的替代掉API中的代码了。

参照:http://hxraid.iteye.com/blog/747625

posted @ 2015-11-23 21:56  Birding  阅读(2262)  评论(0编辑  收藏  举报