# Java 反射的使用

Posted 爱码代码的喵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# Java 反射的使用相关的知识,希望对你有一定的参考价值。

Java 反射的使用

文章目录

概念

  • Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

反射机制功能

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。
  • 生成动态代理。

缺点

  • 性能问题:反射操作的效率要比正常操作效率低很多。

  • 安全限制:使用反射通常需要程序的运行没有安全方面的限制。

  • 程序健壮性:反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。

Java 反射相关 API

  • Java在将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制。

  • Class类是一个比较特殊的类,它是反射机制的基础,Class类的对象表示正在运行的Java程序中的类或接口,也就是任何一个类被加载时,即将类的.class文件(字节码文件)读入内存的同时,都自动为之创建一个java.lang.Class对象。

类 Class<T>

  • Class 类的实例表示正在运行的 Java 应用程序中的类和接口

常见方法

返回方法
<U> Class<? extends U>asSubclass(Class<U> clazz) 强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类。
Tcast(Object obj) 将一个对象强制转换成此 Class 对象所表示的类或接口
static Class<?>forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。
static Class<?>forName(String name, boolean initialize, ClassLoader loader) 使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。
<A extends Annotation>AgetAnnotation(Class<A> annotationClass)如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null
Annotation[]getAnnotations() 返回此元素上存在的所有注释。
Class<?>[]getClasses() 返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。
ClassLoadergetClassLoader() 返回该类的类加载器。
Constructor<T>getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor<?>[]getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
Annotation[]getDeclaredAnnotations() 返回直接存在于此元素上的所有注释。
Class<?>[]getDeclaredClasses() 返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
Constructor<?>[]#getDeclaredConstructors() 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
FieldgetDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field[]getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
MethodgetDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[]getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Class<?>getDeclaringClass() 如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。
T[]getEnumConstants() 如果此 Class 对象不表示枚举类型,则返回枚举类的元素或 null。
FieldgetField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
Field[]getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
Type[]getGenericInterfaces() 返回表示某些接口的 Type,这些接口由此对象所表示的类或接口直接实现。
TypegetGenericSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type
Class<?>[]getInterfaces() 确定此对象所表示的类或接口实现的接口。
MethodgetMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[]getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
StringgetName()String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
PackagegetPackage() 获取此类的包。
URLgetResource(String name) 查找带有给定名称的资源。
InputStreamgetResourceAsStream(String name) 查找具有给定名称的资源。
Class<? super T>getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
booleanisAnnotation() 如果此 Class 对象表示一个注释类型则返回 true。
booleanisAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
booleanisAnonymousClass() 当且仅当底层类是匿名类时返回 true
booleanisArray() 判定此 Class 对象是否表示一个数组类。
booleanisAssignableFrom(Class<?> cls) 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。
booleanisEnum() 当且仅当该类声明为源代码中的枚举时返回 true。
booleanisInstance(Object obj) 判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。
booleanisInterface() 判定指定的 Class 对象是否表示一个接口类型。
booleanisLocalClass() 当且仅当底层类是本地类时返回 true
booleanisMemberClass() 当且仅当底层类是成员类时返回 true
booleanisPrimitive() 判定指定的 Class 对象是否表示一个基本类型。
booleanisSynthetic() 如果此类是复合类,则返回 true,否则 false
TnewInstance()创建此 Class 对象所表示的类的一个新实例。
StringtoString() 将对象转换为字符串。

注解的使用

  • 注解就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,从而做相对应的处理。注解跟注释很像,区别是注释是给人看的而注解是给程序看的,它可以被编译器读取。

内置注解

作用在代码的注解

  • @Override : 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated: 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings : 指示编译器去忽略注解中声明的警告。

元注解

  • @Retention 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
  • @Documented : 标记这些注解是否包含在用户文档中。
  • @Target: 标记这个注解应该是哪种 Java 成员。
  • @Inherited : 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

其它注解

  • @FunctionalInterfaceJava 8 开始支持,标识一个匿名函数或函数式接口。

自定义注解的使用

  • 编写自定义注解,获取所有注解的内容

  • Table.java

/**
 * 实体类表注解
 *
 * @author LiDong
 * @version 1.0.0
 * @createTime 9/7/2022 8:39 AM
 */
// This annotation is used to Class
@Target(ElementType.TYPE)
// When  run is  effective
@Retention(RetentionPolicy.RUNTIME)
// Java document annotation
@Documented
public @interface Table 

    /**
     * 表名
     *
     * @return String
     */
    String name() default "";

    /**
     * 表名大小写
     *
     * @return StrCaseEnum
     */
    StrCaseEnum tableNameCase() default StrCaseEnum.LOWER;

    /**
     * 引擎
     *
     * @return EngineEnum
     */
    EngineEnum engine() default EngineEnum.INNODB;

    /**
     * 自增开始数值
     *
     * @return int
     */
    int autoIncrementNum() default 0;

    /**
     * 字符集
     *
     * @return CharacterSetEnum
     */
    CharacterSetEnum characterSet() default CharacterSetEnum.UTF_8;

    /**
     * 表排序类型
     *
     * @return TableOrderEnum
     */
    TableOrderEnum orderType() default TableOrderEnum.UTF8_GENERAL_CI;

    /**
     * 表备注
     *
     * @return String
     */
    String comment() default "";

  • 获取这个注解标记的类并且获取注解的内容
 /**
   * 扫描所有注解标注的表 初始化 initDataBaseContext
   *
   * @param initDataBaseContext 数据库表创建上下文
   */
private void scanAnnotationGetTableInfo(InitDataBaseContext initDataBaseContext) 
    List<Class<Table>> allClassByAnnotationList = ClassUtils.getAllClassByAnnotation(Table.class);
    List<DataBaseTable> allTableList = new ArrayList<>();
    for (Class<Table> clazz : allClassByAnnotationList) 
        DataBaseTable dataBaseTable = initDataBaseTable(clazz);
        if (Objects.isNull(dataBaseTable) || ObjectUtils.isEmpty(dataBaseTable.getTableName())) 
            continue;
        
        iniTableColumn(dataBaseTable, clazz);
        initTableIndex(dataBaseTable, clazz);
        initTablePrimaryKey(dataBaseTable, clazz);
        initTableUniqueKey(dataBaseTable, clazz);
        allTableList.add(dataBaseTable);
    
    // 构建每张表的外键 所有的表和列的信息构建完成后再构建外键
    initTableForeignKey(allTableList, allClassByAnnotationList);
    if (Objects.nonNull(initDataBaseContext)) 
        initDataBaseContext.setDataBaseTableList(allTableList);
    


/**
  * 构建表的索引信息
  *
  * @param dataBaseTable 表
  * @param clazz         clazz
  */
private void initTableIndex(DataBaseTable dataBaseTable, Class<Table> clazz) 
    TableIndex tableIndexAnnotation = clazz.getAnnotation(TableIndex.class);
    List<DataBaseColumn> dataBaseColumnList = dataBaseTable.getDataBaseColumnList();
    List<DataBaseIndex> dataBaseIndexList = new ArrayList<>();
    if (Objects.nonNull(tableIndexAnnotation)) 
        String[] columns = tableIndexAnnotation.columns();
        for (String column : columns) 
            DataBaseIndex dataBaseIndex = new DataBaseIndex();
            List<DataBaseColumn> tempColumnList = new ArrayList<>();
            if (column.contains(BaseConst.COLON)) 
                String[] nameAndColumn = column.split(BaseConst.COLON);
                String name = nameAndColumn[0];
                dataBaseIndex.setName(name);
                String columnStr = nameAndColumn[1];
                String[] columnArray = columnStr.split(BaseConst.COMMA);
                // 联合索引
                for (String columnName : columnArray) 
                    for (DataBaseColumn dataBaseColumn : dataBaseColumnList) 
                        if (columnName.equalsIgnoreCase(dataBaseColumn.getName())) 
                            tempColumnList.add(dataBaseColumn);
                            break;
                        
                    
                
            
            dataBaseIndex.setColumns(tempColumnList);
            dataBaseIndexList.add(dataBaseIndex);
        
    
    dataBaseTable.setDataBaseIndexList(dataBaseIndexList);

ClassUtils

  • 找到注解标注的类

  • 找到接口的实现类

  • ClassUtils.java

@SuppressWarnings("all")
public final class ClassUtils 

    /**
     * 类名后缀
     */
    private static final String CLASS_SUFFIX = ".class";
    private static final String L_FILE = "file";
    private static final String L_JAR = "jar";
    private static final Pattern COMPILE_PATTERN_ONE = Pattern.compile(".", Pattern.LITERAL);
    private static final Pattern COMPILE_PATTERN_TWO = Pattern.compile("/", Pattern.LITERAL);
    private static final Pattern COMPILE_PATTERN_THREE = Pattern.compile("\\\\\\\\", Pattern.LITERAL);
    private static final Pattern COMPILE_PATTERN_FOUR = Pattern.compile("/", Pattern.LITERAL);
    private static final char POINT = '.';

    private ClassUtils() 
    

    /**
     * 根据类接口查到所有的class
     *
     * @param clazz 接口文件
     * @return List
     */
    public static <T> List<Class<T>> getAllClassByInterface(Class<T> clazz) 
        List<Class<T>> list = new ArrayList<>();
        try 
            List<Class<T>> allClass = getAllClass(clazz.getPackage().getName());
            // 循环判断路径下的所有类是否实现了指定的接口 并且排除接口类自己
            for (Class<T> aClass : allClass) 
                if (clazz.isAssignableFrom(aClass)) 
                    if (!clazz.equals(aClass)) 
                        // 自身并不加进去
                        list.add(aClass);
                    
                
            
         catch (Exception e) 
            throw new RuntimeException(e);
        
        return list;
    

    /**
     * 根据注解查到所有的 class
     *
     * @param clazz 接口文件
     * @return List
     */
    public static <T> List<Class<T>> getAllClassByAnnotation(Class<T> clazz) 
        try 
            List<Class<T>> allClass = getAllClass(clazz.getPackage().getName());
            return allClass.stream().filter((a) -> a.isAnnotationPresent((Class<? extends Annotation>) clazz)).collect(Collectors.toList());
         catch (Exception e) 
            throw new RuntimeException(e);
        
    

    /**
     * 根据类接口查到所有的class(指定包名)
     *
     * @param clazz 接口文件
     * @return List
     */
    public static <T> List<Class<T>> getAllClassByAnnotation(Class<T> clazz, String packageName) 
        try 
            List<Class<T>> allClass = getAllClass(packageName);
            return allClass.stream().filter((a) -> a.isAnnotationPresent((Class<? extends Annotation>) clazz)).collect(Collectors.toList());
         catch (Exception e) 
            throw new RuntimeException(e);
        
    

    /**
     * 从一个指定路径下查找所有的类
     *
     * @param packageName 包名
     * @return List
     */
    private static <T> List<Class<T>> getAllClass(String packageName) 
        List<String> classNameList = getClassPathsByPackage(packageName);
        List<? extends Class<?>> collect = classNameList.stream().map((name) -> 
            try 
                return Class.forName(name);
             catch (Throwable e) 
                return null;
            
        ).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        return (List<Class<T>>) collect;
    


    /**
     * 获取某包下所有类
     *
     * @param packageName 包名
     * @return List
     */
    public static List<String> getClassPathsByPackage(String packageName) 
        List<String> fileNames = null;
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        String packagePath = COMPILE_PATTERN_ONE.matcher(packageName).replaceAll(Matcher.quoteReplacement("/"));
        URL url = loader.getResource(packagePath);
        if (url != null) 
            String type = url.getProtocol();
            if (L_FILE.equals(type)) 
                String fileSearchPath = url.getPath();
                fileSearchPath = fileSearchPath.substring(0, fileSearchPath.indexOf("/classes"));
                fileNames = getClassPathsByFile(fileSearchPath);
             else if (L_JAR.equals(type)) 
                try 
                    JarURLConnection jarUrlConnection = (JarURLConnection) url.openConnection();
                    JarFile jarFile = jarUrlConnection.getJarFile();
                    fileNames = getClassPathsByJar(jarFile);
                 catch (IOException e) 
                    throw new RuntimeException("Open package url failed:" + e.getMessage());
                
             else 
                throw new RuntimeException("File system not support for this type" + type);
            
        
        return fileNames以上是关于# Java 反射的使用的主要内容,如果未能解决你的问题,请参考以下文章

java反射机制

Java反射-初步入门

Java的基本使用之反射

慕课网_反射——Java高级开发必须懂的

java 反射机制

从基本抽象类调用派生类方法(反射)