反射使用的三个步骤
用于反射的类,如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("只是起演示作用,没有实际意义。");
}
@Override
public 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 ); //AC
System.out.println( returnValue ); //结果为 155.5 是正确的
/* 测试 自动装箱和自动拆箱 结果:
* 在获取 Method 的时候是没有 自动装箱和自动拆箱 的,但是 包装类的 Integer.TYPE==int.class
* 但是 在invoke() 时 参数 是可以 自动装箱和自动拆箱 的
*/
//演示静态方法//
Method method2 = clazz.getMethod( "print", new Class[0] );
//静态方法不需要 通过 对象 调用 所有 直接 用 null
//print() 方法的返回值是 void ,所以invoke方法返回的是 null
Object 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();
//如果没有属性直接返回 obj
if ( 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 );
}
}