Java反射
![](/img/default_cover.jpg)
Java反射
笙沉一、反射概述
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过镜子可以看到类的结构,所以,我们形象的称之为:反射。
- 动态语言:
- 是一类再运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以删除或是其他结构上的变化,通俗来说就是在运行时,代码可以根据某些条件改变自身结构。
- 如:C#、JavaScript、PHP、Python、Erlang。
- 静态语言:
- 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
- Java不是动态语言,但是Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程时更加灵活。
- Java反射机制提供的功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
二、反射功能的体验
一个自定义的Person类:
1 | public class Person { |
测试类:
1 | //没有反射之前,可以做的操作 |
由上面的示例,我们自然会想到下面两个问题:
- new和反射都能实例化对象,那么开发中应该用哪个?
- 一般来说还是建议使用new来实例化对象。new只能用于编译期就能确定的类型。
- 反射的使用场景:在编译时并不能够确定需要实例化哪一个对象,需要在运行时才能确定,这时候才会使用反射。
- 反射既然能直接调用私有结构,那么封装性的意义是什么。
- 封装性解决的并不是安全问题,而仅是一种作用域的声明,避免无意识的冲突和篡改。private的声明只是建议最好不要调用此结构,相当道德约束,而非法律约束。
- 而反射解决的是能不能调用的问题。只要是你想调用的代码结构,尽管是私有的,反射仍然能够让你调用它。
三、对Class的理解
注意:class和Class没有任何关系,class是声明类的关键字,Class是JDK提供的java.lang.Class类,不要混淆!
类的加载过程:
- 程序经过Javac.exe命令以后,会生成一个或多个字节码文件(.class文件)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。即,Class的实例就对应着一个运行时类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//获取Class实例方式
public void test3() throws ClassNotFoundException {
//方式一:调用运行时类大的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1); //class com.reflection.Person
//方式二;通过运行时类的对象,调用getClass()
Person person = new Person();
Class clazz2 = person.getClass();
System.out.println(clazz2); //class com.reflection.Person
//方式三:调用Class静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.reflection.Person");
System.out.println(clazz3); //class com.reflection.Person
//方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.reflection.Person");
System.out.println(clazz4); //class com.reflection.Person
//获取的都是同一个运行时类,因为类只加载一次
System.out.println(clazz1 == clazz2); //true
System.out.println(clazz1 == clazz3); //true
System.out.println(clazz1 == clazz4); //true
}可以有Class对象的结构:
- class:外部类、成员(成员内部类,静态内部类)、局部内部类、匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
注意:只要元素类型和维度一致,就是同一个Class
1
2
3
4
5
6
7
8
public void test() {
int[] a = new int[10];
int[] b = new int[100];
Class c1 = a.getClass();
Class c2 = b.getClass();
System.out.println(c1 == c2); //true
}
使用ClassLoader加载配置文件
1 | username=Tom |
1 |
|
四、反射详细API
1、Class类对象:
public T newInstance()
:创建对象public String getName()
:返回完整类名带包名public String getSimpleName()
:返回类名public Field[] getFields()
:返回类中public修饰的属性public Field[] getDeclaredFields()
:返回类中所有的属性public Field getDeclaredField(String name)
:根据属性名name获取指定的属性public native int getModifiers()
:获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号public Method[] getDeclaredMethods()
:返回类中所有的实例方法public Method getDeclaredMethod(String name, Class<?>… parameterTypes)
:根据方法名name和方法形参获取指定方法public Constructor<?>[] getDeclaredConstructors()
:返回类中所有的构造方法public Constructor getDeclaredConstructor(Class<?>… parameterTypes)
:根据方法形参获取指定的构造方法public native Class<? super T> getSuperclass()
:返回调用类的父类public Class<?>[] getInterfaces()
:返回调用类实现的接口集合
2、获取构造方法:
Constructor getConstructor(Class[] params)
:根据构造函数的参数,返回一个具体的具有public属性的构造函数Constructor getConstructors()
:返回所有具有public属性的构造函数数组Constructor getDeclaredConstructor(Class[] params)
:根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)Constructor getDeclaredConstructors()
:返回该类中所有的构造函数数组(不分public和非public属性)
2.1、Constructor类方法:
public String getName()
:返回构造方法名public int getModifiers()
:获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号public Class<?>[] getParameterTypes()
:返回构造方法的修饰符列表(一个方法的参数可能会有多个。)public T newInstance(Object … initargs)
:创建对象【参数为创建对象的数据】
3、获取类的成员方法:
Method getMethod(String name, Class[] params)
: 根据方法名和参数,返回一个具体的具有public属性的方法Method[] getMethods()
: 返回所有具有public属性的方法数组Method getDeclaredMethod(String name, Class[] params)
:根据方法名和参数,返回一个具体的方法(不分public和非public属性)Method[] getDeclaredMethods()
:返回该类中的所有的方法数组(不分public和非public属性)
3.1、Method类方法
public String getName()
:返回方法名public int getModifiers()
:获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号public Class<?> getReturnType()
:以Class类型,返回方法类型public Class<?>[] getParameterTypes()
:返回方法的修饰符列表(一个方法的参数可能会有多个。)public Object invoke(Object obj, Object… args)
:调用方法
4、获取类的成员变量:
Field getField(String name)
:根据变量名,返回一个具体的具有public属性的成员变量Field[] getFields()
:返回具有public属性的成员变量的数组Field getDeclaredField(String name)
: 根据变量名,返回一个成员变量(不分public和非public属性)Field[] getDelcaredFields()
:返回所有成员变量组成的数组(不分public和非public属性)
4.1、Field类方法:
public String getName()
:返回属性名public int getModifiers()
:获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号public Class<?> getType()
:以Class类型,返回属性类型public void set(Object obj, Object value)
:设置属性值public Object get(Object obj)
:读取属性值public void setAccessible(boolean flag)
:默认false,设置为true为打破封装