类的生命周期
- 同一个 JVM 中的所有线程、所有变量都处于同一个进程里
Java 的核心类库在 jre/lib/rt. jar 文件中
类的加载:将类的 class 文件(二进制数据)读入内存,并为之创建一个 java.lang.Class 对象(由类加载器完成)
类的连接:把类的二进制数据合并到 JRE 中包括:验证、准备(为类的类变量分配内存并设置默认初始值)、解析
类的初始化:对类变量进行初始化包括:声明类变量时指定初始值、使用静态初始化块为类变量指定初始值(执行顺序与它们在源代码中的排列顺序相同)
类加载器
- 启动类加载器(Bootstrap ClassLoader):该加载器是用 C 语言实现的,负责加载存放在 $JAVA_HOME\jre\lib下,或被 -Xbootclasspath 参数指定的路径中的,并且能被虚拟机识别的类库(如 rt.jar,所有的 java. 开头的类均被 Bootstrap ClassLoader 加载),启动类加载器是无法被 Java 程序直接引用的
- 扩展类加载器(Extension ClassLoader):该加载器由 sun.misc.Launcher$ExtClassLoader 实现,它负责加载 $JAVA_HOME\jre\lib\ext 目录中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如 javax.开头的类),开发者可以直接使用扩展类加载器
- 应用程序类加载器(Application ClassLoader):该类加载器由 sun.misc.Launcher$AppClassLoader 来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器
双亲委派模型
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载
通过反射查看类信息
- 反射:在运行时期,动态地去获取一个类中的信息(类的信息、构造器信息、方法信息、字段等信息)
获得 Class 对象
- 在 JVM 中,一个类用其全限定类名和其类加载器作为其唯一标识
- 包装类和 Void 类的静态字段 TYPE 表示其基本类型的 Class 对象
所有的具有相同的维数和相同元素类型的数组共享同一个 Class 对象
使用 Class 类的
Class<?> forName(String className)
类方法(该字符串参数的值是某个类的全限定类名,会对类初始化)调用某个类的 class 属性来获取该类对应的 Class 对象(所有的数据类型都有 class 属性,包括数组、基本类型以及
void
)- 调用某个对象的
Class<?> getClass()
方法(可能需要强转)
从 Class 中获取信息
- 获取 Class 对应类所包含的构造器(由 Constructor 对象表示)
Constructor<T> getConstructor(Class<?>… parameterTypes)
:获取此 Class 对象对应类的、带指定形参列表的public
构造器Constructor<T> getDeclaredConstructor(Class<?>.. parameterTypes)
:获取此 Class 对象对应类的、带指定形参列表的构造器,与访问权限无关Constructor<?>[] getConstructors()
:获取此 Class 对象对应类的所有public
构造器Constructor<?>[] getDeclaredConstructors()
:获取此 Class 对象对应类的所有构造器,与访问权限无关 - 获取 Class 对应类所包含的方法(由 Method 对象表示)
Method getMethod(String name, Class<?>.. parameterTypes)
:获取此 Class 对象对应类的、带指定形参列表的public
方法(包括继承的方法)Method getDeclaredMethod(String name, Class<?>.. parameterTypes)
:获取此 Class 对象对应类的、带指定形参列表的方法,与访问权限无关(不包括继承的方法)Method[] getMethods()
:获取此 Class 对象所表示的类的所有public
方法(包括继承的方法)Method[] getDeclaredMethods()
:获取此 Class 对象对应类的全部方法,与访问权限无关(不包括继承的方法) - 访问 Class 对应类所包含的字符(由 Field 对象表示)
Field getField(String name)
:获取此 Class 对象对应类的、指定名称的public
成员变量(包括继承的字段)Field getDeclaredField(String name)
:获取此 Class 对象对应类的、指定名称的成员变量,与成员变量的访问权限无关(不包括继承的字符)Field[] getFields()
:返回此 Class 对象对应类的所有public
成员变量(包括继承的字符)Field[] getDeclaredFields()
:获取此 Class 对象对应类的全部成员变量,与成员变量的访问权限无关(不包括继承的字符) - 其它实例方法
ClassLoader getClassLoader()
:获取该类的类加载器Class<?>[] getInterfaces()
:获取此 Class 对象所表示的类或接口实现的接口Class<? super T> getSuperclass()
:获取该 Class 对象对应类的超类的 Class 对象int getModifiers()
:获取此类或接口的所有修饰符(返回的整数应使用 Modifier 工具类的方法来解码)Package getPackage()
:获取此类的包String getName()
:以字符串形式返回此 Class 对象所表示的类的名称String getSimpleName()
:以字符串形式返回此 Class 对象所表示的类的简称Class<?> getComponentType()
:返回表示数组元素类型的 ClassisArray()
、isEnum()
、isInterface()
、isInstance(Object obj)
、isAssignableFrom(Class<?> cls)
使用反射生成并操作对象
Constructor、Method、Field 都在 java.lang.reflect 包下,直接父类都是 AccessibleObject
如果反射的对象需要调用对应类中的
private
修饰的构造器、方法或成员变量,应调用 Constructor 对象、Method 对象或 Field 对象的如下方法:void setAccessible(boolean flag)
:设置此对象的 accessible 标志为指定的布尔值(值为 true 时,指示反射的对象在使用时取消访问权限检查)先检查要调用的构造器或方法是否有 public 修饰,再检查是否有 private 修饰
创建对象
方式 1:使用 Class 对象的
T newInstance()
方法来创建该 Class 对象对应类的实例,要求该 Class对象的对应类有无参数构造器方式 2:先使用 Class 对象获取指定的构造器 Constructor 对象,再调用 Constructor 对象的
T newInstance(Object… initargs)
方法来创建该 Class 对象对应类的实例
调用方法
先使用 Class 对象获取指定的方法 Method 对象,再调用 Method 对象的 invoke() 方法
Object invoke(Object obj, Object…args)
:该方法中的 obj 是执行该方法的对象,后面的 args 是执行该方法时传入该方法的实参如果调用的是类方法,第一个参数设置为 null
如果调用是数组型参数(形参个数可变)的方法,把实际参数作为 Object[] 的元素再传递,即:
Object invoke(执行该方法的对象, new Object[]{ 所有实参 })
返回值的类型是 Object ,可能需要强转
访问成员变量值
- Field 提供了如下两组方法来读取或设置成员变量值:
Object get(Object obj)
:获取 obj 对象上此 Field 表示的字段的值xxx getXxx(Object obj)
:获取 obj 对象的该成员变量的值(此处的 Xxx 对应 8 种基本类型,如果该成员变量的类型是引用类型,则取消 get 后面的 Xxx)void setXxx(Object obj, Xxx val)
:将 obj 对象的该成员变量设置成 val 值(此处的 Xxx 对应 8 种基本类型,如果该成员变量的类型是引用类型,则取消 set 后面的 Xxx)
操作数组
在 java. lang. reflect 包下的 Array 类中的类方法:
Object newInstance(Class<?>componentType,int…length)
:创建一个具有指定的元素类型、指定维度的新数组int getLength(Object array)
:以 int 形式返回指定数组对象的长度xxx getXxx(Object array, int index)
:返回 array 数组中第 index 个元素void setXxx(Object array, int index, xxx val)
:将 array 数组中第 index 个元素的值设为 val其中 xxx 是各种基本数据类型,如果数组元素是引用类型,则方法变成
get(ObjeCt array, int index)
set(Object array, int index, Object val)
使用反射加载资源文件
- Class 类中的
InputStream getResourceAsStream(String name)
:name 不以 '/' 开头时默认是从当前类的字节码所在的路径去加载资源,以 '/' 开头则是从 classpath 的根路径去加载资源 ClassLoader 类中的
InputStream getResourceAsStream(String name)
:从 classpath 的根路径去加载资源,返回所读取资源的输入流,name 不能以 '/' 开头获取 ClassLoader 对象的方法
- Class 类中的
ClassLoader getClassLoader()
:返回该类的类加载器 - Thread 类中的
ClassLoader getContextClassLoader()
:返回该线程的上下文 ClassLoader
- Class 类中的
使用反射生成 JDK 动态代理
- 通过使用 Proxy 类和 InvocationHandler 接口可以生成 JDK 动态代理类或动态代理对象,即在程序中为一个或多个接口动态地生成实现类或创建实例
- 特点:
- 代理的对象必须有实现的接口
- 需要为每个对象创建代理对象
- 动态代理的最小单位是类(所有类中的方法都会被处理)
Proxy 类
构造器:protected Proxy(InvocationHandler h)
类方法
Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces)
:创建一个动态代理类所对应的 Class 对象,该代理类将实现 interfaces 所指定的多个接口(第一个 ClassLoader 参数指定生成动态代理类的类加载器)Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
:使用指定的 InvocationHandler 创建一个动态代理对象,该代理对象的实现类实现了 interfaces 指定的系列接口,执行代理对象的每个方法时都会被替换执行 InvocationHandler 对象的 invoke 方法
InvocationHandler 接口
- 执行动态代理对象的所有方法时,都会被替换成执行 invoke 方法
Object invoke(Object proxy, Method method, Object[] args)
- proxy:代表动态代理对象
- method:代表正在执行的方法
- args:代表调用目标方法时传入的实参
public class MyInvocationHandler implements InvocationHandler {
private Object target; // 需要被代理的对象
// 在构造函数中初始化 target 对象
public MyInvocationHandler(Object target) {
this.target = target;
}
// 执行动态代理对象的所有方法时,都会被替换成执行如下的 invoke 方法
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
... // 执行其它方法
// 以 target 作为主调来执行 method 方法
Object result = method.invoke(target, args);
... // 执行其它方法
return result;
}
}
// 获取 JDK 动态代理对象的方法
public <T> T getProxyObject(T target) {
MyInvocationHandler handler = new MyInvocationHandler(target);
// 注意:动态创建的代理对象只能赋给 target 所实现的接口类型变量
return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
}
// 将 JDK 动态代理生成的 class 字节码输出到本地文件系统
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
cglib 动态代理
- 原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 标注的类进行代理
// 自定义的 InvocationHandler 需实现 org.springframework.cglib.proxy.InvocationHandler
// 获取 cglib 动态代理对象的方法
public <T> T getProxyObject() {
MyInvokationHandler handler = new MyInvokationHandler();
handler.setTarget(target);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass()); // 设置代理对象需要继承的类
enhancer.setCallback(handler); // 设置代理对象需要回调的方法
return(T) enhancer.create(); // 创建一个代理对象
}
// 将 cglib 动态代理生成的 class 字节码输出到本地文件系统
System.setProperty("cglib.debugLocation", "d:/");