一、反射相关的主要API
java.lang.Class:代表一个类 用来描述类的类 (每个类都有构造器、变量、方法等相似点,因此抽象描述为一个Class类)
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
二、java.lang.Class
1.关于java.lang.Class类的理解:
1) 类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。
加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。换句话说,Class的实例就对应着一个运行时类。(万事万物皆对象)
2)加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
2.获取类的方式:(获取Class类的实例)
主要有以下四种,其中第三种最常用:
public void test01() {
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.xxx.java.Person");
//方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.xxx.java.Person");
//以下输出都是true
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
System.out.println(clazz1 == clazz4);
}
3.class实例可以是哪些结构:
public void test02(){
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class; //枚举类
Class c6 = Override.class; //注解
Class c7 = int.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //Class本身
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要数组的元素类型与维度一样,就是同一个Class,输出true
System.out.println(c10 == c11);
}
4.类的加载与ClassLoader
1)类加载过程中的“加载”阶段会将类的class文件读入内存,并为之创建一个Class对象,此过程由类的加载器完成。该Class指的就是java.lang.Class
2)类加载器的作用:
a.类加载:将class文件字节码内容加载到内存中, 并将这些静态数据转换成方法区的运行时数据结构, 然后在堆中生成一个代表这个类的java.lang.Class对象, 作为方法区中类数据的访问入口。
b.类缓存:标准的JavaSE类加载器可以按要求查找类, 但一旦某个类被加载到类加载器中, 它将维持加载(缓存) 一段时间。 不过JVM垃圾回收机制可以回收这些Class对象。(这也就是上面的test01()为什么输出为true)
三、创建运行时类的对象
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常,设置为public。
在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象(很多框架底层就是使用反射创建对象)
2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
public void test03() throws IllegalAccessException, InstantiationException {
Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();
}
四、获取运行时类的完整结构
public void test04(){
Class clazz = Person.class;
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性,包括:权限修饰符,数据类型,变量名
Field[] fields = clazz.getFields();
//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法,
//包括:注解,权限修饰符,返回值类型,方法名(参数类型1 形参名1,...) throws XxxException{}
Method[] methods = clazz.getMethods();
//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
//getConstructors():获取当前运行时类中声明为public的构造器
Constructor[] constructors = clazz.getConstructors();
//getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
//获取运行时的父类
Class superclass = clazz.getSuperclass();
//获取运行时类的带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
//获取运行时类的带泛型的父类的泛型
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(actualTypeArguments[0].getTypeName());
//获取运行时类实现的接口
Class[] interfaces = clazz.getInterfaces();
//获取运行时类的父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
//获取运行时类所在的包
Package pack = clazz.getPackage();
//获取运行时类声明的注解
Annotation[] annotations = clazz.getAnnotations(); }
五、调用运行时类的指定结构
public void test05() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性
//下面的方法不常用,因为只能获取运行时类中声明为public的属性
Field id = clazz.getField("id");
//下面的方法更常用:
//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.获取、设置指定对象的此属性值。
name.set(p,"Tom"); name.get(p);
//获取运行时类中的指定的方法
//1.获取指定的某个方法
//getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表 Method method1=
clazz.getDeclaredMethod("methodName", String.class);
//2.保证当前方法是可访问的
method1.setAccessible(true);
//3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
//invoke()的返回值即为对应类中调用的方法的返回值(和原来方法的返回值一样)。如果调用的运行时类中的方法没有返回值,则此invoke()返回null
Object returnValue = show.invoke(p,"str1");
//如何调用静态方法
Method staticMethod1 = clazz.getDeclaredMethod("methodName2"); staticMethod1.setAccessible(true);
//invoke()方法参数1:可以传入类,也可以传入null 参数2:给方法形参赋值的实参
Object returnVal = staticMethod1.invoke(Person.class);
//获取指定的构造器,通常不使用该方法创建对象,而是直接使用newInstance()方法调用无参构造器,具体见第三点
//getDeclaredConstructor():参数:指明构造器的参数列表
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("Tom"); }