Java基本原理__02_反射 (Reflect)

利用反射获取类的相关信息

获取类的相关信息

利用反射API,可以获取类中声明的所有相关信息。

  • 在类中声明的属性
  • 在类中声明的方法
  • 在类中声明的构造器
  • ...

获取属性信息API

Class cls = ...
Field[] flds = cls.getDeclaredFields();

案例:

//动态检查一个类的属性信息
Scanner in = new Scanner(System.in);
String className = in.nextLine();
Class cls = Class.forName(className);
Field[] flds = cls.getDeclaredFields();
for(Field f: flds){
    //f 代表 类中的每个属性信息
    System.out.println(f); 
}

获取方法信息API

//获取类中的方法信息
Method[] methods = 
    cls.getDeclaredMethods();

案例:

//动态加载类
Scanner  in = new Scanner(System.in);
String className = in.nextLine();
//forName() 加载类的时候,如果多次执行
// 方法加载类,实际上只加载一次
Class cls = Class.forName(className);
//动态获取类的方法信息
Method[] methods = 
    cls.getDeclaredMethods();
for(Method m:methods){
    System.out.println(m); 
}

经典案例

模拟实现JUnit3 功能:执行一个类中全部以test为开头方法 注意:类有无参数构造器,方法是无参数的方法。

分析:因为需要运行期间动态分析类的相关信息,必须使用反射API实现!

思路:

  1. 动态加载类
  2. 找到全部方法信息
  3. 遍历全部的方法信息,找到以test为开头的方法。
  4. 执行无参数的test方法。

代码:

//找到一个类中以test为开头方法
Scanner in = new Scanner(System.in);
String className = in.nextLine();
Class cls = Class.forName(className);
Method[] methods =
    cls.getDeclaredMethods();
Object obj = cls.newInstance();
for(Method m:methods){
    //getName() 获取方法信息中的方法名
    String name = m.getName();
    if(name.startsWith("test")){
        //m 是以test为开头方法
        System.out.println(m); 
        //num 代表一个方法的参数个数
        int num = 
            m.getParameterTypes().length;
        if(num==0){
            //执行以test为开头的方法
            Object val=m.invoke(obj);
            System.out.println(val); 
        }
    }
}

动态执行方法API

Method 类上提供了“动态执行方法”的方法

动态执行方法 API

//invoke: 调用
method.invoke(对象, 方法参数...)
//在这个对象上执行method对应的方法
//假设 method 代表 test1方法
//将对象 是 obj
//这样调用invoke相当于 obj.test1()

反射总结

  1. 反射是Java的一套API,提供了Java的动态执行功能
  2. 反射不能乱用
    • 反射API执行效率低,尽量不用
    • 如果必须动态功能,再使用!
      • 不知道类名,方法名,属性名...
  3. 反射必须了解的API
    • Class.forName()
    • cls.newInstance()
    • m.invoke()
  4. 反射API可以访问私有成员!(可以打破封装)

执行私有方法:

Method m=cls.getDeclaredMethod(
    "add", int.class, String.class)
//setAccessible 方法可以关闭原有访问控制机制!使不能执行的方法可以执行了!
m.setAccessible(true);
m.invoke(obj, 5, "abc");

获取一个指定的方法信息

API

Method m=cls.getDeclaredMethod(
    "add", int.class, String.class)

 

演示案例 

演示工程目录结构

Java基本原理__02_反射 (Reflect)

演示Spring中反射的应用  Spring IOC 原理

pom.xml 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.study</groupId>
  <artifactId>reflect-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
   <dependencies>
  	<dependency>
  		<groupId>org.apache.commons</groupId>
  		<artifactId>commons-dbcp2</artifactId>
  		<version>2.1.1</version>
  	</dependency>
  	<dependency>
  		<groupId>dom4j</groupId>
  		<artifactId>dom4j</artifactId>
  		<version>1.6.1</version>
  	</dependency>
  </dependencies>
  
</project>

context.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- resources/context.xml -->
<beans>
	<bean id="foo" class="demo.Foo"></bean>
	<bean id="koo" class="demo.Koo"></bean>
	<bean id="date" class="java.util.Date"></bean>
</beans>

Foo.java

package demo;

public class Foo {

	int age = 5;
	String name = "Tom";
	
	public Foo() {
	}

	@Override
	public String toString() {
		return "Foo [age=" + age + ", name=" + name + "]";
	}
}

Koo.java

package demo;

public class Koo {

	int a = 5;
	double d = 6.0;
	
	public Koo(){
	}

	@Override
	public String toString() {
		return "Koo [a=" + a + ", d=" + d + "]";
	}
}

ApplicationContext.java

package demo;

import java.util.HashMap;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 演示反射基本原理
 * @author Cher_du
 *
 */
public class ApplicationContext {
	//beans 用于缓存被管理的对象
	private HashMap<String,Object> beans = new HashMap<String,Object>();
	
	/**
	 * 根据配置文件,初始化容器环境
	 * @param xml
	 * @throws DocumentException 
	 * @throws ClassNotFoundException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	public ApplicationContext(String xml) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException{
		/*
		 * 读取遍历配置文件,根据配置文件中的信息
		 * 动态加载类,动态创建对象,将对象缓存到
		 * beans集合中
		 */
		//导入dom4j读取XML文件
		SAXReader reader = new SAXReader();
		//getClass().getClassLoader()
		//.getResourceAsStream(文件名)
		//从"包"中读取文件,文件在"包"中!!!
	   Document	doc = reader.read(getClass().getClassLoader().getResourceAsStream(xml));
	   //解析XML的内容
	   System.out.println(doc.asXML());
	   //访问根元素<beans>
	   Element root = doc.getRootElement();
	   //查询到<bean>
	   List<Element> list = root.elements();
	   //遍历所以bean元素
	   for(Element e:list){
		   //e代表xml文件中的每一个bean元素
		   //读取class属性的值,值为类名
		   String className = e.attributeValue("class");
		   String id = e.attributeValue("id");
		   //动态加载类,动态创建对象
		   Class cls = Class.forName(className);
		   Object obj = cls.newInstance();
		   //将对象缓存到beans集合中
		   beans.put(id, obj);
		   System.out.println(id+":"+obj);
	   }
	}
	
	public Object getBean(String id) {
		//从beans集合中查找id对应的对象
		return beans.get(id);
	}
	
}

Demo.java

package demo;

public class Demo {

	public static void main(String[] args) throws Exception {
		String cfg = "context.xml";
		ApplicationContext ctx = new ApplicationContext(cfg);
		Object bean = ctx.getBean("foo");
		System.out.println(bean);
	}
}

运行Demo.java文件中main方法:

Java基本原理__02_反射 (Reflect)

演示通过反射获取类与对象的信息 

 City.java

package reflect;

public class City {
	private int name;

	public int getName() {
		return name;
	}

	public void setName(int name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "City [name=" + name + "]";
	}

	
}

 Demo01.java

package reflect;

import java.util.Scanner;

public class Demo01 {
	
	public static void main(String[] args) throws ClassNotFoundException {
		/*
		 * 动态加载类到内存中
		 */
		System.out.println("请输入类名");
		Scanner in = new Scanner(System.in);
		//运行期间从控制台"动态"获取"类名"
		String className = in.nextLine();
		//在程序运行前,是不指定类名是什么的
		/*
		 * 动态加载类到方法区中,当类名错误时候,
		 * 类名对应的磁盘上没有class文件,就发生
		 * 类没有找到异常!
		 */
		
		Class cls = Class.forName(className);
		//检查加载的结果
		System.out.println(cls);
	}

}

运行后输出: 

Java基本原理__02_反射 (Reflect)

Demo02.java

package reflect;

import java.util.Scanner;

public class Demo02 {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		Scanner in = new Scanner(System.in);
		//动态加载类
		String className = in.nextLine();
		Class cls = Class.forName(className);
		//动态创建对象
		Object obj = cls.newInstance();
		System.out.println(obj);
	}
}

运行后输出: 

Java基本原理__02_反射 (Reflect)

Demo03.java

package reflect;

import java.lang.reflect.Field;
import java.util.Scanner;

public class Demo03 {
	
	public static void main(String[] args) throws ClassNotFoundException {
		
		//显示 某个类 的属性信息
		Scanner in = new Scanner(System.in);
		String className = in.nextLine();
		//动态加载类
		Class cls = Class.forName(className);
		//获取这个类的全部属性信息
		/*
		 * getDeclaredFields 用于获取类中声明的
		 * 属性信息
		 */
		Field[] flds = cls.getDeclaredFields();
		
		for(Field f:flds){
			System.out.println(f);
		}
		in.close();
	}

}

运行后输出: 

Java基本原理__02_反射 (Reflect)

Demo04.java

package reflect;

import java.lang.reflect.Method;
import java.util.Scanner;

public class Demo04 {
	
	public static void main(String[] args) throws ClassNotFoundException {
		//动态加载类
		Scanner in = new Scanner(System.in);
		String className = in.nextLine();
		//forName() 加载类的时候,如果多次执行
		//方法加载类,实际上只加载一次
		Class cls = Class.forName(className);
		//动态获取类的方法信息
		Method[] methods = cls.getDeclaredMethods();
		for(Method m:methods){
			System.out.println(m);
		}
		
		in.close();
	}

}

 运行后输出: 

Java基本原理__02_反射 (Reflect)

TestDemo.java 

package reflect;

public class TestDemo {
	public void testA(){
		System.out.println("Call testA...");
	}
	public void testB(){
		System.out.println("Call testB...");
	}
	public String testB2(){
		System.out.println("Call testB2...");
		return "B2";
	}
	public void f(){
		System.out.println("Call f...");
	}

}

Demo05.java

package reflect;

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

public class Demo05 {

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//找到一个类中以test开头的方法
		Scanner in = new Scanner(System.in);
		String className = in.nextLine();
		Class cls = Class.forName(className);
		Method[] methods = cls.getDeclaredMethods();
		Object obj = cls.newInstance();
		
		for(Method m :methods){
			//getName() 获取方法信息中的方法名
			String name = m.getName();
			if(name.startsWith("test")){
				//m 是以test为开头的方法
				System.out.println(m);
				//num 代表一个方法的参数个数
				int num = m.getParameterTypes().length;
				if(num == 0){
					//执行以test为开头的方法
					Object val = m.invoke(obj);
					System.out.println(val);
				}
			}
		}
	}
}

运行后输出: 

Java基本原理__02_反射 (Reflect)

Goo.java

package reflect;

public class Goo {
	private String name;
	private String add(int n, String name){
		return n+name;
	}
	
	@Override
	public String toString() {
		return "Goo [name=" + name + "]";
	}
}

Demo06.java

package reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;

public class Demo06 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
		Scanner in = new Scanner(System.in);
		String className = in.nextLine();
		Class cls = Class.forName(className);
		//找到被执行的方法信息
		Method m = cls.getDeclaredMethod("add", int.class,String.class);
		Object obj = cls.newInstance();
		//执行对象私有方法
		m.setAccessible(true);//破坏私有功能
		Object val = m.invoke(obj, 5,"Tom");
		System.out.println(val);
		
		Field f	= cls.getDeclaredField("name");
		f.setAccessible(true); // 取消属性的访问权限控制,即使private属性也可以进行访问。
		f.set(obj, "地址");
		System.out.println(obj.toString());
	}

}

运行后输出: 

Java基本原理__02_反射 (Reflect)