Java 反射机制
Java Reflection
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class 类
在Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头。
我们创建了一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的)此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例!
1.每一个运行时类只加载一次!
2.有了Class的实例以后,我们才可以进行如下的操作:
1)*创建对应的运行时类的对象
2)获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、...)
3)*调用对应的运行时类的指定的结构(属性、方法、构造器)
4)反射的应用:动态代理
Class本身也是一个类
Class 对象只能由系统建立对象
实例化Class类对象
1.调用运行时类本身的.class属性
Class clazz1 = Person.class;
2.通过运行时类的对象获取
Person p = new Person();
Class clazz3 = p.getClass();
3.通过Class的静态方法获取.
String className = "com.atguigu.java.Person";
Class clazz4 = Class.forName(className);
4.(了解)通过类的加载器
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz5 = classLoader.loadClass(className);
了解:ClassLoader
ClassLoader loader = this.getClass().getClassLoader(); InputStream is = loader.getResourceAsStream("com\\atguigu\\java\\jdbc.properties"); Properties pros = new Properties(); pros.load(is); String name = pros.getProperty("user"); System.out.println(name);
此方法可以获取包内的文件做为输入流。
创建类对象并获取类的完整结构
创建运行时类的对象:
调用Class对象的newInstance()方法
要 求: 1)类必须有一个无参数的构造器。 2)类的构造器的访问权限需要足够。
//1.根据全类名获取对应的Class对象 String name = “atguigu.java.Person"; Class clazz = null; clazz = Class.forName(name); //2.调用指定参数结构的构造器,生成Constructor的实例 Constructor con = clazz.getConstructor(String.class,Integer.class); //3.通过Constructor的实例创建对应类的对象,并初始化类属性 Person p2 = (Person)con.newInstance("Peter",20); System.out.println(p2);
通过反射调用类的完整结构
全部的Field
public Field[] getFields() 返回此Class对象所表示的类或接口及其父类的public的Field。
public Field[] getDeclaredFields() 返回此Class对象所表示的类或接口本身的全部Field。
Field方法中:
public int getModifiers() 以整数形式返回此Field的修饰符
String str1 = Modifier.toString(f.getModifiers());
public Class<?> getType() 得到Field的属性类型
public String getName() 返回Field的名称。
全部的方法
public Method[] getDeclaredMethods() 返回此Class对象所表示的类或接口本身的全部方法
public Method[] getMethods() 返回此Class对象所表示的类或接口及其父类中的public的方法
Method类中:
public Class<?> getReturnType() 取得全部的返回值
public Class<?>[] getParameterTypes() 取得全部的参数
public int getModifiers() 取得修饰符
public String getName() 返回方法名
public Class<?>[] getExceptionTypes() 取得异常信息
public Class<?> Annotation[] get Annotations() 取得注解
全部的构造器
public Constructor<T>[] getConstructors() 返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors() 返回此 Class 对象表示的类声明的所有构造方法。
Constructor类中:
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();
所继承的父类
public Class<? Super T> getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
public Type getGenericSuperclass(); 获取带泛型的父类
//3*.获取父类的泛型 public void test3(){ Class clazz = Person.class; Type type1 = clazz.getGenericSuperclass(); ParameterizedType param = (ParameterizedType)type1; Type[] ars = param.getActualTypeArguments(); System.out.println(((Class)ars[0]).getName()); }
泛型相关
获取父类泛型类型:Type getGenericSuperclass()
泛型类型:ParameterizedType
获取实际的泛型类型参数数组:getActualTypeArguments()
其他的
public Class<?>[] getInterfaces() 确定此对象所表示的类或接口实现的接口。
public Package getPackage() 获取所在的包
通过反射调用类中的指定方法、指定属性
调用指定属性
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。
public void setAccessible(true)访问私有属性时,让这个属性可见。
调用指定方法
1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
说明:1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
动态代理---反射的应用
代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上
静态代理:要求被代理类和代理类同时实现相应的一套接口;通过代理类的对象调用重写接口的方法时,实际上执行的是被代理类的同样的方法的调用。
动态代理:在程序运行时,根据被代理类及其实现的接口,动态的创建一个代理类。当调用代理类的实现的抽象方法时,就发起对被代理类同样方法的调用。
//动态代理的使用,体会反射是动态语言的关键 interface Subject { void action(); } // 被代理类 class RealSubject implements Subject { public void action() { System.out.println("我是被代理类,记得要执行我哦!么么~~"); } } class MyInvocationHandler implements InvocationHandler { Object obj;// 实现了接口的被代理类的对象的声明 // ①给被代理的对象实例化②返回一个代理类的对象 public Object blind(Object obj) { this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj .getClass().getInterfaces(), this); } //当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method方法的返回值时returnVal Object returnVal = method.invoke(obj, args); return returnVal; } } public class TestProxy { public static void main(String[] args) { //1.被代理类的对象 RealSubject real = new RealSubject(); //2.创建一个实现了InvacationHandler接口的类的对象 MyInvocationHandler handler = new MyInvocationHandler(); //3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象。 Object obj = handler.blind(real); Subject sub = (Subject)obj;//此时sub就是代理类的对象 sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用 //再举一例 NikeClothFactory nike = new NikeClothFactory(); ClothFactory proxyCloth = (ClothFactory)handler.blind(nike);//proxyCloth即为代理类的对象 proxyCloth.productCloth(); } }