反射

反射

  1. 是 JAVA API, 是Java提供的现成的类!!
    • 接受API提供的功能!
  2. 是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); 

反射用途

  1. eclipse 中解析类的结构使用了反射
  2. JUnit识别被测试方法使用了反射
    • JUnit3利用反射查找test开头的方法
    • JUnit4利用反射解析@Test查找测试方法
  3. Spring管理Bean对象, 注入Bean属性使用了反射
    • 利用反射创建Bean对象实例
    • 利用反射注入Bean的属性
  4. 注解的解析使用了反射
    • 利用反射API支持注解
  5. 强行执行私有方法(访问私有属性)

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);
            }
        }
    }
}
Demo.java

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); 
    }
}
ApplicationContext.java

作业

  1. 实现 JUnit3 原型
  2. 实现 JUnit4 原型
  3. 实现 Spring 原型
  4. 调用一个对象的私有方法

  

 

posted @ 2017-04-02 17:53  唐胜伟  阅读(325)  评论(0编辑  收藏  举报