本人学java出身,但说起来惭愧,对java的底层却不是很了解,比方说一会儿要淡到的类加载机制。
大家在运行java程序的时候有没有想过,这个程序是怎么运行的。也许有得朋友会说,先编译成字节码,然后再加载到内存运行。不错,确实是这样,那新问题又来了,到底是怎么加载的。这里就会涉及到java的类加载机制。
理解类加载机制,可以在程序运行的过程中动态的加载相应的类,从而提高程序的灵活性和适应性。
一个程序HelloWorld.java,它需要运行,并不是简简单单地将其字节码加载到内存就完事儿了,它需要jdk和jre的支持。这就像盖楼一样,没有下面的地基,这个楼就无法完成。所以在加载HelloWorld.class之前,要将jdk和jre加载到内存。那么怎么加?这里就使用了所谓的加载器(classloader)。说到这里,不知道大家有没有注意,我们需要加载 jdk, jre, HelloWorld.class, 这三者的加载需要使用一个加载器行不行?答案:不行。因为这三者的级别不一样,所以需要使用不同的加载器。首先,jdk 是java的核心内库,也就是说它是最底层的,所以加载他的时候需要一个能跟底层打交道的加载器,这里jvm提供了bootstrap class loader(它是由c编写的,其他的加载器都是用java写的)。其次,jre是jdk的扩展类库,jvm提供了 extension class loader。最后,jvm提供了application class loader, 专门用来加载用户定义的类,例如HelloWorld。
整个加载顺序也如前言描述一样,bootstrap class loader, extension class loader, application class loader。首先一上来bootstrap class loader 就加载其他的classloader, 有了这些classloader后面的load工作就按序进行了。在这里,每一个classloader都持有上一层classloader的引用,所以classloader之间不是继承关系,而是关联的关系。在加载过程中,classloader首先找上一层loader是不是加载过了,如果已经加载过了,就不会再次load。这样就保证了安全性,例如自己写的String永远就没有机会被加载,因为上层加载过了。另外,每一种classloader只负责自己管辖范围的类。除此之外,还有其他的加载器,在这里就不详细列出了。
这里引用了别人的一个例子:
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
Class c = hello.getClass();
ClassLoader loader = c.getClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}
sun.misc.Launcher$ExtClassLoader@addbf1
null
Process finished with exit code 0