Java 反射的应用
在学习反射之前,让我们先了解“类(Class)”。“方法”、“属性”、“类”都是名词,那么相应的在Java中会有这样一些特殊的类:“方法类(Method类)”、“属性类(Field类)”、“构造器类(Constructor类)”、“类类(Class类)”。
如上所示,任何Java的类或接口都是Class类的一个实例。
反射就是Java自我管理这些(类,对象)的机制。
1) 反射的作用
-
- 可以通过反射机制发现对象的类型,发现类型的方法/属性/构造器
- 可以创建对象并访问任意对象方法和属性等
2) Class加载
类加载到内存:Java将磁盘类文件加载到内存中吗,为一个对象(实例),这个对象是Class的实例。
3) Class实例代表Java中类型
-
- 获得基本类型实例
- int.class
- long.class
- ...
- 获得类类型(Class)实例:
- 获得基本类型实例
Class cls = String.class; Class cls = Class.forName("java.lang.String"); Class cls = "abc".getClass();
1.通过放射获得队形的类/属性/方法/构造器
【案例】反射演示_“发现”对象的类/属性/方法/构造器
- 版本01
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; /** * 反射演示 */ public class Demo1 { public static void main(String[] args) throws ClassNotFoundException { reflect("s"); //java.lang.String reflect(1); //java.lang.Integer } /** * 反射方法,用于发现 * obj 的类型是什么 * obj 有哪些属性 * obj 有哪些方法 * obj 有哪些构造器 * @param obj 表示被“反射”的对象,被用于“发现”的对象 */ public static void reflect(Object obj) { //1. getClass() // 返回对象的类型,是Object类的方法 Class<?> cls = obj.getClass(); System.out.println("类:" + cls.getName()); //2. getDeclaredFields(); // 返回在类上获得声明的所有属性(字段) Field[] fields = cls.getDeclaredFields(); System.out.println("属性:"); for(Field field : fields) { System.out.println( field.getType()+" :" + //属性类型 field.getName()); //属性名称 } //3. getDeclaredMethods // 返回在类上获得声明的所有方法 Method[] methods = cls.getDeclaredMethods(); System.out.println("方法:"); for(Method method : methods) { System.out.print(method.getReturnType()+" "); System.out.print(method.getName()+" "); System.out.println( Arrays.toString(method.getParameterTypes())); } //4. getDeclaredConstructors // 返回在类上获得声明的所有构造器 Constructor[] constructors = cls.getDeclaredConstructors(); System.out.println("构造器:"); for(Constructor c : constructors) { System.out.print(c.getName() + " "); System.out.println( Arrays.toString(c.getParameterTypes())); } } }
- 版本02
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; /** *反射演示 */ public class Demo2 { public static void main(String[] args) throws ClassNotFoundException { reflect(new Foo()); //reflect.Foo } /** * 反射方法,用于发现 * obj 的类型是什么 * obj 有哪些属性 * obj 有哪些方法 * obj 有哪些构造器 * @param obj 表示被“反射”的对象,被用于“发现”的对象 */ public static void reflect(Object obj) { //1. getClass() // 返回对象的类型,是Object类的方法 Class<?> cls = obj.getClass(); System.out.println("类:" + cls.getName()); //2. getDeclaredFields(); // 返回在类上获得声明的所有属性(字段) Field[] fields = cls.getDeclaredFields(); System.out.println("属性:"); for(Field field : fields) { System.out.println( field.getType()+" :" + //属性类型 field.getName()); //属性名称 } //3. getDeclaredMethods // 返回在类上获得声明的所有方法 Method[] methods = cls.getDeclaredMethods(); System.out.println("方法:"); for(Method method : methods) { System.out.print(method.getReturnType()+" "); System.out.print(method.getName()+" "); System.out.println( Arrays.toString(method.getParameterTypes())); } //4. getDeclaredConstructors // 返回在类上获得声明的所有构造器 Constructor[] constructors = cls.getDeclaredConstructors(); System.out.println("构造器:"); for(Constructor c : constructors) { System.out.print(c.getName() + " "); System.out.println( Arrays.toString(c.getParameterTypes())); } } } class Foo { int a = 3; public double add(int b, Double d) { return a+b+d; } }
结果:
Java是如何知道程序员开发程序的类、属性、方法的?通过反射技术(反射技术是Java底层JVM运行程序的机制)
如下所示,Eclipse开发工具的“代码提示”功能就是利用反射写的。
2.通过发射创建对象实例
【案例】反射演示_根据类名创建对象实例
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; /** *反射演示 */ public class Demo3 { public static void main(String[] args) throws Exception { //执行create()方法前提是参数类有无参构造器 Object obj = create("reflect.Foo3"); reflect(obj); reflect(create("java.lang.String")); } /** * 任务1:根据“类名”创建对象实例 * @param className * @return */ public static Object create(String className) { try { //1 加载类 //1.1 在classpath中查找对应的类 //1.2 采用“懒加载”方式装载到内存 Class cls = Class.forName(className); //2 创建类实例 Object obj = cls.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("没搞定!", e); } } /** * 反射方法,用于发现 * obj 的类型是什么 * obj 有哪些属性 * obj 有哪些方法 * obj 有哪些构造器 * @param obj 表示被“反射”的对象,被用于“发现”的对象 * @throws Exception */ public static void reflect(Object obj) throws Exception { //1. getClass() // 返回对象的类型,是Object类的方法 Class<?> cls = obj.getClass(); System.out.println("类:" + cls.getName()); //2. getDeclaredFields(); // 返回在类上获得声明的所有属性(字段) Field[] fields = cls.getDeclaredFields(); System.out.println("属性:"); for(Field field : fields) { System.out.println( field.getType()+" :" + //属性类型 field.getName()); //属性名称 } //3. getDeclaredMethods // 返回在类上获得声明的所有方法 Method[] methods = cls.getDeclaredMethods(); for(Method method : methods) { System.out.print(method.getReturnType()+" "); System.out.print(method.getName()+" "); System.out.println( Arrays.toString(method.getParameterTypes())); } //4. getDeclaredConstructors // 返回在类上获得声明的所有构造器 Constructor[] constructors = cls.getDeclaredConstructors(); System.out.println("构造器:"); for(Constructor c : constructors) { System.out.print(c.getName() + " "); System.out.println( Arrays.toString(c.getParameterTypes())); } } } class Foo3 { int a = 3; public double add(int b, Double d) { return a+b+d; } }
注:
- Class.forName() 静态方法,可以利用类名在CLASSPATH中查找对应的类,并且装载到内存,返回这个“class”
- Class.forName() 加载类的过程采用“懒惰方式”
- “懒惰方式”,即检查发现如果已经加载了(内存中存在)就不再加载,直接返回已经加载的类,相当于“手工”去检查内存在是否已经加载某个类
- newInstance() 方法,会利用默认(无参数)构造器创建类实例(实例对象)
3.通过反射访问某对象的某属性
【案例】反射演示_访问某对象的某属性
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; /** *反射演示 */ public class Demo4 { public static void main(String[] args) throws Exception { //执行create()方法前提是参数类有无参构造器 Object obj = create("reflect.Foo4"); System.out.println(getFieldValue(obj, "a")); } /** * 任务1:根据“类名”创建对象实例 * @param className * @return */ public static Object create(String className) { try { //1 加载类 //1.1 在classpath中查找对应的类 //1.2 采用“懒加载”方式装载到内存 Class cls = Class.forName(className); //2 创建类实例 Object obj = cls.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("没搞定!", e); } } /** * 任务2:访问某对象的某属性 * @param obj * @param fieldName * @return */ public static Object getFieldValue( Object obj, String fieldName) { try { //1. 反射出类型 Class cls = obj.getClass(); //2. 反射出类型字段 Field field = cls.getDeclaredField(fieldName); //3. 在对象obj上读取field属性值 Object val = field.get(obj); return val; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("没搞定!", e); } } /** * 反射方法,用于发现 * obj 的类型是什么 * obj 有哪些属性 * obj 有哪些方法 * obj 有哪些构造器 * @param obj 表示被“反射”的对象,被用于“发现”的对象 * @throws Exception */ public static void reflect(Object obj) throws Exception { //1. getClass() // 返回对象的类型,是Object类的方法 Class<?> cls = obj.getClass(); System.out.println("类:" + cls.getName()); //2. getDeclaredFields(); // 返回在类上获得声明的所有属性(字段) Field[] fields = cls.getDeclaredFields(); System.out.println("属性:"); for(Field field : fields) { System.out.println( field.getType()+" :" + //属性类型 field.getName()); //属性名称 } //3. getDeclaredMethods // 返回在类上获得声明的所有方法 Method[] methods = cls.getDeclaredMethods(); for(Method method : methods) { System.out.print(method.getReturnType()+" "); System.out.print(method.getName()+" "); System.out.println( Arrays.toString(method.getParameterTypes())); } //4. getDeclaredConstructors // 返回在类上获得声明的所有构造器 Constructor[] constructors = cls.getDeclaredConstructors(); System.out.println("构造器:"); for(Constructor c : constructors) { System.out.print(c.getName() + " "); System.out.println( Arrays.toString(c.getParameterTypes())); } } } class Foo4 { int a = 3; public double add(int b, Double d) { return a+b+d; } }
注:
field.get(obj) 可以获得对象属性值
field.set(Object obj, Object value) 可以设置对象属性值
4.通过反射方位某对象的方法
【案例】反射演示_访问某对象的某方法
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; /** *反射演示 */ public class Demo5 { public static void main(String[] args) throws Exception { //执行create()方法前提是参数类有无参构造器 Object obj = create("reflect.Foo5"); Object mObj = call( obj, //obj "add", //方法名 new Class[]{int.class, Double.class}, //参数类型 new Object[]{2, 3.5}); //参数 System.out.println(mObj); } /** * 任务1:根据“类名”创建对象实例 * @param className * @return */ public static Object create(String className) { try { //1 加载类 //1.1 在classpath中查找对应的类 //1.2 采用“懒加载”方式装载到内存 Class cls = Class.forName(className); //2 创建类实例 Object obj = cls.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("没搞定!", e); } } /** * 任务2:访问某对象的某属性 * @param obj * @param fieldName * @return */ public static Object getFieldValue( Object obj, String fieldName) { try { //1. 反射出类型 Class cls = obj.getClass(); //2. 反射出类型字段 Field field = cls.getDeclaredField(fieldName); //3. 在对象obj上读取field属性值 Object val = field.get(obj); return val; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("没搞定!", e); } } /** * 任务3:访问某对象的某方法 * @param obj 被调用对象 * @param method 方法名 * @param paramTypes 方法参数类型列表 * @param params 方法调用参数列表 * @return 在对象obj上调用方法签名是(method,paramTypes)的方法, * params是传递的参数,返回的是方法的结果, * 如果无返回值,返回null */ public static Object call( Object obj, String method, Class[] paramTypes, Object[] params) { try { //1. 发现类型 Class cls = obj.getClass(); //2. 发现方法 Method m = cls.getDeclaredMethod(method, paramTypes); //3. 在对象obj调用方法m,传递参数类别params Object val = m.invoke(obj, params); return val; } catch(Exception e) { throw new RuntimeException("错误了!", e); } } /** * 反射方法,用于发现 * obj 的类型是什么 * obj 有哪些属性 * obj 有哪些方法 * obj 有哪些构造器 * @param obj 表示被“反射”的对象,被用于“发现”的对象 * @throws Exception */ public static void reflect(Object obj) throws Exception { //1. getClass() // 返回对象的类型,是Object类的方法 Class<?> cls = obj.getClass(); System.out.println("类:" + cls.getName()); //2. getDeclaredFields(); // 返回在类上获得声明的所有属性(字段) Field[] fields = cls.getDeclaredFields(); System.out.println("属性:"); for(Field field : fields) { System.out.println( field.getType()+" :" + //属性类型 field.getName()); //属性名称 } //3. getDeclaredMethods // 返回在类上获得声明的所有方法 Method[] methods = cls.getDeclaredMethods(); for(Method method : methods) { System.out.print(method.getReturnType()+" "); System.out.print(method.getName()+" "); System.out.println( Arrays.toString(method.getParameterTypes())); } //4. getDeclaredConstructors // 返回在类上获得声明的所有构造器 Constructor[] constructors = cls.getDeclaredConstructors(); System.out.println("构造器:"); for(Constructor c : constructors) { System.out.print(c.getName() + " "); System.out.println( Arrays.toString(c.getParameterTypes())); } } } class Foo5 { int a = 3; public double add(int b, Double d) { return a+b+d; } }
注:
- 方法签名由方法名称与参数列表组成(没有返回值和访问控制符),被成为方法签名
- call 方法标识,调用任意对象的任意方法
- 调用无参方法时这样写:
PS:
getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。
getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。
java.lang.reflect.Method.invoke(Object obj, Object... args) invoke第一个参数obj,如果电泳的方法是static方法,obj参数可以传null。