一、类的加载、连接、初始化
1、类----->动态获取对象、运行时信息、创建Java对象、调用Java方法、访问并修改成员变量值
2、一个JVM的所有线程、所有变量都在同一个进程中,使用该JVM的进程的内存
两个JVM不会共享数据
3、加载类的二进制数据来源:
(1)本地文件系统加载class文件
(2)JAR包 eg.数据库驱动类
(3)通过网络加载class文件
(4)Java源文件动态编译并加载
4、类连接:把类的二进制数据合并到JRE
5、创建实例:new、反射、反序列化
6、final型的类变量如果在编译时可以确定下来,类变量相当于宏变量
7、Class Loader的loadClass()加载类,Class的forName()静态方法强制初始化类
二、类加载器
1、类加载器:将.class文件加载到内存中,生成对应的java.lang.Class对象,
一旦类被载入JVM中,同一个类就不会被再次载入了
使用类的全限定类名唯一标识
根类加载器 BootStrap | 扩展类加载器 Extension | 系统类加载器 Sysytem |
---|---|---|
类加载器是由JVM自身实现的 | 负责加载JRE的扩展目录 | 负责JVM启动时加载java命令 |
不由Java实现的 | 其父类加载器是根类加载器 | getSystemClassLoader(),加载路径是程序运行的当前路径 |
2、类加载机制:全盘负责、父类委托、缓存机制
缓存机制:所有加载过的Class都会被缓存,因此修改了Class之后,必须重启JVM,程序修改才会生效
3、自定义类加载器:继承ClassLoader
4、URLClassLoader:可以从本地/远程主机获取二进制文件来加载类
eg.加载MySQL驱动,获取数据库连接
//URLClassLoader
URLClassLoader urlClassLoader = new URLClassLoader(urls);
//Driver
Driver driver = (Driver) urlClassLoader.loadClass("com.mysql.jdbc.Driver").newInstance();
三、发射查看类信息
1、编译时类型:instanceof强制类型转换;运行时类型:反射
2、类被加载后,生成对应Class对象,获取Class对象的3种方法:
Class的forName静态方法 | 类的class属性 | 对象的getClass()方法 |
---|---|---|
Class clazz = Class.forName(A) | Class clazz = A.class; | Class clazz = a.getClass(); |
参数是全限定类名 |
3、从Class类中获取信息:构造器、方法、成员变量、内部类、注解等
构造器 | 方法 | 成员变量 |
---|---|---|
getConstructor(Class[] | getMethod(String name,Class[] params) | getField(String name) |
getConsturctors() | getMethods() | getFields() |
getDeclaredConstructor() | getDeclaredMethod(String name,Class[] params) | getDeclaredFieldString name) |
getDeclaredConsturctors(): | getDeclaredMethods() | getDeclaredFields() |
Annotation | 内部类 | 修饰符、所在包、类名 | 接口、枚举、注解 |
---|---|---|---|
getAnnotation(Class annotationClass) | getDeclaredClasses() | getModifiers() | isAnnotation() |
getDeclaredAnnotations() | getDeclaringClass() | getPackage() | isAnnotationPresent() |
getAnnotationByType(Class annotationClass) | getInterfaces() | getName() | isAnonymousClass() |
getDeclaredAnnotationByType(Class annotationClass) | getSuperClass() | getSimpleName() | … |
4、对于只能在源代码上保留的注解,使用获得的Class对象无法访问到该注解对象 @Retention(Value=SOURCE)
5、javac命令编译Java源文件时,默认生成的class文件不包含方法得形参名信息
四、利用反射生成对象
1、反射生成对象的两种方式:
(1)用Class对象的newInstance()创建类的实例,要求Class对象含有默认构造器 (常见)
根据配置文件信息创建Java对象使用(1)方法,通过配置文件读取类名,利用反射创建对象
(2)先用Class对象获取Constructor对象,再调用Constructor的newInstance()方法创建类的实例
//(1)
Class<T> clazz = Class.forName(clazzName);
clazz.newinstance();
//(2)
Class<T> clazz = Class.forName("...");
Constructor constructor = clazz.getConstructor(String.class);
Object obj = constructor.newInstance("..");
2、Class对象的getMethods()获取方法,Method中含有invoke()方法,用于调用指定方法
可以先调用Method的setAccessible(boolean flag),指定是否使用访问权限检查,true取消权限检查,从而实现利用反射调用private方法
3、getDeclaredField()可以获取所有方法,getField()只能获取public方法
五、利用反射生成JDK动态代理
1、通过使用java.lang.reflect包下的Proxy类和InvocationHandler接口,生成JDK动态代理类或动态代理对象
2、Proxy:用于创建动态代理类和代理对象的静态方法,即所有动态代理类的父类
Proxy创建动态代理实例的两种方式:
(1)static Class getProxyClass(ClassLoader loader, Class…interfaces)
ClassLoader :指定生成动态代理类的类加载器
interfaces:代理类将实现interfaces指定的接口
(2)static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
直接创建动态代理对象,实现interfaces指定接口,执行代理对象的每个方法时会被替换执行InvocationHandler对象的invoke()方法
//常用(2)
InvocationHandler handler = new MyInvocationHandler();
Person p = (Foo) Proxy.newInstance(Person.class.getClassLoader(), new Class[]{Person.class}, handler);
/**
* 执行动态代理对象的所有方法都会执行invoke方法
* proxy:动态代理对象
* method:正在执行的方法
* args:调用目标方法是传入的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) {
...
}
2、JDK动态代理只能为接口创建动态代理,动态代理可以实现解耦,通常生成动态代理时都是为指定目标对象生成动态代理,这种动态代理在AOP中称为AOP代理
3、在反射中使用泛型,可以避免反射生成的对象进行强制类型转换
(参考Java疯狂讲义)