美文网首页Java 杂谈JAVA学习记录
java学习笔记-反射(1)-Class

java学习笔记-反射(1)-Class

作者: 廖小明的赖胖子 | 来源:发表于2019-02-16 16:06 被阅读0次

    慢慢来比较快,虚心学技术

    前言:在Java中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。

    反射

    反射的概念(是什么):

    java中的反射------相当于对象的镜子,对象在照镜子时可以获取自身的信息,比如:属性,方法和构造器,实现接口等,而且这面镜子还可以直接调用执行java对象的方法

    反射的用处(有什么用):

    1.当接收到别人传过来的类时,可以进行解析

    2.当只有类名时,可以获取到对象的完整信息,从而执行相应处理

    多用于可配置和动态代理

    java反射涉及的几个类:

    java.lang. Class:反射的核心类,可以获取类的属性,方法等信息

    java.lang.ClassLoader:类的加载器,用来把类(class)装载进 JVM

    java.lang.reflect.Field :类的成员变量,用来获取和设置类的属性

    java.lang.reflect.Method:类的方法,用来获取类中方法的信息或调用类的方法

    java.lang.reflect.Modifier:类的修饰符工具,可以用来判断类中属性或者方法的修饰级别

    为方便后续学习,事先创建一个基本类

    public class BaseEntity {
    
        private String name;
    
        private Integer age;
    
        public BaseEntity(){
            super();
        }
    
        public BaseEntity(String name,Integer age){
            super();
            this.name = name;
            this.age = age;
        }
    
        private void addAge(String name,Integer age){
            if(this.name.equals(name)){
                this.age+=age;
            }else{
                System.out.println("名称不符,请核验");
            }
        }
    
    }
    
    基本类BaseEntity

    Class类

    class是一个用来描述类的类,封装了当前对象所对应的类信息,我们可以通过该类获取目标对象的属性,方法等信息,还可以根据class类创建目标对象实例

    获取Class类

    1.通过类名获取 : 类名.class

    Class clazz = BaseEntity.class;
    

    2. 通过类全类名获取:Class.forName(全类名)--这种方式一般在开发中用的比较多,多为配置项获取实例,如最简单的JDBC获取驱动方式

    Class clazz =Class.forName("com.java.entity.BaseEntity");
    

    3.通过对象实例获取

    BaseEntity baseEntity = new BaseEntity("test",50);
    
    Class clazz = baseEntity.getClass();
    

    获取父类Class

    Class clazz = Class.forName("com.java.entity.BaseEntity");
    
    Class personClazz = sonClazz.getSuperclass();
    

    创建目标对象实例

    Object newObject = clazz.newInstance();
    

    类的字段Field

    通过class类获取目标对象的字段对象,获取到字段对象后可以对具体对象的属性值进行读取和设置操作

    获取字段列表:

    //获取基本公共字段(无法获取保护字段和父类字段)
    Field[] fields = clazz.getFields();  //结果是null,因为BaseEntity没有public字段
    
     //获取所有字段(无法获取父类字段)
    fields = clazz.getDeclaredFields();  //结果是name和age的对应字段对象
    

    获取指定字段

    一般使用getDeclaredField方法获取,两种方法找不到对应字段时都会抛NoSuchFieldException异常

    方法结构如下:

    Field getField(字段名);

    Field getDeclaredField(字段名) ;

    //getField()---只能获取到公共字段
    Field field = clazz.getField("name");
    
    //getDeclaredField()----可以获取到指定字段,不管是否保护字段
    Field field = clazz.getDeclaredField("name");
    

    设置对象对应字段的值field.set(Object obj,Object value)

    方法结构如下:

    void set(实例对象,字段值)

    如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作

    //如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作
    field.setAccessible(true);
    
    //设置属性值
    field.set(baseEntity,"base");
    

    获取对象对应字段的值field.get(Object obj)

    //如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作
    field.setAccessible(true);
    
    //获取实例baseEntity对应属性的属性值(此处获取的是name的值)
    field.get(baseEntity);        //-----------得到结果为base
    

    类的方法Method

    通过class类获取目标对象的方法对象,获取到方法对象后可以对具体对象的方法进行调用和方法

    获取方法列表

    两种方法获取都不能够获取到父类的方法,只能通过获取父类Class来简介获取父类方法

    //获取公共方法列表,无法获取保护方法
    Method[] methods = clazz.getMethods();
    
    //获取全部方法列表
    Method[] methods = clazz.getDeclaredMethods();
    

    获取指定方法

    两种方法的区别和获取指定对象的区别一致,方法内的参数结构如下:

    Method getMethod("方法名",方法参数类型列表)

    Method getDeclaredMethod("方法名",方法参数类型列表)

    //获取BaseEntity的addAge方法对象
    Method method = clazz.getMethod("addAge",String.class,Integer.class);
    
    Method method = clazz.getDeclaredMethod("addAge",String.class,Integer.class);
    

    执行指定方法 invoke(Object obj, Object... args)

    反射执行指定方法是框架中用的比较普遍的用法,方法结构如下:

    Object invoke(具体实例对象, 参数值列表)

    如果是保护方法,执行方法前都需要先setAccessible(true)方可操作

    method.setAccessible(true);
    
    //执行调用baseEntity的addAge方法,传入参数name和age的值
    method.invoke(baseEntity, "base", 12);
    
    //之后再获取baseEntity的age值看一下结果:
    Field field = clazz.getDeclaredField("age");
    
    field.setAccessible(true);
    field.get(baseEntity,"age");//---------------得到结果:50+12= 62,方法执行正确
    

    类的修饰符工具Modifier

    如上所述,方法和属性字段都有保护和公共之分,那么如何判断一个方法或者属性是否公共呢?java反射提供了Modifier工具类,该工具类对类和成员访问修饰符进行解码,通过判断类和成员修饰符的整数编码,判定类和成员是否保护以及修饰符类型

    Modifier类将修饰符作为整数编码代号,具体部分常量如下:

    public static final int PUBLIC  = 0x00000001;
    public static final int PRIVATE  = 0x00000002;
    public static final int PROTECTED  = 0x00000004;
    public static final int STATIC  = 0x00000008;
    public static final int FINAL  = 0x00000010;
    public static final int SYNCHRONIZED  = 0x00000020;
    

    Modifier工具类提供如下静态方法判断类和成员的修饰符:

    Modifier.isPublic(int mod)
    Modifier.isPrivate(int mod)
    Modifier.isProtected(int mod)
    Modifier.isStatic(int mod)
    Modifier.isFinal(int mod)
    Modifier.isSynchronized(int mod)
    

    所以,我们可以通过调用Modifier工具类的方法来判断我们的方法或者属性字段是否公共字段/方法,其中,可以使用field.getModifiers(),method.getModifiers()获取字段或方法修饰符的对应编码值

    //判断field是否公共字段
    if(!Modifier.isPublic(field.getModifiers())){
         field.setAccessible(true);
    }
    
    field.set(baseEntity,50);
    

    综合使用-动态执行方法(通过传入全类名,通过传入具体对象)

    ①依旧使用BaseEntity作为基本类操作

    ②创建ClassUtil类,作为工具类

    public class ClassUtil {
    
        /**
         * 执行方法
         *
         * @param className 目标class名称
         * @param methodName 目标方法名称
         * @param args 执行目标方法所需要的参数值
         * @return java.lang.Object 执行目标方法后的返回值
         *
         * @author *** 2019/2/15
         * @version 1.0
         **/
        public static Object invoke(String className,String methodName,Object ...args){
            Class clazz = null;
            Class[] parementerType = new Class[args.length];
    
            try {
                //通过传入的class路径反射class
                clazz = Class.forName(className);
    
                for(int i=0;i<args.length;i++){
                    parementerType[i] = args[i].getClass();
                }
    
                //获取要执行的目标方法对象
                Method method = clazz.getDeclaredMethod(methodName,parementerType);
    
                System.out.println("执行方法:"+method.getName());
    
                //开启权限,执行方法
                method.setAccessible(true);
                return method.invoke(clazz.newInstance(),args);
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        /**
         * 执行方法
         *
         * @param obj 要执行方法的对象
         * @param methodName 要执行的目标方法
         * @param args 要执行目标方法需要的参数
         *
         * @return java.lang.Object 执行目标方法需要的参数值
         *
         * @author *** 2019/2/15
         * @version 1.0
         **/
        public static Object invoke(Object obj,String methodName,Object ...args){
    
            Class clazz = null;
            Class[] parementerType = new Class[args.length];
    
            try {
                //通过传入的class路径反射class
                clazz = obj.getClass();
    
                for (int i = 0; i < args.length; i++) {
                    parementerType[i] = args[i].getClass();
                }
    
                //获取要执行的目标方法对象
                Method method = clazz.getDeclaredMethod(methodName, parementerType);
    
                System.out.println("执行方法:" + method.getName());
    
                //开启权限,执行方法
                method.setAccessible(true);
                return method.invoke(obj, args);
    
            }catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    }
    

    ③综合测试

    //传入全类名调用方法
    ClassUtil.invoke("com.java.entity.BaseEntity","setAge",123);
    
    //传入具体对象调用方法
    BaseEntity baseEntity = new BaseEntity();
    ClassUtil.invoke(baseEntity,"setAge",123);
    System.out.println(baseEntity.getAge());
    

    总结

    1.java反射-对象在照镜子时可以获取自身的信息,比如:属性,方法和构造器,实现接口等,而且这面镜子还可以直接调用执行java对象的方法

    2.java反射的主要类为Class,ClassLoader,Field,Method,Modifier

    3.Class类是一个用来描述类的类,封装了当前对象所对应的类信息,我们可以通过该类获取目标对象的属性,方法等信息,还可以根据class类创建目标对象实例

    4.Field类,通过class类获取目标对象的字段对象,获取到字段对象后可以对具体对象的属性值进行读取和设置操作。对于保护的字段的读取和更改,需要先调用setAccessible(true)方法。

    5.Method类,通过class类获取目标对象的方法对象,获取到方法对象后可以对具体对象的方法进行调用和方法。对于保护的方法执行,需要先调用setAccessible(true)方法。

    6.java反射提供了一个专门封装了类和类的成员修饰符的工具类Modifier,用于判断类与类的成员的修饰符。其底层使用的是修饰符整数编码,本人认为与日志级别的设计思想一致,值得学习

    参考文档

    【1】java编程思想

    【2】https://www.cnblogs.com/tech-bird/p/3525336.html

    相关文章

      网友评论

        本文标题:java学习笔记-反射(1)-Class

        本文链接:https://www.haomeiwen.com/subject/paxieqtx.html