Java筑基——反射(1):基本类周边信息获取
相关文章:
- Java筑基——反射(1):基本类周边信息获取;
- Java筑基——反射(2):泛型周边信息获取;
- Java筑基——反射(3):类内部信息获取
目录
- 1. 前言
- 2. 正文
- 2.1 类的生命周期
- 2.2 获取类类型
- 2.3 获取类名
- 2.4 获取超类的 `Class` 对象
- 2.5 获取直接继承或实现的接口的 `Class` 对象列表
- 2.6 获取类修饰符
- 2.7 `Class` 对象的 `isXXX()` 方法
- 2.8 获取数组的元素类型
- 3. 最后
- 参考
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 loadedClass<?> 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 statssun.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)
方法,第二个参数的值是 false
。boolean 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 {@Overridepublic void work() {}
}
public class SuperHero extends Man implements XRayVision, SuperHearing, SuperSmell {@Overridepublic void hearSubtleNoises() {}@Overridepublic void trackBySmell() {}@Overridepublic 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.Cloneable
和 java.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 个修饰符:public
,static
和 final
。
测试代码:
第一步:获取打包所有修饰符的 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. 最后
本文介绍了基本类的周边信息获取,希望能够帮助到大家。巩固好反射知识,对于阅读框架源码是很有帮助的。
参考
- 从一道面试题来认识java类加载时机与过程;
- 反射中Class.forName()和ClassLoader.loadClass()的区别;
- What is the difference between canonical name, simple name and class name in Java Class?;
- Java中isAssignableFrom()方法与instanceof()方法用法;
- 夯实JAVA基本之二 —— 反射(1):基本类周边信息获取。
Java筑基——反射(1):基本类周边信息获取相关推荐
- 查看患者信息java_Java通过反射查看类的信息示例
本文实例讲述了Java通过反射查看类的信息.分享给大家供大家参考,具体如下: 一 点睛 1 通过反射获取Class对象 使用Class类的forName()静态方法.该方法需要传入字符串参数,该字符串 ...
- java 泛型反射_Java使用反射来获取泛型信息示例
本文实例讲述了Java使用反射来获取泛型信息.分享给大家供大家参考,具体如下: 一 点睛 获得了Field对象后,就可以很容易地获得该Field的数据类型,即使用如下代码即可获得指定Field的类型: ...
- java反射之获取class对象,Java之反射机制(获取Class对象的三种方式)
Java之反射机制(获取Class对象的三种方式) 开发工具与关键技术:MyEclipse 10,java 作者:刘东标 撰写时间:2019-06-14 如何得到各个字节码对应的实例对象? 每个类被加 ...
- java 操作属性值_java反射机制根据属性名获取属性值的操作
一.考虑安全访问范围内的属性,没有权限访问到的属性不读取 /** * 根据属性名获取属性值 * * @param fieldName * @param object * @return */ priv ...
- java 获取运行时参数,Java8增强反射可以在运行时获取参数名
技术公众号:Java In Mind(Java_In_Mind),欢迎关注! 原文:Java8增强反射可以在运行时获取参数名 介绍 在JDK增强意见:JPE 118:Access to Paramet ...
- java通过属性名获取属性值_java反射机制根据属性名获取属性值的操作
java反射机制根据属性名获取属性值的操作,属性,对象,反射,访问权限,还可以 java反射机制根据属性名获取属性值的操作 易采站长站,站长之家为您整理了java反射机制根据属性名获取属性值的操作的相 ...
- java爬取验证码图片_JAVA HttpClient实现页面信息抓取(获取图片验证码并传入cookie实现信息获取)...
JAVA HttpClient实现页面信息抓取(获取图片验证码并传入cookie实现信息获取) 发布时间:2018-05-18 16:41, 浏览次数:632 , 标签: JAVA HttpClien ...
- 高德h5地图api接口_html5通过腾讯地图、高德地图、百度地图开发api接口获取坐标对应的周边信息...
在通过 geolocation 获取到当前的 GPS 坐标后,需要通过"逆地理位置解析"才能得到街道对应的街道.建筑物.周边等相关信息. 下面我使用国内的三家主要的地图厂商(腾讯地 ...
- java获取method,2.5 反射——Class对象功能_获取Method
>[info] 反射--Class对象功能_获取Method * Method:方法对象 * 执行方法: * Object invoke(Object obj, Object... args) ...
最新文章
- sift+图像匹配 算法
- python search函数 中文_python使用正则表达式的search()函数实现指定位置搜索功能...
- 超好用的QQ微信TIM防撤回开源小工具
- #!/bin/sh与#!/bin/bash的区别
- [译]Vulkan教程(20)重建交换链
- Android 创世纪 第三天
- options请求_跨域共享资源(CORS)和OPTIONS 请求
- java 登录md5加密_javaMD5加密及登录验证(备忘)
- java冒泡排序图解_[图解] 冒泡排序
- 关于云桌面的几个常见问题
- Windows平台安装SQLite3数据库
- SRAM、PSRAM、SPI FLASH初步认识
- C++/MFC 面试题(一)
- 马克思主义哲学笔记(三)
- Ubuntu下安装Miniconda
- ppt中加入html,如何在ppt中插入html网页.ppt
- 自编译 micropython ESP32固件指南以及16MB固件分享
- [生存志] 第62节 圣人教子之术
- 木马情报报告:内部抓捕botnet-Dridex
- NuGet Package Explorer
热门文章
- Cesium调用高德地图服务实现搜索地点定位详解
- setcookie各个参数详解
- How to configure Copyright Profiles in IntelliJ IDEA
- Qt Creator打造VScode one dark pro主题配色
- 安全L1-AD.3-DNS代理原理及配置
- XDL-(1)Linux文件操作命令
- java如何接收十六进制_JAVA十六进制数据接收与传输
- 为四川汶川大地震遇难者默哀
- java 类作为参数_如何将类类型作为函数参数传递
- 旧手机利用(flutter+声网RTC+声网RTM)