Java 反射
内容纲要
反射
- 允许程序在运行时来进行自我检查并且对内部的成员进行操作
- 反射主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义
反射机制的作用
- 在运行时判断任意一个对象所属的类
- 在运行时获取类的对象
- 在运行时访问java对象的属性,方法,构造方法等
java.lang.reflect类库里面主要的类
- Field :表示类中的成员变量
- Method :表示类中的方法
- Constructor :表示类的构造方法
- Array :该类提供了动态创建数组和访问数组元素的静态方法
反射依赖的class
用来表示运行时类型信息的对应类
- 每个类都有唯一一个与之相对应的Class对象
- Class类为类类型,而Class对象为类类型对象
Class类的特点
- Class类也是类的一种,class则是关键字
- Class类只有一个私有的构造函数,只有JVM能够创建Class类的实例
- JVM中只有唯一一个和类相对应的Class对象来描述其类型信息
获取Class对象的三种方式
- Object-> getClass()
- 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
- 通过Class类的静态方法: forName(String className)(常用)
public static void main(String[] args) throws ClassNotFoundException { //第一种方式获取Class对象 ReflectTarget reflectTarget = new ReflectTarget(); Class reflectTargetClass1 = reflectTarget.getClass(); System.out.println("1st : " + reflectTargetClass1.getName()); //第二种方式获取Class对象 Class reflectTargetClass2 = ReflectTarget.class; System.out.println("2nd: " + reflectTargetClass2.getName()); //判断第一种方式获取的class对象和第二种方式获取的是否是同一个 System.out.println(reflectTargetClass1 == reflectTargetClass2); //第三种方式来获取Class对象 Class reflectTargetClass3 = Class.forName("demo.reflect.ReflectTarget"); System.out.println("3rd: " + reflectTargetClass3.getName()); System.out.println(reflectTargetClass2 == reflectTargetClass3); }
在运行期间,一个类,只有一个与之对应的Class对象产生
Class对象就像一面镜子,透过这面镜子可以看到类的结构
反射的主要用法
- 如何获取类的构造方法并使用
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 通过Class对象可以获取某个类中的:构造方法;
*
* 获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class ConstructorCollector {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName("demo.reflect.ReflectTarget");
//1.获取所有的公有构造方法
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
//2.获取所有构造方法
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
//3.获取单个带参数的公有方法
System.out.println("*****************获取公有、有两个参数的构造方法*******************************");
Constructor con = clazz.getConstructor(String.class, int.class);
System.out.println("con = " + con);
//4.获取单个私有的构造方法
System.out.println("******************获取私有构造方法*******************************");
con = clazz.getDeclaredConstructor(int.class);
System.out.println("private con = " + con);
System.out.println("******************调用私有构造方法创建实例*******************************");
//暴力访问(忽略掉访问修饰符)
con.setAccessible(true);
ReflectTarget reflectTarget = (ReflectTarget) con.newInstance(1);
}
}
- 如何获取类的成员变量并使用
方法返回值 | 方法名称 | 方法说明 |
---|---|---|
Field | getDeclaredField(String name) | 获取指定name名称的(包含private修饰的)字段,不包括继承的字段 |
Field[] | getDeclaredField() | 获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段 |
Field | getField(String name) | 获取指定name名称、具有public修饰的字段,包含继承字段 |
Field[] | getField() | 获取修饰符为public的字段,包含继承字段 |
/*
* 获取成员变量并调用:
*
* 1.批量的
* 1).Field[] getFields():获取所有的"公有字段"
* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 1).public Field getField(String fieldName):获取某个"公有的"字段;
* 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
* 设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
*/
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class FieldCollector {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象
Class reflectTargetClass = Class.forName("demo.reflect.ReflectTarget");
//1.获取所有公有的字段
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = reflectTargetClass.getFields();
for (Field f : fieldArray){
System.out.println(f);
}
//2.获取所有的字段
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = reflectTargetClass.getDeclaredFields();
for (Field f : fieldArray){
System.out.println(f);
}
//3.获取单个特定公有的field
System.out.println("*************获取公有字段并调用***********************************");
Field f = reflectTargetClass.getField("name");
System.out.println("公有的field name : " + f);
ReflectTarget reflectTarget = (ReflectTarget)reflectTargetClass.getConstructor().newInstance();
//4.给获取到的field赋值
f.set(reflectTarget, "待反射一号");
//5.验证对应的值name
System.out.println("验证name : " + reflectTarget.name);
//6.获取单个私有的Field
System.out.println("**************获取私有字段targetInfo并调用********************************");
f = reflectTargetClass.getDeclaredField("targetInfo");
System.out.println(f);
f.setAccessible(true);
f.set(reflectTarget, "13810592345");
System.out.println("验证信息" + reflectTarget);
}
}
- 如何获取类的成员方法并使用
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
):
*/
public class MethodCollector {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1、获取Class对象
Class reflectTargetClass = Class.forName("demo.reflect.ReflectTarget");
//2、获取所有公有方法
System.out.println("***************获取所有的public方法,包括父类和Object*******************");
Method[] methodArray = reflectTargetClass.getMethods();
for (Method m : methodArray) {
System.out.println(m);
}
//3、获取该类的所有方法
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = reflectTargetClass.getDeclaredMethods();
for (Method m : methodArray) {
System.out.println(m);
}
//4、获取单个公有方法
System.out.println("***************获取公有的show1()方法*******************");
Method m = reflectTargetClass.getMethod("show1", String.class);
System.out.println(m);
//5、调用show1并执行
ReflectTarget reflectTarget = (ReflectTarget) reflectTargetClass.getConstructor().newInstance();
m.invoke(reflectTarget, "待反射方法一号");
//6、获取一个私有的成员方法
System.out.println("***************获取私有的show4()方法******************");
m = reflectTargetClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);
String result = String.valueOf(m.invoke(reflectTarget, 20));
System.out.println("返回值 : " + result);
}
}
反射的获取源
用XML来保存类相关的信息以供反射调用
用注解来保存类相关的信息以供反射调用
注解
提供一种为程序元素设置元数据的方法
- 元数据是添加到程序元素如方法、字段、类和包上的额外信息
- 注解是一种分散式的元数据设置方式,XML是集中式的设置方式
- 注解不能直接干扰程序代码的运行
- 核心package为 java.long.annotation
- 注解均继承自Annotation接口
注解的功能
- 作为特定的标记,用于告诉编译器一些信息
- 编译时动态处理,如动态生成代码
- 运行时动态处理,作为额外信息的载体,如获取注解信息
注解的分类
元注解
用于修饰注解的注解,通常用在注解的定义上
- @Target:注解的作用目标
- packages,types (类、接口、枚举、Annotation类型)
- 类型成员(方法、构造方法、成员变量、枚举值)
- 方法参数和本地变量(如循环变量、catch参数)
- @Retention:表明注解的生命周期
- RetentionPolicy枚举说明
- SOURCE:编译后不会在,class文件中出现
- CLASS:编译后存在
- RUNTIME:运行时可获得
- @Documented:注解是否应当被包含在javaDoc文档中
- @Inherited:是否运行子类继承该注解
自定义注解格式
public @interface 注解名{
修饰符 返回值 属性名() 默认值;
修饰符 返回值 属性名() 默认值;
...
}
注解属性支持的类型
- 所有基本数据类型:int,float, boolean, byte,double,char,long,short
- Enum类型
- String类型
- Annotation类型
- Class类型
- 以上类型数组
自定义注释的获取与使用
package demo.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class AnnotationParser {
//解析类的注解
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class clazz = Class.forName("demo.annotation.ImoocCourse");
//这里获取的是class对象的注解,而不是其里面的方法和成员变量的注解
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annotation : annotations){
CourseInfoAnnotation courseInfoAnnotation = (CourseInfoAnnotation) annotation;
System.out.println("课程名:" + courseInfoAnnotation.courseName() + "\n" +
"课程标签:" + courseInfoAnnotation.courseTag() + "\n" +
"课程简介:" + courseInfoAnnotation.courseProfile() + "\n" +
"课程序号:" + courseInfoAnnotation.courseIndex() );
}
}
//解析成员变量上的标签
public static void parseFieldAnnotation() throws ClassNotFoundException {
Class clazz = Class.forName("demo.annotation.ImoocCourse");
Field[] fields = clazz.getDeclaredFields();
for(Field f : fields){
//判断成员变量中是否有指定注解类型的注解
boolean hasAnnotation = f.isAnnotationPresent(PersonInfoAnnotation.class);
if(hasAnnotation){
PersonInfoAnnotation personInfoAnnotation = f.getAnnotation(PersonInfoAnnotation.class);
System.out.println("名字:" + personInfoAnnotation.name() + "\n" +
"年龄:" + personInfoAnnotation.age() + "\n" +
"性别:" + personInfoAnnotation.gender() + "\n");
for(String language : personInfoAnnotation.language()){
System.out.println("开发语言:" + language);
}
}
}
}
//解析方法注解
public static void parseMethodAnnotation() throws ClassNotFoundException{
Class clazz = Class.forName("demo.annotation.ImoocCourse");
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
/*
* 判断方法中是否有指定注解类型的注解
*/
boolean hasAnnotation = method.isAnnotationPresent(CourseInfoAnnotation.class);
if(hasAnnotation){
CourseInfoAnnotation courseInfoAnnotation = method.getAnnotation(CourseInfoAnnotation.class);
System.out.println("课程名:" + courseInfoAnnotation.courseName() + "\n" +
"课程标签:" + courseInfoAnnotation.courseTag() + "\n" +
"课程简介:" + courseInfoAnnotation.courseProfile() + "\n"+
"课程序号:" + courseInfoAnnotation .courseIndex() + "\n");
}
}
}
public static void main(String[] args) throws ClassNotFoundException {
//parseTypeAnnotation();
parseFieldAnnotation();
//parseMethodAnnotation();
}
}
注解的工作原理
//保存jdk动态代理为注解生成的代理类
-Djdk.proxy.ProxyGenerator.saveGenerator.Files= true
//跟踪类加载
-XX:+TraceClassLoading
- 通过键值对的形式为注解属性赋值
- 编译器检查注解的使用范围,将注解信息写入元素属性表
- 运行时JVM将RUNTIME的所有注解属性取出并最终存入map里
- 创建AnnotationInvocationHandler实例并传入前面的map
- JVM使用JDK动态代理为注解生成代理类,并初始化处理器
- 调用invoke方法,通过传入方法名返回注解对应的属性值
共有 0 条评论