在上一个小节,介绍了如何获取一个类的class信息,获取class的修饰符,以及获取类中的成员(字段,方法,构造函数),这一章节,将对部分class中提供的方法进行深度解析。
1.Class.getName()和Class.getCanonicalName()以及Class.getSimpleName()有什么区别?
这里先给出一个例子,先观察这个例子的输出结果,就可以看出区别:
/**
* @Project: jdk
* @description: Class.getName()和Class.getCanonicalName()以及Class.getSimpleName()的测试
* @author: sunkang
* @create: 2018-10-04 23:25
* @ModificationHistory who when What
**/
interface Person{
void eat();
}
public class TestClassName {
//这个是一个幂名内部类的实现的一个对象
static Person person = new Person() {
@Override
public void eat() {
System.out.println("eat something");
}
};
class innerClass {
}
public static void main(String[] args) {
//对于普通类
Class test= TestClassName.class;
System.out.println("普通类getName :"+test.getName());
System.out.println("普通类getCanonicalName :"+test.getCanonicalName());
System.out.println("普通类getSimpleName :"+test.getSimpleName());
//对于数组
Class arrayClass = new String[]{}.getClass();
System.out.println("数组getName :"+arrayClass.getName()); //[Ljava.lang.String;
System.out.println("数组getCanonicalName :"+arrayClass.getCanonicalName()); //java.lang.String[]
System.out.println("数组getSimpleName :"+arrayClass.getSimpleName());//String[]
//对于内部类
Class innerClass= innerClass.class;
System.out.println("内部类getName :"+innerClass.getName()); //这个是外部类+$+外部类
System.out.println("内部类getCanonicalName :"+innerClass.getCanonicalName()); //外部类+.+外部类
System.out.println("内部类getSimpleName :"+innerClass.getSimpleName());
//对于匿名内部类
Class clazz = person.getClass();
System.out.println("匿名内部类getName:"+clazz.getName()); //返回com.java.reflect.classes.TestClass$1
System.out.println("匿名内部类getCanonicalName :"+clazz.getCanonicalName()); //返回null
System.out.println("匿名内部类getSimpleName :"+clazz.getSimpleName()); //返回空
}
}
输出结果如下:
普通类getName :com.java.reflect.classes.TestClassName
普通类getCanonicalName :com.java.reflect.classes.TestClassName
普通类getSimpleName :TestClassName
数组getName :[Ljava.lang.String;
数组getCanonicalName :java.lang.String[]
数组getSimpleName :String[]
内部类getName :com.java.reflect.classes.TestClassName$innerClass
内部类getCanonicalName :com.java.reflect.classes.TestClassName.innerClass
内部类getSimpleName :innerClass
匿名内部类getName:com.java.reflect.classes.TestClassName$1
匿名内部类getCanonicalName :null
匿名内部类getSimpleName :
可以发现Class.getSimpleName是得到类的简单信息,重点区分Class.getName()和Class.getCanonicalName()的区别,在普通类中这两个其实是一样的,但是在数组,内部类或者匿名类中输出的结果是不一样的
,从输出结构可以看出这两者的区别,特别是匿名类的时候,getName的结果为com.java.reflect.classes.TestClassName$1而getCanonicalName为null
可以查看getCanonicalName的源码可以发现:
public String getCanonicalName() {
if (isArray()) { //先判断数组
//得到数组的单元的getCanonicalName的名称
String canonicalName = getComponentType().getCanonicalName();
if (canonicalName != null)
return canonicalName + "[]"; //最后加上[]
else
return null;
}
if (isLocalOrAnonymousClass())//如果是匿名内部类,返回null
return null;
Class<?> enclosingClass = getEnclosingClass();//这里这剩下内部类和普通类,得到内部类的外部类
if (enclosingClass == null) { // top level class //这里说明是普通类
return getName();//是普通类,getCanonicalName和getName是一样的
} else {
String enclosingName = enclosingClass.getCanonicalName(); //外部类的名称信息
if (enclosingName == null)
return null;
return enclosingName + "." + getSimpleName();//如果是内部类,那么信息为外部类的信息+ .+内部类的简单的名称
}
}
2.class.getFileds和class.getDeclaredFields的深度解析
先看看下面的例子:
/**
* @Project: jdk
* @description: fileds字段测试
* @author: sunkang
* @create: 2018-10-05 14:45
* @ModificationHistory who when What
**/
public class TestFields extends FiledConstants {
public int age;
public static String addr;
private String email;
public static void main(String[] args) {
Class clazz =TestFields.class;
//得到public的字段,包括继承的父类的,接口的,实现逻辑是先加载本地的,然后加载接口的(采用了递归),然后加载父类的(采用了递归)
Field[] fields= clazz.getFields();
for( Field field : fields){
System.out.println(" publicField :"+field.toGenericString());
}
System.out.println("-----------------------------------");
//得到本地声明的字段
Field[] declaredFields = clazz.getDeclaredFields();
for(Field field : declaredFields){
System.out.println("declaredField :"+field.toGenericString());
}
}
}
class FiledConstants implements interfaceConstants{
public int age;
public String name;
private String phoneNumber;
}
interface interfaceConstants {
int age = 22;
}
输出结果为: 可以发现字段并不能覆盖,age字段存在不同的类中都是可以获取到的。
publicField :public int com.java.reflect.classes.TestFields.age
publicField :public static java.lang.String com.java.reflect.classes.TestFields.addr
publicField :public int com.java.reflect.classes.FiledConstants.age
publicField :public java.lang.String com.java.reflect.classes.FiledConstants.name
publicField :public static final int com.java.reflect.classes.interfaceConstants.age
-----------------------------------
declaredField :public int com.java.reflect.classes.TestFields.age
declaredField :public static java.lang.String com.java.reflect.classes.TestFields.addr
declaredField :private java.lang.String com.java.reflect.classes.TestFields.email
思考一下: class.getFileds是如何获取父类和以及接口的公开的字段的,怎么实现的?实现过程中运用到了哪些思想?
带着这个问题我们先看看class.getFileds这个方法:源码这样写的
@CallerSensitive
public Field[] getFields() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyFields(privateGetPublicFields(null));//copyFileds运用了深克隆
}
private Field[] privateGetPublicFields(Set<Class<?>> traversedInterfaces) {
checkInitted();
Field[] res;
ReflectionData<T> rd = reflectionData(); //ReflectionData对象缓存了方法,字段,构造函数等信息
if (rd != null) {//先判断缓存的对象是否存在
res = rd.publicFields;
if (res != null) return res;//在判断缓存对象的publicFields是否有值,有直接返回
}
// No cached value available; compute value recursively.
// Traverse in correct order for getField().
List<Field> fields = new ArrayList<>();
if (traversedInterfaces == null) {
traversedInterfaces = new HashSet<>();
}
// Local fields 先加载本地的字段
Field[] tmp = privateGetDeclaredFields(true);//得到公共的字段
addAll(fields, tmp); //放入fields集合中
// Direct superinterfaces, recursively
for (Class<?> c : getInterfaces()) { //得到接口的类
if (!traversedInterfaces.contains(c)) { //对重复接口进行删除
traversedInterfaces.add(c); //
addAll(fields, c.privateGetPublicFields(traversedInterfaces)); //利用递归加入字段
}
}
// Direct superclass, recursively
if (!isInterface()) { //判断该类是否是一个接口,如果不是一个接口,走下面的判断
Class<?> c = getSuperclass(); //得到父类
if (c != null) {
addAll(fields, c.privateGetPublicFields(traversedInterfaces)); //递归得到父类的字段信息
}
}
res = new Field[fields.size()];
fields.toArray(res); //转换为数组
if (rd != null) {
rd.publicFields = res; //对共有的字段进行缓存
}
return res;
}
接下来先看ReflectionData这个类缓存了哪些东西 ?
private static class ReflectionData<T> {
volatile Field[] declaredFields; //声明的字段 (本类的)
volatile Field[] publicFields; // 公开的字段(包括继承和接口的)
volatile Method[] declaredMethods; //同上
volatile Method[] publicMethods;//同上
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields; // 得到一个类中的中间的结果,声明并且是公开的
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
先看看reflectionData()方法是干嘛的?,这里的ReflectionData是一个软引用,也就是当jvm内存不足的时候会发生内存的回收,所以这个缓存是在jvm内存不足的时候发生的回收的,这个做法是可以借鉴的。
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
if (useCaches && //默认useCaches 为true
reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd; //返回缓存对象
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
return newReflectionData(reflectionData, classRedefinedCount); //创建新的缓存对象
}
其他的细节,可以自己参考源码
总结: 可以借鉴的点
递归思想
: class.getFileds先得到本地的字段信息,然后去得到该类的接口和父类信心,利用递归思想,得到每一层的接口的字段或者是父类的字段缓存
: ReflectionData是一个方法级别的缓存,当同时调用class.getFileds两遍的时候,第二遍会直接返回缓存的对象,并且这个缓存是软引用,当jvm内存不足的时候会发生回收,这里不用强引用,用软引用说明是可以回收的缓存深克隆
:copyFields()方法运用了深度克隆,杜绝了这个类的字段操作对其他的字段的影响
2.class.getMethods()和 class.getDeclaredMethods()以及 class.getConstructors()和class.getDeclaredConstructors()的深度解析
类比字段的获取信息,基本这两个基本是一样的,但是方法获取的时候,需要
排除子类覆盖父类方法的对应父类方法
排除子类实现了接口方法的对应接口方法
3.class.newInstance()深度解析
还是上一节的例子:
public class ClassTrouble {
public static void main(String... args) {
try {
Class<?> c = Class.forName("com.java.reflect.classes.Cls");
c.newInstance(); // InstantiationException
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
class Cls {
private Cls() {}
}
输出结果为:
java.lang.IllegalAccessException: Class com.java.reflect.classes.ClassTrouble can not access a member of class com.java.reflect.classes.Cls with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.Class.newInstance(Class.java:436)
at com.java.reflect.classes.ClassTrouble.main(ClassTrouble.java:10)
来进一步来了解class.newInstance(),首先先看到源码newInstance部分:
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) { //这里是安全管理器不用管,一般默认为null
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};//构造一个空class数组,模拟一个无参的构造函数的参数
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);//先得到一个无参的构造函数,这里可以是私有的,也可以是共有的无参构造函数
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true); //设置构造函数可以访问
return null;
}
});
cachedConstructor = c; //替换过去
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();//得到修饰符
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {//this代表class对象,modifiers 为构造函数的修饰符,在上个例子中,类是可以访问的,构造函数是私有的,所以这里调用之后是false
Class<?> caller = Reflection.getCallerClass();//得到上级的调用类
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);//严格确保成员是否可以访问,类的访问权限为protect ,构造函数为私有,不可以访问,这里会抛出异常
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
return tmpConstructor.newInstance((Object[])null); //实质上是调用了构造函数的newInstance的方法
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
先看看 Reflection.ensureMemberAccess的方法
public static void ensureMemberAccess(Class<?> var0, Class<?> var1, Object var2, int var3) throws IllegalAccessException {
if (var0 != null && var1 != null) {
if (!verifyMemberAccess(var0, var1, var2, var3)) { //这里验证失败会抛出异常,与测试结果的异常是一样的,verifyMemberAccess这里的验证逻辑可以自己去看一下,基本遵循了java的访问权限的控制
throw new IllegalAccessException("Class " + var0.getName() + " can not access a member of class " + var1.getName() + " with modifiers \"" + Modifier.toString(var3) + "\"");
}
} else {
throw new InternalError();
}
}
总结:
class.newInstance()调用过程是先得到无参的构造函数
,然后验证该构造函数是否有访问权限
(访问权限参考下图),如果没有则抛出异常,本质上是调用Construtor.newInstance()方法
。
4. class对象获取注解的案例
需要注意一点的class.getDeclaredAnnotations()和class.getAnnotations()输出结果是一样的,是返回指定类上面的所有注解
/**
* @Project: jdk
* @description: 注解的测试
* @author: sunkang
* @create: 2018-10-05 17:00
* @ModificationHistory who when What
**/
@Test1(name = "test1")
public class TestAnnotations extends ParentAnnotations {
public static void main(String[] args) {
Class annoClass = TestAnnotations.class;
//1. 得到指定类的所有注解
Annotation[] declaredAnnotations = annoClass.getDeclaredAnnotations();//得到这个类上的直接注解,不包含继承的注解
for(Annotation annotation : declaredAnnotations ){
System.out.println(annotation.annotationType()); //对应注解的类型信息
}
Annotation[] annotations = annoClass.getAnnotations();//与getDeclaredAnnotations()方式相同
for(Annotation annotation : annotations ){
System.out.println(annotation.annotationType());
}
//2. 得到指定的注解
if(annoClass.isAnnotationPresent(Test1.class)){ //判断指定类是否存在注解
Test1 anotation = (Test1) annoClass.getAnnotation(Test1.class); //得到指定的注解
System.out.println(anotation.name());
}
}
}
@Test2
class ParentAnnotations{
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Test1{
String name() default "";
}
@Retention(RetentionPolicy.RUNTIME) //说明在运行期间生效
@Target({ElementType.TYPE,ElementType.METHOD})//target 表示指定在哪些目标,比如类,接口,枚举上面,或者是方法上面
@interface Test2{
}
测试结果如下:
interface com.java.reflect.classes.Test1
interface com.java.reflect.classes.Test1
test1