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实现!
思路:
- 动态加载类
- 找到全部方法信息
- 遍历全部的方法信息,找到以test为开头的方法。
- 执行无参数的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()
反射总结
- 反射是Java的一套API,提供了Java的动态执行功能
- 反射不能乱用
- 反射API执行效率低,尽量不用
-
如果必须动态功能,再使用!
- 不知道类名,方法名,属性名...
-
反射必须了解的API
- Class.forName()
- cls.newInstance()
- m.invoke()
- 反射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)
演示案例
演示工程目录结构
演示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方法:
演示通过反射获取类与对象的信息
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);
}
}
运行后输出:
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);
}
}
运行后输出:
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();
}
}
运行后输出:
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();
}
}
运行后输出:
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);
}
}
}
}
}
运行后输出:
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());
}
}
运行后输出: