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

71 篇文章 0 订阅
35 篇文章 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("只是起演示作用,没有实际意义。");
	}
	
	
	@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 );
	}
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值