首页 > 技术文章 > JDK反射类Class记录

zhangyjblogs 2021-05-27 20:06 原文

前言

最近在复习dubbo时候,在dubbo自动装配启动时候,ConfigurationClassParser处理内部类时候processMemberClasses(ConfigurationClass, SourceClass)时候,竟然模糊了如何获取内部类,以前学习过,但是没有总结,这次记录下来,后续可以方便查看。毕竟Class的方法容易混淆,容易忘记。

Class类

Class容易混淆的方法记录

getClasses():该方法获取的是包含父类和当前类声明的public类型的内部类,注意是public的

getDeclaredClass():用于外部类获取内部类,比如A类有内部类B,那么通过A.class.getDeclaredClass()方法获取所有内部类,但是不包括父类。该方法返回当前Class声明的public,private ,default,private的内部类,注意不包括父类

getDeclaringClass():用于内部类获取外部类,比如A类有内部类B,那么通过B.class.getDeclaringClass()方法将获取到A的Class对象

getEnclosingClass():用于内部类获取外部类,比getDeclaringClass()多的是可以获取匿名内部类的外部类

代码测试如下

package org.pandu.test;
public class SpiMain {
    private class SpiMainPrivate {
    	public void spiMainPrivate_MethodPublic() {
        }
    	private void spiMainPrivate_MethodPrivate() {
        }
    }
    public class SpiMainPublic {
    	public void spiMainPublic_MethodPublic() {
        }
    	private void spiMainPublic_MethodPrivate() {	
        }
    }
    public void spiMainPublic() {
    }
    private void spiMainPrivate() {
    }
}

package org.pandu.test;
import java.lang.reflect.Method;
import java.util.Arrays;
public class TestMain extends SpiMain {
	private class MainPrivate {
		public void mainPrivate_MethodPublic() {	
        }
    	private void mainPrivate_MethodPrivate() {
        }
    }
    public class MainPublic {
    	public void mainPublic_MethodPublic() { 	
        }
    	private void mainPublic_MethodPrivate() {
        }
    }
    
	public static void main(String[] args) {
		 System.out.println(Arrays.toString(TestMain.class.getClasses()));	//结果 [class org.pandu.test.TestMain$MainPublic, class org.pandu.test.SpiMain$SpiMainPublic]
		 System.out.println(Arrays.toString(TestMain.class.getDeclaredClasses()));	//结果 [class org.pandu.test.TestMain$MainPrivate, class org.pandu.test.TestMain$MainPublic]
		 System.out.println(TestMain.class.getDeclaringClass());	//结果 null
		 System.out.println("-------------------");
		 System.out.println(Arrays.toString(MainPublic.class.getClasses()));	//结果 []
		 System.out.println(Arrays.toString(MainPublic.class.getDeclaredClasses()));	//结果 []
		 System.out.println(MainPublic.class.getDeclaringClass());	//结果 class org.pandu.test.TestMain
		 System.out.println("---------==----------");
		 Method[] methodsTestMain = TestMain.class.getMethods();
		 for(Method method : methodsTestMain) {
			 System.out.println(method.getName() + ":" +method.getDeclaringClass());
			 //method.getDeclaringClass()结果 获取此方法所在的类class
//			 main:class org.pandu.test.TestMain
//			 spiMainPublic:class org.pandu.test.SpiMain
//			 wait:class java.lang.Object
//			 wait:class java.lang.Object
//			 wait:class java.lang.Object
//			 equals:class java.lang.Object
//			 toString:class java.lang.Object
//			 hashCode:class java.lang.Object
//			 getClass:class java.lang.Object
//			 notify:class java.lang.Object
//			 notifyAll:class java.lang.Object
		 }
	}
}
//匿名内部类获取外部类 常见new Runnable{} 类型
public class ClassTest {
	/**
     * 1、Class.GetEnclosingClass
     * <doc>获取对应类的直接外部类Class对象</doc>
     * 2、Class.GetDeclaringClass
     * <doc>获取对应类的声明类Class对象</doc>
     * 3、两者的区别
     * <p>
     * GetEnclosingClass与GetDeclaringClass很相近
     * 两者的区别在于匿名内部类、getEnclosingClass对匿名内部类也有效
     */
    public static void main(String[] args) {
        Class<?> enclosingClass = InnerClass.class.getEnclosingClass();
        System.out.println(enclosingClass.getName());//结果org.pandu.test.ClassTest

        Class<?> declaringClass = InnerClass.class.getDeclaringClass();
        System.out.println(declaringClass.getName());//结果org.pandu.test.ClassTest

        //注意:GetEnclosingClass获取的是直接定义该类的外部类Class实例、这点和getDeclaringClass一致
        Class<?> enclosingClass1 = InnerClass.InnerInnerClass.class.getEnclosingClass();
        System.out.println(enclosingClass1.getName());//结果org.pandu.test.ClassTest$InnerClass

        Class<?> declaringClass1 = InnerClass.InnerInnerClass.class.getDeclaringClass();
        System.out.println(declaringClass1.getName());//结果org.pandu.test.ClassTest$InnerClass

        //针对匿名内部类的测试
        DifferentBetweenClassGetEnclosingClassAndDeclaringClass s = new DifferentBetweenClassGetEnclosingClassAndDeclaringClass();
        HelloService inst = s.getHelloService();
        inst.sayHello();
    }

    private class InnerClass {
        private class InnerInnerClass {
        }
    }

    public interface HelloService {
        void sayHello();
    }

    public static class DifferentBetweenClassGetEnclosingClassAndDeclaringClass {
        HelloService getHelloService() {
            //匿名内部类
            return new HelloService() {

                @Override
                public void sayHello() {
                    System.out.println(this.getClass().getEnclosingClass());//结果 class org.pandu.test.ClassTest$DifferentBetweenClassGetEnclosingClassAndDeclaringClass
                    System.out.println(this.getClass().getDeclaringClass());//结果 null
                }
            };
        }
    }
}

在使用反射对象时比如Method和Field的getDeclaringClass方法将获取到所属类对象,获取声明的类。

getSimpleName getName getCanonicalName区别

package org.pandu.test;
public class ClassNameTest {
	private class InnerClass {
        private class InnerInnerClass {
        }
    }
	public static void main(String[] args) {
		System.out.println("ClassNameTest.class.getSimpleName()-->" + ClassNameTest.class.getSimpleName());
		System.out.println("ClassNameTest.class.getName()-->" + ClassNameTest.class.getName());
		System.out.println("ClassNameTest.class.getCanonicalName()-->" + ClassNameTest.class.getCanonicalName());
		System.out.println();
		
		System.out.println("ClassNameTest.InnerClass.class.getSimpleName()-->" + ClassNameTest.InnerClass.class.getSimpleName());
		System.out.println("ClassNameTest.InnerClass.class.getSimpleName()-->" + ClassNameTest.InnerClass.class.getName());
		System.out.println("ClassNameTest.InnerClass.class.getSimpleName()-->" + ClassNameTest.InnerClass.class.getCanonicalName());
		System.out.println();
		
		System.out.println("ClassNameTest.InnerClass.InnerInnerClass.class.getSimpleName()-->" + ClassNameTest.InnerClass.InnerInnerClass.class.getSimpleName());
		System.out.println("ClassNameTest.InnerClass.InnerInnerClass.class.getSimpleName()-->" + ClassNameTest.InnerClass.InnerInnerClass.class.getName());
		System.out.println("ClassNameTest.InnerClass.InnerInnerClass.class.getSimpleName()-->" + ClassNameTest.InnerClass.InnerInnerClass.class.getCanonicalName());
//		结果
//		ClassNameTest.class.getSimpleName()-->ClassNameTest
//		ClassNameTest.class.getName()-->org.pandu.test.ClassNameTest
//		ClassNameTest.class.getCanonicalName()-->org.pandu.test.ClassNameTest
//
//		ClassNameTest.InnerClass.class.getSimpleName()-->InnerClass
//		ClassNameTest.InnerClass.class.getSimpleName()-->org.pandu.test.ClassNameTest$InnerClass
//		ClassNameTest.InnerClass.class.getSimpleName()-->org.pandu.test.ClassNameTest.InnerClass
//
//		ClassNameTest.InnerClass.InnerInnerClass.class.getSimpleName()-->InnerInnerClass
//		ClassNameTest.InnerClass.InnerInnerClass.class.getSimpleName()-->org.pandu.test.ClassNameTest$InnerClass$InnerInnerClass
//		ClassNameTest.InnerClass.InnerInnerClass.class.getSimpleName()-->org.pandu.test.ClassNameTest.InnerClass.InnerInnerClass

	}
}

getSimpleName():获取的就是最直接的类名

getName():获取到的都是包含路径的类名。内部类有点特殊,getName获取是路径.类名$内部类

getCanonicalName():获取到的都是包含路径的类名。内部类有点特殊,getName获取是路径.类名.内部类

获取注解

//如果此类中存在这样的注释,则使用java.lang.Class类的getAnnotation()方法来获取指定注释类型的注释。该方法以对象的形式返回该类。
//简单理解为获取class上的指定注解对象,可以获取继承注解,但是无法获取重复注解
//传入注解类型,获取该对象上的特定一个注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
    Objects.requireNonNull(annotationClass);

    return (A) annotationData().annotations.get(annotationClass);
}

//根据注解类型,获取该对象上的特定一个注解,无法获取继承下来的注解,也无法获取重复注解
//继承下来的注解?意思就是@inherited注解修饰的注解@A,@A修饰某个类,则该类的子类也被@A修饰
@Override
@SuppressWarnings("unchecked")
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) {
    Objects.requireNonNull(annotationClass);

    return (A) annotationData().declaredAnnotations.get(annotationClass);
}

//功能和getDeclaredAnnotation(Class<A> annotationClass)差不多,只是还可以获取重复注解
@Override
public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) {
    Objects.requireNonNull(annotationClass);

    return AnnotationSupport.getDirectlyAndIndirectlyPresent(annotationData().declaredAnnotations,
                                                             annotationClass);
}

例子

//create a custom Annotation 
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface MyAnnotation {
	// This annotation has two attributes. 
	public String key(); 
	public String value(); 
} 

@Controller
@Service
@MyAnnotation(key = "GFG1", value = "GeeksForGeeks1") 
class ClassA {
}

@RestController
@ComponentScan(value="aa")
@ComponentScan(value="bb")
class ClassB extends ClassA{
}

//ClassB.class.getAnnotation(RestController.class) 结果是RestController对象
//ClassB.class.getAnnotation(MyAnnotation.class) 结果是MyAnnotation对象
//ClassB.class.getDeclaredAnnotation(MyAnnotation.class) 结果是null
//ClassB.class.getDeclaredAnnotation(RestController.class) 结果是RestController对象
//ClassB.class.getDeclaredAnnotation(ComponentScan.class) 结果是null
//ClassB.class.getDeclaredAnnotationsByType(MyAnnotation.class) 结果null
//ClassB.class.getDeclaredAnnotationsByType(ComponentScan.class) 结果是两个ComponentScan对象

PS:继承注解和组合注解

继承注解:@inherited注解修饰的注解@A,@A修饰某个类,则该类的子类也被@A修饰

组合注解:比如spring @RestController上有注解@Controller,这个就是组合注解。

//获取该对象上的所有注解,包括继承注解和重复注解
public Annotation[] getAnnotations() {
    return AnnotationParser.toArray(annotationData().annotations);
}

//获取该对象上的显式标注的所有注解,无法获取继承下来的注解
public Annotation[] getDeclaredAnnotations()  {
    return AnnotationParser.toArray(annotationData().declaredAnnotations);
}

例子

//create a custom Annotation 
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface MyAnnotation {
	// This annotation has two attributes. 
	public String key(); 
	public String value(); 
} 

@Controller
@Service
@MyAnnotation(key = "GFG1", value = "GeeksForGeeks1") 
class ClassA {
}

@RestController
@ComponentScan(value="aa")
@ComponentScan(value="bb")
class ClassB extends ClassA{
}
//输出 ClassB.class.getDeclaredAnnotations(), 只能获取到@RestController这个注解
//输出 ClassB.class.getAnnotations() 获取到@RestController、@MyAnnotation、两个ComponentScan注解,总计四个注解
//与getAnnotation(Class<A> annotationClass)的区别在于可以获取重复注解。两者都可以获取继承注解
public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) {
    Objects.requireNonNull(annotationClass);

    AnnotationData annotationData = annotationData();
    return AnnotationSupport.getAssociatedAnnotations(annotationData.declaredAnnotations,
                                                      this,
                                                      annotationClass);
}
//输出 ClassB.class.getAnnotationsByType(ComponentScan.class) 结果获取到两个ComponentScan注解
//输出 ClassB.class.与getAnnotation(ComponentScan.class) 结果null

//当前Class对象是否被指定的annotationClass注解
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
    return GenericDeclaration.super.isAnnotationPresent(annotationClass);
}

综上发现规律,也经过查询验证

Declared是表示自身,getDeclaredXXX意思是获取自身内部的class、注解、method、field,包括所有访问权限(private public default)。可以翻译为directly present。

直接声明(directly present):
注解在元素上直接声明。用Declared

间接声明(indirectly present):
重复注解在元素上直接声明。规律是ByType

声明(present):

并不是"直接声明"注解和"间接声明"注解的合集,而是"直接声明"注解和父类继承下来的"直接声明"的合集

相关联(associated ):

"关联"是"直接修饰"注解、"间接修饰"注解以及父类继承下来的注解的合集

image-20210527013925243

Class类里面方法规律记录

java.lang.Class中有关getDeclared和get的方法以getDeclaredMethods和getMethods为例说明:
getDeclaredMethod(s):返回自身类的所有公用(public)方法包括私有(private)方法,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
getMethod(s):返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
总之:getDeclaredMethods的关键词是:自身,所有方法,不继承而getMethods的关键词是public 继承

getDeclaredField(s)和getField(s)同method。

getDeclaredAnnotation(s):返回是当前成员所有的注释,不包括继承的。
getAnnotation(s):返回此元素上存在的所有注释,包括继承的所有注释。

关键在于继承的问题上,getDeclaredAnnotations和getAnnotations是否相同,就在于父类的注解是否可继承

而Declaring是内部访问外部,比如Class.getDeclaringClass()返回外部类 java.lang.reflect.Method.getDeclaringClass()获取方法所归属的class

而Enclosing类同Declaring,只是多了匿名内部类获取外部类

获取Class对象的三种区别

测试例子

package org.pandu.test;

public class GetClassTest {
	public static void main(String[] args) throws ClassNotFoundException {
		Class<?> c1 = Person.class;
		System.out.println("-----");
		Class<?> c2 = Class.forName("org.pandu.test.Person");
		System.out.println("-----");
		Class<?> c3 = new Person().getClass();
		System.out.println("-----");
		Class<?> c4 = new Person().getClass();
		System.out.println("-----");
		boolean flag1 = c1==c2 ? true : false;
		boolean flag2 = c3==c4 ? true : false;
		if (flag1 && flag2) {
			System.out.println("result equals");
		}
	}
//	输出结果
//	-----
//	Person:静态代码块
//	-----
//	Person:动态代码块
//	Person:构造方法
//	-----
//	Person:动态代码块
//	Person:构造方法
//	-----
//	result equals
}

class Person {
    static {
        System.out.println("Person:静态代码块");
    }
    {
        System.out.println("Person:动态代码块");
    }
    public Person(){
        System.out.println("Person:构造方法");
    }
}

(1)类名.class:JVM将使用类装载器,将类装入内存(前提是:类还没有装入内存),不做类的初始化工作,返回Class的对象。

(2)Class.forName(“类名字符串”):装入类,并做类的静态初始化,返回Class的对象。

(3)实例对象.getClass():对类进行静态初始化、非静态初始化;返回引用运行时真正所指的对象(子对象的引用会赋给父对象的引用变量中)所属的类的Class的对象。

场景:T.class获取class时候,在不需要执行static{}时候使用,Class.forName(T.class)在需要执行static{}时候使用,比如预先做一些预处理功能。

推荐阅读