Java:反射学习的整理

1.前言

个人对于反射的学习总结以及经验,在认真学习反射之前,一直不能理解为什么要用反射,也不知道反射主要运用点在哪里,但是听说反射很重要。

  • 反射是什么?
    反射的使用是一种动态编程,主要是可以在运行中创建对象,调用方法、可以获取类中的一切。
  • 反射可以干什么?
    (1)代理模式
    代理模式被用在Spring的AOP中
    代理模式在日志实现中例如log4j
    (2)获取类中的全部信息(变量、方法这些,还要就是注解,自定义注解)。
    (3)有一句话说的好,不存在完美的框架,虽然反射会破坏封装性,但是也可以让你修改原本封装好的私有变量,或者私有方法。
    (4)跳过编译过程

注:代码段都是用了junit进行单员测试写的,需要获得源码可以去我的github获取
在reflex分支
我的github地址

2反射的学习

2.1 概念

在面向对象的世界里,万事万物皆对象。(java语言中,静态的成员、普通数据类型除外)
类是不是对象呢?类是(哪个类的对象呢?)谁的对象呢?
类是对象,类是java.lang.Class类的实例对象

2.2 这个对象到底如何表示

    /**
     * java是面向对象编程的语言,所以全部都是对象
     * 类是不是对象?是谁的对象?
     * 类是对象,类是java.lang.Class类的实例对象
     *任何一个类都是Class的实例对象,这个实例对象有三种表示方式
     */
  /**
     * java是面向对象编程的语言,所以全部都是对象
     * 类是不是对象?是谁的对象?
     * 类是对象,类是java.lang.Class类的实例对象
     *任何一个类都是Class的实例对象,这个实例对象有三种表示方式
     */
    @Test
    public void test2() throws ClassNotFoundException {
        /**
         * 说明每一个类中都有一个class静态变量
         */
        Class s = Student.class;

        /**
         * 每一个对象中可以调用getClass()方法获取本对象Class
         */
        Student a =new Student();
        
        Class s1 = a.getClass();
        /**
         * 知道类的路径可以使用Class的静态方法来获取类
         */
        Class s2 = Class.forName("com.imooc.reflex.Student");
        /**
         * 三种方法获取的类的类对象都是一样的
         */
        System.out.println(s1 == s2);

        System.out.println(s2 == s);
    }

2.3 跳过编译过程

首先泛型的概念大家都知道、但是集合类中泛型在底层实现的时候存放的都是Object类型,基本所有的基本类型或者自己创建的对象都是Object的子类,所以我一个ArraysList 也可以存放String的变量,只要在编译阶段使用反射跳过泛型,不然直接调用add方法,编译会过不去,就会报错。

  /**
     * 泛型只是为了防止编译过程中传入不符合自己定义泛型的对象(输入错误)
     * 还说明编译以后的泛型是去泛型化的,泛型只在编译阶段有效,反射会绕过编译
     * 底层存储还是用Object类型存储的,下面就来证明这个。
     * 这个例子就是往ArrayList<Interge>泛型中插入字符串
     */
    @Test
    public void test3() throws Exception {
        ArrayList<Integer> arr1 = new ArrayList<Integer>();
        arr1.add(1);
        Class c = arr1.getClass();
        Method m = c.getMethod("add", Object.class);
        m.invoke(arr1,"字符串");
        for(Object i : arr1){
            System.out.println(i);
        }
    }

结果如下图:
可见ArraysList里面保存了字符串。
可见,利用反射可以跳过编译

2.5获取基本的数据类型

  /**
     * 反射还能获取类中所有属性、方法、注解、构造方法、私有属性.....
     */
    @Test
    public void test4() throws Exception {
        /**
         * 获取属性,这样只能获取public 属性
         */
        Class c = Person.class;
        Field[] f = c.getFields();
        for(Field i : f){
            System.out.println(i.getName());
        }
        /**
         * 获取本类所有私有的属性
         */
        Field[] t = c.getDeclaredFields();
        for(Field i:t){
            System.out.println(i.getName());
        }
 }

2.5获取类中的所有方法

 @Test
    public void test4() throws Exception {
   	    /**
         *获取类中的所有方法并且使用
         */
        Person t1 = (Person) c.newInstance();
        Method[] m  =c.getMethods();
        for(Method i : m){
            if(i.getName() .equals("setEmail") ){
                i.invoke(t1,"1160827860@qq.com");
            }else if(i.getName().equals("setName")){
                i.invoke(t1,"lzy");
            }else if(i.getName().equals("setAge")){
                i.invoke(t1,21);
            }
        }
        Method p = c.getMethod("print");
        p.invoke(t1);
      }

在这里插入图片描述

2.6获取构造方法,并且使用

    @Test
    public void test4() throws Exception {
  		 /**
         * 获取构造方法,并且使用
         */
        Constructor construct = c.getConstructor(String.class,int.class,String.class);
        t1 = (Person)construct.newInstance("sz",25,"a1160827860@163.com");
        p.invoke(t1);
    }

2.7其他需要注意的问题

2.7.1编译、运行中加载类

编译时刻加载类是静态加载类、运行时刻加载类是动态加载类

2.7.2其他的关键字

void关键字都存在类类型

2.7.3为什么要使用反射

就像底下这个代码一样:
比如我们完成了Peson类,但是noExist类不存在,如果采用new 创建一个对象的方式,就无法运行,如果使用反射,就可以运行。
1.在项目中如果要先对已经完成的代码,进行测试就可以采用动态加载的方式。
2.例如企业中更新软件,不是删除原来的代码,而是采用动态加载的方式

    /**
     * 静态加载在编译的时候加载,例如本例下面,如果没有Student
     * 就会报错,但是我有Person类,我输入Person也不能过编译
     * 动态加载就可以避免这种情况(也就是使用反射的方法)
     * 静态加载引发的问题
     */
    @Test
    public void test(){


        String chose = "Student";
        if(chose.equals("Person")){
            Person p = new Person();
            p.toString();
        }else if(chose.equals("Student")){
            Student t = new Student();
            t.toString();
        }
//        else{
//            noExist no = new noExist();
//        }
    }

    /**
     * 使用动态加载,来解决上面这个问题
     * 虽然没有noExist类但是可以允许使用其他分支
     * 而第一个在编译阶段都过不去
     */
    @Test
    public void test1() throws Exception {
        String chose ="Person";
        if(chose.equals("Person")){
            Person p = new Person();
            System.out.println(p.toString());
        }else  if("noExist".equals(chose)){
            Class c = Class.forName("com.imooc.reflex.noExist");
            Object e = c.newInstance();
            e.toString();
        }
    }

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值