在java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的,这个策略虽然稍微增加了一些性能开销,但会给java应用程序带来高度的灵活性,java的动态扩展特性就是依赖动态加载和动态连接来实现的。
1.加载
1.1过程:
*类的加载就是把类的.class文件中的二进制数据读入到内存中。
*转换字节流的数据结构(静态-运行时),把它存放在java运行时数据区的方法区内
*在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。Class对象封装了类在方法区的数据结构。并且向java程序提供了访问类在方法区的数据结构的入口。
类加载采用了缓存机制,如果存在相同Class,不重复加载。
1.2虚拟机的方法区
java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区,我们这里需要了解的主要是方法区。方法区的主要作用是存储被装载的类的类型信息,当java虚拟机装载某个类型的时候,需要类装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class中的类型信息,将这些信息存储到方法区中。
1、这个类型的全限定名
2、这个类型的直接超类的全限定名
3、这个类型是类类型还是接口类型
4、这个类型的访问修饰符
5、任何直接超接口的全限定名的有序列表
6、该类型的常量池
7、字段信息
8、方法信息
9、除了常量以外的所有类变量
10、一个到class类的引用
1.3Class类
Class类是一个非常重要的java基础类,每当装载一个新的类型的时候,java虚拟机都会在java堆中创建一个对应于新类型的Class实例,该实例就代表此类型,通过该Class实例我们就可以访问该类型的基本信息。上面说到在方法区中会存储某个被装载类的类型信息,我们就可以通过Class实例来访问这些信息。
1.4类加载器:
- 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自
java.lang.ClassLoader
。 - 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
- 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader()
来获取它。
除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader
类的方式实现自己的类加载器,以满足一些特殊的需求。
不同的类加载器为相同名称的类创建了独立的名称空间。
1.5类加载双亲委派机制介绍和分析
JVM在加载类时默认采用的是双亲委派机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
2.验证
当类别加载后,就进入连接阶段。连接就是把已经读入到内存的类的二进制数据合并到虚拟机的运行环境去。连接的第一步是类的验证,保证被加载的类有正确的内部结构,并且与其他类协调一致。如果jvm检查到错误,那么就会抛出相应的Error对象。
由java编译器生成的java类的二进制数据肯定是正确的,为什么还要进行类的验证,因为java虚拟机并不知道某个特定的.class文件到底是如何被创建的,这个.class文件有可能是由正常的java编译器生成的,也可能是由黑客特制的,黑客视图通过它来破坏jvm环境。类的验证能提高程序的健壮性,确保程序被安全的执行。
3.准备
在准备阶段,jvm虚拟机为类的静态变量分配内存,并设置默认的初始值。如int初始化值是0占是2个字节等。
4.解析
在解析阶段,jvm会把类的二进制数据中的符号引用替换为直接引用。
5.初始化
在初始化阶段,jvm执行类的初始化语句,为类的静态变量赋予初始值。在程序中静态变量的初始化有两种途径:(1) 在静态变量的声明处进行初始化,(2)在静态代码块进行初始化。jvm会按照他们的先后顺序进行初始化。
6.反射实现
反射用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
6.1forName
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
forName使用ClassLoader来加载由String路径找到的.class文件,转换成Class的类,这里的initialize参数是很重要的,可以觉得被加载同时是否完成初始化的工作。
6.2获取类的构造器、属性和方法
*Constructor[] getConstructors()
*Field[] getFields()
*Method[] getMethods()
这些方法的底层机制就是通过Class的入口来访问JVM中方法区的类的类型基本信息。
6.3反射获取类实例
1.先取该类的信息,
Class c = yourclass.getClass();
2.从Class信息中取出定义号的构造函数
Constructor[] cons = c.getConstructors();
3.调用某个构造函数得到对象
Object o = cons[i].newInstance(Object[] initargs) ;
4.强转
7.动态代理
JVM动态代理
Proxy.(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy.
$Proxy0类实现了interfaces的接口,并继承了Proxy类。
*加载目标类实现的接口到内存中(Class)
*如果 根据接口的名称从缓存中获取对象(Object),如果代理对象的Class实例已经存在,则直接返回.
*否者 ProxyGenerator gen = new ProxyGenerator(final String name,Class[] interfaces);
final byte[] classFile = gen.generateClassFile();
这里通过ProxyGenerator和名字和Class对象来生成字节码。
*根据代理类的字节码生成代理类的实例
(2)实例化$Proxy0并在构造方法中把代理类传过去,接着$Proxy0调用父类Proxy的构造器,为InvocationHandler赋值。
InvocationHandler获得初始化后,调用InvocationHandler的 public Object invoke(Object proxy, Method method, Object[] args)方法,来调用代理类中的invoke。
其中的proxy就是实例化的$Proxy0,args就是方法的参数。method就是被调用的方法。具体看下面:
- private static Method m1;
- private static Method m3;
- private static Method m0;
- private static Method m2;
- // 在静态代码块中获取了4个方法:Object中的equals方法、UserService中的add方法、Object中的hashCode方法、Object中toString方法
- static
- {
- try
- {
- m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
- Class.forName("java.lang.Object")
- });
- m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", new Class[0]);
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- }
- catch(NoSuchMethodException nosuchmethodexception)
- {
- throw new NoSuchMethodError(nosuchmethodexception.getMessage());
- }
- catch(ClassNotFoundException classnotfoundexception)
- {
- throw new NoClassDefFoundError(classnotfoundexception.getMessage());
- }
通过这样的方式来进行方法的调用的。
Cglib动态代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
其实动态代理,意思就是动态的(运行期)产生出一个和目标类相同 接口或继承的 类 来代替目标类 做一些事(多态),这才叫代理!
参考:http://m.oschina.net/blog/149055
http://www.blogjava.net/zhuxing/archive/2008/08/08/220841.html
http://blog.sina.com.cn/s/blog_63ac730e0101136r.html
http://www.360doc.com/content/12/0329/14/8101845_198938177.shtml
http://www.cnblogs.com/rongxh7/archive/2010/04/11/1709334.html