Java 反射机制是构建框架技术的基础。
Java 反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能。
java 反射有3个动态性质:
> 运行时生成对象实例,
> 运行期间调用方法,
> 运行时更改属性。
如何理解java反射机制?首先回顾一下JAVA程序的执行过程,想程序运行,Java类必须被java虚拟机加载。运行的程序在编译时就已经加载了所需要的类。
eg: Person.java > 编译器 >Person.class >JVM >运行程序
java 反射机制在编译时并不确定是哪个类被加载,而是在程序运行的时候才加载,探知,使用。这特点就是反射。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
java反射机智能够知道类的基本结构,这种对java类结构探知的能力,称为Java类的“自审”,如java代码的 自动提示功能(对自己行为的描述,self representation),就是利用了java反射的原理,是对所创建对象的探知和自审( 检测)。
通过Java 反射,可以实现以下功能:
> 在运行时判断任意一个对象所属的类。
> 在运行时构造任意一个类的对象
> 在运行时判断任意一个类所具有的方法和属性
> 在运行是调用任意一个对象的方法
Java反射常用API:
Class 类,反射的核心类,反射所有的操作都是围绕该类类生成的。通过Class类,可以获取类的属性,方法等内容信息。
Field类,表示类的属性,可以获取和设置类中属性的值。
Method类,表示类的方法,它可以用来获取类中方法的信息,或者执行方法。
Constructor类:表示类的构造方法。
在java程序中使用反射的基本步骤如下:
a. 导入java.lang.reflect.*;
b. 获取需要操作的类的Java.lang.Class对象,
c. 调用Class 的方法获取Field,Method等对象,
d. 使用反射API 进行操作 (The Reflection API https://docs.oracle.com/javase/tutorial/reflect/)
反射的应用
获取类的信息:通过反射获取类的信息分为两步:首先获取Class对象,然后通过Class对象获取信息。
1.获取Class对象
每个类被加载后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。Java程序中获得Class对象通常由如下3中方式。
1)调用对象的getClass()方法
getClass()方法是java.lang.Object类中的一个方法,所有的java 对象都可以调用该方法,该方法会返回该对象所属类对应的Class对象。
eg Studendt p = new Student(); Class cla = p.getClass();
2. 调用对象的class属性。
调用某个类的class属性可以获取该类对应的Class 对象,这种方式需要在编译期就知道类的名称,eg: Class cla = Student.class
3. 使用Class 类的forName()静态方法
使用Class 类的forName()静态方法也可以获取该类对应的Class 对象,该方法需要传入字符串参数,该字符串参数的值是某个类的全名,即要在类名前添加完整的包名。
eg Class cla = Class.forName("com.project.lwh.reflection.Student"); //正确
eg Class cla = Class.forName("Student"); //错误
如果传入的字符串不是类的全名,就会抛出一个ClassNotFoundException异常。
后两种方式都是直接根据类来获取该类的Class 对象,相比之下调用某个类的class属性来获取该类对应的Class对象这种方式更有优势,原因如下两点:
a. 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。
b.程序性能更高,因为这种方式无须调用方法,所以性能更好。
因此,大部分时候都应该使用调用某个类的class属性的方式来获取指定类的Class 对象。
2.从Class 对象获取信息
在获得了某个类所对应的Class对象之后,程序就可以调用Class 对象的方法来获得该类的真实信息。Class类提供了大量实例方法来或取Class对象所对应类的详细信息。
1)访问Class 对应的类所包含的构造方法
访问Class 对应的类所包含的构造方法常用方法由:
a. 方法: Constructor getConstructor(Class[] params)
说明:返回此Class对象所表示的类的指定的public 构造方法,params参数是按声明顺序标志该方法参数类型的Class对象的一个数组。构造方法的参数类型与params所指定的参数类型所匹配。eg: Constructor co = c.getConstructor(String.class,List.class);
Constructor[] getConstructor() 返回此Class对象所表示的类的所有public 构造方法
Constructor getDeclaredConstructor(Class[] params) 返回此Class 对象所表示的类的指定构造方法,与构造方法的访问级别无关。
Constructor[] getDeclaredConstructors() 返回此Class对象所表示的所有构造方法,与构造方法的访问级别无关。
2) 访问Class 对应的类所包含的方法
访问Class 对用的类所报的方法常用的方法有:
方法: Method getMethod(String name,Class[] params),
说明:返回 此Class对象所表示的类的指定public 方法,name 参数用于指定方法的名称,params 参数是 按声明顺序标志该方法参数类型的Class对象的一个数组,例如:c.getMethod("info",String.class); //c 为某Class 对象; c.getMethod("info",String.class,Integer.class);
Method[] getMethods(); 返回此Class对象所表示的类的所有public 方法
Method getDeclaredMethod(String name, Class[] params) 返回此Class对象所表示的类的指定的方法,与方法的访问级别无关。
Method[] getDeclaredMethods() 返回此Class对象所表示的类的全部方法,与方法的访问级别无关。
2)访问Class对应的类所包含的方法
方法:Method getMethod(String name,Class[] params)
说明: 返回此Class对象所表示的类的指定的public方法,name 参数用于指定方法名称, params 参数是按声明顺序标志该方法参数类型的,Class对象的一个数组。例如:c.getMethod("info",String.class);// c为某Class对象。
//c.getMethod("info",String.class,Integer.class)
Method[] getMethods() 返回此Class对象所表示的类的所有public 方法。
Method[] getDeclaredMethod(String name, Class[] params) 返回此Class对象所表示的类的指定方法,与方法的访问级别无关。
Method[] getDeclaredMethods() 返回此Class 对象所表示的类的全部方法,与方法的访问级别无关。
3)访问类包含属性的常用方法
方法: Field getField(String name)
说明:返回此Class对象所表示的类的指定的public 属性,name参数用于指定属性名称, 如:c.getField("age"); //c为某Class对象,age为属性名。
Field[] getFields() 返回此Class对象所表示的类的所有public 属性。
Field[] getDeclaredFiled(String name) 返回此Class对象所表示的类的的指定属性,与属性的访问级别无关。
Field[] getDeclaredFields() 返回此Class对象所表示的类的全部属性,与属性的访问级别无关。
4)访问Class对应的类所包含的注释
访问Class对应的类所包含的注释的常用方法:
访问类包含注释的常用方法
方法:<A extends Annotation>A getAnnotation(Class<A> annotationClass)
说明:试图获取该Class对象所表示类上指定类型的注释,如果该类型的注释不存在则返回null值,其中 annotationClass参数对应于注释类型的Class对象。
Annotation[] getAnnotations() 返回此元素上存在的所有注释
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注释
Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
访问Class对应 的类的其他信息
访问Class对应的类的其他一些常用方法的定义
Class[] getDeclaredClasses() 返回该Class对象的对应类里包含的全部内部类
Class[] getDeclaringClass() 访问Class对应的类所在的外部类
Class[] getInterfaces[] 返回该Class对应类所实现的全部接口
int getModifiers() 返回此类或接口的所有修饰符,返回的修饰符由public ,protected,private,final,static 和abstract等对应的常量组成,返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符。
Package getPackage() 获取此类的包
String getName()此字符串形式返回此Class对象所表示的类的名称
String getSimpleName() 此字符串形式返回此Class对象所表示的类的简称
Class getSuperclass() 返回该Class所表示的类的超类对应的Class对象。
Method, Constructor,Field这3个类都定义在java.lang.reflect下,并实现了java.lang.reflect.Member接口,程序可以通过 Method对象来执行对应的方法,通过 Constructor对象来调用相应的构造方法创建对象,通过Field对象直接访问并修改对象的属性。
https://blog.youkuaiyun.com/DaJian35/article/details/79705193
在使用枚举类的时候,建议用getDeclaringClass
返回枚举类。但是为什么不用getClass
呢?下面来看看代码:
public enum FruitEnum{
BANANA,APPLE;
public static void main(String[] args) {
System.out.println(BANANA.getDeclaringClass());
System.out.println(BANANA.getClass());
}
}
# 运行结果
class FruitEnum
class FruitEnum
}
public enum FruitEnum{
BANANA{
String getName() {
return "香蕉";
}
},APPLE{
String getName() {
return "苹果";
}
};
abstract String getName();
public static void main(String[] args) {
System.out.println(BANANA.getDeclaringClass());
System.out.println(BANANA.getClass());
}
}
# 运行结果
class FruitEnum
class FruitEnum$1
- 20这种情况下就不同了。因为此时
BANANA
和APPLE
相当于FruitEnum
的内部类。下面来看看Enum
的源码:
public final Class<E> getDeclaringClass() {
Class var1 = this.getClass();
Class var2 = var1.getSuperclass(); // 获取上一级的父类
return var2 == Enum.class?var1:var2;
}
当上一级的父类不是Enum
,则返回上一级的class
。因此对枚举类进行比较的时候,使用getDeclaringClass
是万无一失的。
通过反射来创建对象的方式有两种:
1)使用Class对象的newInstance()方法创建对象
使用Class对象的newInstance()方法来创建Class对象对应类的实例,这种方式要求该Class对象的对应类由默认构造方法,而执行newInstance()方法时实际上是利用默认构造方法来创建该类的实例。
eg: Class cla= Date.class;
Date d =(Date)cla.newInstance();
2)使用Constructor对象创建对象
使用Constructor对象创建对象,要先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造方法来创建实例。
eg: Class cla = Date.class;
Constructor cu = cla.getConstructor(long.class);
Date d= (Date) cu.newInstance(1987);