JVM(四) 类的加载

What?

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。


class对象只能被java虚拟机创建,从封装的源码中就可以看出:

    /*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.
     */
    private Class() {}


加载.class文件的方式

1、从本地系统中直接加载(最简单,用得最多)

2、通过网络下载.class文件

用URLClassLoader的方法便可以实现,参考官网api:

http://docs.oracle.com/javase/7/docs/api/

  • URLClassLoader
    public URLClassLoader(URL[] urls)
    Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader. The URLs will be searched in the order specified for classes and resources after first searching in the parent class loader. Any URL that ends with a '/' is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a JAR file which will be downloaded and opened as needed.

    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文件,然后再加载到内存中。

 

两种类型的类加载器

1、Java虚拟机自带的加载器

根类加载器(Bootstrap),C++编写,程序员无法在Java代码中获得该类

扩展类加载器(Extension),Java编写

系统类加载器(System),Java编写

2、用户自定义的类加载器

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的getClassLoader

http://docs.oracle.com/javase/7/docs/api/

  • getClassLoader
    public ClassLoader getClassLoader()
    Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.

    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/

  • newProxyInstance
    public static Object newProxyInstance(ClassLoader loader,
                          Class<?>[] interfaces,
                          InvocationHandler h)
                                   throws IllegalArgumentException
    Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler. This method is equivalent to:
         Proxy.getProxyClass(loader, interfaces).
             getConstructor(new Class[] { InvocationHandler.class }).
             newInstance(new Object[] { handler });
     

    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.lang

Class LinkageError

分析:

LinkageError在Error下面,我们不会跟他打交道,JVM才会。

LinkageError的子类标识了一个类依赖另外一个类, 然后后者在前者编译后有一些不兼容的问题。比如A使用了B,正常编译不会有错。但是如果在Jdk1.6编译器上编译A和B,然后又用JDK1.5编译了A和B,那么前者混合后者是不兼容的,这个时候就报连接错误LinkageError。

项目中在Web开发中,你用JDK1.6编译的类文件,部署到服务器上,服务器上如果是较低版本JDK1.5及以下的,那么有可能是对的,有可能就会报上述错误;如果开发是1.5,服务器上是1.6,不会报错,因为程序都是向下兼容的。







评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值