反射的理解 #
Reflection(反射)被视为动态语言的关键,反射机制允许程序在**执行期(Runtime)**借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
框架 = 反射 + 注解 + 设计模式
反射机制能提供的功能 #
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
Class类的理解 #
java.lang.Class
类的加载及反射过程如下
-
程序经过
javac.exe命令编译以后,会生成一个或多个字节码文件(.class结尾)。 -
接着我们使用
java.exe命令对某个字节码文件进行解释运行。字节码文件被ClassLoader加载到JVM内存中,此过程就称为类的加载。加载到内存中的类,我们就称为运行时类(Runtime Class),此运行时类,就作为java.lang.Class的一个实例。-
换句话说,Class的实例只对应着加载到内存中的一个运行时类。
-
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
-
一个加载的类在 JVM 中只会有一个Class实例
-
-
此时就可以通过Class类的实例方法获取
Field、Method、Constructor等实例对象完成反射
获取Class实例的几种方式 #
方式一:调用运行时类的属性:class
Class clazz = ClassName.class;
方式二:通过运行时类的对象,调用getClass()方法
Person p = new Person();
Class clazz = p.getClass();
方式三:调用Class的静态方法:forName(String classPath),可能抛出ClassNotFoundException异常
全类名:是
类所属包名 + . + 类名,例如java.lang.String
Class clazz = Class.forName("全类名");
方式四:使用类的加载器
ClassLoader cl = this.getClass().getClassLoader();
Class clazz = cl.loadClass("全类名");
创建类的对象的方式 #
方式一:构造方法,new Constructor()
方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxFactory、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。
方式三:通过反射
Class实例可以是哪些结构的说明 #
-
class:类
-
interface:接口
-
[]:数组 -
enum:枚举
-
annotation:
注解@interface -
primitive type:基本数据类型
-
void
数组的Class实例中,只要类型和维度一样,那么两个Class实例相等
int[] a = new int[10];
int[] b = new int[100];
a.class == b.class
//结果为true
Class类的常用方法 #
static Class forName(String name):根据全类名返回Class对象Object newInstance():调用默认无参构造,创建实例getName():获取Class所代表的结构全类名get Class[] getInterfaces():返回Class对象实现的所有接口的Class对象ClassLoader getClassLoader():返回该类的类加载器Class getSuperclass():返回Class对象的超类Class对象Constuctor[] getConstrctors():返回类的所有构造方法Field[] getDeciaredFields():返回所有属性Method getMethod(String name,Class ... paramTypes):返回该类中参数为Class ... paramTypes的方法Package getPackage():获取包信息
通过Class实例创建类实例 #
Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
使用此方法,要求:
- 运行时类必须提供空参的构造器
- 空参的构造器的访问权限足够。通常,设置为public。
没有无参的构造器就不能创建对象了吗? #
**不是!**只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
- 通过Class类的
getDeclaredConstructor(Class … parameterTypes)取得本类的指定参数类型的构造器 - 通过Constructor实例化对象。
static class Apple{
public Apple(int a){ }
}
public static void main(String[] args) throws Exception {
// 获取Class对象
Class<Apple> appleClass = Apple.class;
// 根据参数获取有参构造
Constructor<Apple> declaredConstructor = appleClass.getDeclaredConstructor(int.class);
// 设置可见性
declaredConstructor.setAccessible(true);
// 创建实例
Apple apple = declaredConstructor.newInstance(1);
}
获取类结构的方法 #
接口、父类 #
Class<?>[] getInterfaces():获取实现的全部接口Class<? Super T> getSuperclass():获取继承的父类
构造器 #
Constructor<T>[] getConstructors():获取有所public构造方法Constructor<T>[] getDeclaredConstructors():获取所有构造方法
Constructor类 #
int getModifiers():获取所有修饰符之和,例如public static,返回值为1 + 8 = 9String getName():获取方法名称Class<?>[] getParameterTypes():获取参数类型
| 修饰符 | 返回值 |
|---|---|
| public | 1 |
| private | 2 |
| protected | 4 |
| static | 8 |
| final | 16 |
| synchronized | 32 |
| volatile | 64 |
| transient | 128 |
| native | 256 |
| interface | 512 |
| abstract | 1024 |
| strict | 2048 |
方法 #
Methos[] getDeclaredMethods():获取所有方法Methos[] getMethos():获取所有public方法
Method类 #
Class<?> getReturnType():获取返回值类型对象Class<?>[] getParameterTypes():获取所有参数类型对象int getModifiers():获取修饰符Class<?>[] getExceptionTypes():获取抛出异常类对象
属性 #
Field[] getFields():获取所有public的属性Field[] getDeclaredFields():获取所有属性
Field类 #
int getModifiers():获取修饰符Class<?> getType():获取属性类型对象String getName():获取属性名称
注解 #
<A extends Annotation> A getAnnotation(Class<A> annotationClass):获取指定类型的注解Annotation[] getAnnotations():获取所有注解
泛型 #
Type getGenericSuperclass():获取父类泛型类型
static class MyList extends ArrayList<Number> {
}
public static void main(String[] args) {
Class<MyList> myListClass = MyList.class;
Class<? super MyList> superclass = myListClass.getSuperclass();
System.out.println(superclass.getName()); // java.util.ArrayList
Type genericSuperclass = myListClass.getGenericSuperclass();
System.out.println(genericSuperclass.getTypeName()); // java.util.ArrayList<java.lang.Number>
}
所在包 #
Package getPackage():获取所在包
调用类的指定结构 #
public class Person{
private String name;
public Person(String name){
this.name = name;
}
public String show(){
System.out.println(name);
}
}
关于setAccessible方法的使用 #
- Method、Field、Constructor对象都有
setAccessible(boolean flag)方法 - 此方法可以启动和禁用访问安全检查(权限)
- 如果参数flag为true,则可以无视private等修饰符进行访问
调用方法 #
Method getDeclaredMethod(String name,Class ... parameterTypes):获取方法Object invoke(Object obj,Object ... args):执行方法- 此方法的返回值就是调用方法的返回值,如没有返回值则为
null - 如果方法为非静态方法,则参数
obj为null - 如果方法权限不足,如private,则需要在调用
invoke()之前,显式的调用setAccessible(true)设为可访问
//一、获取类的Class实例、并获得运行时类的实例
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
//二、获取需要调用的方法,getDeclaredMethod():
//参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
//三、保证当前方法可以访问
show.setAccessible(true);
//四、调用方法的invoke()
//参数1:方法的调用者 参数2:给方法形参赋值的实参
//invoke()的返回值即为对应类中调用的方法的返回值。
Object returnValue = show.invoke(p,"CHN");
调用属性 #
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.设置指定对象的此属性值
name.set(p,"Tom");
//4、获取指定对象的此属性值
System.out.println(name.get(p));
调用指定的构造器 #
Class clazz = Person.class;
//1.获取指定的构造器
//getDeclaredConstructor():参数:指明构造器的参数列表
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象,此时才会进行类加载
Person per = (Person) constructor.newInstance("Tom");