Java筑基——反射(1):基本类周边信息获取

相关文章:

1. 前言

本文会介绍反射相关的知识,为了不和泛型类的方法混在一起,本文集中介绍基本类的反射知识。主要内容包括:

  • 类的生命周期是什么?
  • 有几种方式获取类类型,这些方式之间的区别是什么?
  • 如何获取类的超类或实现的接口?
  • 如何获取类及接口的访问修饰符?
  • 获取类名的几种方法及区别。

2. 正文

2.1 类的生命周期

类从被加载到虚拟机内存中开始,直到被卸载出内存为止,它的整个生命周期包括:加载验证准备解析初始化使用卸载这 7 个阶段。其中,验证准备解析这三个部分统称为连接(linking)。

加载(Loading): 这是类加载过程的第一个阶段,在这个阶段,虚拟机需要完成三件事情:通过一个类的全限定名来获取定义这个类的二进制字节流;将这个二进制字节流所代表的静态存储结构转化为方法区的运行时数据结构;在 Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区里数据的访问入口。

加载阶段既可以使用系统提供的类加载器,也可以使用用户自定义的类加载器。加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,因此加载阶段尚未完成,连接阶段可能已经开始。

需要注意的是,同一个类只会被类加载器加载一次。

验证(Verification):这是连接阶段的第一步,验证是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证包括:文件格式验证,元数据验证,字节码验证和符号引用验证。

准备(Preparation):这个阶段为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。

需要注意的是,准备阶段不分配类中的实例变量的内存,因为实例变量将会在对象实例化时随着对象一起分配在 Java 堆中。

例如,

public static int value = 18;//在准备阶段value初始值为0 。在初始化阶段才会变为18 。

解析(Resolution):这个阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

初始化(Initialization):这是类加载过程的最后一步。

前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的 Java 程序代码。

初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的。

2.2 获取类类型

这里有一个 Fruit 类:

package com.java.advanced.features.reflect;

public class Fruit {
    public String taste;

    public Fruit() {
    }
}

我们可以通过四种方式获取 Fruit类的类类型:

第一种方式:通过类对象获取

Fruit fruit = new Fruit();
Class fruitClass1 = fruit.getClass();
System.out.println("fruitClass1 = " + fruitClass1);

打印信息:

fruitClass1 = class com.java.advanced.features.reflect.Fruit

第二种方式:通过类的 class 对象获取

Class fruitClass2 = Fruit.class;
System.out.println("fruitClass2 = " + fruitClass2);

打印信息:

fruitClass2 = class com.java.advanced.features.reflect.Fruit

第三种方式:通过Class.forName(fullClassName)获取

Class fruitClass3 = null;
try {
    fruitClass3 = Class.forName("com.java.advanced.feat
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
System.out.println("fruitClass3 = " + fruitClass3);

打印信息:

fruitClass3 = class com.java.advanced.features.reflect.Fruit

第四种方式:通过 Classloader.loadClass(fullClassName) 获取

Class fruitClass4 = null;
try {
    fruitClass4 = _01_GetClassTest.class.getClassLoader().loadClass("com.java.advanced.features.reflect.Fruit");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
System.out.println("fruitClass4 = " + fruitClass4);

打印信息:

fruitClass4 = class com.java.advanced.features.reflect.Fruit

从上面的打印信息,可以看到:这四种方式的打印信息是一样的,但是这四种方式获取到的类对象是否相等呢?

我们通过代码验证一下:

System.out.println("result = " + (fruitClass1 == fruitClass2
        && fruitClass2 == fruitClass3
        && fruitClass3 == fruitClass4));

打印结果:

result = true

它们是一模一样的,这是因为类只会被加载一次。

到这里,我们知道了获取类对象有 4 种方式,那么这 4 种方式之间有什么区别呢?

前两种方式:通过类对象获取和通过类的 class 对象获取,不会抛出异常;后两种方式:通过Class.forName(fullClassName)获取和通过 Classloader.loadClass(fullClassName) 获取,可能会抛出 ClassNotFoundException 异常。

通过Class.forName(fullClassName)获取的 Class 是已经完成初始化的,通过 Classloader.loadClass(fullClassName) 获取的 Class 是还没有连接的。为什么有这样的区别,我们需要去看一下源码:

先看 Class.forName():

public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

public static Class<?> forName(String name, boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException
{
    Class<?> caller = null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        // Reflective call to get caller class is only needed if a security manager
        // is present.  Avoid the overhead of making this call otherwise.
        caller = Reflection.getCallerClass();
        if (sun.misc.VM.isSystemDomainLoader(loader)) {
            ClassLoader ccl = ClassLoader.getClassLoader(caller);
            if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                sm.checkPermission(
                    SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
        }
    }
    return forName0(name, initialize, loader, caller);
}

forName(String name, boolean initialize, ClassLoader loader)的方法文档注释里,可以看到:

调用 Class.forName("Foo") 等价于调用 Class.forName("Foo", true, this.getClass().getClassLoader())

关键的就是第二个参数:boolean initialize,表示这个类是否要被初始化。

结论:Class.forName(fullClassName) 方式表示类是被初始化的。

再看 ClassLoader 类的 loadClass() 方法:

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);
                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;

loadClass(String name) 调用的是 loadClass(String name, boolean resolve)方法,第二个参数的值是 falseboolean resolve 的意思是是否连接该类(Links the specified class)。

结论:Classloader.loadClass(fullClassName) 不会链接类。

如果加载的类依赖于初始化值的话,就需要采用 Class.forName(fullClassName) 的方式,而不能采用Classloader.loadClass(fullClassName) 方式。

2.3 获取类名

查看文档,获取类名的方法有:

public String getName()
public String getSimpleName()
public String getCanonicalName()
public String getTypeName()

这 4 个方法的区别是什么?它们分别适用于什么场合呢?

为了方便测试,这里写了一个泛型的打印方法:

private static <T> void print(Class<T> clazz, String label) {
    System.out.println(label + ".getName() = " + clazz.getName());
    System.out.println(label + ".getSimpleName() = " + clazz.getSimpleName());
    System.out.println(label + ".getCanonicalName() = " + clazz.getCanonicalName())
    System.out.println(label + ".getTypeName() = " + clazz.getTypeName());
    System.out.println();
}

我们先通过简单的 Fruit 类来说明:

print(Fruit.class, "simpleClass");

打印信息:

simpleClass.getName() = com.java.advanced.features.reflect.Fruit
simpleClass.getSimpleName() = Fruit
simpleClass.getCanonicalName() = com.java.advanced.features.reflect.Fruit
simpleClass.getTypeName() = com.java.advanced.features.reflect.Fruit

可以看到除了 getSimpleName() 外,其他三个打印信息是一样的。

但是,它们是有区别的。

下面展示会出现区别的场景:

嵌套类场景:

嵌套类就是静态内部类,大家不要把它和成员内部类混淆在一起了。

package com.java.advanced.features.reflect.basicclass;
public class OuterClass {
    public static final class StaticInnerClass {
    }
}

测试代码:

print(OuterClass.StaticInnerClass.class, "staticInnerClassClass");

打印结果:

staticInnerClassClass.getName() = com.java.advanced.features.reflect.basicclass.OuterClass$StaticInnerClass
staticInnerClassClass.getSimpleName() = StaticInnerClass
staticInnerClassClass.getCanonicalName() = com.java.advanced.features.reflect.basicclass.OuterClass.StaticInnerClass
staticInnerClassClass.getTypeName() = com.java.advanced.features.reflect.basicclass.OuterClass$StaticInnerClass

可以看到,getCanonicalName() 获取到的名字和 getName() 以及 getTypeName() 就不一样了。

数组场景:

分别对一维和二维的字符串数组,打印相关信息:

print(String[].class, "1-arrayClass");
print(String[][].class, "2-arrayClass");

打印结果:

1-arrayClass.getName() = [Ljava.lang.String;
1-arrayClass.getSimpleName() = String[]
1-arrayClass.getCanonicalName() = java.lang.String[]
1-arrayClass.getTypeName() = java.lang.String[]

2-arrayClass.getName() = [[Ljava.lang.String;
2-arrayClass.getSimpleName() = String[][]
2-arrayClass.getCanonicalName() = java.lang.String[][]
2-arrayClass.getTypeName() = java.lang.String[][]

可以看到,getName() 获取的名字和 getCanonicalName() 以及 getTypeName() 不同。

匿名内部类场景:

print(new Serializable() {
}.getClass(), "anonymousInnerClass");

打印结果:

anonymousInnerClass.getName() = com.java.advanced.features.reflect.basicclass._02_GetClassNamePackageNameTest$1
anonymousInnerClass.getSimpleName() = 
anonymousInnerClass.getCanonicalName() = null
anonymousInnerClass.getTypeName() = com.java.advanced.features.reflect.basicclass._02_GetClassNamePackageNameTest$1

可以看到,匿名内部类通过 getSimpleName() 获取到的是空字符串,通过 getCanonicalName() 获取到的是 null

到这里,对这 4 种获取名字的方法做一下总结:

getName():返回的是虚拟机里面的 class 的表示,是动态加载类所需要的,例如用于Class.forName(String name)
getCanonicalName():返回的是更容易理解的表示 用于获取 import 的名字;
getSimpleName():获取类名的标识符;
getTypeName(): 如果不是数组,就调用 getName();是数组,就是类名后面跟维数([]),几维就跟几个[]。

2.4 获取超类的 Class 对象

查看 Class 类的文档有两个函数:

// 获取 Class 对象的普通类型父类
public native Class<? super T> getSuperclass();
// 获取 Class 对象的泛型类型父类
public Type getGenericSuperclass()

下面通过例子说明 getSuperclass() 的使用。至于 getGenericSuperclass() 后面会介绍。

需要注意的是,不能把这里的调用者 Class 对象局限为类对应的 Class 对象,还应该包括接口,基本类型以及 void 对应的 Class 对象。

这里我们定义一个 Apple 类,它继承 Fruit 类:

public class Fruit {
    public String taste;
}
public class Apple extends Fruit {
}

测试代码如下:

System.out.println("Apple.class.getSuperclass() = " + Apple.class.getSuperclass());
System.out.println("Collection.class.getSuperclass() = " + Collection.class.getSuperclass());
System.out.println("int.class.getSuperclass() = " + int.class.getSuperclass());
System.out.println("void.class.getSuperclass() = " + void.class.getSuperclass());
System.out.println("stringArray.getClass().getSuperclass() = " + String[].class.getSuperclass());
System.out.println("integerArray.getClass().getSuperclass() = " + Integer[].class.getSuperclass());

打印信息:

Apple.class.getSuperclass() = class com.java.advanced.features.reflect.Fruit
Collection.class.getSuperclass() = null
int.class.getSuperclass() = null
void.class.getSuperclass() = null
stringArray.getClass().getSuperclass() = class java.lang.Object
integerArray.getClass().getSuperclass() = class java.lang.Object

如果是类,就返回超类;
如果是接口,基本类型或 void,就返回 null
如果是数组,就返回 Object

2.5 获取直接继承或实现的接口的 Class 对象列表

查看文档,相关的函数有:

// 获取 Class 对象的直接继承或实现的接口 Class 对象列表
public Class<?>[] getInterfaces() 
// 获取 Class 对象的直接继承或实现的泛型接口 Class 对象列表
public Type[] getGenericInterfaces()

下面通过例子说明 getInterfaces() 的使用。至于 getGenericInterfaces() 后面会介绍。

需要注意的是,不能把这里的调用者 Class 对象局限为类对应的 Class 对象,还应该包括接口,基本类型以及 void 对应的 Class 对象。

还有一点需要注意的是,getInterfaces() 方法返回的是 Class<?>[],这是一个数组。这是因为在 Java 中,一个类可以实现多个接口,一个接口可以继承于多个接口。

为了演示这个函数的使用,我们首先需要准备一些接口和类,并且为了便于理解类的关系,会提供一个类图出来。

public interface SuperPower {
}
public interface XRayVision extends SuperPower {
    void seeThroughWalls();
}
public interface SuperHearing extends SuperPower {
    void hearSubtleNoises();
}
public interface SuperSmell extends SuperPower {
    void trackBySmell();
}
public interface SuperHearSmell extends SuperHearing, SuperSmell {
}
public interface Workable {
    void work();
}
public class Man implements Workable {
    @Override
    public void work() {}
}
public class SuperHero extends Man implements XRayVision, SuperHearing, SuperSmell {
    @Override
    public void hearSubtleNoises() {}
    @Override
    public void trackBySmell() {}
    @Override
    public void seeThroughWalls() {}
}

类图如下:
在这里插入图片描述
还需要封装一个打印方法:

private static <T> void printGetInterfaces(Class<T> clazz) {
    Class<?>[] interfaces = clazz.getInterfaces();
    if (interfaces.length == 0) {
        System.out.println("返回数组长度为 0。");
        System.out.println();
        return;
    }
    for (Class<?> element : interfaces) {
        System.out.println(element.getName());
    }
    System.out.println();

测试代码:

对于 SuperHero ,这是一个继承于 Man 类,并且实现了多个接口的类:

printGetInterfaces(SuperHero.class);

打印结果:

com.java.advanced.features.reflect.basicclass.XRayVision
com.java.advanced.features.reflect.basicclass.SuperHearing
com.java.advanced.features.reflect.basicclass.SuperSmell

关于打印结果,需要做一下说明:打印的顺序就是 SuperHero 实现接口的顺序:

public class SuperHero extends Man implements XRayVision, SuperHearing, SuperSmell

所以,我们可以根据索引来获取对应的接口:

Class<?>[] interfaces = SuperHero.class.getInterfaces();
System.out.println("interfaces[0] = " + interfaces[0]);
System.out.println("interfaces[1] = " + interfaces[1]);
System.out.println("interfaces[2] = " + interfaces[2]);

打印信息:

interfaces[0] = interface com.java.advanced.features.reflect.basicclass.XRayVision
interfaces[1] = interface com.java.advanced.features.reflect.basicclass.SuperHearing
interfaces[2] = interface com.java.advanced.features.reflect.basicclass.SuperSmell

对于 SuperHearSmell 接口,它继承了多个接口:

printGetInterfaces(SuperHearSmell.class);

打印信息:

com.java.advanced.features.reflect.basicclass.SuperHearin
com.java.advanced.features.reflect.basicclass.SuperSmell

对于基本类型及void:

printGetInterfaces(int.class);
printGetInterfaces(void.class);

打印结果:

返回数组长度为 0。
返回数组长度为 0。

对于数组类型:

printGetInterfaces(String[].class);

打印结果:

java.lang.Cloneable
java.io.Serializable

总结一下:

对于实现了接口的类,会返回所实现接口的数组,数组中元素顺序和实现顺序一致;
对于继承了接口的接口,会返回所直接继承的接口数组,数组中元素顺序和继承顺序一致;
对于没有实现接口的类或没有继承接口的接口,会返回长度为 0 的数组;
对于基本类型或 void 类型,会返回长度为 0 的数组;
对于数组类型,会返回由 java.lang.Cloneablejava.io.Serializable 组成的数组。

2.6 获取类修饰符

我们会遇到这样的需求,判断一个类是不是 pulibc 的?是不是 static 的?是不是 final 的?

这就需要获取类修饰符。这需要进行两步操作。

第一步要调用 Class 类的 getModifiers() 方法,返回 Class 对象以整数编码的 Java 语言修饰符。

public native int getModifiers();

这个函数返回i一个 int 值,Class 对象包含的所有修饰符都打包在这个 int 值里面了。

第二步要调用 Modifier 类的相关静态函数,来解析上面得到的 int 值,或者判断 int 值里面是否有某个修饰符。

// 是否包含 public 修饰符
public static boolean isPublic(int mod)
// 是否包含 private 修饰符
public static boolean isPrivate(int mod)
// 是否包含 protected 修饰符
public static boolean isProtected(int mod)
// 是否包含 static 修饰符
public static boolean isStatic(int mod)
// 是否包含 final 修饰符
public static boolean isFinal(int mod)
// 是否包含 synchronized 修饰符
public static boolean isSynchronized(int mod)
// 是否包含 volatile 修饰符
public static boolean isVolatile(int mod)
// 是否包含 transient 修饰符
public static boolean isTransient(int mod)
// 是否包含 native 修饰符
public static boolean isNative(int mod)
// 是否包含 interface 修饰符
public static boolean isInterface(int mod)
// 是否包含 abstract 修饰符
public static boolean isAbstract(int mod)
// 是否包含 strictfp 修饰符
public static boolean isStrict(int mod)
// 解析参数 mod 对应的修饰符
public static String toString(int mod) 

需要说明的一点是,Modifier 类不仅仅是应用于解析或判断类修饰符,也包括接口修饰符,变量修饰符,方法修饰符。

下面我们看代码演示:

测试类是 StaticInnerClass

public class OuterClass {
    public static final class StaticInnerClass {
    }
}

StaticInnerClass 包含 3 个修饰符:publicstaticfinal

测试代码:
第一步:获取打包所有修饰符的 int 值:

Class<OuterClass.StaticInnerClass> clazz = OuterClass.StaticInnerClass.class;
int modifiers = clazz.getModifiers();
System.out.println("modifiers = " + modifiers);

打印结果:

modifiers = 25

第二步,调用 Modifier 类的相关函数来解析上一步得到的 int 值或判断是否包含某个修饰符。

System.out.println("Modifier.toString(modifiers) = " + Modifier.toString(modifiers));
System.out.println("Modifier.isPublic(modifiers) = " + Modifier.isPublic(modifiers));
System.out.println("Modifier.isStatic(modifiers) = " + Modifier.isStatic(modifiers));
System.out.println("Modifier.isFinal(modifiers) = " + Modifier.isFinal(modifiers));

打印结果:

Modifier.toString(modifiers) = public static final
Modifier.isPublic(modifiers) = true
Modifier.isStatic(modifiers) = true
Modifier.isFinal(modifiers) = true

我们再看一个接口的例子:

public interface OuterInterface {
    public static interface InnerInterface {
    }
}

获取打包了所有修师符的 int 值:

int modifiers3 = OuterInterface.InnerInterface.class.getModifiers();
System.out.println("modifiers3 = " + modifiers3); // 打印:1545

解析 int 值或判断是否包含某修饰符:

System.out.println("Modifier.toString(modifiers3) = " + Modifier.toString(modifiers3));
System.out.println("Modifier.isPublic(modifiers3) = " + Modifier.isPublic(modifiers3));
System.out.println("Modifier.isAbstract(modifiers3) = " + Modifier.isAbstract(modifiers3));
System.out.println("Modifier.isStatic(modifiers3) = " + Modifier.isStatic(modifiers3));
System.out.println("Modifier.isInterface(modifiers3) = " + Modifier.isInterface(modifiers3));

打印结果:

Modifier.toString(modifiers3) = public abstract static interface
Modifier.isPublic(modifiers3) = true
Modifier.isAbstract(modifiers3) = true
Modifier.isStatic(modifiers3) = true
Modifier.isInterface(modifiers3) = true

2.7 Class 对象的 isXXX() 方法

这些方法比较容易掌握,这里只是把演示代码放出来:

@Deprecated
public class _09_ClassIsXXXMethodTest {
    public static void main(String[] args) {
        // isAnnotation()  是不是注解类型
        System.out.println("Override.class.isAnnotation() = " + Override.class.isAnnotation());
        // isAnnotationPresent(Class<? extends Annotation> annotationClass)
        boolean annotationPresent = _09_ClassIsXXXMethodTest.class.isAnnotationPresent(Deprecated.class);
        System.out.println("annotationPresent = " + annotationPresent);
        // isAnonymousClass()  是不是匿名类
        System.out.println("new Serializable(){}.getClass().isAnonymousClass() = " + new Serializable() {
        }.getClass().isAnonymousClass());
        // isArray() 是不是数组
        System.out.println("String[].class.isArray() = " + String[].class.isArray());
        // isAssignableFrom(Class<?> cls)
        System.out.println("Collection.class.isAssignableFrom(List.class) = " + Collection.class.isAssignableFrom(List.class));
        // isEnum()
        System.out.println("Color.class.isEnum() = " + Color.class.isEnum());
        // isInstance(Object obj)
        List<String> list = new ArrayList<>();
        System.out.println("Collection.class.isInstance(list) = " + Collection.class.isInstance(list));
        // isInterface() 是不是接口
        System.out.println("Serializable.class.isInterface() = " + Serializable.class.isInterface());

        class A {

        }
        // isLocalClass() 是不是局部类,定义在方法内部的类
        System.out.println("A.class.isLocalClass() = " + A.class.isLocalClass());
		// 是不是成员类
        System.out.println("B.class.isMemberClass() = " + B.class.isMemberClass());
        // isPrimitive() 是不是原生类型
        System.out.println("int.class.isPrimitive() = " + int.class.isPrimitive());
    }

    class B {

    }
}

但是,下面的这两个方法需要仔细区分一下,在实际开发中特别容易搞不清楚:

public native boolean isInstance(Object obj);
public native boolean isAssignableFrom(Class<?> cls);

isInstance(Object obj) 方法:判断参数指定的对象是否是调用者 Class 对象所表示的类及其子类的实例

这里我们还是以 Apple 类来说明,它继承 Fruit 类:

public class Fruit {
    public String taste;
}
public class Apple extends Fruit {
}

测试代码:

Apple apple = new Apple();
Fruit fruit = new Fruit();
System.out.println("Fruit.class.isInstance(apple) = " + Fruit.class.isInstance(apple));
System.out.println("Fruit.class.isInstance(fruit) = " + Fruit.class.isInstance(fruit));

运行结果:

Fruit.class.isInstance(apple) = true
Fruit.class.isInstance(fruit) = true

其实,isInstance() 方法和 instanceof 关键字作用是一样的,我们来看一下使用 instanceof 关键字的写法:

System.out.println("apple instanceof Fruit = " + (apple instanceof Fruit));
System.out.println("fruit instanceof Fruit = " + (fruit instanceof Fruit));

打印结果:

apple instanceof Fruit = true
fruit instanceof Fruit = true

接着,isAssignableFrom(Class<?> cls) 方法:它的参数是一个 Class 对象,用于判断调用者Class 对象是否和参数Class 对象为同一类型或者是参数 Class 对象的超类或超接口。

测试代码:

boolean assignableFrom = Fruit.class.isAssignableFrom(Apple.class);
System.out.println("assignableFrom = " + assignableFrom);

运行结果:

assignableFrom = true

2.8 获取数组的元素类型

Class 类里的函数:

public native Class<?> getComponentType();

这个函数的含义是返回类表示的数组的元素类型。如果类不是一个数组,那么返回 null

参看测试代码:

public class _10_GetArrayComponentTypeTest {
    public static void main(String[] args) {
        String[] stringArray = (String[]) Array.newInstance(String.class, 1);
        Class<?> componentType = stringArray.getClass().getComponentType();
        System.out.println(componentType.getName());

        int[] intArray = {1, 2, 3};
        Class<?> componentType1 = intArray.getClass().getComponentType();
        System.out.println(componentType1.getName());

        String notArray = "";
        Class<?> componentType2 = notArray.getClass().getComponentType();
        System.out.println(componentType2);
    }
}

打印信息:

java.lang.String
int
null

3. 最后

本文介绍了基本类的周边信息获取,希望能够帮助到大家。巩固好反射知识,对于阅读框架源码是很有帮助的。

参考

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

willwaywang6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值