当前位置: 代码迷 >> 综合 >> Java---类反射---使用
  详细解决方案

Java---类反射---使用

热度:9   发布时间:2023-10-21 17:27:15.0

反射使用的三个步骤

    用于反射的类,如Method,可以在java.lang.reflect包中找到。使用这些类的时候必须要遵循三个步骤:

    第一步:获得你想操作的类的java.lang.Class对象。在运行中的Java程序中,用java.lang.Class类来描述类和接口等。

    第二步:调用诸如getDeclaredMethods的方法,取得该类中定义的所有方法的列表。

    第三步:使用反射的API来操作这些信息。

类的解剖(获取类的定义信息)

以解剖 User 为例:

User类:

package cn.hncu.javaSE.reflect.fetch;public class User {private String id;private String name;private int age;public int i; //这个属性 只是为了演示 分解Filed 时用的,没有实际意义double d; //这个属性 只是为了演示 分解Filed 时用的,没有实际意义public User() {}public User(String name, int age) {this.name = name;this.age = age;}public User(String id, String name, int age) {this.id = id;this.name = name;this.age = age;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}//这个函数 没有 实际意义 为了演示 分解Method public double sum( int x, double y ) throws NumberFormatException,IllegalArgumentException {return x+y;}//这个函数 没有 实际意义 为了演示 分解Method public static void print(){System.out.println("只是起演示作用,没有实际意义。");}@Overridepublic String toString() {return "User [id=" + id + ", name=" + name + ", age=" + age + "]";}}

演示解剖的类:

package cn.hncu.javaSE.reflect.fetch;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;import org.junit.Test;/*** 2018年5月19日 上午9:09:33* @author <a href="mailto:447441478@qq.com">宋进宇</a>*	分解 构造器、方法和属性*/
public class Decompose {//分解构造器(构造方法)@Test //为了代码简洁,直接抛异常public void fetchConstructor() throws Exception{//获得类模板对象Class<?> c = Class.forName( "cn.hncu.javaSE.reflect.fetch.User" );
//		//获得该类模板以及其父类模板 的 所有public 的构造器。
//		Constructor<?>[] cons = c.getConstructors();//获得所有该类模板定义(包括 private )的 构造器。Constructor<?>[] cons = c.getDeclaredConstructors();for (Constructor<?> constructor : cons) {//获得修饰符,如:public、static、final...//这里是用一个 整形数 来表示 修饰符,没一位代表一个修饰符int mod = constructor.getModifiers();System.out.println( "Modifiers_int:" + mod );System.out.println( "Modifiers_String:" + Modifier.toString( mod ) );//打印 构造器的名称System.out.println( "名称:" + constructor.getName() );//获得 参数的类型Class<?>[] parameterTypes = constructor.getParameterTypes();int i = 1;for (Class<?> clazz : parameterTypes) {System.out.println( "parameterTypes" + (i++) + ":" + clazz );}/** 还可以 获得 异常的泛型 参数的泛型 * constructor.getGenericExceptionTypes();* constructor.getGenericParameterTypes();*/System.out.println("--------------------------------------");}}//分解方法@Test //为了代码简洁,直接抛异常public void fetchMethod() throws Exception{//获得类模板对象Class<?> cls = Class.forName( "cn.hncu.javaSE.reflect.fetch.User" );//		//获得该类模板以及其父类模板 的 所有public 的方法。
//		Method[] methods = cls.getMethods();//获得该类模板自身定义 的 所有方法( 包括private)。Method[] methods = cls.getDeclaredMethods();for ( int i = 0; i < methods.length; i++ ) {Method method = methods[i];//获得修饰符int mod = method.getModifiers();System.out.println( "Modifiers: " + Modifier.toString( mod ) );//获得 返回参数类型 --- 和构造器 唯一的不同Class<?> returnType = method.getReturnType();System.out.println( "ReturnType: " + returnType );//获得方法名System.out.println( "方法名:" +method.getName() );//获得参数类型Class<?>[] parameterTypes = method.getParameterTypes();System.out.print( "ParameterTypes: " );for (Class<?> c : parameterTypes) {System.out.print( c + " ");}System.out.println();//获得异常的类型Class<?>[] exceptionTypes = method.getExceptionTypes();System.out.print("ExceptionTypes: ");for (Class<?> c : exceptionTypes) {System.out.print( c + " " );}System.out.println();/** 还可以 获得 异常的泛型 参数的泛型  返回值的泛型* method.getGenericExceptionTypes();* method.getGenericParameterTypes();* method.getGenericReturnType();*/System.out.println("------------------------------------------");}}//分解属性@Test //为了代码简洁,直接抛异常public void fetchField() throws Exception{//获得类模板对象Class<?> clazz = Class.forName( "cn.hncu.javaSE.reflect.fetch.User" );
//		//获得该类模板以及其父类模板 的 所有public 的属性。
//		Field[] fields = clazz.getFields();//获得该类模板自身定义 的 所有属性( 包括 private )。Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {//获得修饰符int mod = field.getModifiers();System.out.println( "Modifiers: " + Modifier.toString( mod ) );//获得属性类型Class<?> type = field.getType();System.out.println( "Type: " + type );//获得属性名System.out.println( "属性名:" + field.getName() );/** 可以获得属性的泛型* field.getGenericType();*/System.out.println("---------------------------------");}}
}

类的调用(调用类中的成员)

调用的类 以上面的 User 类

演示调用的类:

package cn.hncu.javaSE.reflect.fetch;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;import org.junit.Test;/*** 2018年5月19日 上午10:12:35* @author <a href="mailto:447441478@qq.com">宋进宇</a>*	演示 操纵构造器、方法和属性  外加 暴力访问 private的构造器、方法和属性*/
public class Operation {//构造器的操作(调用)@Test //为了代码简洁,直接抛异常public void operationConstructor() throws Exception{//1.获得类模板对象Class<?> c = Class.forName( "cn.hncu.javaSE.reflect.fetch.User" );//2.获得空参的构造器 public User()Constructor<?> constructor = c.getDeclaredConstructor( new Class[0] );//3.操作该构造器Object obj = constructor.newInstance( new Object[0] );System.out.println( obj );/* 上面通过类反射 获得的 一个空参实例 与传统方式:User u = new User();* 相比是不是 更加麻烦,但是为什么有类反射这个机制?* 答案很显然,通过类反射,我们就不会依赖 某个 未知的类,而是依赖已有的类String(解耦)* 这样的话,不管要调用的类写了没有,我们都可以事先搭好框架,等具体的类完成时,* 通过配置文件的方法 ,把具体的类全名注入 程序就可以跑了。*///下面演示有参的构造器生成一个实例//获得有参 构造器 public User(String name, int age)//有参时 就涉及到 可变参数,可变参数 可以是 以一个数组 的形式传入,也可以通过具体参数的形式传入Class<?> parameterTypes[] = { String.class, int.class };Constructor<?> constructor2 = c.getConstructor( parameterTypes ); //以一个数组 的形式传入//创建一个有参实例Object obj2 = constructor2.newInstance( "Jack", 22 ); //具体参数的形式传入System.out.println( obj2 );}//方法的操作(调用)@Test//为了代码简洁,直接抛异常public void operationMethod() throws Exception {//1.获得类模板对象Class<?> clazz = Class.forName( "cn.hncu.javaSE.reflect.fetch.User" );//演示非静态方法////非静态方法需要通过 对象 去调用该方法如:new User.sum( 100, 55.5 );//所以先要有一个对象/** Constructor<?> constructor = c.getDeclaredConstructor( new Class[0] );* Object obj = constructor.newInstance( new Object[0] );* 上面 这两句 跟 下面 这一句 是一样的(只有空参的才可以)*///采用 clazz.newInstance() 空参的实例 Object obj = clazz.newInstance();//2.获得该类模板的方法Method method = clazz.getDeclaredMethod("sum", new Class[]{ int.class, double.class } );//3.操作该方法// sum() 方法有返回值,所以invoke()方法的返回值 就是sum()方法的返回值Object returnValue = method.invoke(obj, 100, 55.5 ); //AC//测试 自动装箱和自动拆箱 ://Method method = clazz.getDeclaredMethod("sum", new Class[]{ Integer.class, double.class } ); // WA//Method method = clazz.getDeclaredMethod("sum", new Class[]{ Integer.TYPE, double.class } ); // AC//Object returnValue = method.invoke(obj, new Integer( 100 ), 55.5 ); //ACSystem.out.println( returnValue ); //结果为 155.5 是正确的/* 测试 自动装箱和自动拆箱 结果:* 	在获取  Method 的时候是没有 自动装箱和自动拆箱 的,但是 包装类的 Integer.TYPE==int.class* 	但是 在invoke() 时 参数 是可以 自动装箱和自动拆箱 的*///演示静态方法//Method method2 = clazz.getMethod( "print", new Class[0] );//静态方法不需要 通过 对象 调用 所有 直接 用 null//print() 方法的返回值是 void ,所以invoke方法返回的是 nullObject returnValue2 = method2.invoke( null, new Object[0] ); System.out.println( returnValue2 );}//属性的操作(调用)@Test//为了代码简洁,直接抛异常public void operationField() throws Exception {//1.获得类模板对象Class<?> cls = Class.forName( "cn.hncu.javaSE.reflect.fetch.User" );//非静态的 属性 需要用过对象 去调用User obj = new User( "U001", "张飞", 99 );//2.获取类模板的属性
//		Field field = cls.getDeclaredField( "id" );//3.操作该属性
//		Object value = field.get( obj ); //WA 私有的成员变量 是不能在其它类中 被访问的//可以通过 get方法获取;也可暴力访问,待会演示//2.获取该类模板上的方法Method method = cls.getMethod("getId", new Class[0] );//3.操纵 该方法Object value = method.invoke(obj, new Object[0] );System.out.println( "id:" + value );//2.获取类模板的属性Field field = cls.getDeclaredField( "i" ); // public修饰的属性//3.操作该属性//给 obj的属性:i 赋值field.setInt( obj, 100 );//读取 obj的属性:i 的值Object value2 = field.get( obj );System.out.println( "i: " + value2 );}//演示 暴力访问当前类无法访问的属性@Test//为了代码简洁,直接抛异常public void accessViolence() throws Exception {User obj = new User( "U001", "张飞", 99 );//1.获得类模板对象Class<?> c = Class.forName( "cn.hncu.javaSE.reflect.fetch.User" );//2.获取该类模板上的属性Field field = c.getDeclaredField( "id" );//设置可以访问 ***************** 暴力访问的关键field.setAccessible( true );//可以读System.out.println( "id: " + field.get( obj ) ); //AC 暴力访问成功//同样可以写field.set( obj, "U007" );//AC 暴力修改成功System.out.println( "id: " + field.get( obj ) ); //AC 暴力访问成功 System.out.println( obj );//构造器、方法 都一样  setAccessible( true ) 就可以暴力访问}
}

模拟Java内省的功能

    设计一个方法Object getModel(Map map,Class cls),传入一个包含所有值的Map,然后再传入Model类的class,那么返回Model类的实例,这个实例里面已经包含好了所有相关的数据。也就是把Map中的数据通过反射,设置回到Model类实例中。

package cn.hncu.javaSE.reflect.utils;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class BeanUtils {/*** 给定 一个类模板对象 和 对应的 属性的map 就可以封装出一个 该类模板对象的一个实例* @param c 类模板对象* @param map 对应的属性的map* @return 该类模板对象的一个实例* @throws NoSuchMethodException* @throws SecurityException * @throws InstantiationException* @throws IllegalAccessException* @throws IllegalArgumentException* @throws InvocationTargetException*/public static<T> T populate( Class<T> c, Map<String, Object> map ) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{//先构造一个空参对象//1.获得空参构造器Constructor<T> con = c.getDeclaredConstructor( new Class[0] );//2.获得一个实例对象T obj = con.newInstance( new Object[0] );//获取c中所有属性Field[] flds = c.getDeclaredFields();//如果没有属性直接返回 objif ( flds == null || flds.length == 0 ) {return obj;}//遍历所有属性for (Field field : flds) {//获取属性名称String fieldName = field.getName();//到map找 是否有对应的 值Object value = map.get( fieldName );//如果 value 为null 则跳过if ( value == null ) {continue;}//生成 符合规则的 方法名 set+属性名首字符大写+剩余部分String methodName = "set" + fieldName.substring( 0, 1 ).toUpperCase() + fieldName.substring( 1 );//获取方法Method method = c.getDeclaredMethod( methodName, field.getType() );//调用方法method.invoke( obj, value );}return obj;}public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {Map<String, Object> map = new HashMap<String, Object>();map.put( "name", "abc" );map.put( "age", 22 );map.put( "price", 12.3 );Person p = populate( Person.class, map ); //任意符合JavaBean规范的值对象类System.out.println( p );Book b = populate( Book.class, map ); //任意符合JavaBean规范的值对象类System.out.println( b );}
}