zoukankan      html  css  js  c++  java
  • 反射

    反射

    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. 调用一个对象的私有方法

      

  • 相关阅读:
    Luogu-P1404 平均数
    树的直径与重心
    卡常技巧
    背包问题总结
    Codevs-1521 华丽的吊灯
    区间dp与环形dp
    Luogu-P1308 神经网络
    拓扑排序
    01分数规划
    Python学习 4day__基础知识
  • 原文地址:https://www.cnblogs.com/tangshengwei/p/6659459.html
Copyright © 2011-2022 走看看