• Java反射


    Class对象的获取

    三种方式获取Class对象

    1. 调用对象的getClass方法
    2. 调用类名.class
    3. Class.forName("类的全名"), 这种方式是去JVM中查找,可能会抛出ClassNotFoundException
    public class SubPerson extends Person {
    
        public Long subId;
        private String subName;
        private SubPerson() {
        }
        public SubPerson(Long subId, String subName) {
            this.subId = subId;
            this.subName = subName;
        }
    
    }
    
    public class Person {
    
        public Long id;
        private String name;
        public Person() {
        }
    
        class Card{
            private String id;
            public Card() {
            }
            public String getId() {
                return id;
            }
            public void setId(String id) {
                this.id = id;
            }
        }
    }
    
    
    //1
    Class personClass1 = new Person().getClass();
     System.out.println(personClass1);
     
     //2
     Class personClass2 = Person.class;
     System.out.println(personClass2);
     
     //3
     try {
         Class personClass3 = Class.forName("com.banary.reflect.Person");
         System.out.println(personClass3);
     }catch (ClassNotFoundException e){
         e.printStackTrace();
     }
    

    获取类名

    public static void ClassMethodTest(){
        //获取全类名
        System.out.println(Person.class.getName());
        System.out.println(int.class.getName());
        System.out.println(int[][][].class.getName());
        System.out.println(Person.Card.class.getName());
        System.out.println(new Runnable() {
            @Override
            public void run() {
            }
        }.getClass().getName());
    
        Runnable runnable = ()-> System.out.println("test");
        System.out.println(runnable.getClass().getName());
    
        System.out.println("------------分割线------------");
    
        //获取类名
        System.out.println(Person.class.getSimpleName());
        System.out.println(int.class.getSimpleName());
        System.out.println(int[][][].class.getSimpleName());
        System.out.println(Person.Card.class.getSimpleName());
        System.out.println(new Runnable() {
            @Override
            public void run() {
            }
        }.getClass().getSimpleName());
        System.out.println(runnable.getClass().getSimpleName());
    
        System.out.println("------------分割线------------");
    
        /**
         * 获取官方类名
         * 1. getCanonicalName() 返回的也是全限定类名,但是对于内部类,不用 $ 开头,而用 .。
         * 2. getCanonicalName() 对于数组类型的 Class,同 simplename 一样直接在后面添加 [] 。
         * 3. getCanonicalName() 不同于 simplename 的地方是,不存在 canonicalName 的时候返回 null 而不是空字符串。
         * 4. 局部类和匿名内部类不存在 canonicalName。
         */
        System.out.println(Person.class.getCanonicalName());
        System.out.println(int.class.getCanonicalName());
        System.out.println(int[][][].class.getCanonicalName());
        System.out.println(Person.Card.class.getCanonicalName());
        System.out.println(new Runnable() {
            @Override
            public void run() {
            }
        }.getClass().getCanonicalName());
        System.out.println(runnable.getClass().getCanonicalName());
    }
    

    获取修饰符

    1. java的修饰符
    • 用来限制作用域,如 public、protected、priviate
    • 用来提示子类复写,abstract
    • 用来标记为静态类 static
    • 注解
    • 定义常量,final
    1. 获取类的修饰符
    // 十进制
    System.out.println(Modifier.class.getModifiers());
    // 十六进制
    System.out.println(Integer.toHexString(Modifier.class.getModifiers()));
    // 对应的真实修饰符
    System.out.println(java.lang.reflect.Modifier.toString(Modifier.class.getModifiers()));
    

    java中修饰符的表示方式

    通过上述的方法,可见java中是采用二进制位来表示修饰符,下列为java中修饰符实现

    public class Modifier {
    
        // 2^0
        public static final int PUBLIC           = 0x00000001;
        // 2^1
        public static final int PRIVATE          = 0x00000002;
        // 2^2
        public static final int PROTECTED        = 0x00000004;
        public static final int STATIC           = 0x00000008;
        public static final int FINAL            = 0x00000010;
        public static final int SYNCHRONIZED     = 0x00000020;
        public static final int VOLATILE         = 0x00000040;
        public static final int TRANSIENT        = 0x00000080;
        public static final int NATIVE           = 0x00000100;
        public static final int INTERFACE        = 0x00000200;
        public static final int ABSTRACT         = 0x00000400;
        public static final int STRICT           = 0x00000800;
        
        public static String toString(int mod) {
            StringBuilder sb = new StringBuilder();
            int len;
    
            if ((mod & PUBLIC) != 0)        sb.append("public ");
            if ((mod & PROTECTED) != 0)     sb.append("protected ");
            if ((mod & PRIVATE) != 0)       sb.append("private ");
    
            /* Canonical order */
            if ((mod & ABSTRACT) != 0)      sb.append("abstract ");
            if ((mod & STATIC) != 0)        sb.append("static ");
            if ((mod & FINAL) != 0)         sb.append("final ");
            if ((mod & TRANSIENT) != 0)     sb.append("transient ");
            if ((mod & VOLATILE) != 0)      sb.append("volatile ");
            if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");
            if ((mod & NATIVE) != 0)        sb.append("native ");
            if ((mod & STRICT) != 0)        sb.append("strictfp ");
            if ((mod & INTERFACE) != 0)     sb.append("interface ");
    
            if ((len = sb.length()) > 0)    /* trim trailing space */
                return sb.toString().substring(0, len-1);
            return "";
        }
    
    }
    

    获取类的属性

    类的属性包括Field、Method、Constructor

    1. Field
    • 获取Field
    Class personClass = SubPerson.class;
    //获取自身的所有的 public 属性,包括从父类继承下来的, 可能会抛出运行时异常SecurityException
    Field[] fields = personClass.getFields();
    for (Field field : fields) {
        System.out.println(field.getName());
    }
    //根据field名获取, 可能会抛出NoSuchFieldException(检查型), SecurityException
    try {
        Field field1 = personClass.getField("subId");
        System.out.println(field1.getName());
    } catch (NoSuchFieldException e){
        e.printStackTrace();
    }
    
    System.out.println("------------分割线------------");
    
    //获取所有的属性,但不包括从父类继承下来的属性
    Field[] declaredFields = personClass.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        System.out.println(declaredField.getName());
    }
    
    System.out.println("------------分割线------------");
    
    try{
        Field field2 = personClass.getDeclaredField("subName");
        System.out.println(field2.getName());
    } catch (NoSuchFieldException e){
        e.printStackTrace();
    }
    
    • 获取Field类型
    # 能够获取到泛型类型
    public Type getGenericType() {}
    
    public Class<?> getType() {}
    
    • 获取修饰符
    # 同class和Method的修饰符一样
    public int getModifiers() {}
    
    • 获取值和设置值
    #设置值
    public Object get(Object obj);
    public int getInt(Object obj);
    public long getLong(Object obj) throws IllegalArgumentException, IllegalAccessException;
    public float getFloat(Object obj) throws IllegalArgumentException, IllegalAccessException;
    public short getShort(Object obj) throws IllegalArgumentException, IllegalAccessException;
    public double getDouble(Object obj) throws IllegalArgumentException, IllegalAccessException;
    public char getChar(Object obj) throws IllegalArgumentException, IllegalAccessException;
    public byte getByte(Object obj) throws IllegalArgumentException, IllegalAccessException;
    public boolean getBoolean(Object obj) throws IllegalArgumentException, IllegalAccessException
    public void set(Object obj, Object value);
    # 获取值
    public void getInt(Object obj,int value);
    public void getLong(Object obj,long value) throws IllegalArgumentException, IllegalAccessException;
    public void getFloat(Object obj,float value) throws IllegalArgumentException, IllegalAccessException;
    public void getShort(Object obj,short value) throws IllegalArgumentException, IllegalAccessException;
    public void getDouble(Object obj,double value) throws IllegalArgumentException, IllegalAccessException;
    public void getChar(Object obj,char value) throws IllegalArgumentException, IllegalAccessException;
    public void getByte(Object obj,byte b) throws IllegalArgumentException, IllegalAccessException;
    public void getBoolean(Object obj,boolean b) throws IllegalArgumentException, IllegalAccessException
    

    Class 本身不对成员进行储存,它只提供检索,所以需要用 Field、Method、Constructor 对象来承载这些成员,所以,针对成员的操作时,一般需要为成员指定类的实例引用。

    1. Method
      同获取Field类似
    • 方法名
    getName()
    
    • 方法参数
    #获取所有的参数
    public Parameter[] getParameters() {}
    // 获取所有的参数类型
    public Class<?>[] getParameterTypes() {}
    // 获取所有的参数类型,包括泛型
    public Type[] getGenericParameterTypes() {}
    
    • 方法返回值
    // 获取返回值类型
    public Class<?> getReturnType() {}
    // 获取返回值类型包括泛型
    public Type getGenericReturnType() {}
    
    • 方法的修饰符
    public int getModifiers() {}
    
    • 方法可能会抛出的异常
    public Class<?>[] getExceptionTypes() {}
    # 包括泛型
    public Type[] getGenericExceptionTypes() {}
    
    • 执行方法
    #invoke() 方法中第一个参数 Object 实质上是 Method 所依附的 Class 对应的类的实例,如果这个方法是一个静态方法,那么 ojb 为 null,后面的可变参数 Object 对应的自然就是参数
    #invoke() 返回的对象是 Object,所以实际上执行的时候要进行强制转换。
    #在对 Method 调用 invoke() 的时候,如果方法本身会抛出异常,那么这个异常就会经过包装,由 Method 统一抛出 InvocationTargetException。而通过 InvocationTargetException.getCause() 可以获取真正的异常。
    public Object invoke(Object obj, Object... args) {}
    
    1. Constructor
    • 获取构造器方法
      同获取Field类似,但是由于构造器不能继承,所以getConstructor()方法获取不到父类的构造器方法
    public static void getConstructor(){
            Class subPersonClass = SubPerson.class;
    
            //获取所有的public构造器,由于父类的构造器不能继承,所以获取不到
            Constructor[] constructors = subPersonClass.getConstructors();
            for (Constructor c : constructors) {
                System.out.println(c.toString());
            }
    
            System.out.println("------------分割线------------");
    
            //获取所有的构造器,包括私有和公开的
            Constructor[] declaredConstructors = subPersonClass.getDeclaredConstructors();
            for (Constructor declaredConstructor :declaredConstructors) {
                System.out.println(declaredConstructor.toString());
            }
        }
    
    • 创建对象
      在 Java 反射机制中有两种方法可以用来创建类的对象实例:Class.newInstance() 和 Constructor.newInstance(),原因:
    # Class.newInstance() 只能调用无参的构造方法,而 Constructor.newInstance() 则可以调用任意的构造方法。
    # Class.newInstance() 通过构造方法直接抛出异常,而 Constructor.newInstance() 会把抛出来的异常包装到 InvocationTargetException 里面去,这个和 Method 行为一致。
    # Class.newInstance() 要求构造方法能够被访问,而 Constructor.newInstance() 却能够访问 private 修饰的构造器。
    

    反射中的数组

    • 数组本质上是一个 Class,而在 Class 中存在一个方法isArray()用来识别它是否为一个数组。
    getName();
    #获取数组的里面的元素的类型,比如 int[] 数组的 componentType 自然就是 int
    getComponentType();
    
    • 反射中动态创建数组
      反射创建数组是通过 Array.newInstance() 这个方法
    public static Object newInstance(Class<?> componentType, int... dimensions)
            throws IllegalArgumentException, NegativeArraySizeException {}
    

    e.g.

    # 创建一个 int[2][3] 的数组
    Array.newInstance(int.class,2,3);
    
    • Array 的读取与赋值
    public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException;
    
    public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException;
    
    public static void set(Object array, int index, Object value) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
    
    public static void setBoolean(Object array, int index, boolean z) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
    
    public static Object get(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
    
    public static short getShort(Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
    

    反射中的枚举

    • 同数组一样,枚举本质上也是一个 Class 而已,但反射中还是把它单独提出来了
    // 用来判定 Class 对象是不是枚举类型
    Class.isEnum()
    
    // 获取所有的枚举常量
    Class.getEnumConstants()
    
    // 判断一个 Field 是不是枚举常量
    Field.isEnumConstant()
    
    • 枚举的获取与设定
      因为等同于 Class,所以枚举的获取与设定就可以通过 Field 中的 get() 和 set() 方法。需要注意的是,如果要获取枚举里面的 Field、Method、Constructor 可以调用 Class 的通用 API,值得注意的是,枚举类的Constructor可以获取,但是不能实例化
    public static void reflectEnum() throws Exception{
            Class colorClass = Color.class;
            if(colorClass.isEnum()){
                System.out.println(String.format("class Color %s is Enum", colorClass.getName()));
                Field[] fields = colorClass.getDeclaredFields();
                for (Field field:fields) {
                    if(field.isEnumConstant()){
                        System.out.println(String.format("%s is EnumConstant", field.getName()));
                    }else{
                        System.out.println(String.format("%s is not EnumConstant", field.getName()));
                    }
                }
            }
    
            System.out.println("_____________________");
    
            Constructor[] constructors = colorClass.getDeclaredConstructors();
            for(Constructor constructor : constructors){
                System.out.println(constructor.getName());
                constructor.setAccessible(true);
                Color color = (Color) constructor.newInstance("123123");
                System.out.println(color.getKey());
            }
            Color color = (Color) colorClass.newInstance();
            System.out.println(color.getKey());
        }
    
  • 相关阅读:
    【LeetCode】297. 二叉树的序列化与反序列化
    【剑指Offer】面试题37. 序列化二叉树
    【剑指Offer】面试题59
    【剑指Offer】面试题57
    趣图:向客户介绍的产品VS实际开发的产品
    Spring AOP的实现机制
    Mysql高性能优化规范建议
    JavaScript八张思维导图
    深入 Nginx:我们是如何为性能和规模做设计的
    教你用认知和人性来做最棒的程序员
  • 原文地址:https://www.cnblogs.com/warking/p/7999882.html
Copyright © 2020-2023  润新知