• Java 高级开发必修知识---反射


     
    程序猿们经常说的一句话:反射反射。。。程序员的快乐
     
     
    反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
    对于任意一个对象,都能够调用它的任意一个方法和属性;
    这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
     

    文章开始之前 提一下:

    java反射操作其实就是主要围绕Class,Field,Methon,Constructor等几个类来操作其中的方法

    万物皆对象。但基本数据类型 和 使用static修饰的成员不是对象

    但基本数据类型的封装类是对象

    Class类的使用

    1) 在面向对象的世界里,万事万物皆对象

    A. Java语言中,普通数据类型,静态成员不是对象,其他皆对象

    B. 每一个类也是对象

    C. 类是java.lang.Class类的实例对象

    There is a class named Class

    对象的表示:

    普通类对象表示:

    Foo foo = new Foo();

    Class类实例对象表示:

    //Foo也是一个实例对象,是Class类的实例对象

    任何一个类都是Class的实例对象,这个实例对象有三种表达方式

    1. 任何一个类都有一个隐含的静态成员变量class

    Class c1 = Foo.class;

    2. 已经指定该类的对象通过getClass方法

    Foo foo = new Foo();

    Class c2 = foo.getClass();

    官网:c1/c2 表示了Foo类的类类型(class type

    一个类,本身就是一个对象,它是Class类的对象

    万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型

    3. ForName(“类的全称”);

    Class c3 = null;

    c3=Class.forName(“包名+.某类名”);

    不管是那种表达方式,都是Class类的对象,都是Foo类的类类型。所以:

    C1=c2=c3

    完全可以通过类的类类型创建该类的对象(创建Foo的实例对象,但要做强制类型转换,向下转型

    前提要求:需要有无参数的构造方法

    Comont comont = new Comont();
    Class c1 = comont.getClass();
    Class c2 = Comont.class;
    try {
    Comont c = (Comont) c2.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    System.out.println(c1 == c2);
    comont.start();

    动态加载类

    Class.forName(“类的全称”);

    1. 不仅表示了类的类类型,还代表动态加载类

    2. 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类

    (1) New 对象是静态加载类,在变异时刻就需要加载所有的可能使用到的类

    (2) 动态加载类,在运行时刻加载

    编译不会报错

    运行时刻报错。找不到该类

    Class c = Class.forName(arg[0]);

    共同实现接口类 cc = (共同实现接口类) c.newInstance();

    3. 使用记事本开发可明显区分

    Java 类 要运行类:动态加载类,不需要重新编译测试类,直接运行即可

    功能性的类:尽量使用动态加载

    基本数据类型也有类类型

    Class c1 = int.class;
    Class c2 = String.class;//String类的字节码

    数据类型和包装类的类类型不同

    Void也是类

    Class c3 = void.class;

    基本数据类型

    Void关键字

    都存在类类型

    方法也是对象,方法是Method的对象

    反射:某类的字节码表示

    获取方法信息

    1. c.getName()

    (1) 基本数据类型返回类型名

    (2) 类返回包名+类名类的名称

    2. c1.getSimpleName()

    (1) 返回不带包名的类的名称

    栗子:通过反射可以获取到任何类的信息

    需求:打印类的信息,获取类的 成员函数

    package cn.pro;
    
    import java.lang.reflect.Method;
    
    /**
     * 
     * @author: 房上的猫
     * 
     * @time: 下午5:34:45
     * 
     * @博客地址: https://www.cnblogs.com/lsy131479/
     *
     */
    
    public class mymain {
        public static void printClassMessage(Object obj) {
            // 要获取类的信息 首先要获取类的类类型
            // 形参obj 该对象所属类的信息
            Class c = obj.getClass();// 传递的是哪个子类的对象 c就是该        子类的类类型
            // getClass()方法:native修饰代表 存在不是java 代码 ,调度        外用 ///该方法java内调用底层c语言实现
    
            // 获取累的名称
            System.out.println("类的名称是:" + c.getName());
            // Method类是方法对象
            // 一个成员方法就是一个Method
            // getMethods()方法获取的是所有的public修饰的函数,包括父类        继承而来的
            Method[] ms = c.getMethods();
            // c.getDeclaredMethods()获取的是所有该类自己声明的方法,不        问访问权限.所有。所有。所有
            String[] name = new String[ms.length];
            for (int i = 0; i < ms.length; i++) {
                // 得到方法的返回值类型--得到的是返回值类型的类类型
                Class returnType = ms[i].getReturnType();
                // 得到返回值名字
                String returnName = returnType.getName();
                // 得到方法的名称
                name[i] = ms[i].getName();
                // 获取参数列表类型--得到的是参数列表的类型的类类型
                Class[] parameterTypes = ms[i].getParameterTypes();
    Int params=parameterTypes.length;            
    String[] paramNames = new String[params];
                for (int j = 0; j < params; j++) {
                    // 得到参数列表名
                    paramNames[j] = ms[j].getName();
                }
            }
        }
    }

    通过反射可以获取任何类的类信息

    比较牛逼

    获取类的成员变量构造函数信息

    成员变量也是对象

    Java.lang.reflect.Field的对象

    Field类封装了关于成员变量的操作

    栗子:通过反射可以获取到任何类的信息

    需求:打印类的信息,获取类的成员变量

    package cn.reflect;
    
    import java.lang.reflect.Field;
    
    /**
     * 
     * @author: 房上的猫
     * 
     * @time: 下午3:49:32
     * 
     * @博客地址: https://www.cnblogs.com/lsy131479/
     *
     */
    
    public class myField {
        public static void printFieldMessage(Object obj) {
            Class c = obj.getClass();
            // getFidlds() 方法获取的是类的所有的public的成员变量信息
            Field[] fs = c.getFields();
    
            // getDeclaredFields() 获取的是该类自己声明的成员信息 不问访问权限.所有。所有。所有
            // Field[] fs = c.getDeclaredFields();
    
            for (Field field : fs) {
                // 得到成员变量的类型的类类型
                Class fieldType = field.getType();
                // 得到成员变量类型的名字
                String typeName = fieldType.getName();
                // 得到成员变量的名字
                String fieldName = field.getName();
                //
    
            }
    
        }
    }

    构造函数也是对象

    Java.lang.Constructor的对象 其中封装了构造函数的信息

    栗子:通过反射可以获取到任何类的信息

    需求:打印类的信息,获取类的构造函数信息

    package cn.reflect;
    
    import java.lang.reflect.Constructor;
    
    /**
     * 
     * @author: 房上的猫
     * 
     * @time: 下午3:49:32
     * 
     * @博客地址: https://www.cnblogs.com/lsy131479/
     *
     */
    
    public class myCon {
        public static void printConMessage(Object obj) {
            Class c = obj.getClass();
            // getConstructors() 获得所有的共有的构造方法
            Constructor[] cs = c.getConstructors();
            // getDeclaredConstructors() 得到所有的构造方法(必须是自己声明的) 不问访问权限.所有。所有。所有
            // Constructor[] cs = c.getDeclaredConstructors();
            for (Constructor constructor : cs) {
                // 获取构造函数名
                String name = constructor.getName();
                // 获取构造函数的参数列表------>得到的是参数列表的类类型
                Class[] parameterTypes = constructor.getParameterTypes();
                for (Class class1 : parameterTypes) {
                    // 获取构造函数参数列表名
                    String name2 = class1.getName();
                }
            }
        }
    }

    其他

    Class类还可以获取类的其他信息,这里将不再详细讲解

    想要了解的可以创建Class类的实例对象

    Class c = obj.getClass();

    c.get...

    自行查看并尝试

    或阅读帮助文档,查看Class类的所有API

    记住一点:在任何情况下想要获取一个类的信息,首先要得到这个类的类类型

    得到类的类类型,得到这个类的类信息就轻而易举得到了

    方法的反射

    1. 如何获取某个方法?

    方法的名称和方法的参数列表才能唯一决定某个方法

    2. 方法反射的操作

    Method.invoke(对象,参数列表)

    栗子:

    package cn.reflect;
    
    import java.lang.reflect.Method;
    
    /**
     * 
     * @author: 房上的猫
     * 
     * @time: 下午4:25:25
     * 
     * @博客地址: https://www.cnblogs.com/lsy131479/
     *
     */
    
    public class MethodDemo1 {
        public static void main(String[] args) throws Exception {
    
            // 要获取print(int,int)方法
            A a = new A();
            /*
             * 1.要获取一个方法就是要获取一个类的信息,获取类的信息首先要获取类的类类型
             */
    
            Class c = a.getClass();
            /*
             * 2.获取方法 名称和参数列表
             * 
             * getMethod(name, parameterTypes)获取的是public的方法
             * 
             * c.getDeclaredMethod(name, parameterTypes)获取的是所有自己声明的方法 不问访问权限
             * 
             * 
             * 参数解析: 1.name:方法名 2.parameterTypes:参数列表类型(0或多个)
             */
            Method m = c.getMethod("print", new Class[] { int.class, int.class });
    
            /*
             * 方法的反射操作
             * 
             * a.print(10,20);方法的反射操作是用m对象来进行方法调用 和a.print调用的效果
             * 
             * 方法如果没有返回值返回null 有返回值返回具体的返回值 返回object类型,需要做强制类型转换
             */
    
            Object o = m.invoke(a, new Object[] { 10, 20 });
            // 就等于调用了print(int,int)方法
    
            /*
             * 如果方法无参数列表则:
             * 
             * c.getMethod("print"); m.invoke(a);
             */
    
        }
    }
    
    class A {
        public void print(int a, int b) {
            System.out.println(a + b);
        }
    
        public void print(String a, String b) {
            // a的大写形式 and b的小写形式
            System.out.println(a.toUpperCase() + "," + b.toLowerCase());
        }
    }

    升华操作:

    通过反射了解集合泛型的本质

    通过ClassMethod来认识泛型的本质

    相信读者们看到这里心中一定会有这样两个疑问:

    什么是泛型?

    泛型什么时候有效?

     那我们探讨一下这两个话题:

     

    package cn.reflect;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    
    /**
     * 
     * @author: 房上的猫
     * 
     * @time: 下午6:31:38
     * 
     * @博客地址: https://www.cnblogs.com/lsy131479/
     *
     */
    
    public class MethodDemo2 {
        public static void main(String[] args) {
            // 普通集合
            ArrayList list = new ArrayList<>();
    
            // 泛型集合 集合内只能存放'<>'尖括号内类型的值
            ArrayList<String> list1 = new ArrayList<>();
            // 集合的泛型防止错误输入
    
            // 利用反射了解集合泛型
            Class c1 = list.getClass();
            Class c2 = list1.getClass();
            list1.add("1");
            System.out.println(c1 == c2);
            /*
             * 反射的操作都是编译之后的操作(运行时)
             * 
             * c1==c2结果返回true 表示两个集合类运行时都是同一类类型
             * 
             * 说明编译之后集合的泛型是去泛型化的(编译完之后就没有泛型存在了)
             * 
             * java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
             * 
             * 验证:可以通过方法的反射来操作,绕过编译
             */
            try {
                Method m = c2.getMethod("add", Object.class);
                m.invoke(list1, 100);// 大胆猜想:绕过编译操作就绕过了泛型
                // 集合大小返回2 .说明:绕过编译阶段后可以向泛型集合添加任何类型的值
                System.out.println(list1.size());
                // 尝试查看泛型集合内的值 发现值已经添加进去
                System.out.println(list1);
    
                /*
                 * 在这里如果使用for增强遍历将会抛异常
                 * 
                 * 因为后面添加的100是int类型的值
                 * 
                 * 而集合泛型是String类型
                 * 
                 * 使用for增强遍历内部其实做出的操作是:集合的每个值都使用一个集合泛型类型的数据类型来接受遍历
                 * 而int类型使用String类型来接受,众所周知将会报错(这里的泛型类型是String)
                 * 
                 * 会有类型转换错误
                 */
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    结论:

    反射(ClassMethodField,,Constructor  ... )的操作都是绕过编译,都是在运行时刻来执行的

    凡事都是一把双刃剑,都有利弊两面,不可能有事物十全十美,反射是好,但也不要盲目使用

    反射优缺点

    优点:
    (1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。 
    (2)与Java动态编译相结合,可以实现无比强大的功能 
    缺点: 
    (1)使用反射的性能较低 
    (2)使用反射相对来说不安全 
    (3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性 

    ok...到此为止!!!后续如果有新的想法将会继续总结。。敬请期待 

    (C) 房上的猫 。 保留所有权利。
     https://www.cnblogs.com/lsy131479/

    如需转载,请注明出处!!!

  • 相关阅读:
    C#操作Excel开发报表系列整理
    API安全的最佳实践
    低代码介绍与行业动态
    农产品供应链基于区块链架构设计
    AIOps案例介绍
    智慧交通与车联网介绍
    Spring Boot服务中使用Kong Ingress控制器
    辅导你的软件团队获得成功
    项目失败原因
    微前端架构介绍
  • 原文地址:https://www.cnblogs.com/lsy131479/p/8798217.html
Copyright © 2020-2023  润新知