反射机制是什么

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法

对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

 

反射机制能做什么

反射机制主要提供了以下功能: 

  • 在运行时判断任意一个对象所属的类;

  • 在运行时构造任意一个类的对象;

  • 在运行时判断任意一个类所具有的成员变量和方法;

  • 在运行时调用任意一个对象的方法;

  • 生成动态代理。

 

反射机制获取类的三种方法

//第一种方式:  
Class c1 = Class.forName("Student");  

//第二种方式:  
//java中每个类型都有class 属性.  
Class c2 = Student.class;  
   
//第三种方式:  
//java语言中任何一个java对象都有getClass 方法  
Student e = new Student();  
Class c3 = e.getClass(); //c3是运行时类 (e的运行时类是Student)  

 

创建对象

Class c = Class.forName("Student");  
  
//创建此Class 对象所表示的类的一个新实例  
Object obj = c.newInstance(); //调用了Student的无参数构造方法.

 

反射简单示例

反射调用一般分为3个步骤:

  • 得到要调用类的class

  • 得到要调用的类中的方法(Method)

  • 方法调用(invoke)

TestReflect.java

package com.mimvp.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/*
 * 反射调用一般分为3个步骤:
 * 1. 得到要调用类的class
 * 2. 得到要调用的类中的方法(Method)
 * 3. 方法调用(invoke)
 * 
 * @author mimvp.com
 * @date 2016.12.20
 */
public class TestReflect {

	public TestReflect() {
		// TODO Auto-generated constructor stub
	}

	public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		Foo foo = new Foo("I am Foo Object");
		
		Class clazz = foo.getClass();		// 1. 类名
		Method m1 = clazz.getDeclaredMethod("outInfo");						// 2. 函数名
		Method m2 = clazz.getDeclaredMethod("setMsg", String.class);	// 2. 函数名
		Method m3 = clazz.getDeclaredMethod("getMsg");						// 2. 函数名
		
		m1.invoke(foo);										// 3. invoke 类名
		m2.invoke(foo, "Reset Msg Info");			// 3. invoke 类名 + 参数
		String msg = (String)m3.invoke(foo);		// 3. invoke 类名
		System.out.println("m3 msg: " + msg);
	}
}


class Foo {
	private String msg;
	
	public Foo(String msg) {
		this.msg = msg;
	}
	
	public void setMsg(String msg) {
		this.msg = msg;
	}
	
	public String getMsg() {
		return this.msg;
	}
	
	public void outInfo() {
		System.out.println("This is Java Reflect Test Class");
	}
}

运行结果:

This is Java Reflect Test Class
m3 msg: Reset Msg Info

 

反射机制应用场景

1. 新建一个测试类

Student.java

package com.mimvp.reflect;

public class Student {

	private String name;
	private int age;

	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
	public int getAge() {
		return this.age;
	}
	
	public static void hi(String name, int age) {
		System.out.println("hi, I am Name: " + name + ", Age: " + age);
	}
}

 

2. 反射使用步骤

反射调用一般分为3个步骤:

  • 得到要调用类的class

  • 得到要调用的类中的方法(Method)

  • 方法调用(invoke)

	public static void test1() {
		try {
			Class cls = Class.forName("com.mimvp.reflect.Student");
			Method m = cls.getDeclaredMethod("hi", new Class[]{String.class, int.class});
			m.invoke(cls.newInstance(), "mimvp.com", 20);			// 20
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

3. 方法调用中的参数类型

在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class

	// 调用参数类型
	public static void test2() {
		try {
			Class cls = Class.forName("com.mimvp.reflect.Student");
			Method m1 = cls.getDeclaredMethod("setAge", int.class);		// 不能使用Integer.class,否则报错
			Method m2 = cls.getDeclaredMethod("getAge");
			
			Object cls2 = cls.newInstance();
			int age = (int)m2.invoke(cls2);						
			System.out.println("before Age: " + age);		// 0
			
			m1.invoke(cls2, 30);			// set 30
			
			age = (int)m2.invoke(cls2);
			System.out.println("after Age: " + age);			// 30
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

4. static 静态方法的反射调用

static方法调用时,不必得到对象示例,如下:

	// 调用static静态方法
	public static void test3() {
		try {
			Class cls = Class.forName("com.mimvp.reflect.Student");
			Method m = cls.getDeclaredMethod("hi", String.class, int.class);
			m.invoke(cls, "mimvp.com", 20);							// 20 静态方法hi,不需要newInstance
//			m.invoke(cls.newInstance(), "mimvp.com", 20);		// 20
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

5. private私有成员变量赋值

如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。

	// private私有成员赋值
	public static void test4() {
		try {
			Class cls = Class.forName("com.mimvp.reflect.Student");
			Object student = cls.newInstance();
			Field field = cls.getDeclaredField("age");
			field.setAccessible(true);					// 设置允许访问 
			field.set(student, 10);
			int age = (int)field.get(student);
			System.out.println("Age: " + age);		// 10
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

6. 动态使用get,set方法

在某些场合下,动态使用类中的get,set方法,可以先反射调用set方法,再反射调用get方法达到如上效果

	// set / get
	public static void test5() {
		try {
			Class cls = Class.forName("com.mimvp.reflect.Student");
			Object student = cls.newInstance();

			Method setMethod = cls.getDeclaredMethod("setAge", int.class);
			setMethod.invoke(student, 100);
			
			Method getMethod = cls.getDeclaredMethod("getAge");
			int age = (int)getMethod.invoke(student);
			System.out.println("Age: " + age);		// 100
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

反射获取属性

	// 获取属性
	public static void test6() {
		try {
			Class cls = Class.forName("java.lang.Integer");
			Field[] fields = cls.getDeclaredFields();

			System.out.println("aaa: " + Modifier.toString(cls.getModifiers()));		// aaa: public final
			System.out.println("bbb: " + cls.getSimpleName());							// bbb: Integer
			
			// 通过追加的方法,将每个属性拼接到此字符串中  
            // 最外边的public定义
			StringBuffer sb = new StringBuffer();
			sb.append(Modifier.toString(cls.getModifiers()) + " class " + cls.getSimpleName() + "{\n");
			for(Field field : fields) {
				sb.append("\t");
				sb.append(Modifier.toString(field.getModifiers()) + " ");		// 获得属性的修饰符,例如public,static等
				sb.append(field.getType().getSimpleName() + " ");				// 属性的类型的名字
				sb.append(field.getName() + ";\n");									// 属性的名字+换行
			}
			sb.append("}");
			System.out.println(sb);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

运行结果:

aaa: public final
bbb: Integer
public final class Integer{
	public static final int MIN_VALUE;
	public static final int MAX_VALUE;
	public static final Class TYPE;
	static final char[] digits;
	static final char[] DigitTens;
	static final char[] DigitOnes;
	static final int[] sizeTable;
	private final int value;
	public static final int SIZE;
	public static final int BYTES;
	private static final long serialVersionUID;
}

 

 

参考推荐

Java 反射机制详解