类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
class对象只能被java虚拟机创建,从封装的源码中就可以看出:
/* * Constructor. Only the Java Virtual Machine creates Class * objects. */ private Class() {}
1、从本地系统中直接加载(最简单,用得最多)
2、通过网络下载.class文件
用URLClassLoader的方法便可以实现,参考官网api:
http://docs.oracle.com/javase/7/docs/api/
If there is a security manager, this method first calls the security manager's checkCreateClassLoader method to ensure creation of a class loader is allowed.
Parameters: urls - the URLs from which to load classes and resources Throws: SecurityException - if a security manager exists and its checkCreateClassLoader method doesn't allow creation of a class loader. See Also: SecurityManager.checkCreateClassLoader()3、从zip,jar包等归档文件中加载.class文件(常用)
4、从专有数据库中提取.class文件(少见)
5、将Java源文件动态编译为.class文件(少见)
经常发生在Web应用里面,假如说有一个托管的主机,将java源文件放到指定目录下,这个时候会自动编译源文件为class文件,然后再加载到内存中。
根类加载器(Bootstrap),C++编写,程序员无法在Java代码中获得该类
扩展类加载器(Extension),Java编写
系统类加载器(System),Java编写
java.lang.ClassLoader的子类
用户可以定制类的加载方式
从api文档中看,类加载器是是一个抽象类,所以不能直接实例化。需要继承然后进行实例化。
http://docs.oracle.com/javase/7/docs/api/
public abstract class ClassLoader extends Object A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a "class file" of that name from a file system.Every Class object contains a reference to the ClassLoader that defined it.
再来看api文档的Class的getClassLoaderhttp://docs.oracle.com/javase/7/docs/api/
If a security manager is present, and the caller's class loader is not null and the caller's class loader is not the same as or an ancestor of the class loader for the class whose class loader is requested, then this method calls the security manager's checkPermission method with a RuntimePermission("getClassLoader") permission to ensure it's ok to access the class loader for the class.
If this object represents a primitive type or void, null is returned.
Returns: the class loader that loaded the class or interface represented by this object. Throws: SecurityException - if a security manager exists and its checkPermission method denies access to the class loader for the class. See Also: ClassLoader, SecurityManager.checkPermission(java.security.Permission), RuntimePermission 以上内容提到如果类是由根类加载器进行加载的,那么就返回null。举例说明:
package jvmDemo; public class Test1 { public static void main(String[] args) throws Exception { Class clazz = Class.forName("java.lang.String"); System.out.println(clazz.getClassLoader()); } }运行结果:null
分析:
java.lang.*由根类加载器负责。由根类加载器进行加载的,那么就返回null。
再来分析一个:
package jvmDemo; public class Test1 { public static void main(String[] args) throws Exception { Class clazz = Class.forName("jvmDemo.C"); System.out.println(clazz.getClassLoader()); } } class C{}运行结果:sun.misc.Launcher$AppClassLoader@73d16e93
分析:
App就是application的简称,AppClassLoader就是应用加载器,那么我们自己编写的C类就是由应用加载器加载的。
再来看一下动态代理:
InvocationHandler接口->Proxy,找到newProxyInstance方法
http://docs.oracle.com/javase/7/docs/api/
Proxy.newProxyInstance throws IllegalArgumentException for the same reasons that Proxy.getProxyClass does.
Parameters: loader - the class loader to define the proxy class interfaces - the list of interfaces for the proxy class to implement h - the invocation handler to dispatch method invocations to Returns: a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces Throws: IllegalArgumentException - if any of the restrictions on the parameters that may be passed to getProxyClass are violated NullPointerException - if the interfaces array argument or any of its elements are null, or if the invocation handler, h, is null 分析:动态创建一个类,那么需要第一个参数类加载器将类加载到内存中,然后生成类的一个对象,这个对象就是我们最后要生成的代理对象,然后返回。
类加载器并不需要等到某个类被“首次主动使用”时再加载它
JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)
如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。
关于LinkageError错误,官网api文档是这么说的:
http://docs.oracle.com/javase/7/docs/api/
java.langLinkageError在Error下面,我们不会跟他打交道,JVM才会。
LinkageError的子类标识了一个类依赖另外一个类, 然后后者在前者编译后有一些不兼容的问题。比如A使用了B,正常编译不会有错。但是如果在Jdk1.6编译器上编译A和B,然后又用JDK1.5编译了A和B,那么前者混合后者是不兼容的,这个时候就报连接错误LinkageError。
项目中在Web开发中,你用JDK1.6编译的类文件,部署到服务器上,服务器上如果是较低版本JDK1.5及以下的,那么有可能是对的,有可能就会报上述错误;如果开发是1.5,服务器上是1.6,不会报错,因为程序都是向下兼容的。
