分享

JAVA反射总结

 land_zhj 2016-08-21

反射相关的类和接口简介

Java.lang.reflect包中定义了一些接口和类,简单记录下他们的作用

相关类说明

这些类都比较简单,对照API一看就懂,主要是平时用到的比较少,这里先简单说明下,不在此展开

说明
AccessibleObject Field、MethodConstructor对象的基类
Field final类, 对应类或接口的单个字段
Method final类, 对应类或接口的单独某个方法
Constructor<T> final类, 对应类的单个构造方法
Modifier 对应类和成员访问修饰符, 如public,static,final,native,synchronized
Array final类, 对应数组
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
ReflectPermission final类, 反射操作的Permission

另外反射机制还经常用到java.lang中的类, 如ClassPackate还有各种原始类型等

相关接口继承关系图

反射相关接口继承关系图

相关接口说明

这里先简单介绍下,后文还会详细说明这些接口

接口 说明
AnnotatedElement 被注解标注的元素都实现的接口
实现类有AccessibleObject及其子类,ClassPackage
Member 成员接口, 反映字段、方法、构造函数的信息
Type 所有类型的父接口, 之后的文章会详细介绍
GenericDeclaration 各种声明类型的父接口, 如Class,Constructor,Method
TypeVariable<D extends GenericDeclaration> 类型变量类型
ParameterizedType 参数化类型, 如Collection<String>
GenericArrayType 数组类型, 它里面的元素为参数化类型或类型变量(上面那俩)
WildcardType 通配符类型, 如<?>, <? extends Number>, <? super Integer>
InvocationHandler 代理处理器接口

ObjectClassType的关系和区别

所有的类都继承Object是毫无疑问的, Class也是类,也继承Object
Class类特殊的地方是它在运行时用来描述类的各种元信息,对象是类的实例,而类在运行时的描述就是Class
Type是个接口,用来表示某个对象是什么类型的,Class类实现了好几个接口,其中一个就是Type
后面的文章内容更能体现出区别来


反射的具体功能

反射作用挺多, 比如反编译, 通过反射机制访问对象的属性、方法、构造方法等, 先简单给出一些示例

获取类

有如下三种方法:

  1. 通过Class.forName(), 如:Class clazz = Class.forName("java.lang.String");
  2. 通过类的class属性, 如:Class clazz = String.class;
  3. 通过实例对象的getClass方法, 如: Class clazz = user.getClass();

获取对象实例

  1. 通过Class实例的newInstance(), 如:String str = (String)clazz.newInstance();

反射相关接口的详细说明

这些接口的关系可参考上面那幅图, 这里只详细说明AnnotatedElement、Member、InvocationHandler、GenericDeclaration这几个接口
Type接口及其字类在Type详解中详细说明

AnnotatedElement

这个接口最简单, 实现了该接口的类都能添加注解, 其实现类有: AccessibleObject, Class, Constructor, Field, Method, Package
它有如下方法:

  1. boolean isAnnotationPresent(Class<? extends Annotation> annotationClass): 该类上是否存在annotationClass这个注解
  2. Annotation[] getAnnotations(): 获取这个元素上的所有注解(包括父类上被@Inherited标记的注解)
  3. <A extends Annotation> A getAnnotation(Class<A> annotationClass): 获取这个元素上指定类型的注解, 没有返回null
  4. Annotation[] getDeclaredAnnotations(): 获取直接标注在这个元素上的注解

Member

表示组成类的成员, 其实现类有: Constructor, Field, Method 它有如下方法:

  1. boolean isSynthetic(): 是否是复合类
  2. int getModifiers(): 以数字形式返回修饰符
  3. String getName(): 返回成员的简单名, 如: 属性名字, 方法名字(只有名字,不包括参数), 构造器名字
  4. Class<?> getDeclaringClass(): 返回声明该成员的声明类
// getName方法的测试
package com.test;
public class TestMember {
    private String name;              // 属性名为: name
    public void hello(String name) {} // 方法名(不包括参数)为: hello
    public TestMember(String name) {} // 构造器名为: com.test.TestMember
    public static void main(String[] args) throws Exception {
        System.out.println(TestMember.class.getDeclaredField("name").getName());                    // name
        System.out.println(TestMember.class.getDeclaredMethod("hello", String.class).getName());    // hello
        System.out.println(TestMember.class.getDeclaredConstructor(String.class).getName());        //com.test.TestMember
        System.out.println(TestMember.class.getDeclaredField("name").getDeclaringClass().getName());//com.test.TestMember
    }
}

InvocationHandler

代理处理器类的实例需要实现的接口, 只有一个方法:

  1. Object invoke(Object proxy, Method method, Object[] args): 代理对象执行方法时真正调用的函数

其中:

  • Object proxy: 表示代理对象(比如通过Proxy.newProxyInstance()得到的对象), 不是实现了该接口的代理处理器对象
  • Method method: 被代理对象的方法, 就是接口原来实现类里的方法, 代理对象执行的时候会调用被代理对象的方法(取决于代码怎么写)
  • Object[] args: 上面那个方法需要的参数

通常代理处理器类的实例handler会作为参数传到java代理类ProxynewProxyInstance方法中来产生一个代理对象(与被代理对象实现了同一接口)
调用代理对象的方法时, 真正调用的就是上面的invoke函数.
还是来个例子吧(HelloImpl实现了Hello接口, 并实现了sayHello()方法)

// 代理处理器类
public class AOPHandler implements InvocationHandler {
    private Object target;  // 这个用来表示被代理的对象
    public AOPHandler(Object target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy:" + proxy.getClass().getName());   // proxy:com.sun.proxy.$Proxy0
        System.out.println("method:" + method.getClass().getName()); // proxy:java.lang.reflect.Method
        return method.invoke(target, args);  // 调用被代理对象的的方法, 即 target 的 method
    }
}
// 测试类
public class ProxyTest {
    public static void main(String[] args) throws Exception {
        Object obj = Class.forName("com.test.HelloImpl").newInstance();
        InvocationHandler handler = new AOPHandler(obj);  // 创建代理处理器对象的时候把被代理的对象传进去
        // 这里生成代理类对象, 参数依次为: 被代理对象的ClassLoader, 被代理对象实现的所有接口, 代理处理器
        Hello proxy = (Hello) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
        proxy.sayHello();  // 这就调用了那个invoke方法
    }
}

GenericDeclaration

可以声明类型变量的实体的公共接口, 就是那些能够声明范型类型<T>的地方, 注意是声明而不是使用.其实现类有: Class, Constructor, Method
它就一个方法:

  1. TypeVariable<?>[] getTypeParameters(): 按照声明顺序返回声明的类型变量

对它可以这么理解:
在定义类或方法时, 我们一般可以声明范型, 如<T> T getData(), 因此可以声明这些<T>、<F>的实体, 都实现了该接口;
而那些声明出来的T、F叫类型变量(TypeVariable), 后面会说到.


下面就把Type的来龙去脉彻底弄清楚

Type

Type是所有类型的父接口, 如原始类型(raw types,对应Class)、 参数化类型(parameterized types, 对应ParameterizedType)、 数组类型(array types,对应GenericArrayType)、 类型变量(type variables, 对应TypeVariable)和基本(原生)类型(primitive types, 对应Class), 子接口有ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType, 实现类有Class

ParameterizedType

具体的范型类型, 如Map<String, String>
有如下方法:

  1. Type getRawType(): 返回承载该泛型信息的对象, 如上面那个Map<String, String>承载范型信息的对象是Map
  2. Type[] getActualTypeArguments(): 返回实际泛型类型列表, 如上面那个Map<String, String>实际范型列表中有两个元素, 都是String
  3. Type getOwnerType(): 返回是谁的member.(上面那两个最常用)
public class TestType {
    Map<String, String> map;
    public static void main(String[] args) throws Exception {
        Field f = TestType.class.getDeclaredField("map");
        System.out.println(f.getGenericType());                               // java.util.Map<java.lang.String, java.lang.String>
        System.out.println(f.getGenericType() instanceof ParameterizedType);  // true
        ParameterizedType pType = (ParameterizedType) f.getGenericType();
        System.out.println(pType.getRawType());                               // interface java.util.Map
        for (Type type : pType.getActualTypeArguments()) {
            System.out.println(type);                                         // 打印两遍: class java.lang.String
        }
        System.out.println(pType.getOwnerType());                             // null
    }
}

TypeVariable

类型变量, 范型信息在编译时会被转换为一个特定的类型, 而TypeVariable就是用来反映在JVM编译该泛型前的信息.
它的声明是这样的: public interface TypeVariable<D extends GenericDeclaration> extends Type
也就是说它跟GenericDeclaration有一定的联系, 我是这么理解的:
TypeVariable是指在GenericDeclaration中声明的<T>、<C extends Collection>这些东西中的那个变量T、C; 它有如下方法:

  1. Type[] getBounds(): 获取类型变量的上边界, 若未明确声明上边界则默认为Object
  2. D getGenericDeclaration(): 获取声明该类型变量实体
  3. String getName(): 获取在源码中定义时的名字

注意:

  • 类型变量在定义的时候只能使用extends进行(多)边界限定, 不能用super;
  • 为什么边界是一个数组? 因为类型变量可以通过&进行多个上边界限定,因此上边界有多个
public class TestType <K extends Comparable & Serializable, V> {
    K key;
    V value;
    public static void main(String[] args) throws Exception {
        // 获取字段的类型
        Field fk = TestType.class.getDeclaredField("key");
        Field fv = TestType.class.getDeclaredField("value");
        Assert.that(fk.getGenericType() instanceof TypeVariable, "必须为TypeVariable类型");
        Assert.that(fv.getGenericType() instanceof TypeVariable, "必须为TypeVariable类型");
        TypeVariable keyType = (TypeVariable)fk.getGenericType();
        TypeVariable valueType = (TypeVariable)fv.getGenericType();
        // getName 方法
        System.out.println(keyType.getName());                 // K
        System.out.println(valueType.getName());               // V
        // getGenericDeclaration 方法
        System.out.println(keyType.getGenericDeclaration());   // class com.test.TestType
        System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
        // getBounds 方法
        System.out.println("K 的上界:");                        // 有两个
        for (Type type : keyType.getBounds()) {                // interface java.lang.Comparable
            System.out.println(type);                          // interface java.io.Serializable
        }
        System.out.println("V 的上界:");                        // 没明确声明上界的, 默认上界是 Object
        for (Type type : valueType.getBounds()) {              // class java.lang.Object
            System.out.println(type);
        }
    }
}

GenericArrayType

范型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是ParameterizedTypeTypeVariable类型,它只有一个方法:

  1. Type getGenericComponentType(): 返回数组的组成对象, 即被JVM编译后实际的对象
public class TestType <T> {
    public static void main(String[] args) throws Exception {
        Method method = Test.class.getDeclaredMethods()[0];
        // public void com.test.Test.show(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],int[])
        System.out.println(method);
        Type[] types = method.getGenericParameterTypes();  // 这是 Method 中的方法
        for (Type type : types) {
            System.out.println(type instanceof GenericArrayType);
        }
    }
}
class Test<T> {
    public void show(List<String>[] pTypeArray, T[] vTypeArray, List<String> list, String[] strings, int[] ints) {
    }
}
  • 第一个参数List<String>[]的组成元素List<String>ParameterizedType类型, 打印结果为true
  • 第二个参数T[]的组成元素TTypeVariable类型, 打印结果为true
  • 第三个参数List<String>不是数组, 打印结果为false
  • 第四个参数String[]的组成元素String是普通对象, 没有范型, 打印结果为false
  • 第五个参数int[] pTypeArray的组成元素int是原生类型, 也没有范型, 打印结果为false

WildcardType

该接口表示通配符泛型, 比如? extends Number 和 ? super Integer 它有如下方法:

  1. Type[] getUpperBounds(): 获取范型变量的上界
  2. Type[] getLowerBounds(): 获取范型变量的下界

注意:

  • 现阶段通配符只接受一个上边界或下边界, 返回数组是为了以后的扩展, 实际上现在返回的数组的大小是1
public class TestType {
    private List<? extends Number> a;  // // a没有下界, 取下界会抛出ArrayIndexOutOfBoundsException
    private List<? super String> b;
    public static void main(String[] args) throws Exception {
        Field fieldA = TestType.class.getDeclaredField("a");
        Field fieldB = TestType.class.getDeclaredField("b");
        // 先拿到范型类型
        Assert.that(fieldA.getGenericType() instanceof ParameterizedType, "");
        Assert.that(fieldB.getGenericType() instanceof ParameterizedType, "");
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
        // 再从范型里拿到通配符类型
        Assert.that(pTypeA.getActualTypeArguments()[0] instanceof WildcardType, "");
        Assert.that(pTypeB.getActualTypeArguments()[0] instanceof WildcardType, "");
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
        // 方法测试
        System.out.println(wTypeA.getUpperBounds()[0]);   // class java.lang.Number
        System.out.println(wTypeB.getLowerBounds()[0]);   // class java.lang.String
        // 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
        System.out.println(wTypeA);
    }
}

再写几个边界的例子:

  • List<? extends Number>, 上界为class java.lang.Number, 属于Class类型
  • List<? extends List<T>>, 上界为java.util.List<T>, 属于ParameterizedType类型
  • List<? extends List<String>>, 上界为java.util.List<java.lang.String>, 属于ParameterizedType类型
  • List<? extends T>, 上界为T, 属于TypeVariable类型
  • List<? extends T[]>, 上界为T[], 属于GenericArrayType类型

它们最终统一成Type作为数组的元素类型


Type及其子接口的来历

泛型出现之前的类型

没有泛型的时候,只有原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。

泛型出现之后的类型

泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、限定符类型 、泛型数组类型。

与泛型有关的类型不能和原始类型统一到Class的原因

  • 产生泛型擦除的原因

原始类型和新产生的类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的。

  • Java中如何引入泛型

为了使用泛型又不真正引入泛型,Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。

  • Class不能表达与泛型有关的类型

因此,与泛型有关的参数化类型、类型变量类型、限定符类型 、泛型数组类型这些类型编译后全部被打回原形,在字节码文件中全部都是泛型被擦除后的原始类型,并不存在和自身类型对应的字节码文件。所以和泛型相关的新扩充进来的类型不能被统一到Class类中。

  • 与泛型有关的类型在Java中的表示

为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

  • 引入Type的原因

为了程序的扩展性,最终引入了Type接口作为Class和ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType这几种类型的总的父接口。这样可以用Type类型的参数来接受以上五种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class

  • Type接口中没有方法的原因

从上面看到,Type的出现仅仅起到了通过多态来达到程序扩展性提高的作用,没有其他的作用。因此Type接口的源码中没有任何方法。

Class类简介

Class类位于java.lang包中, Class类的实例表示正在运行的Java应用程序中的类和接口.
枚举算类, 注解算接口; 数组算类,它映射的Class对象被有着相同元素和大小的数组共享;
Java的原生(primitive types)类型(boolean、byte、char、short、int、long、float、double)和关键字void也代表Class对象.

Class类实现了Serializable、GenericDeclaration、Type、AnnotatedElement接口

Class类无public构造函数, Class类对象是由JVM在类加载的时候调用类加载器的defineClass方法创建的.
如下方法能够获取类名:

  • obj.getClass().getName()
  • Foo.class.getName()

判定方法

这些方法可以判断某个类是否是接口、是否是枚举、是否是注解、是否有某个注解、是否是匿名类、是否是某个类的超类(是否可强制转换为该类)等

  1. boolean isArray(): 是否是数组; int[].class.isArray()true
  2. boolean isAnnotation(): 是否是注解, 若返回true,则isInterface也为tureTarget.class.isAnnotation()true
  3. boolean isEnum(): 是否是枚举类
  4. boolean isPrimitive(): 是否是原生类型(共9个, 包装类返回false), void.class.isArray()true
  5. boolean isInterface(): 是否是接口
  6. boolean isMemberClass(): 是否是成员类, 类的定义在另一个类里面的那种
  7. boolean isAnonymousClass(): 是否是匿名类
  8. boolean isLocalClass(): 是否是本地类
  9. boolean isSynthetic(): 是否是复合类 (接口Member也有这个方法)
  10. boolean isInstance(Object obj): obj是否是该类的一个实例
  11. boolean isAssignableFrom(Class<?> cls): cls是否可以被转换成该类
  12. boolean isAnnotationPresent(Class<? extends Annotation> annotationClass): 该类上是否存在这个注解(继承自接口AnnotatedElement)

下面是几个方法的实例说明:

public class Main {
    class Test{}  // 这是传说中的成员类
    public static void main(String[] args) {
        class LocalClass{}  // 这是传说中的本地类
        Object obj = new Hello(){    // Hello是其他地方定义的一个接口, 有一个sayHello()方法, 这就是匿名类(无类名)
            public void sayHello() {
            }
        };
        System.out.println(Test.class.isMemberClass());         // true
        System.out.println(obj.getClass().isAnonymousClass());  // true
        System.out.println(LocalClass.class.isLocalClass());    // true
        System.out.println(Hello.class.isInstance(obj));        // true
        System.out.println(Bean.class.isAnnotationPresent(Target.class));        // true
    }
}
  • instanceof、 isInstance、 isAssignableFrom的区别

instanceof运算符只被用于对象引用变量, 比如: 自身类或子类的实例 instanceof 自身类 返回true
isInstance(Object obj)instanceof运算符的动态等价, 比如: 自身类.class.isInstance(自身类或子类的实例) 返回true
isAssignableFrom(Class<?> cls)是两个类之间的关系, 比如: 自身类.class.isAssignableFrom(自身类或子类.class) 返回true

获取内容系列方法

通过Class类可以获取这个类的一些内容性质的信息, 比如属性、方法、构造方法、注解

获取属性

  1. Field[] getFields(): 获取本类或父类中所有public属性
  2. Field getField(String name): 获取本类或父类中特定名字的public属性
  3. Field[] getDeclaredFields(): 获取本类中声明的所有属性
  4. Field getDeclaredField(String name): 获取本类中声明的特定名字的属性

获取方法

  1. Method[] getMethods(): 获取本类或父类中所有public方法(包括构造器方法)
  2. Method getMethod(String name, Class<?>... parameterTypes): 获取本类或父类中特定名字和参数的public方法
  3. Method[] getDeclaredMethods(): 获取本类中声明的所有方法(包括非public但不包括继承来的)
  4. Method getDeclaredMethod(String name, Class<?>... parameterTypes): 获取本类中声明的特定名字和参数的方法(最常用)

获取构造方法

  1. Constructor<?>[] getConstructors(): 获取本类中所有public构造器
  2. Constructor<T> getConstructor(Class<?>... parameterTypes): 获取本类中特定参数的public构造器
  3. Constructor<?>[] getDeclaredConstructors(): 获取本类中所有构造器
  4. Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes): 获取本类中指定参数的构造器

获取注解

这几个方法均继承自接口AnnotatedElement

  1. Annotation[] getAnnotations(): 获取这个元素上的所有注解(包括父类上被@Inherited标记的注解)
  2. <A extends Annotation> A getAnnotation(Class<A> annotationClass): 获取这个元素上指定类型的注解, 没有返回null
  3. Annotation[] getDeclaredAnnotations(): 获取直接标注在这个元素上的注解

父类子类(接口)相关

  1. Class<? super T> getSuperclass(): 返回本类的父类(直接超类);int[].classObjectint.classnullObject.classnull
  2. Type getGenericSuperclass(): 以Type的形式返回本类的父类, 带有范型信息(没有范型信息时把Class以Type形式返回)
  3. Class<?>[] getInterfaces(): 返回本类直接实现的接口
  4. Type[] getGenericInterfaces(): 以Type的形式返回本类直接实现的接口, 带有范型信息
  5. <U> Class<? extends U> asSubclass(Class<U> clazz) : 把当前类转为clazz表示的子类(或自己), 不能转抛ClassCastException异常

asSubclass的作用

ArrayList.class.asSubclass(List.class)得到的还是ArrayList.class, 看起来没什么作用
但是它的作用体现在窄化未知的Class类型的范围, 比如通常我们用到Class.forName("XXX"), 它的返回是Class<?>比较宽泛, 我们可以窄化一下:Class.forName("XXX").asSubclass(List.class).newInstance(). 当XXX不是List的子类时,抛出ClassCastException异常

内部类相关

  1. Class<?> getEnclosingClass(): 获取底层类的直接封闭类, 如上面LocalClass的封闭类为Main, 那个匿名类的封闭类也是Main
  2. Constructor<?> getEnclosingConstructor(): 若该Class对象是在一个构造方法中的本地类或匿名类时, 返回这个构造器对象, 表示底层类直接封闭构造方法, 否则返回null; 上面的LocalClass不在构造方法中,因此返回null
  3. Method getEnclosingMethod(): 若该Class对象是在一个方法中的本地类或匿名类时, 返回这个Method对象, 表示底层类的直接封闭方法, 否则返回null
  4. Class<?> getDeclaringClass(): 该类是另一个类的成员(isMemberClass),则返回该类的声明类(外部类); 接口Member中也有该方法
  5. Class<?>[] getDeclaredClasses() : 返回该类中直接声明的所有类
  6. Class<?>[] getClasses() : 返回该类中直接声明的所有public类

名字相关

  1. static Class<?> forName(String className): 返回与给定的字符串名称相关联的Class对象
  2. String getSimpleName(): 返回源码中定义的简单类名, 匿名类返回空串, 数组返回”组件类型[]”
  3. String getCanonicalName: 返回底层类的Java语言规范中定义的标准名称
  4. public String getName(): 返回此Class对象所表示的实体(类,接口,数组类,基本类型或void)的名字, 略复杂,规则如下:
    • 若是原始类型(class), 则返回Java语言规范中定义的标准名称
    • 若是原生类型(primitive)或void, 直接返回关键字对应的字符串
    • 若是数组, 则用”[“代表数组维度,后面跟上元素类型代码,具体如下表:
元素类型 类型代码
boolean Z
byte B
char C
double D
float F
int I
long J
short S
class or interface Lclassname;

来个例子:

public class ClassTest {
    public static void main(String[] args) throws Exception {
        Class clazz = String.class;
        System.out.println(clazz.getSimpleName());    // String
        System.out.println(clazz.getCanonicalName()); // java.lang.String
        System.out.println(clazz.getName());          // java.lang.String

        clazz = int.class;
        System.out.println(clazz.getSimpleName());    // int
        System.out.println(clazz.getCanonicalName()); // int
        System.out.println(clazz.getName());          // int

        String[] strings = {"a", "b"};
        clazz = strings.getClass();
        System.out.println(clazz.getSimpleName());    // String[]
        System.out.println(clazz.getCanonicalName()); // java.lang.String[]
        System.out.println(clazz.getName());          // [Ljava.lang.String;

        int[][] array = {new int[]{1, 2}, new int[]{10, 20}};
        clazz = array.getClass();
        System.out.println(clazz.getSimpleName());    // int[][]
        System.out.println(clazz.getCanonicalName()); // int[][]
        System.out.println(clazz.getName());          // [[I
    }
}

其他方法

  1. TypeVariable<Class<T>>[] getTypeParameters(): 继承自接口GenericDeclaration,按照声明顺序返回声明的类型变量
  2. Class<?> getComponentType(): 若该类是个数组,则返回组件的类型

还有一些方法暂不介绍了.


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多