反射
首次接触反射之后对反射的理解是:反射是框架底层的实现,通过动态加载类,然后动态创建对象,然后调用方法去实现。(…脑子空白)
Java提供的API,其功能是动态的加载类,动态创建对象,动态调用对象,动态访问属相等
系统的学习反射
1.学习反射时搞清楚如下概念
(1.)静态
-静态:约定执行流程,在运行期间按照固定规则进行
-案例:
Foo foo = new Foo();
String s = foo.hello();
System.out.println(s);
//说明:编译以后,按照时间按照固定规则及执行
(2.)动态
-动态:在运行期间动态加载类,动态创建对象,动态执行方法。
-案列:执行一个类中的全部以test为开头的方法
-说明:在程序运行之前不知道类名,方法名。
2.反射API(API参照Jdk7文档)
(1.)功能
- 动态加载类
- 语法: Class cls = Class.forName(类名)(图片未上传)
- 动态创建对象
- 前提:程序执行之前之前不知道类名,在运行期间动态获取类名,动态加载类并且创建对象。
- 语法:Object obj = cls.newInstance();
- 注意:动态调用对象的无参构造器,被调用的类必须有无参构造器,如果没有将抛出异常(如:扩展流java.io.BufferedInputStream,高级流等;将抛出java.lang.InstantiationException和java.lang.NoSuchMethodException异常),反射API也提供了调用有参构造器额方法,但是不常用。
动态执行方法
找到方法信息
- 语法1. Method[] methods = cls.getDeclaredMethods();
说明:返回一个类的全部信息,只包含当前类的方法信息,返回值得数组,每个元素代表一个方法详细信息。
语法2. Method[] methods = cls.getMothods();
说明:找到全部共有方法信息想(含继承方法)
语法3. Method method = cls.getDeclaredMethod(方法名,参数类型列表);
- 说明:找到一个特定的方法信息,里面包含参数(根据方法的签名找到一个方法),参数类型列表是Class数组对象,如:Class[] types = {int.class,String.clss};
以上方法详细信息: Method对象包含全部的方法信息。
- 执行方法
- 语法:Object obj = method.invoke(对象,传递的参数…);
- 说明: invoke调用,执行一个方法。
- 注意:对象和method必须是相关的,对象的类型上一定存在指定的方法,如果没有就会抛出异常,参数必须匹配,如果不匹配也会出现异常。
- 例子:
/** * 执行某个类中全部以age为开头的方法 * 都是非静态方法 * 方法都是无参数无返回值的方法 * 类有无参数构造器 * @author soft01 */ - public static void main(String[] args)throws Exception { Scanner in = new Scanner(System.in); System.out.println("请输入类名:"); String className = in.nextLine(); //动态加载类 Class cls = Class.forName(className); //动态查找方法 Method[] methods = cls.getDeclaredMethods(); //遍历每个方法,查找方法名是以age为开头的 Object obj = cls.newInstance(); for(Method method:methods){ String name = method.getName(); //获取方法的参数类型列表 Class[] types = method.getParameterTypes();//获取参数类型列表 System.out.println(name+":"+Arrays.toString(types)); //检查参数类型列表长度 if(types.length!=0){ continue; } if(name.startsWith("age")){ System.out.println(name+":"); //string API //找到了test开头的方法 method.invoke(obj);//传递的参数... } } }
动态访问属性
- 语法1. Field[] fields = cls.getDeclaredFields();
说明:显示一个类的所有属性。
语法2. Field field = cls.getDeclaredField(属性名(name));
- 例子:
package cn.tedu.demo;
import java.lang.reflect.Field;
import java.util.Scanner;
/**
* 动态读写属性
* @author soft01
*
*/
public class Demo6 {
public static void main(String[] args)throws Exception {
Scanner in = new Scanner(System.in);
System.out.println("输入类名: ");
String className = in.nextLine();
//动态加载类
Class cls = Class.forName(className);
//动态获取属性信息
System.out.println("属性名陈: ");
String name = in.nextLine();
//查找属性 : 在方法区中的类信息里查找属性信息
Field field = cls.getDeclaredField(name);
//读取属性
// obj 参数是包含属性值的对象
Object obj = cls.newInstance();
field.setAccessible(true);//私有属性,或者方法(method)就可以访问喽 打破了封装
//field.set(obj, "meimei");
field.set(obj, 100);
Object val = field.get(obj);
System.out.println(val);
}
}
// 其中:field.setAccessible(true);用于打开不可见属性的访问权限,可以打破封装访问不可以见属性。
何时使用反射
当设计一个操作,在操作时候不知道类名,方法名,属性;反之,尽量不要使用反射,反射性能“稍慢”。
实现Junit4原型
- 业务需求:动态执行一个类中全部使用@Test标记的方法
- 分析:不知道类名,不知道方法名,必须使用反射。
- 注意: 创建的注解@annotation类时加上传播机制注解(@Retention(RetentionPolicy.SOURCE))
- 例子:
import java.lang.reflect.Method;
import java.util.Scanner;
public static void main(String[] args) throws Exception {
Scannner scan = new Scanner(System.in);
System.out.println("请输入类名");
String className = scan.nextLine();
Class cls = Class.forName(className);
Object obj = cls.newInstance();
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
/*
* 查找注解
* 查找方法是否有@Test注解,有则返回注解类型对象
* 如果没有则返回null
*/
Test annotation = method.getAnnotation(Test.class);
//注意Test注解接口 一定要加上传播机制注解
System.out.println(annotation);
//如果是私有方法,需加上如下代码
//method.setAccessible(true);
if(annotation != null){
//* 根据业务需求如果是要做测试就执行此方法。
//* 如果是注入 就添加属性 设置值。
method.invoke(obj);//完成了动态执行@Test方法
}
}
}
//注解是如果工作的,原理是什么:在 运 行 期 间利用反射API解析并且处理注解。
图略
模仿Spring 读取配置配置文件
模仿Spring 读取配置配置文件
//需要引用dom4j的jar包
public class ApplicationContext{
private Map<String,Object> beans;
public ApplicationContext(String xml){
beans = new HashMap<String,Object>();
//高级流依赖低级流
SaxReader reader = new SaxReader();
//读取报下的xml文件 也可以是照片等
InputStream in = this.getClass().getLoader().getResourceAsStream(xml);
Document doc = reader.read(in);
//获取根节点<beans> 子元素<bean id="" class="">
Element root = doc.getRootElement();
//获取全部子元素bean
List<Element> list = root.ellments();
//遍历每个bean,为每个bean创建一个对象
for(Element bean:list){
//获取bean的 id class属性
String id= bean.attributeValue("id");
String className = bean.attributeValue("class");
//利用反射API创建对象
Class cls = Class.forName(className);
Object obj = cls.newInstance();
//缓存到map对象中
beans.put(id,Obj);
}
}
public getBean(String id){
//根据key的值找到对应的value 通过map对象
return beans.getBean(id);
}
}