zoukankan      html  css  js  c++  java
  • java反射机制,以及对反射机制的了解

    反射是什么?反射有什么用?我相信大家在开始学的时候都会有疑惑,直到如今我学的还不够深入只能简单的说说反射的作用,理论的我也听得很迷糊,接下来我就以几个例子来

    写写反射的用处; 494696003群,有很多一起学习的人~不妨可以来耍耍;

    * A反射概述
        * JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
        * 对于任意一个对象,都能够调用它的任意一个方法和属性;
        * 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
        * 要想解剖一个类,必须先要获取到该类的字节码文件对象。
        * 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。

    * B:三种方式
        * a:Object类的getClass()方法,判断两个对象是否是同一个字节码文件
        * b:静态属性class,锁对象
        * c:Class类中静态方法forName(),读取配置文件
    * C:案例演示
        * 获取class文件对象的三种方式

    案例1:

    package com.reflect2;

    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;

    /*
     * 利用反射来实现榨水果汁的例子
     * */
    public class Demo_reflect1 {
            public static void main(String[] args) {
                /*Juicer j=new Juicer();*/
                try {
                    BufferedReader br=new BufferedReader(new FileReader("reflectFruit.txt"));
                    Class clazz=Class.forName(br.readLine());
                    Fruit f=(Fruit) clazz.newInstance();
                    f.squeeze();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                
            }

    }
    interface Fruit{
        public void squeeze();
    }
    class Apple implements Fruit{
        public void squeeze(){
            System.out.println("榨出一杯苹果汁");
        }
    }
    class Orange implements Fruit{

        @Override
        public void squeeze() {
            System.out.println("榨出一杯橘子汁");
        }
    }
    /*class Juicer{
        public void  juicer(Fruit f){
            f.squeeze();
        }
    }
    */

    这个是通过修改配置文件文件里面的内容来达到榨汁的一个功能,这也是反射的一个用处;

    这样我们只需要修改配置文件里面的水果类名就可以达到我们想要的水果汁。

    以上的clazz.clazz.newInstance()方法其实是采用了无参数的构造方法;那我们会想如果是一个类的有参构造我们怎么获取呢?

    案例2:

    这是person类中的构造和toString()方法方便我们等下好打印;

    package com.reflect2;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;

    import com.reflect.Entity.Person;

    public class Demo3_Constructor {
            public static void main(String[] args) {
                try {
                    Class clazz=Class.forName("com.reflect.Entity.Person");  //这个地方我们把person类的地址放进去,得到一个该类的字节码文件;
                    Constructor con=clazz.getConstructor(String.class,String.class,int.class);  //这个地方的参数不能赋予实际的值,因为还在反射阶段;
                    Person p=(Person) con.newInstance("张三","男",20);  //我们在这里可以看到我们给Person赋予的实际的值
                    System.out.println(p);
                    
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (SecurityException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    }

    在这我们可以看得到我们之前赋予的值了;

    接下来在之前的基础上我们进一步的去了解这个java反射的强大之处——————暴力反射!!!

    俗话说在反射面前一切都是赤裸裸的(当然做不法之事的时候就别反射了!命要紧啊~哈哈)看代码!

    案例3:

    之前我们都是获取一个类的构造方法;接下来我们用java的反射来获取一个类的字段;

    采用的方法是getField();

    之前可能有朋友会问一个问题,我们怎么知道他有些什么字段呢?你在参数里面填了“name”,其实在这里我是为了方便用这个方法

    其实在API里面还有一个方法就是getFields()方法 大家都知道在ENGLISH里面+s代表负数 在这里也不例外(其实在这里我想说一个想法,

    因为这些语言编程都是国外的人发明的,如果是中国人发明的

    我们学起来起码轻松了很多,有很多喜欢编程的人但是英语不行学起来很吃力也减少了很多出现天才的机会,

    在我这里天才不是一定要各个方面都要很强,在某一个领域异于常人你就是天才!咳咳 扯远了回到正题)

    按照这个方法我们打印P;你心里想到的肯定是这个这个人的名字被我们修改了~那么你来看看运行结果吧;

    你肯定在想,我擦 怎么回事, 那么我们来看看异常是啥:说没有找到这个叫name的字段,你肯定很好奇我们不是找到了吗?肯定有name这个属性啊

    其实在这里我们还没有进入正题,我们是暴力反射!暴力反射不暴力点怎么行呢?看代码你就知道了:

    之前我们把person属性是私有了所以得不到~找不到也很正常,你肯定在想那我们怎么办呢?接下来就是java强大之处表现出来了!赤裸在它面前

    public static void main(String[] args) {
                try {
                    Class clazz=Class.forName("com.reflect.Entity.Person");
                    Constructor con=clazz.getConstructor(String.class,String.class,int.class);
                    Person p=(Person) con.newInstance("张三","男",20);
                    /*Field f=clazz.getField("name");
                    f.set(p, "李四");*/
                   Field f= clazz.getDeclaredField("name");  //这个地方就是我们暴力反射的关键declared! (公然的) 意思是获取到所有的不管是私有还是共有的 全部能得到
                    System.out.println(p);

    你看这段代码,在这里你肯定会想,这下得到了吧!那我这里要告诉你一个很有趣的事情:当你对一个人有非法的思想的时候,

    她肯定是不愿意的那么你就用强硬的得到,但是为什么在这里还是打印不出来呢?因为

    还差一步没有走 就是你要对她用强的那么你就要褪去她的防备~(你懂得~),所以还差一步就是:

    Class clazz=Class.forName("com.reflect.Entity.Person");
                    Constructor con=clazz.getConstructor(String.class,String.class,int.class);
                    Person p=(Person) con.newInstance("张三","男",20);
                    /*Field f=clazz.getField("name");
                    f.set(p, "李四");*/
                    Field f=clazz.getDeclaredField("name");
                    f.setAccessible(true);    //这个地方就是褪去她的防备 嘿嘿 里面传一个true,意思是设置为通畅的

           f.set(p, "李四"); 
                    System.out.println(p);

    这样她就是属于你的了~~嘿嘿

    接下来我们再一次来了解反射其他的能力,获取一个类中的方法,还是那个样子无论私有还是公有我们都有办法获取私有就加Declared,我这里用获得公有的做例子了

    案例4:

    这个是在person里面添加的一个无参的方法,等下我们在来一个有参的,方法是找女朋友哈(有没有跟我一样还是单手狗的~我这么英俊居然也单身了~唉 加油吧骚年英俊还要有钱啊~)

    public static void main(String[] args) {
                try {
                    Class clazz=Class.forName("com.reflect.Entity.Person");
                    Constructor con=clazz.getConstructor(String.class,String.class,int.class);
                    Person p=(Person) con.newInstance("张三","男",20);
                    /*Field f=clazz.getField("name");
                    f.set(p, "李四");*/
                    /*Field f=clazz.getDeclaredField("name");
                    f.setAccessible(true);
                    f.set(p, "李四");*/
                    Method m=clazz.getMethod("findGirlFriend");  //这个地方就是我们获取方法的代码 括号里面写方法名字,因为是无参的,参数可以不写
                    
                    m.invoke(p);                  //这个地方是 让这个方法去执行,括号里面是填执行的对象~

    我们来看看结果:

    执行了person里面的方法;接下来我们来一个有参的方法:

    public void findGirlFriend(int num){
            System.out.println("今年找了"+num+"个女朋友");
        }

    在person里面重写一个findGirlFriend(int num)方法 带上参数;

    我们来看看代码:

    在上面的括号里面填上参数的字节码文件,因为还是在反射这个环节所以不能填实参,在下面invoke()方法里面填上对象 和 实际参数

    接下来我们看下打印结果:

    额~可能是我比较饥渴吧,我一下找了10个女朋友~ 哈哈 起是作为程序员我们是最厉害的就是没有的东西我们可以new 没有女朋友new一个出来 没有钱new一堆出来

    看到这里我估计你们对反射有一定的了解了~那我们继续来个更有意思的,因为之前我们一般的认为在ArrayList<Integer> list=new ArrayList<Integer>(); 加了泛型

    的集合里面添加只能是Integer类型的你加其他的会编译就报错,那么在这我们利用反射绕开这个编译的过程直接添加进去(原理就是,泛型只是在编译器起作用,一旦运行时期,泛型会被扔掉~

    那么我们通过反射获取到list的字节码就已经是运行时期了,在这个时候我们再操作,这也验证了反射可以在运行时期对类进行得到、修改等):

    案例5:

    ArrayList<Integer> list=new ArrayList<Integer>();
            list.add(111);
            list.add(222);
            try {
                Class clazz=Class.forName("java.util.ArrayList");  //获取字节码文件 ArrayList存在于util包中
                Method m=clazz.getMethod("add", Object.class);  //我们需要获取到ArrayList的add()方法,add()方法的参数是object在API中写的是E 这的E也就是Object;
                m.invoke(list, "abc");                //在这我们队list进行添加

    这样我们就实现了往泛型为Intege的集合中添加abc~ 这个过程也叫泛型擦除也叫泛型反射~(是不是感觉之前的安全泛型也不过如此是吧~这就是反射的魅力)

    好了,到了这 也快接近尾声了,但是也是一个难点~记得我刚开始的时候是有懵逼过的~那就是java反射的动态代理(代理的意思就是本来该自己做的事情让别人来帮你做)

    例如:春节买票,你叫黄牛给你买 这就是代理:

    案例6:我们先创建一个User借口 写2个抽象方法 添加和删除  因为动态代理是针对借口而言的

    1)package com.proxy.reflect;

    public interface User {
        public void add();
        
        public void delete();
        
    }

    2)写一个User的实现类~ 重写添加删除方法

    package com.proxy.reflect;

    public class UserImp implements User{

        @Override
        public void add() {
            System.out.println("增添");
        }

        @Override
        public void delete() {
            System.out.println("删除");
        }

    }

    3):这就是重点了   创建一个类去实现InvocationHandler 重写它里面的实现方法;

    package com.proxy.reflect;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;

    public class MyInvocationHandler implements InvocationHandler{
        private Object ob;
        
        public  MyInvocationHandler(Object ob){          //这个地方创建一个构造方法,方便将待会儿的需要代理的对象传进来
            this.ob=ob;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("权限校验");            //这个地方就是代理需要做的事情
            method.invoke(ob, args);              //这里就是实现该方法参数为需要实现的代理对象和对象需要的参数(执行被代理ob对象的方法)
            System.out.println("日志记录");  
            return null;
        }
    }

    4):最后这里就是测试类

    package com.proxy.reflect;

    import java.lang.reflect.Proxy;

    public class Test {
        public static void main(String[] args) {
            UserImp ui=new UserImp();          //先创建User实现类对象
            ui.add();
            ui.delete();
            System.out.println("-------------------");  //代理之前
            
            MyInvocationHandler m=new MyInvocationHandler(ui);  //我们需要创建一个代理实现的实现类(这里可以理解为处理者的实现类) 将我们需要代理的对象ui传进去

       //这是一个静态的方法由proxy类名点直接调用,参数(类加载器(我们通过反射获取);需要一个开对象需要实现的借口(通过反射获取);最后需要将我们的处理者放进来m)
            User u= (User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);  


            u.add();
            u.delete();
        }
    }
    那么我们来看看 代理之后的样子:

    这样我们就完成了一个动态代理,下次谁需要代理直接可以用我就可以了~

    到这里反射机制的概述和实现差不多了~反射机制的基础认识不难,但是需要调整好思维,我只是个才学不久的,说的不好勿喷呀~这是我的处女作~哈哈 希望对你们又所帮助~

  • 相关阅读:
    CISP/CISA 每日一题 七
    CISP/CISA 每日一题 六
    CISP/CISA 每日一题 五
    C++编码优化之减少冗余拷贝或赋值
    CISP/CISA 每日一题 四
    CISP/CISA 每日一题 三
    CISP/CISA 每日一题 二
    CISP/CISA 每日一题
    C#与C++ DLL的交互
    数据同步工具otter(二)
  • 原文地址:https://www.cnblogs.com/yeszero/p/6013824.html
Copyright © 2011-2022 走看看