JAVA高级篇之Java Reflection详解

目录

概念

Java programming language and JVM modeling in core reflection

场景

Class加载

ClassLoad加载

Class加载

通过实例

Class详情

字段

方法

注解

类注解

字段注解

方法注解

构造方法

实例化

访问私有变量和私有方法

泛型

返回泛型

 方法参数

数组



概念

Java programming language and JVM modeling in core reflection

The components of core reflection, which include types in this package as well as ClassPackage, and Module, fundamentally present a JVM model of the entities in question rather than a Java programming language model. A Java compiler, such as javac, translates Java source code into executable output that can be run on a JVM, primarily class files. Compilers for source languages other than Java can and do target the JVM as well.

The translation process, including from Java language sources, to executable output for the JVM is not a one-to-one mapping. Structures present in the source language may have no representation in the output and structures not present in the source language may be present in the output. The latter are called synthetic structures. Synthetic structures can include methodsfieldsparametersclasses and interfaces. One particular kind of synthetic method is a bridge method. It is possible a synthetic structure may not be marked as such. In particular, not all class file versions support marking a parameter as synthetic. A source language compiler generally has multiple ways to translate a source program into a class file representation. The translation may also depend on the version of the class file format being targeted as different class file versions have different capabilities and features. In some cases the modifiers present in the class file representation may differ from the modifiers on the originating element in the source language, including final on a parameter and protectedprivate, and static on classes and interfaces.

Besides differences in structural representation between the source language and the JVM representation, core reflection also exposes runtime specific information. For example, the class loaders and protection domains of a Class are runtime concepts without a direct analogue in source code.

见原文:java.lang.reflect (Java SE 18 & JDK 18)

场景

        Bean转为一个Map集合,我们在研发底层代码时,有前段传一个参数给我们数据接口通过一个传输对象接受后,在处理业务中有的第三方接口或者业务需要把一个Bean转换为通用的Map结构。

        Spring IOC实例化,底层通过反射来实例化Bean,实例完成后就放入到ApplicationConxt上下文中。

        AOP反射的实现,在AOP中,就是通过动态代理来实现我们在处理前 后 中的不同阶段来实现公共逻辑抽象与集中处理逻辑。

Class加载

ClassLoad加载

Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.String");

Class加载

Class<?> aClass = Class.forName("java.lang.String");

通过实例

LoadClass loadClass = new LoadClass();
Class<? extends LoadClass> aClass1 = loadClass.getClass();

Class详情

        在这个之前我们定义一个用户Bean来实现获取Class信息,我们定义一用户信息Bean包含:用户ID、用户名称、用户身份证号、定义如下:

package com.jdk.reflect;

import java.io.Serializable;
/**
 * Copyright (C), 2000-2022
 * FileName: UserDto.java
 * Author: yangcaho.cool@gamil.com
 * Date: 2022/4/29 10:49
 * Description: 用户
*/
public class UserDto implements Serializable {
    /**
     * 用户ID
     */
    private Long userId;
    /**
     * 用户名称
     */
    private String userName;
    /**
     * 身份证件号
     */
    private String cardNo;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getCardNo() {
        return cardNo;
    }

    public void setCardNo(String cardNo) {
        this.cardNo = cardNo;
    }
}

字符串链接

  /**
    * @Author: yangchao.cool
    * @Date: 2022/4/29 11:11
     * Description: 字符链接
    */
    private String join(Stream<String> param) {
        StringBuffer result = new StringBuffer();
        param.forEach((key)->{
            if (result.length() > 0) {
                result.append(",");
            }
            result.append(key);
        });
        return result.toString();
    }

获取包信息代码如下:

   Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        Package aPackage = aClass.getPackage();
        System.out.println(String.format("包名称:%s",aPackage.getName()));
        System.out.println(String.format("Class注解:%s", join(Arrays.stream(aPackage.getAnnotations()).map(Annotation::toString))));
        System.out.println(String.format("Class所有的注解:%s", join(Arrays.stream(aPackage.getDeclaredAnnotations()).map(Annotation::toString))));
        System.out.println(String.format("Class版本:%s", aPackage.getSpecificationVersion()));

运行结果如下:

包名称:com.jdk.reflect
Class注解:
Class所有的注解:
Class版本:null

         我们发现我包注解获取为空,其实我们要获取包的逐渐信息我们必须在新创建一个包信息文Java类,我们在对应的包下面创建一个JAVA文件为 package-info.java,这个文件名不修改,不然认识不到这个包类文件。

@Deprecated
package com.jdk.reflect;

运行结果如下:

包名称:com.jdk.reflect
Class注解:@java.lang.Deprecated()
Class所有的注解:@java.lang.Deprecated()
Class版本规范:null

字段

把UserInfoDto字段信息修改为 public修饰,修改后的字段信息如下:

import java.io.Serializable;
/**
 * Copyright (C), 2000-2022
 * FileName: UserDto.java
 * Author: yangcaho.cool@gamil.com
 * Date: 2022/4/29 10:49
 * Description: 用户
*/

public class UserDto implements Serializable {
    /**
     * 用户ID
     */
    public Long userId;
    /**
     * 用户名称
     */
    private String userName;
    /**
     * 身份证件号
     */
    private String cardNo;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getCardNo() {
        return cardNo;
    }

    public void setCardNo(String cardNo) {
        this.cardNo = cardNo;
    }
}

     通过Class种getFields方法来获取字段信息,这个种获取的只有非private修饰的字段才能获取得到,因此结果只能获取到用户ID(userId)代码如下:

 public static void main(String[] args) throws Throwable {
        Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        Field[] fields = aClass.getFields();
//        Field[] declaredFields = aClass.getDeclaredFields();
        print(fields);

    }
    /**
    * @Author: yangchao.cool
    * @Date: 2022/4/29 11:11
     * Description: 字符链接
    */
    private static void print(Field[] param) {
        for (Field f : param) {
            System.out.println(String.format("字段名称:%s,数据类型:%s", f.getName(), f.getType()));
        }
    }

 结果:

字段名称:userId,数据类型:class java.lang.Long

      要想获取到private修饰符字段信息就要使用Class中的getDeclaredFields()方法,返回所有的字段信息 ,代码如下:


    public static void main(String[] args) throws Throwable {
        Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
//        Field[] fields = aClass.getFields();
        Field[] declaredFields = aClass.getDeclaredFields();
        print(declaredFields);

    }
    /**
    * @Author: yangchao.cool
    * @Date: 2022/4/29 11:11
     * Description: 字符链接
    */
    private static void print(Field[] param) {
        for (Field f : param) {
            System.out.println(String.format("字段名称:%s,数据类型:%s", f.getName(), f.getType()));
        }
    }

 运行结果如下:

字段名称:userId,数据类型:class java.lang.Long
字段名称:userName,数据类型:class java.lang.String
字段名称:cardNo,数据类型:class java.lang.String

方法

通过getDeclaredMethods获取所有方法

   Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
     Method[] declaredMethods = aClass.getDeclaredMethods();
      for (Method method :  declaredMethods) {
          System.out.println(String.format("名称:%s, 参数:%s, 返回数据类型:%s", method.getName(), join(Arrays.stream(method.getParameters()).map(Parameter::getName)), method.getReturnType()));
      }

 输出结果如下:

名称:getCardNo, 参数:, 返回数据类型:class java.lang.String
名称:getUserId, 参数:, 返回数据类型:class java.lang.Long
名称:getUserName, 参数:, 返回数据类型:class java.lang.String
名称:setUserId, 参数:arg0, 返回数据类型:void
名称:setCardNo, 参数:arg0, 返回数据类型:void
名称:setUserName, 参数:arg0, 返回数据类型:void
名称:getPrivate, 参数:, 返回数据类型:class java.lang.String

注解

类注解

首先我们在UserDto类新增@Deprecated新增注解,代码如下:

        Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        Annotation[] annotations = aClass.getAnnotations();
        System.out.println("获取到的注解为:" + join(Arrays.stream(annotations).map(Annotation::toString)));

运行结果如下:

获取到的注解为:@java.lang.Deprecated()

字段注解

        在UserDto中的userId新增注解@XmlAttribute @XmlElement注解,通过代码获取字段注解代码如下。

  Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        print(aClass.getDeclaredFields());
  /**
    * @Author: yangchao.cool
    * @Date: 2022/4/29 11:11
     * Description: 字符链接
    */
    private static void print(Field[] param) {
        for (Field f : param) {
            System.out.println(String.format("字段名称:%s,注解:%s", f.getName(), join(Arrays.stream(f.getAnnotations()).map(Annotation::toString))));
        }
    }

运行结果如下:

字段名称:userId,注解:@javax.xml.bind.annotation.XmlAttribute(name=##default, namespace=##default, required=false),@javax.xml.bind.annotation.XmlElement(name=##default, namespace=##default, type=class javax.xml.bind.annotation.XmlElement$DEFAULT, required=false, defaultValue= , nillable=false)
字段名称:userName,注解:
字段名称:cardNo,注解:

方法注解

我们在UserDto中的getPrivate方法增加@Deprecated注解,获取方法的代码如下:

       Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
       print(aClass.getDeclaredFields());
       Method[] declaredMethods = aClass.getDeclaredMethods();
       print(declaredMethods);
/**
     * @Author: yangchao.cool
     * @Date: 2022/4/29 11:11
     * Description: 字符链接
     */
    private static void print(Method[] param) {
        for (Method f : param) {
            System.out.println(String.format("字段名称:%s,注解:%s", f.getName(), join(Arrays.stream(f.getAnnotations()).map(Annotation::toString))));
        }
    }

构造方法

       首先我们在UserDto中新增构造方法,代码如下:


     public UserDto(Long userId) {
        this.userId = userId;
    }

    public UserDto(Long userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

        构造方法可以分为有参数与无参构造方法,可以通过getConstructors返回构造方法数组,通过getConstructor获取指定参数的构造函数,如果没有则抛出异常。获取构造方法的代码如下:

 Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        // 获取所有的构造方法
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor c:constructors) {
            System.out.println(String.format("名称:%s,参数类型:%s", c.getName(), c.getParameterTypes()));
        }

 运行结果如下:

名称:com.jdk.reflect.UserDto,参数类型:[Ljava.lang.Class;@5fe5c6f
名称:com.jdk.reflect.UserDto,参数类型:[Ljava.lang.Class;@6979e8cb

实例化

        通过Constructor中newInstance方法创建实例,代码如下:

        Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        // 获取所有的构造方法
        Constructor<?> constructor = aClass.getConstructor(Long.class, String.class);
        //通过有参实例化数据
        UserDto instance = (UserDto)constructor.newInstance(1000L, "杨好");
        //打印实例化后的数据
        System.out.println(String.format("用户名称:%s, 用户ID:%s", instance.getUserName(), instance.getUserId()));

运行结果如下:

用户名称:杨好, 用户ID:1000

访问私有变量和私有方法

        通过Class中getDeclaredMethod获取Method,在通过Method中invoke调用方法,代码如下:

 Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        // 获取所有的构造方法
        Constructor<?> constructor = aClass.getConstructor(Long.class, String.class);
        //通过有参实例化数据
        UserDto instance = (UserDto)constructor.newInstance(1000L, "杨好");
        //获取私有方法
        Method getPrivate = aClass.getDeclaredMethod("getPrivate");
        //调用对应的方法
        Object result = getPrivate.invoke(instance);
        //打印结果
        System.out.println(String.format("私有方法获取结果:%s", result));

 运行代码异常如下:

Exception in thread "main" java.lang.IllegalAccessException: Class com.jdk.reflect.ClassStructure can not access a member of class com.jdk.reflect.UserDto with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Method.invoke(Method.java:490)
	at com.jdk.reflect.ClassStructure.main(ClassStructure.java:28)

        以上异常是应为我们访问的方法为Private方法,当我们在访问Private方法时我们要通过 Method中的isAccessible判断是否有权限访问,如果没有则直接通过setAccessible设置为True,代码如下:

  Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        // 获取所有的构造方法
        Constructor<?> constructor = aClass.getConstructor(Long.class, String.class);
        //通过有参实例化数据
        UserDto instance = (UserDto)constructor.newInstance(1000L, "杨好");
        //获取私有方法
        Method getPrivate = aClass.getDeclaredMethod("getPrivate");
        //判断是否可以访问,如果没有权限访问 则授权访问
        if (!getPrivate.isAccessible() ) {
            getPrivate.setAccessible(Boolean.TRUE);
        }
        //调用对应的方法
        Object result = getPrivate.invoke(instance);
        //打印结果
        System.out.println(String.format("私有方法获取结果:%s", result));

 运行结果如下:

私有方法获取结果:杨好

泛型

返回泛型

        首先我们在UserDto中新增一个泛型的数据域orgIds数据类型为List<String>,同时新增一个Get方法getOrgIds并返回类型为List<String>。获取返回泛型数据类型代码如下:

       Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        //获取方法
        Method getOrgIds = aClass.getMethod("getOrgIds");
        //获取返回的类型
        Type returnType = getOrgIds.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
            ParameterizedType type = (ParameterizedType) returnType;
            Type[] types = type.getActualTypeArguments();
            for(Type typeArgument : types){
                Class typeArgClass = (Class) typeArgument;
                System.out.println(String.format("泛型数据类型为:%s" ,typeArgClass));
            }
        }

 运行结果下:

泛型数据类型为:class java.lang.String

 方法参数

        首先我们在UserDto中新增一个泛型的数据域orgIds数据类型为List<String>,同时新增一个Get方法setOrgIds参数数据类型为List<String>。获取方法泛型数据类型代码如下:

  Class<?> aClass = Class.forName("com.jdk.reflect.UserDto");
        //获取方法
        Method getOrgIds = aClass.getDeclaredMethod("setOrgIds", List.class);
        //获取返回的类型
        Type[] parameterTypes = getOrgIds.getGenericParameterTypes();
        for (Type returnType: parameterTypes) {
            if (returnType instanceof ParameterizedType) {
                ParameterizedType type = (ParameterizedType) returnType;
                Type[] types = type.getActualTypeArguments();
                for (Type typeArgument : types) {
                    Class typeArgClass = (Class) typeArgument;
                    System.out.println(String.format("参数泛型类型为:%s", typeArgClass));
                }
            }
        }

 运行结果返回String,说明List中的泛型指定值为String。

数组

通过JAVA反射提供的数组,java.lang.reflect.Array实现数组创建于值设置,代码如下:

     //通过 java.lang.reflect.Array 实现数组创建
        Integer[] o = (Integer[]) Array.newInstance(Integer.class, 10);
        //通过下标设置值
        for (int index = 0; index < o.length ; index ++ ) {
//            o[index] = Float.valueOf((new Random().nextFloat()*200)).intValue();
            int value = Float.valueOf((new Random().nextFloat() * 200)).intValue();
            Array.set(o, index , value);
        }
        for (int index =0; index < o.length ; index ++) {
            System.out.println(String.format("下标:%s,值:%s", index +1 , o[index]));
        }

运行结果如下

下标:1,值:79
下标:2,值:68
下标:3,值:33
下标:4,值:45
下标:5,值:51
下标:6,值:21
下标:7,值:94
下标:8,值:109
下标:9,值:79
下标:10,值:50

我在获取数组Class方式代码如下: 

//获取Class
        System.out.println(String.format("数组Class为:%s", o.getClass()));
        //获取数组类型
        String[] strings = new String[3];
        Class stringArrayClass = strings.getClass();
        Class stringArrayComponentType = stringArrayClass.getComponentType();
        System.out.println(String.format("数组类型:%s",stringArrayComponentType));

 运行代码如下:

数组Class为:class [Ljava.lang.Integer;
数组类型:class java.lang.String

 [Ljava.lang.Integer这个是数组的Class,[L是数据Class开始固定标识,后面是数据类型。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值