反射
- 是 JAVA API, 是Java提供的现成的类!!
- 接受API提供的功能!
- 是Java提供的动态执行机制, 动态加载类, 动态创建对象, 动态访问属性, 动态调用方法.
静态与动态
静态: 事先约定的规则, 执行期间按照固定规则执行.
动态: 事先没有约定, 在执行期间动态确定执行规则.
JAVA 中的静态执行: 编译已经就确定执行规则(执行次序), 在运行期间按照编译结果顺序执行.
Foo foo = new Foo(); foo.hello();
JAVA 中的动态执行: 运行期间才能确定加载哪些类, 创建哪些对象, 执行哪些方法...
动态加载类
Java 提供了动态加载类的API
Class cls=Class.forName(类名);
如:
Class cls=Class.forName("demo.Foo");
案例:
Scanner in= new Scanner(System.in); System.out.print("输入类名:"); String className=in.nextLine(); //动态加载类 Class cls=Class.forName(className); System.out.println(cls);
动态创建对象
语法
Object obj = cls.newInstance();
执行cls引用的类信息中的无参数构造器, 动态创建实例. 如果类没有无参数构造器, 则抛出异常!
提示: 反射可以调用有参数构造器.
案例:
Scanner in= new Scanner(System.in); System.out.print("输入类名:"); String className=in.nextLine(); //动态加载类 Class cls=Class.forName(className); System.out.println(cls); //动态创建对象 Object obj = cls.newInstance(); System.out.println(obj);
动态获取类的方法信息
发射API提供了动态获取类中方法信息的API:
Method[] ary= cls.getDeclaredMethods();
案例:
Scanner in= new Scanner(System.in); System.out.print("输入类名:"); String className=in.nextLine(); //动态加载类 Class cls=Class.forName(className); System.out.println(cls); //动态获取类的方法信息 //从cls代表的类信息中获取全部的方法信息 Method[] ary= cls.getDeclaredMethods(); //每天一个Method代表一个方法信息 //方法的所有要素都在这个对象中 for (Method method : ary) { System.out.println(method); }
Method 对象代表方法信息
Method提供了获取方法详细信息的方法:
//获取方法名 String name = method.getName(); //获取返回值类型 Class type = method.getReturnType();
案例:
Scanner in= new Scanner(System.in); System.out.print("输入类名:"); String className=in.nextLine(); //动态加载类 Class cls=Class.forName(className); System.out.println(cls); //动态获取类的方法信息 //从cls代表的类信息中获取全部的方法信息 Method[] ary= cls.getDeclaredMethods(); //每天一个Method代表一个方法信息 //方法的所有要素都在这个对象中 for (Method method : ary) { System.out.println(method); //获取方法的详细信息: System.out.println( method.getName()); System.out.println( method.getReturnType()); String name=method.getName(); //检查一个字符串是否以test为开头 if(name.startsWith("test")){ System.out.println("找到了"); } }
动态执行方法
invoke: 调用 method: 方法
语法:
method.invoke(执行方法的对象, 传递的参数) //正确的情况 购买饮料.invoke(小卖铺, 钱) //错误的情况 购买饮料.invoke(达内前台, 钱)
必须在对象上执行一个非静态方法, 调用方法时候必须有对象.
在invoke方法执行时候, 必须传递包含当前方法的对象!!!
案例:
业务需求: 执行某个类中全部的以test为开头的无参数无返回值的非静态方法.
//动态加载类 Scanner in= new Scanner(System.in); System.out.print("输入类名:"); String className=in.nextLine(); Class cls=Class.forName(className); //动态获取全部方法信息 Method[] ary= cls.getDeclaredMethods(); //迭代全部方法查找以test为开头的方法 Object obj = cls.newInstance();//""; for (Method method : ary) { if(method.getName() .startsWith("test")){ System.out.println(method); //动态执行方法 method.invoke(obj); } }
使用invoke
Object obj=
method.invoke(对象, 参数1, 参数2...)
invoke 方法有返回值, 返回被调用方法执行的结果, 对象后面参数是执行方法时候传递的参数.
invoke 可以调用私有方法.
案例:
//动态加载类 Scanner in = new Scanner(System.in); System.out.print("输入类名:"); String className=in.nextLine(); Class cls = Class.forName(className); //1. 找到demo方法 // Class 提供了根据方法签名找到指定 // 方法信息的API String name="demo";//方法名 //类型列表 Class[] // String.class 表示字符串的类型 // int.class 表示int类型 // 任何.class 表示任何的类型 Class[] types={String.class,int.class}; //根据方法签名在cls查找方法信息 Method method= cls.getDeclaredMethod(name, types); //找到了私有方法 System.out.println(method); //执行私有方法 //打开方法的执行权限!!!违反封装! method.setAccessible(true); Object obj = cls.newInstance(); Object value= method.invoke(obj, "Tom", 12); System.out.println(value);
反射用途
- eclipse 中解析类的结构使用了反射
- JUnit识别被测试方法使用了反射
- JUnit3利用反射查找test开头的方法
- JUnit4利用反射解析@Test查找测试方法
- Spring管理Bean对象, 注入Bean属性使用了反射
- 利用反射创建Bean对象实例
- 利用反射注入Bean的属性
- 注解的解析使用了反射
- 利用反射API支持注解
- 强行执行私有方法(访问私有属性)
JUnit4 原型:

@Retention(RetentionPolicy.RUNTIME) public @interface Demo { } public class TestCase { public void test(){ System.out.println("test"); } @Demo public void hello(){ System.out.println("Hello"); } @Demo public void helloKitty(){ System.out.println("Hello Kitty"); } } /* * 动态执行一个类中全部以@Demo注解标注的方法 */ public class Demo05 { public static void main(String[] args) throws Exception{ //动态加载类 //动态获取全部方法 //动态检查方法的注解信息 Scanner in=new Scanner(System.in); System.out.print("类名:"); String className=in.nextLine(); Class cls = Class.forName(className); Method[] ary= cls.getDeclaredMethods(); Object obj = cls.newInstance(); for (Method method : ary) { //检查一个方法的注解信息 //method.getAnnotation(被检查的注解类型) //返回注解类型对象, 如果为空表示没有注解 //不为空表示找到了被检查的注解Annotation Demo ann=method.getAnnotation( Demo.class); System.out.println(method); System.out.println(ann); if(ann!=null){ method.invoke(obj); } } } }
Spring 原型
代码:

public class ApplicationContext { //是缓存Spring容器的Bean对象 private Map<String, Object> beans= new HashMap<String, Object>(); /** * 利用配置文件初始化当前容器 * 利用xml配置文件, 初始化全部的Bean对象 */ public ApplicationContext(String xml) throws Exception{ //利用DOM4j, 读取XML文件 //解析XML文件内容, 得到Bean的类名 //和Bean的ID: // 根据类名动态加载类并且创建对象 // 将对象和对应的ID添加到map中 //从 Resource(classpath) 中读取流 InputStream in=getClass() .getClassLoader() .getResourceAsStream(xml); SAXReader reader=new SAXReader(); Document doc=reader.read(in); in.close(); //解析xml :<beans><bean><bean>.... Element root=doc.getRootElement(); //读取根元素中全部的bean子元素 List<Element> list= root.elements("bean"); for (Element e : list) { //e 就是 bean 元素 id属性和class属性 String id=e.attributeValue("id"); String className= e.attributeValue("class"); //动态加载类, 动态创建对象 Class cls=Class.forName(className); Object bean=cls.newInstance(); beans.put(id, bean); } } public Object getBean(String id){ //根据id在map查找对象, 并返回对象 return beans.get(id); } //泛型方法: 优点是可以减少一次类型转换 public<T> T getBean( String id, Class<T> cls){ return (T)beans.get(id); } } <?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="foo" class="demo.Foo"></bean> <bean id="date" class="java.util.Date"></bean> <bean id="testCase" class="demo.TestCase"></bean> </beans> public class Demo06 { public static void main(String[] args) throws Exception { ApplicationContext ctx= new ApplicationContext( "spring-context.xml"); Foo foo = (Foo)ctx.getBean("foo"); Foo f2 = ctx.getBean( "foo", Foo.class); System.out.println(foo); System.out.println(f2); } }
作业
- 实现 JUnit3 原型
- 实现 JUnit4 原型
- 实现 Spring 原型
- 调用一个对象的私有方法