#0
鸽了很久的公众号文章终于开始更新的内容了,从标题就能看出,将来几期的源码阅读将进入深水区。一共会安排4期文章讲解Class,2-3期文章讲解ClassLoader。通过对Class类的讲解,能够串联起很多Java的语法知识。
在Java应用运行的时候,一切都是对象。「对象」通过将编译器编写的「以.java为后缀的文件」编译成「以.class为后缀的文件」,然后以.class为后缀的文件被导入虚拟机,所有的对象都是通过这份根据这份文件(又被称为「字节码文件」)而生成的。
为了能够动态获取对象对应类的信息以及动态调用对象对应类的方法,Java提供了「反射机制」。以调用方法为例,这里的动态指的是在「运行期」,能够根据代码逻辑调用对应的方法。对应的静态就是在编写代码的时候,通过[.操作符]调用方法。
Class的设计是为了支持反射机制,但是设计Class又与.class的文件格式息息相关,因此这篇文章将从虚拟机的.class文件格式开始,简单介绍.class字节码文件包含了哪些信息,随后将逐行分析Class类的代码。
老规矩,这里奉上思维导图。
Class源码思维导图
#1
根据《Java虚拟机规范》,.class文件包括以下几部分的信息
- magic
- 确定这段二进制流是.class文件
- minor_version & major_version
- .class文件的主版本号与次版本号
- constant_pool_count & contant_pool
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
- 文本字符串
- 声明为final的常量值
- 字面量
- 符号引用
- access_flags
- 访问标志,用于标识一些类或者接口层次的访问信息
- this_class
- 类索引,表示这个类的全限定名
- super_class
- 父类索引,表示父类的全限定名
- interfaces_count & interfaces
- 接口索引集合,描述这个类实现了哪些接口
- fields_count & fields
- 字段表集合,用于描述接口或类中声明的变量
- methods_count & methods
- 方法表集合,用于描述接口或类中声明的方法
- attributes_count & attributes
- 用于描述某些场景专有的信息
这部分需要对Java语言有一定的了解,如果对有些地方有疑问的建议阅读《深入理解Java虚拟机》这本书。
在Java语言规范中规定,除了以关键字class声明的Class类,枚举(enum),数组(array),基本类型,接口(interface)和注解(annotation)在JVM中都用.class文件进行描述,这些都属于Class类。因此在Class类中提供对应的方法进行判断。
// 接口
public native boolean isInterface();
// 数组
public native boolean isArray();
// 基本类型
public native boolean isPrimitive();
// 注解
public boolean isAnnotation() {
return (getModifiers() & ANNOTATION) != 0;
}
// 枚举
public boolean isEnum() {
return (this.getModifiers() & ENUM) != 0 &&
this.getSuperclass() == java.lang.Enum.class;
}
// 生成的
public boolean isSynthetic() {
return (getModifiers() & SYNTHETIC) != 0;
}
// 是否是参数cls的父类或父接口
public native boolean isAssignableFrom(Class> cls);
// 是否是实例
public native boolean isInstance(Object obj);
// 接口
public native boolean isInterface();
// 数组
public native boolean isArray();
// 基本类型
public native boolean isPrimitive();
// 注解
public boolean isAnnotation() {
return (getModifiers() & ANNOTATION) != 0;
}
// 枚举
public boolean isEnum() {
return (this.getModifiers() & ENUM) != 0 &&
this.getSuperclass() == java.lang.Enum.class;
}
// 生成的
public boolean isSynthetic() {
return (getModifiers() & SYNTHETIC) != 0;
}
// 是否是参数cls的父类或父接口
public native boolean isAssignableFrom(Class> cls);
// 是否是实例
public native boolean isInstance(Object obj);
isSynthetic()
通过getModifiers()
返回的结果判断这个类是否是由编译器生成的
isAssignableFrom()
用于判断继承关系,在.class文件中用于判断继承关系的信息是this_class、super_class和interfaces
isInstance()
判断参数obj是否是Class的实例。
.class文件格式中有一个信息字段叫access_flags,这个参数在Class.java中通过getModifiers()
获取。Java中通过一个整型数字表示,修饰符的种类和对应的数值如下所示。
//PUBLIC: 1
//PRIVATE: 2
//PROTECTED: 4
//STATIC: 8
//FINAL: 16
//SYNCHRONIZED: 32
//VOLATILE: 64
//TRANSIENT: 128
//NATIVE: 256
//INTERFACE: 512
//ABSTRACT: 1024
//STRICT: 2048
public native int getModifiers();
//PUBLIC: 1
//PRIVATE: 2
//PROTECTED: 4
//STATIC: 8
//FINAL: 16
//SYNCHRONIZED: 32
//VOLATILE: 64
//TRANSIENT: 128
//NATIVE: 256
//INTERFACE: 512
//ABSTRACT: 1024
//STRICT: 2048
public native int getModifiers();
前三个修饰符(PUBLIC、PRIVATE、PROTECTED)加上隐式修饰符FRIENDLY(这个修饰符关键字并不存在,只是用来表示上述三个修饰符不存在的情况)用来表示类的访问权限。
作用域 | 当前类 | 同package | 子类 | 其他package |
public | 允许 | 允许 | 允许 | 允许 |
protected | 允许 | 允许 | 允许 | 不允许 |
friendly | 允许 | 允许 | 不允许 | 不允许 |
private | 允许 | 不允许 | 不允许 | 不允许 |
以Text类作为当前类
static,静态修饰符,被修饰的类,方法,变量,代码块存放在方法区。方法区中包含的(类,方法,变量,代码块)是整个程序运行中唯一存在的,并且被所有线程共享。
final,不变修饰符。
- 当类被final修饰时,意味着这个类无法被继承。
- 当变量被final修饰时,意味着这个变量的引用无法被修改
- 当方法被final修饰时,意味这个方法无法被重载
synchronized,同步修饰符。
- 当普通方法被修饰时,进入同步代码时要获取当前实例的锁
- 当类被修饰时(class),进入同步代码块要获取当前类对象(Class)的锁
- 当方法块被修饰时,进入同步代码块要获取给定对象的锁
volatile,可见性修饰符。被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。
transient,序列化修饰符。被transient修饰的变量将不会被序列化。
native,本地修饰符。被修饰的方法需要调用外部的本地C代码库对应的API。
interface,接口修饰符。被interface修饰的类是接口。
abstract,抽象类修饰符。被abstract修饰的类是抽象类。
strict,精确浮点修饰符。当一个class或interface用strictfp声明,内部所有的float和double表达式都会成为符合IEEE-754规范的精确浮点。有关IIIE754浮点数规范可以参考Java源码阅读(六)——Double
#10
为了支持反射机制,Class文件提供了在运行时获取Class信息的方法forName()
。
// 根据类的全限定名获取Class实例
// 等价于Class.forName(className,true,currentLoader)
public static Class> forName(String className)
throws ClassNotFoundException {
Class> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
// 根据类的全限定名获取Class实例
// 等价于Class.forName(className,true,currentLoader)
public static Class> forName(String className)
throws ClassNotFoundException {
Class> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
这里简单解释下getCallerClass()
的作用,目的是返回调用者的类,见名知义。
forName()
根据给定的classLoader和全限定名获得Class实例,如果loader是null,那么这个类通过Bootstrap classLoader进行加载。
class只有在下面这种情况下才会被初始化:参数initialize为true并且类还没有被初始化。
forName()
不能用于获取表示原始类型或void的class对象。
如果类的全限定名表示数组,数组元素的类型被加载但是没有被初始化。
如果参数loader是null,存在安全管理器,并且通过Reflection.getCallerClass()
返回的caller不为null。那么在调用安全管理器的checkPermission确保访问Bootstrap classLoader没问题后,调用forName0()
获取Class信息。
public static Class> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
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);
}
private static native Class> forName0(String name, boolean initialize,
ClassLoader loader,
Class> caller)
throws ClassNotFoundException;
public static Class> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
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);
}
private static native Class> forName0(String name, boolean initialize,
ClassLoader loader,
Class> caller)
throws ClassNotFoundException;
Class提供方法newInstance()
,这个方法支持在通过类的全限定名获取Class信息后生成对象。这个操作等同于通过关键字new生成对象。
newInstance()
是通过获得构造函数的信息,然后通过构造函数实现对象的实例化操作。为了提升性能,Class会将构造函数和调用者的类进行「缓存」
// 用作缓存的私有变量
private volatile transient Constructor cachedConstructor;
private volatile transient Class> newInstanceCallerCache;
public T newInstance()
throws InstantiationException, IllegalAccessException
{
// 首先确保调用者的类拥有调用以public开头方法的权限if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}if (cachedConstructor == null) {if (this == Class.class) {
throw new IllegalAccessException("Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class>[] empty = {};
// 获取构造函数
final Constructor c = getConstructor0(empty, Member.DECLARED);
// 给构造函数赋予可被调用的权限
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Void run() {
c.setAccessible(true);return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
// 构造函数都是无参构造函数
// 这也是为什么编译器会自动生成无参构造函数
Constructor tmpConstructor = cachedConstructor;
int modifiers = tmpConstructor.getModifiers();if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
// 获取调用者,并确认调用者的访问权限
Class> caller = Reflection.getCallerClass();if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
try {
// 通过构造函数完成对象的实例化return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());return null;
}
}
// 用作缓存的私有变量
private volatile transient Constructor cachedConstructor;
private volatile transient Class> newInstanceCallerCache;
public T newInstance()
throws InstantiationException, IllegalAccessException
{
// 首先确保调用者的类拥有调用以public开头方法的权限if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}if (cachedConstructor == null) {if (this == Class.class) {
throw new IllegalAccessException("Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class>[] empty = {};
// 获取构造函数
final Constructor c = getConstructor0(empty, Member.DECLARED);
// 给构造函数赋予可被调用的权限
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Void run() {
c.setAccessible(true);return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
// 构造函数都是无参构造函数
// 这也是为什么编译器会自动生成无参构造函数
Constructor tmpConstructor = cachedConstructor;
int modifiers = tmpConstructor.getModifiers();if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
// 获取调用者,并确认调用者的访问权限
Class> caller = Reflection.getCallerClass();if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
try {
// 通过构造函数完成对象的实例化return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());return null;
}
}
newInstance()
会遇到几个异常情况:
- 对java.lang.Class调用newInstance()进行实例化
- 调用checkMemberAccess时如果没有「PUBLIC级别的访问权限」会抛出异常AccessControlException
- 如果类的定义「没有无参构造函数则」会抛出异常NoSuchMethodException
- 在对无参构造函数赋予可被访问的权限时(c.setAccessible),但是「对象的访问权限无法被更改」时,会抛出异常SecurityException
- 如果调用者没有调用该无参构造函数的权限,则会抛出异常IllegalAccessException
- 在调用无参构造函数的newInstance时也会出现异常
11
Class提供的反射机制都围绕着「类的全限定名」而展开。全限定名是用于标识类的一种方式,以Thread为例,其全限定名是java.lang.Thread。除了全限定名,还有其他用字符串表示Class名称的方式。
支持返回Class名称的方法有
- getName()
- 返回的是「全限定名」
- 虚拟机中Class对象的唯一标识
- getSimpleName()
- 返回的是「类的简称」
- 不包含路径,因此不能保证能唯一标识一个类
- getCanonicalName()
- 返回Java语言规范定义的底层类的「规范名称」
- 返回的是包含路径的类名
- getTypeName()
- 返回「该类类型的名称」
为了更好地理解上述几种方法,在这之前介绍几个方法。
// 用于获取数组中元素的类型
public native Class> getComponentType();
// 判断字符是否是数字
private static boolean isAsciiDigit(char c) {
return '0' <= c && c <= '9';
}
// 获取内部类的名字
private String getSimpleBinaryName() {
Class> enclosingClass = getEnclosingClass();
// 意味着是顶级类
if (enclosingClass == null)
return null;
try {
return getName().substring(enclosingClass.getName().length());
} catch (IndexOutOfBoundsException ex) {
throw new InternalError("Malformed class name", ex);
}
}
// 用于获取数组中元素的类型
public native Class> getComponentType();
// 判断字符是否是数字
private static boolean isAsciiDigit(char c) {
return '0' <= c && c <= '9';
}
// 获取内部类的名字
private String getSimpleBinaryName() {
Class> enclosingClass = getEnclosingClass();
// 意味着是顶级类
if (enclosingClass == null)
return null;
try {
return getName().substring(enclosingClass.getName().length());
} catch (IndexOutOfBoundsException ex) {
throw new InternalError("Malformed class name", ex);
}
}
在这篇文章中将不会涉及内部类的内容,这部分将放在后面讲解。getSimpleBinaryName()
的作用在于通过getSimpleBinaryName
返回的字符串判断这个类是顶级类还是内部类。
getName
简单明了,实际上就是通过本地方法获取全限定名。全限定名有一套标准且复杂的规定
- 如果是内部类,则使用$符号进行连接
- 在只有一层内部类的情况下
- 符号左侧是顶级类,符号的右侧是内部类
- 如果是数组,则使用[来表示,数组是几维,[就有几个
- 其余情况
- boolean -> Z
- byte -> B
- char -> C
- class or interface -> Lclassname
- double -> D
- float -> F
- int -> I
- long -> S
- short -> S
public String getName() {
String name = this.name;
if (name == null)
this.name = name = getName0();
return name;
}
private transient String name;
private native String getName0();
public String getName() {
String name = this.name;
if (name == null)
this.name = name = getName0();
return name;
}
private transient String name;
private native String getName0();
getSimpleName
这里的特殊情况就是内部类是如何表示的。按照Java语言规范,内部类的全限定名是在顶级类后面接[符号$]。
- 如果内部类是成员内部类或静态内部类,符号$接上「简单类名」
- 如果内部类是局部内部类,符号$接上简单类再接「一个或多个数字」
- 如果内部类是匿名内部类,符号$接上「一个或多个数字」
public String getSimpleName() {
// 数组特殊处理,形如int[]
if (isArray())
return getComponentType().getSimpleName()+"[]";
String simpleName = getSimpleBinaryName();
if (simpleName == null) {
// 如果是顶级类,通过getName获取
simpleName = getName();
// 然后截取最后一部分,java.lang.Thread => Thread
return simpleName.substring(simpleName.lastIndexOf(".")+1);
}
// 如果是内部类
int length = simpleName.length();
if (length '$')
throw new InternalError("Malformed class name");
int index = 1;
while (index index++;
return simpleName.substring(index);
}
public String getSimpleName() {
// 数组特殊处理,形如int[]
if (isArray())
return getComponentType().getSimpleName()+"[]";
String simpleName = getSimpleBinaryName();
if (simpleName == null) {
// 如果是顶级类,通过getName获取
simpleName = getName();
// 然后截取最后一部分,java.lang.Thread => Thread
return simpleName.substring(simpleName.lastIndexOf(".")+1);
}
// 如果是内部类
int length = simpleName.length();
if (length '$')
throw new InternalError("Malformed class name");
int index = 1;
while (index index++;
return simpleName.substring(index);
}
getCanonicalName
这个方法和getName()
的区别在三处:
- 数组将是「全限定名+[]」
- 成员内部类和静态内部类将全限定名中的$替换成[.]
- 匿名内部类和局部内部类将返回「null」
public String getCanonicalName() {
// 如果是数组,返回的数组元素类的CanonicalName
if (isArray()) {
String canonicalName = getComponentType().getCanonicalName();
if (canonicalName != null)
return canonicalName + "[]";
else
return null;
}
// 如果是本地类或匿名类,那么返回null
if (isLocalOrAnonymousClass())
return null;
Class> enclosingClass = getEnclosingClass();
// 如果是顶级类,那么返回全限定名
if (enclosingClass == null) {
return getName();
// 如果是内部类,那么会将符号$替换成.
} else {
String enclosingName = enclosingClass.getCanonicalName();
if (enclosingName == nu
return null;
return enclosingName + "." + getSimpleName();
}
}
public String getCanonicalName() {
// 如果是数组,返回的数组元素类的CanonicalName
if (isArray()) {
String canonicalName = getComponentType().getCanonicalName();
if (canonicalName != null)
return canonicalName + "[]";
else
return null;
}
// 如果是本地类或匿名类,那么返回null
if (isLocalOrAnonymousClass())
return null;
Class> enclosingClass = getEnclosingClass();
// 如果是顶级类,那么返回全限定名
if (enclosingClass == null) {
return getName();
// 如果是内部类,那么会将符号$替换成.
} else {
String enclosingName = enclosingClass.getCanonicalName();
if (enclosingName == nu
return null;
return enclosingName + "." + getSimpleName();
}
}
getTypeName
这个方法和getName()
的不同体现在对数组的处理上。它会获取数组,或者多维数组中元素类型的全限定名,然后在末尾添加[]。
public String getTypeName() {
if (isArray()) {
try {
Class> cl = this;
int dimensions = 0;
while (cl.isArray()) {
dimensions++;
cl = cl.getComponentType();
}
StringBuilder sb = new StringBuilder();
sb.append(cl.getName());
for (int i = 0; i sb.append("[]");
}
return sb.toString();
} catch (Throwable e) { /*FALLTHRU*/ }
}
return getName();
}
public String getTypeName() {
if (isArray()) {
try {
Class> cl = this;
int dimensions = 0;
while (cl.isArray()) {
dimensions++;
cl = cl.getComponentType();
}
StringBuilder sb = new StringBuilder();
sb.append(cl.getName());
for (int i = 0; i sb.append("[]");
}
return sb.toString();
} catch (Throwable e) { /*FALLTHRU*/ }
}
return getName();
}
最后放一些例子,让读者直观地感受这四个方法的区别。
public class Text {
public static void main(String[] args) throws InterruptedException {
// 普通类,接口,注解,基本类型,枚举,数组
int[] myArray = new int[2];
// 数组
System.out.println("============Array============");
System.out.println(myArray.getClass().getName());// [I
System.out.println(myArray.getClass().getSimpleName());// int[]
System.out.println(myArray.getClass().getCanonicalName());// int[]
System.out.println(myArray.getClass().getTypeName());// int[]
// 普通类
System.out.println("============Class============");
System.out.println(MyClass.class.getName());// Class.MyClass
System.out.println(MyClass.class.getSimpleName());// MyClass
System.out.println(MyClass.class.getCanonicalName());// Class.MyClass
System.out.println(MyClass.class.getTypeName());// Class.MyClass
// 成员内部类
System.out.println("============Member Inner Class============");
System.out.println(MyClass.MyInnerClass.class.getName());// Class.MyClass$MyInnerClass
System.out.println(MyClass.MyInnerClass.class.getSimpleName());// MyInnerClass
System.out.println(MyClass.MyInnerClass.class.getCanonicalName());// Class.MyClass.MyInnerClass
System.out.println(MyClass.MyInnerClass.class.getTypeName());// Class.MyClass$MyInnerClass
// 匿名内部类
System.out.println("============Anonymous Class============");
System.out.println(new Object(){}.getClass().getName());// Class.Text$1
System.out.println(new Object(){}.getClass().getSimpleName());//
System.out.println(new Object(){}.getClass().getCanonicalName());// null
System.out.println(new Object(){}.getClass().getTypeName());// Class.Text$4
// 接口
System.out.println("============Interface============");
System.out.println(MyInterface.class.getName());// Class.MyInterface
System.out.println(MyInterface.class.getSimpleName());// MyInterface
System.out.println(MyInterface.class.getCanonicalName());// Class.MyInterface
System.out.println(MyInterface.class.getTypeName());// Class.MyInterface
// 注解
System.out.println("============Annotation============");
System.out.println(MyAnnotation.class.getName());// Class.MyAnnotation
System.out.println(MyAnnotation.class.getSimpleName());// MyAnnotation
System.out.println(MyAnnotation.class.getCanonicalName());// Class.MyAnnotation
System.out.println(MyAnnotation.class.getTypeName());// Class.MyAnnotation
// 基本数据类型
System.out.println("============Primitive============");
System.out.println(int.class.getName());// int
System.out.println(int.class.getSimpleName());// int
System.out.println(int.class.getCanonicalName());// int
System.out.println(int.class.getTypeName());// int
// 枚举
System.out.println("============Enum============");
System.out.println(MyEnum.class.getName());// Class.MyEnum
System.out.println(MyEnum.class.getSimpleName());// MyEnum
System.out.println(MyEnum.class.getCanonicalName());// Class.MyEnum
System.out.println(MyEnum.class.getTypeName());// Class.MyEnum
}
}
public class Text {
public static void main(String[] args) throws InterruptedException {
// 普通类,接口,注解,基本类型,枚举,数组
int[] myArray = new int[2];
// 数组
System.out.println("============Array============");
System.out.println(myArray.getClass().getName());// [I
System.out.println(myArray.getClass().getSimpleName());// int[]
System.out.println(myArray.getClass().getCanonicalName());// int[]
System.out.println(myArray.getClass().getTypeName());// int[]
// 普通类
System.out.println("============Class============");
System.out.println(MyClass.class.getName());// Class.MyClass
System.out.println(MyClass.class.getSimpleName());// MyClass
System.out.println(MyClass.class.getCanonicalName());// Class.MyClass
System.out.println(MyClass.class.getTypeName());// Class.MyClass
// 成员内部类
System.out.println("============Member Inner Class============");
System.out.println(MyClass.MyInnerClass.class.getName());// Class.MyClass$MyInnerClass
System.out.println(MyClass.MyInnerClass.class.getSimpleName());// MyInnerClass
System.out.println(MyClass.MyInnerClass.class.getCanonicalName());// Class.MyClass.MyInnerClass
System.out.println(MyClass.MyInnerClass.class.getTypeName());// Class.MyClass$MyInnerClass
// 匿名内部类
System.out.println("============Anonymous Class============");
System.out.println(new Object(){}.getClass().getName());// Class.Text$1
System.out.println(new Object(){}.getClass().getSimpleName());//
System.out.println(new Object(){}.getClass().getCanonicalName());// null
System.out.println(new Object(){}.getClass().getTypeName());// Class.Text$4
// 接口
System.out.println("============Interface============");
System.out.println(MyInterface.class.getName());// Class.MyInterface
System.out.println(MyInterface.class.getSimpleName());// MyInterface
System.out.println(MyInterface.class.getCanonicalName());// Class.MyInterface
System.out.println(MyInterface.class.getTypeName());// Class.MyInterface
// 注解
System.out.println("============Annotation============");
System.out.println(MyAnnotation.class.getName());// Class.MyAnnotation
System.out.println(MyAnnotation.class.getSimpleName());// MyAnnotation
System.out.println(MyAnnotation.class.getCanonicalName());// Class.MyAnnotation
System.out.println(MyAnnotation.class.getTypeName());// Class.MyAnnotation
// 基本数据类型
System.out.println("============Primitive============");
System.out.println(int.class.getName());// int
System.out.println(int.class.getSimpleName());// int
System.out.println(int.class.getCanonicalName());// int
System.out.println(int.class.getTypeName());// int
// 枚举
System.out.println("============Enum============");
System.out.println(MyEnum.class.getName());// Class.MyEnum
System.out.println(MyEnum.class.getSimpleName());// MyEnum
System.out.println(MyEnum.class.getCanonicalName());// Class.MyEnum
System.out.println(MyEnum.class.getTypeName());// Class.MyEnum
}
}