Java反射

反射(Reflect)

反射之中包含了一个「反」字,所以了解反射我们先从「正」开始。

一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。

Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)

Java 是一门面向对象的语言。在面向对象的世界里,万事万物皆对象,既然万事万物皆对象,那么我们的类是不是对象呢?我们写的每一个类都可以看成一个对象,是 java.lang.Class 类的对象。

Class类

Class是一个类,封装了当前对象所对应的类的信息。

一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类。

Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等

对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

Class对象只能由系统建立,一个类(而不是一个对象)在 JVM 中只会有一个Class实例。

获取Class对象的三种方式:
1.通过类名获取 类名.class
2.通过对象获取 对象名.getClass()
3.通过全类名获取 Class.forName(全类名)

Class类的常用方法:
在这里插入图片描述

getFields()与getDeclaredFields()方法

getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的声明的字段。
同样类似的还有getConstructors()和getDeclaredConstructors()、getMethods()和getDeclaredMethods(),这两者分别表示获取某个类的方法、构造函数。

要想获取本类和父类里的所有字段,仅靠getFields()和getDeclaredFields()两个方法是实现不了的,可采用递归解决:

Class tempClass = SubObject.class;
while(tempClass != null) {
  Field[] fields = tempClass.getDeclaredFields();
  tempClass = tempClass.getSuperclass(); 
}

如果想屏蔽Object类的影响,可在while循环处加个判断条件:

Class tempClass = SubObject.class;
while(tempClass != null && !tempClass.getName().equals("java.lang.Object")) {
  Field[] fields = tempClass.getDeclaredFields();
  tempClass = tempClass.getSuperclass(); 
}

Type

Type及相关类所在包位置:java.lang.reflect

关于Type的官方文档注释:

/**
 * Type is the common superinterface for all types in the Java
 * programming language. These include raw types, parameterized types,
 * array types, type variables and primitive types.
 *
 * @since 1.5
 */

public interface Type {
    /**
     * Returns a string describing this type, including information
     * about any type parameters.
     *
     * @implSpec The default implementation calls {@code toString}.
     *
     * @return a string describing this type
     * @since 1.8
     */
    default String getTypeName() {
        return toString();
    }
}

Type是Java语言中所有类型的公共父接口,这些类型包括原始类型,参数化类型,数组类型,类型变量,基本类型。
所有已知的子接口:GenericArrayType, ParameterizedType, TypeVariable, WildcardType
所有已知的实现类:Class

原始类型(raw types)
对应Class类型。
不仅仅包含我们平常所指的类,还包括枚举、数组、注解等;

一个原始类型就是一个没有任何类型参数的泛型类或泛型接口的名字。例如,List<E>是一个泛型接口(generic type),List<String> 是一个参数化类型,而List就是一个原生类型。List<?>是一种特殊的参数化类型,被称为通配符类型(wildcard type)。

参数化类型(parameterized types)
即ParameterizedType类型。
ParameterizedType represents a parameterized type such as Collection<String>. 将泛型类型(如:Collection<E>List<E> )进行参数化后就是参数化类型(如:Collection<String>List<String> )。

数组类型(array types)
即GenericArrayType泛型数组类型。
并不是String[] 、byte[]等数组类型,而是带有泛型的数组,如:T[]。
泛型数组类型是一个数组,且数组的组成元素是 ParameterizedType 或 TypeVariable 类型。
如:

    private T[] value;
    private List<String>[] list;
    private List<T>[] list2;

都是泛型数组类型。

类型变量(type variables)
即TypeVariable类型。
如:

public interface List<E> extends Collection<E> {

中的E就是类型变量。

基本类型(primitive types)
对应Class类型。java的八大基本类型及其包装类型。

Type体系继承关系

在这里插入图片描述

ParameterizedType(参数化类型)

ParameterizedType是Type的子接口,表示一个有参数的类型,例如Collection<String>

下面我们来看看它的三个重要的方法:

  • getRawType(): Type
    该方法的作用是返回当前的ParameterizedType类型的原始类型。如List<String>,返回的是List,即返回当前参数化类型的原始类型。

  • getOwnerType(): Type
    获得这个类型的所有者的类型。这主要是对嵌套定义的内部类而言的,例如于对java.util.Map.Entry<K,V>来说,调用getOwnerType方法返回的就是interface java.util.Map。如果当前类不是内部类,而是一个顶层类,那么getOwnerType方法将返回null。

  • getActualTypeArguments(): Type[]
    该方法返回参数化类型<>中的实际类型参数, 如 Map<String,Person> map 这个 ParameterizedType 返回的是 String 类,Person 类的全限定类名的 Type[]。注意: 无论<>中有几层<>嵌套,这个方法仅仅脱去最外层的<>,之后剩下的内容就作为这个方法的返回值,所以其返回值类型是不确定的。如:

List<ArrayList> a1;//返回ArrayList,Class类型
List<ArrayList<String>> a2;//返回ArrayList<String>,ParameterizedType类型
List<T> a3;//返回T,TypeVariable类型
List<? extends Number> a4; //返回? extends Number,WildcardType类型
List<ArrayList<String>[]> a5;//返回ArrayList<String>[],GenericArrayType 类型

下面让我们用一段例子来看一下具体的用法:

/**
 * ParameterizedType represents a parameterized type such as Collection<String>.
 */
public class ParameterizedTypeTest<T> {

    //是ParameterizedType,即参数化类型的变量
    private Collection<T> collection;
    private HashMap<String, Object> map;
    private HashMap<String, List<String>> map2;
    private Map.Entry<String, Object> entry;
    private HashSet<String> set;
    private Class<?> clz;

    //测试getActualTypeArguments()获取类型参数
    private List<ArrayList> a1;//返回ArrayList,Class类型
    private List<ArrayList<String>> a2;//返回ArrayList<String>,ParameterizedType类型
    private List<T> a3;//返回T,TypeVariable类型
    private List<? extends Number> a4; //返回? extends Number,WildcardType类型
    private List<ArrayList<String>[]> a5;//返回ArrayList<String>[],GenericArrayType 类型


    //不是ParameterizedType,是普通类型的变量
    private Integer i;
    private String str;

    private static void printParameterizedType() {
        Field[] fields = ParameterizedTypeTest.class.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            Type type = f.getGenericType();

            //打印是否是ParameterizedType类型
            System.out.println("FieldName: " + f.getName() + " , is instanceof ParameterizedType : " +
                    (type instanceof ParameterizedType));

            if (type instanceof ParameterizedType) {
                System.out.print("getActualTypeArguments:");
                for (Type param : ((ParameterizedType) type).getActualTypeArguments()) {
                    //打印实际类型参数
                    System.out.print(param);
                    if(param instanceof Class) {
                        System.out.print(" ,是Class类型");
                    }
                    if (param instanceof ParameterizedType) {
                        System.out.print(" ,是ParameterizedType类型");
                    }
                    if (param instanceof GenericArrayType) {
                        System.out.print(" ,是GenericArrayType类型");
                    }
                    if (param instanceof TypeVariable) {
                        System.out.print(" ,是TypeVariable类型");
                    }
                    if (param instanceof WildcardType) {
                        System.out.print(" ,是WildcardType类型");
                    }
                    System.out.print(", ");
                }
                System.out.print("\n");
                //打印所在的父类的类型
                System.out.println("getOwnerType:" + ((ParameterizedType) type).getOwnerType());
                //打印原始类型
                System.out.println("getRawType:" + ((ParameterizedType) type).getRawType());
            }
            System.out.println("---------------------------------");
        }
    }
    public static void main(String[] args) {
        printParameterizedType();
    }

}

输出结果:

FieldName: collection , is instanceof ParameterizedType : true
getActualTypeArguments:T ,是TypeVariable类型, 
getOwnerType:null
getRawType:interface java.util.Collection
---------------------------------
FieldName: map , is instanceof ParameterizedType : true
getActualTypeArguments:class java.lang.String ,是Class类型, class java.lang.Object ,是Class类型, 
getOwnerType:null
getRawType:class java.util.HashMap
---------------------------------
FieldName: map2 , is instanceof ParameterizedType : true
getActualTypeArguments:class java.lang.String ,是Class类型, java.util.List<java.lang.String> ,是ParameterizedType类型, 
getOwnerType:null
getRawType:class java.util.HashMap
---------------------------------
FieldName: entry , is instanceof ParameterizedType : true
getActualTypeArguments:class java.lang.String ,是Class类型, class java.lang.Object ,是Class类型, 
getOwnerType:interface java.util.Map
getRawType:interface java.util.Map$Entry
---------------------------------
FieldName: set , is instanceof ParameterizedType : true
getActualTypeArguments:class java.lang.String ,是Class类型, 
getOwnerType:null
getRawType:class java.util.HashSet
---------------------------------
FieldName: clz , is instanceof ParameterizedType : true
getActualTypeArguments:? ,是WildcardType类型, 
getOwnerType:null
getRawType:class java.lang.Class
---------------------------------
FieldName: a1 , is instanceof ParameterizedType : true
getActualTypeArguments:class java.util.ArrayList ,是Class类型, 
getOwnerType:null
getRawType:interface java.util.List
---------------------------------
FieldName: a2 , is instanceof ParameterizedType : true
getActualTypeArguments:java.util.ArrayList<java.lang.String> ,是ParameterizedType类型, 
getOwnerType:null
getRawType:interface java.util.List
---------------------------------
FieldName: a3 , is instanceof ParameterizedType : true
getActualTypeArguments:T ,是TypeVariable类型, 
getOwnerType:null
getRawType:interface java.util.List
---------------------------------
FieldName: a4 , is instanceof ParameterizedType : true
getActualTypeArguments:? extends java.lang.Number ,是WildcardType类型, 
getOwnerType:null
getRawType:interface java.util.List
---------------------------------
FieldName: a5 , is instanceof ParameterizedType : true
getActualTypeArguments:java.util.ArrayList<java.lang.String>[] ,是GenericArrayType类型, 
getOwnerType:null
getRawType:interface java.util.List
---------------------------------
FieldName: i , is instanceof ParameterizedType : false
---------------------------------
FieldName: str , is instanceof ParameterizedType : false
---------------------------------

TypeVariable(类型变量)

范型信息在编译时会被转换为一个特定的类型,而TypeVariable就是用来反映JVM在编译该泛型前的信息。(通俗的来说,TypeVariable就是我们常用的T,K这种泛型变量)

  • Type[] getBounds()
    返回当前类型变量的上边界,如果没有指定上边界,则默认为Object。
  • D getGenericDeclaration()
    获取声明该类型变量的实体,即在哪里声明了该类型变量。如:public interface Collection<E>,那么声明类型变量E的实体就是java.util.Collection
  • String getName()
    返回当前类型变量的名称,即K、V、E之类名称

具体的用法:

public class TypeVariableTest<K extends Number, T> {


    private K key;  //K指定了上边界Number
    private T value;  //T没有指定上边界,其默认上边界为Object

    public static void main(String[] args){
        Type[] types = TypeVariableTest.class.getTypeParameters();
        for (Type type : types){
            TypeVariable t = (TypeVariable) type;
            //输出名称
            System.out.println("getName(): " + t.getName());
            int index = t.getBounds().length - 1;
            //输出上边界
            System.out.println("getBounds(): " + t.getBounds()[index]);
            //输出所在的类的类型
            System.out.println("getGenericDeclaration(): " + t.getGenericDeclaration());
        }
    }

}

输出结果:

getName(): K
getBounds(): class java.lang.Number
getGenericDeclaration(): class com.example.javaadvanced.reflect.TypeVariableTest
getName(): T
getBounds(): class java.lang.Object
getGenericDeclaration(): class com.example.javaadvanced.reflect.TypeVariableTest

GenericArrayType(泛型数组类型)

泛型数组类型,用来描述ParameterizedType、TypeVariable类型的数组。
这个接口只有一个方法:

/**
 * {@code GenericArrayType} represents an array type whose component
 * type is either a parameterized type or a type variable.
 * @since 1.5
 */
public interface GenericArrayType extends Type {
    Type getGenericComponentType();
}

该方法返回泛型数组的元素的类型,如List[],则该方法返回List。注意:无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。

具体的用法:

public class GenericArrayTypeTest<T> {

    //泛型数组类型
    private T[] value;
    private T[][] value2;
    private List<String>[] list;
    private List<T>[] list2;

    //不是泛型数组类型
    private List<String> singleList;
    private T singleValue;

    public static void main(String[] args){
        Field[] fields = GenericArrayTypeTest.class.getDeclaredFields();
        for (Field field: fields){
            field.setAccessible(true);
            //输出当前变量是否为GenericArrayType类型
            System.out.print("Field: " + field.getName() + " is instanceof GenericArrayType : " + (field.getGenericType() instanceof GenericArrayType));
            if (field.getGenericType() instanceof GenericArrayType){
                //如果是GenericArrayType,则输出当前泛型类型
                System.out.print("; getGenericComponentType(): " + (((GenericArrayType)field.getGenericType()).getGenericComponentType()));
            }
            System.out.print("\n");
        }
    }

}

输出结果:

Field: value is instanceof GenericArrayType : true; getGenericComponentType(): T
Field: value2 is instanceof GenericArrayType : true; getGenericComponentType(): T[]
Field: list is instanceof GenericArrayType : true; getGenericComponentType(): java.util.List<java.lang.String>
Field: list2 is instanceof GenericArrayType : true; getGenericComponentType(): java.util.List<T>
Field: singleList is instanceof GenericArrayType : false
Field: singleValue is instanceof GenericArrayType : false

WildcardType(通配符类型)

表示通配符类型,比如 <?>, <? extends Number>等

  • Type[] getLowerBounds()
    得到下边界的Type数组
  • Type[] getUpperBounds()
    得到上边界的Type数组

注:如果没有指定上边界,则默认为Object,如果没有指定下边界,则默认为null。

通过一个例子了解一下:

public class WildcardTypeTest {
    private List<? extends Number> numbers;
    private List<? super Integer> integers;

    public static void main(String[] args) {

        Field[] fields  = WildcardTypeTest.class.getDeclaredFields();
        for (Field field: fields) {
            field.setAccessible(true);
            Type type = field.getGenericType();
            if(type instanceof ParameterizedType) {
                Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];
                //输出其是否为通配符类型
                System.out.println( actualTypeArgument + " is instanceof WildcardType : " + (actualTypeArgument instanceof WildcardType));
                if (actualTypeArgument instanceof WildcardType) {
                    int lowIndex = ((WildcardType) actualTypeArgument).getLowerBounds().length - 1;
                    int upperIndex = ((WildcardType) actualTypeArgument).getUpperBounds().length - 1;
                    //输出上边界与下边界
                    System.out.println("getLowerBounds(): " + (lowIndex >= 0 ? ((WildcardType) actualTypeArgument).getLowerBounds()[lowIndex] : "null")
                            + "; getUpperBounds(): " + (upperIndex >= 0 ? ((WildcardType) actualTypeArgument).getUpperBounds()[upperIndex] : "Object"));
                }
            }
        }

    }

}

输出结果:

? extends java.lang.Number is instanceof WildcardType : true
getLowerBounds(): null; getUpperBounds(): class java.lang.Number
? super java.lang.Integer is instanceof WildcardType : true
getLowerBounds(): class java.lang.Integer; getUpperBounds(): class java.lang.Object

Class(类类型)

上述中的原始类型(raw types)和基本类型(primitive types)都对应Class类型。

枚举、注解、数组、基本数据类型以及平常我们创建的类(不是泛型类)都是Class类型。

/**
 * 枚举、注解、数组、基本数据类型以及平常我们创建的类(不是泛型类)都是Class类型
 */
public class ClassTypeTest {

    private HashMap hashMap;
    private HashMap<String, Object> hashMap2;//不是Class类型,是ParameterizedType类型
    private int[] arr; //数组

    private int i;//基本类型
    private Integer integer;//基本类型的包装类型

    private double d;
    private Double aDouble;

    enum Fruit{
        APPLE, GRAPE
    }

    private Fruit apple = Fruit.APPLE;//枚举


    public @interface MyAnnotation{

    }
    private MyAnnotation annotation; //注解

    public static void main(String[] args) {

        Field[] fields = ClassTypeTest.class.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            Type type = f.getGenericType();
            System.out.println("FieldName: " + f.getName() + " , is instanceof Class : " + (type instanceof Class) );

        }

    }

}

输出结果:

FieldName: hashMap , is instanceof Class : true
FieldName: hashMap2 , is instanceof Class : false
FieldName: arr , is instanceof Class : true
FieldName: i , is instanceof Class : true
FieldName: integer , is instanceof Class : true
FieldName: d , is instanceof Class : true
FieldName: aDouble , is instanceof Class : true
FieldName: apple , is instanceof Class : true
FieldName: annotation , is instanceof Class : true

GenericDeclaration

GenericDeclaration接口继承了AnnotatedElement接口,是所有“可以声明(定义)类型变量”的实体(如Class,Constructor,Method)的公共接口。也就是说只有实现了这个接口的实体才能在该实体上定义类型变量。

目前实现GenericDeclaration接口的类包括Class(类), Method(方法), Constructor(构造器),也就是说只能在这几种对象上进行类型变量的声明(定义)。GenericDeclaration的直接实现子类没有Field类,所以属性上面不能定义类型变量。GenericDeclaration的getTypeParameters()方法用来获取该GenericDeclaration的类型变量。

/**
 * A common interface for all entities that declare type variables.
 *  * @since 1.5
 */
public interface GenericDeclaration extends AnnotatedElement {
    /**
     * Returns an array of {@code TypeVariable} objects that
     * represent the type variables declared by the generic
     * declaration represented by this {@code GenericDeclaration}
     * object, in declaration order.  Returns an array of length 0 if
     * the underlying generic declaration declares no type variables.
     *
     * @return an array of {@code TypeVariable} objects that represent
     *     the type variables declared by this generic declaration
     * @throws GenericSignatureFormatError if the generic
     *     signature of this generic declaration does not conform to
     *     the format specified in
     *     <cite>The Java&trade; Virtual Machine Specification</cite>
     */
    public TypeVariable<?>[] getTypeParameters();
}
  • TypeVariable<?>[] getTypeParameters()
    获取当前“实体”上声明的类型变量,按被声明的顺序返回一个TypeVariable对象的数组。
public class GenericDeclarationTest {

    public static void main(String[] args) {
        HashMap<String, Object> hashMap = new HashMap<>();
        Class clz = hashMap.getClass();
        Type type = clz.getGenericSuperclass();//获取直接父类的类型Type
        System.out.println("getGenericSuperclass()=" + type);
        if(type instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            System.out.println("actualTypeArguments:" + Arrays.toString(actualTypeArguments));
        }

        System.out.println("getSuperclass()=" + clz.getSuperclass());


        Type[] types = clz.getGenericInterfaces();
        System.out.println("getGenericInterfaces()=" + Arrays.toString(types));

        TypeVariable[] typeVariables = clz.getTypeParameters();//getTypeParameters()获取类型变量K,V
        System.out.println("actualTypeArguments:" + Arrays.toString(typeVariables));
        
    }
}

输出结果:

getGenericSuperclass()=java.util.AbstractMap<K, V>
actualTypeArguments:[K, V]
getSuperclass()=class java.util.AbstractMap
getGenericInterfaces()=[java.util.Map<K, V>, interface java.lang.Cloneable, interface java.io.Serializable]
actualTypeArguments:[K, V]

总结

通过反射相关接口来获取被擦除的类型参数信息,这几个接口是对类型参数的一个分类,通过它们提供的一些方法,我们可以逐步的获取到最原始的类型参数信息。

常见面试题

为什么说反射比较消耗性能

参考:
Java篇 - 反射机制分析(附面试中的坑)

JVM实现反射的秘诀?
如何提高使用Java反射的效率?
万字总结之反射(框架之魂)
面试官说:大家都说 Java 反射效率低,你知道原因在哪里么
请问Java反射的性能为什么比直接调用慢一个数量级左右?
关于反射调用方法的一个log

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值