第18章 类加载机制与反射
1.Java类加载器的原理和机制
(1)JVM和类
当调用java命令运行某个Java程序时,该命令将会启动一个Java虚拟机进程。
(2)类的加载
类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。
(3)类的连接
当类加载之后,系统为之生成一个对应的Class对象,接着进入连接阶段,负责把类的二进制数据合并到JRE中。
(4)类的初始化
虚拟机负责对类进行初始化,主要是对类变量进行初始化。指定初始值有两种方式:
- 声明类变量时指定初始值;
- 使用静态初始化块为类变量指定初始值。
JVM初始化一个类的步骤:
- 假如这个类还没有被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还没有初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句。
对于一个final型的类变量,如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于“宏变量”。
当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化,使用Class的forName()静态方法才会导致强制初始化该类。
2.类加载器
JVM启动时,初始类加载器由三个类加载器组成:
Bootstrap ClassLoader:根类加载器
Extension ClassLoader:扩展类加载器
System ClassLoader:系统类加载器
JVM类加载器机制主要有三种:
全盘负责、父类委托、缓存机制
JVM的根类加载器并不是Java实现的,而且由于程序通常无须访问根类加载器,因此访问扩展类加载器的父类加载器时返回null。
自定义类加载器:ClassLoader类和URLClassLoader类
除根类加载器之外的所有类加载器都是ClassLoader子类的实例。ClassLoader提供两个关键方法:
loadClass(String name , Boolean resolve):根据指定名称来加载类,获取指定类对应的Class对象
findClass(String name):根据指定名称来查找类
3.通过反射查看类信息
程序需要在运行时发现对象和类的真实信息,有两种做法:
1)假设在编译和运行时都完全知道类型的具体信息,在这种情况下,可以先使用instanceof运算符进行判断,再2利用强制类型转换将其转换成其运行时类型的变量即可。
2)编译时根本无法预知该对象和类可能属于哪些类,程序只靠运行时信息来发现该对象和类的真实信息,必须使用反射。
第二种方法的步骤如下:
(1)获得Class对象
每个类被加载之后,系统就会生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。一般采用两种方法:
1)使用Class类的forName(String clazzName)静态方法,传入字符串参数(是某个类的全限定类名,即添加完整包名)
2)调用某个类的class属性来获取该类对应的Class对象,例如Person.class
(2)从Class中获取信息
通过Class对象可以得到大量的Method、Constructor、Field等对象。
方法参数反射利用java.lang.reflect包下的Executable抽象类获取指定类的构造器或方法的属性信息。Executable基类提供了isVarArgs()方法用于判断该方法或构造器是否包含数量可变的形参,通过getModifiers()方法来获取该方法或构造器的修饰符。
4.使用反射生成并操作对象
Class对象可以获得类里的方法(Method)、构造器(Constructor)、成员变量(Field)。这三个类都位于java.lang.reflect包下,并实现了java.lang.reflect.Member接口。
(1)创建对象
通过反射来生成对象需要先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。具体来说利用指定构造器来创建Java对象,需要如下三个步骤:
- 获取该类的Class对象(两种方法)
- 利用Class对象的getConstructor()方法来获取指定构造器
- 调用Constructor的newInstance()方法来创建Java对象。
如:
Class<?> jframeClazz = Class.forName(“javax.swing.JFrame”);
Constructor ctor = jframeClazz.getConstructor(String.class);
Object boj = ctor.newInstance(“测试窗口”);
System.out.println(obj);
(2)调用方法
通过Class对象的getMethod()方法获取指定方法,返回Method对象。
Method里包含一个invoke()方法,方法签名如下:
Object invoke(Object obj , Object …args):后面的args是执行该方法时传入该方法的实参。
(3)访问成员变量值
通过Class对象的getField()方法获取该类指定成员变量。使用getXxx(Object obj)获取obj对象的成员变量值(如果成员变量的类型是引用类型,则取消get后面的Xxx)和setXxx(Object obj , Xxx val)将obj对象的成员变量设置成val值。包括private修饰的成员变量。
(4)操作数组
使用Array提供的常用方法动态地创建数组,操作数组元素等。
5.使用反射生成JDK动态代理
使用Proxy和InvocationHandler创建动态代理
Proxy提供了用于创建动态代理类和代理对象地静态方法,它也是所有动态代理类的父类。
6.使用反射来获取泛型信息
当成员变量的类型是有泛型类型的类型,如Map<String , Integer>类型,则不能准确的得到成员变量的泛型参数,可以使用如Type gType = f.getGenericType()获取该成员变量的泛型类型。然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType代表被参数化的类型,也就是增加了泛型限制的类型。
推荐阅读
-
第18章 类加载机制与反射
-
jvm虚拟机笔记<三> 类文件结构与类加载机制
-
Java内存区域与虚拟机类加载机制
-
php _autoload自动加载类与机制分析
-
java的反射和它的类加载机制
-
类加载器与双亲委派机制
-
类加载机制与反射
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第24章:反射与简单Java类:课时111:单级属性赋值
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第23章:反射与类操作:课时105:反射调用构造方法(含关系图-重要)
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第24章:反射与简单Java类:课时110:属性自动赋值实现思路