反射
1.什么时候会发生类初始化
-
类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类;
- new一个类的对象;
- 调用类的静态成员(除了final常量)和静态方法;
- 使用java.lang.reflect包的方法对类进行反射调用;
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类;(包括含有default方法的接口)
- 直接使用java虚拟机运行某一个主类,程序会初始化该主类
-
类的被动调用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化,但是会导致父类的初始化
- 通过数组定义类引用,不会触发此类的初始化;
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了);
- Class.forName(String s,false,ClassLoader cl),可以只生成Class对象而不进行初始化
2.定义
反射:将类的各个组成部分封装为其他对象,这就是反射机制
好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
3.获取Class对象的方式:
- Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
- 类名.class:通过类名的属性class获取
多用于参数的传递
- 对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
4.Class对象功能:
- 获取成员方法们:
Method[] getMethods();//获得当前类以及super类的public方法
Method getMethod(String name, 类<?>... parameterTypes)
Method[] getDeclaredMethods() //获取当前类声明的所有方法,不包括父类中的
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
- 获取全类名
String getName() - 获取成员变量们
-
Field[] getFields() :获取当前类以及super的所有public修饰的成员变量
-
Field getField(String name) 获取指定名称的public修饰的成员变量
-
Field[] getDeclaredFields() 获取当前类所有的成员变量,不考虑修饰符
-
Field getDeclaredField(String name)
- 获取构造方法们
- Constructor<?>[] getConstructors() 获取类的public类型的构造方法
- Constructor< T > getConstructor(类<?>… parameterTypes)
- Constructor< T > getDeclaredConstructor(类<?>… parameterTypes)
- Constructor< ? >[] getDeclaredConstructors()
- Constructor<?>[]newInstance() 通过类的不带参数的构造方法创建这个类的一个对象
5. Field:成员变量
-
设置值:void set(Object obj, Object value)
-
获取值:get(Object obj)
- 忽略访问权限修饰符的安全检查:setAccessible(true):暴力反射
6.Constructor:构造方法
-
创建对象:T newInstance(Object… initargs)
-
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
Class.newInstance();
Class.getConstructor(new Class[]{}).newInstance(new Object[]{});
7.Method:方法对象
-
执行方法:Object invoke(Object obj, Object… args)
-
获取方法名称:String getName
8.使用方法
9.动态代理类
概念
- 属于java.lang.reflect包,主要涉及到两个类:
(1) Interface InvocationHandler:接口中仅定义一个方法
public object invoke(Object obj,Method method, Object[] args)//第一个参数obj指代理类,method是被代理的方法对象,args为该方法的参数数组
(2) Proxy:该类即为动态代理类,其中主要包含以下内容
protected Proxy(InvocationHandler h)//构造函数,定义调用逻辑
static Class getProxyClass (ClassLoader loader, Class[] interfaces)//获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)//返回代理类的一个实例,返回后的代理对象可以当作被代理的类对象使用(可使用被代理类的在Subject接口中声明过的方法)
步骤
- 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
- 创建被代理的类以及接口
- 通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理对象
- 通过代理对象调用方法
实例
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);
//创建动态代理类
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(),
new Class[] { Foo.class });
//创建动态代理类的实例
Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);
//直接创建动态代理类的实例
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
特点
由Proxy类的静态方法创建的动态代理类具有以下特点:
- 动态代理类是public、final和非抽象类型的;
- 动态代理类继承了java.lang.reflect.Proxy类;
- 动态代理类的名字以“$Proxy”开头;
- 动态代理类实现getProxyClass()和newProxyInstance()方法中参数interfaces指定的所有接口;
- Proxy 类的isProxyClass(Class<?> cl)静态方法可用来判断参数指定的类是否为动态代理类。只有通过Proxy类创建的类才是动态代理类;
- 动态代理类都具有一个public 类型的构造方法,该构造方法有一个InvocationHandler 类型的参数。
10.题目
- 以下说法正确的是:(ACD)
A. 动态代理类借助实现的InvocationHandler进行函数调用
B. 动态代理类对象必须与代理类具有完全相同的接口列表
C. 动态代理类只可以代理接口
D. java.lang.reflect包中的Proxy类提供了创建动态代理类的方法
- 下列说法正确的是(C)
A. 泛型只在编译时有效,无法在运行期获取类的泛型声明
B. 反射机制可以获取泛型对象的具体泛型类型
C. 可以通过反射机制绕过编译器对泛型类型的检查
特别注意: 泛型只在编译的时候用于类型的检查,java程序加载进内存后,泛型就不存在了,而jdk的反射是模拟java运行时的环境读取和调用程序,因此,不能获得泛型的实际类型。