首页 > 技术文章 > java反射

pecool 2018-02-04 20:41 原文

本文按照以下思路帮助自己和路人理清反射。

1.什么是反射?

2.反射能干什么?

3.怎么用?

4.案例实操:

5.总结:

一、什么是反射?

    百度百科:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及

  动态调用对象方法的功能称为java语言的反射机制。

  说的通俗一点,反射是在程序编译完成后,在.class运行阶段起作用的,你给我传过来什么,我都能访问它祖宗十八代,从而达到我的目的。重点在于动态,动态指的是运行阶段,而非编译阶段。

二、反射能干什么?

  常见的有加载数据库驱动中Class.forName(driverClass),还有许多框架例如spring也大量的使用反射,还有自己的项目中抽取dao层公共方法写到BaseDaoImpl中需要知道传递的实体类型等等,都可以通过反射来获取。

 

三、怎么用?

  三种方式可以选择,总之是要获取Class类对象

  3.1.通过对象(不常用,对象都有了还反过用对象再反射属性方法,多此一举)

    Person p = new Person();

    Class<? extends Person> clazz = p.getClass();

  3.2.通过类名(不常用,没有必要)

    Class<? extends Person> clazz = Person.class();

  3.3.通过字符串全类名(常用,一般情况下,我们没法传递过去一个类对象,但是我们可以通过该类的全限定名,间接得到类的信息)

    Class<?> clazz = Class.forName("com.xx.myReflect");

四、案例实操

  前面都很简单,下面就使用第三种方式,具体测试反射在字段,普通方法,构造方法上的一些使用,其余的类似,自己探索,被测试的类如下。

  4.1 测试字段

  

@Test
	public void testField(){
		try {
			//通过反射在运行期间加载MyReflect类的.class文件,得到Class对象,Class对象的详细描述了呗代理类
			Class<?> clazz =  Class.forName("com.cissst.vo.MyReflect");
			//因为要给字段中设值,因此产生Reflect的实例,父类是Object
			Object object = clazz.newInstance();
			//得到本类(不包含父类)中的声明字段数组
			Field[] fields = clazz.getDeclaredFields();
			//循环设置字段值,输出字段值
			for(Field f : fields){
				//因为某些字段或者方法是私有的,反射需要将此标识设置为true
				f.setAccessible(true);
				//判断如果当前字段类型是否是Integer
				//如果使用getName,获取的是全限定名
				if(f.getType().getSimpleName().equals("Integer")){
					//是设置为1
					f.set(object, 1);
				}
				//判断如果当前字段类型是否是String
				if(f.getType().getSimpleName().equals("String")){
					//是设置为..
					f.set(object, "秦琼");
				}
				//最后输出,腰肢具体详情请查看API
				System.out.println(f.get(object));
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

 

  4.2 测试方法

/*
	 * 测试普通方法
	 */
	@Test
	public void testMethod(){
			try {
				//通过反射在运行期间加载MyReflect类的.class文件,得到Class对象,Class对象的详细描述了呗代理类
				Class<?> clazz = Class.forName("com.cissst.vo.MyReflect");
				//产生Reflect的实例,父类是Object
				Object object = clazz.newInstance();
				//指定方法名,指定参数类型,得到该方法,还有方法查API,很好用
				Method method = clazz.getMethod("method", String.class);
				//执行此方法
				method.invoke(object, "李逵");
			} catch (Exception e) {
				e.printStackTrace();
			} 
	}

 

  4.3 测试构造方法

  

/*
	 * 测试构造方法
	 * 
	 */
	@Test
	public void testConstractorMethod(){
		try {
			//通过反射在运行期间加载MyReflect类的.class文件,得到Class对象,Class对象的详细描述了呗代理类
			Class<?> clazz = Class.forName("com.cissst.vo.MyReflect");
			//调用无参构造
			Constructor<?> constructor = clazz.getDeclaredConstructor();
			//通过无参构造实例化得到类对象
			Object obj1 = constructor.newInstance();
			//看得到的ojb和MyReflect是否是同一宗亲
			System.out.println(obj1 instanceof MyReflect);
			//得到所有的构造
			Constructor<?>[] constructors = clazz.getDeclaredConstructors();
			//循环操作构造
			for(Constructor<?> con : constructors){
				//这里做个简单的判断,实际可能比这更复杂
				if(con.getParameterCount()==0){
					//无参构造实例化
					Object ojb2 = con.newInstance();
					//有参构造
				}else if(con.getParameterCount()==2){
					//有参构造实例化
					Object ojb3 = con.newInstance(1,"林彪");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

 

 

被测试类:

package com.cissst.vo;

/**
 * 普通类:用此类测试反射
 * @author phoebe
 */
public class MyReflect{
	
	//字段
	private Integer id;
	private String name;
	
	
	/*
	 * 无参构造方法
	 */
	public MyReflect(){
		System.out.println("无参构造is running");
	}
	
	/*
	 * 有参构造方法
	 */
	public MyReflect(Integer id,String name){
		this.id = id;
		this.name = name;
		System.out.println("有参构造");
	}
	
	/*
	 * 无参普通方法
	 */
	public void method(){
		System.out.println("无参普通方法");
	}
	/*
	 * 无参普通方法
	 */
	public void method(String str){
		System.out.println("有参普通方法");
	}
	
}

 

五、总结

  反射使得代码更灵活,可以提高复用性。

推荐阅读