JAVA反射总结


反射相关的类和接口简介

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

相关类说明

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

说明
AccessibleObjectField、MethodConstructor对象的基类
Fieldfinal类, 对应类或接口的单个字段
Methodfinal类, 对应类或接口的单独某个方法
Constructor<T>final类, 对应类的单个构造方法
Modifier对应类和成员访问修饰符, 如public,static,final,native,synchronized
Arrayfinal类, 对应数组
Proxy提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
ReflectPermissionfinal类, 反射操作的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, 直接返回关键字对应的字符串
    • 若是数组, 则用”[“代表数组维度,后面跟上元素类型代码,具体如下表:
元素类型类型代码
booleanZ
byteB
charC
doubleD
floatF
intI
longJ
shortS
class or interfaceLclassname;

来个例子:

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(): 若该类是个数组,则返回组件的类型

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值