ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java反射和注解

Java反射和注解 一 . 反射1. 反射概念反射程序运行时动态获取类的字节码信息 (Class 对象)并且操作构造器、成员变量、成员方法。正常编码编译期就知道类反射运行时才拿到类动态创建对象、操作成员变量事先代码里不用写死new Student()/new Teacher()。入口Class对象字节码对象2 .1 反射第一步:获取 Class 对象反射第一步拿到 Class 对象第二步通过 Class 对象获取构造器、方法或者字段对象第三步如果是私有成员打开访问权限第四步执行实例化、调用或者读写获取 Class 对象 3 种方式方式 1类名.class【静态方式推荐不触发类加载】Class c1 User.class;不需要创建对象直接通过类的字面量获取 Class。方式 2对象.getClass()【已经有实例对象时使用】User u new User();Class? c2 u.getClass();方式 3Class.forName(全类名)【最常用框架底层用的最多】Class? c3 Class.forName(“com.User”);传入完整包名 类名字符串会触发类加载加载、链接、初始化。JDBC 加载驱动、Spring Bean 扫描底层就是这个写法。2 . 2 反射第二步从 Class 对象获取「构造器 / 方法 / 成员变量」1. 从class类中获取构造器Constructor用来创建对象getConstructor(参数类型...)获取public 公开的构造器getDeclaredConstructor(参数类型...)获取任意权限(public/private)构造器常用getConstructors()获取所有 public 构造器数组getDeclaredConstructors()获取全部构造器数组3.setAccessible(true)暴力访问私有构造4.调用newInstance()创建对象//获取到这个类的class对象CatcCat.class;// 1. 获取【本类任意权限包括私有】的有参构造器对象第二步Constructorconstructor1c.getDeclaredConstructor(String.class,Int.class);// 2. 获取构造器名字 参数个数并打印System.out.println(constructor1.getName()--constructor1.getParameterCount());//得到全类名和参数个数2// 3. 暴力反射关闭访问权限检查第三步constructor1.setAccessible(true);// 禁止检查访问权限// 4. 通过构造器实例化对象第四步Catcat(Cat)constructor1.newInstance(参数1,参数2);// 5. 打印对象System.out.println(cat);核心:利用反射机制调用类的构造器来创建对象。反射拿到 Constructor构造方法对象主要作用是在运行时动态创建类的实例还可以读取构造器信息甚至调用私有构造创建对象。问 :setAccessible(true)是修改类中构造器的权限为 public 吗不是只是临时关闭访问权限检查并没有修改源码里 private 修饰符。只对当前这个 constructor 对象生效。2.从 Class 对象获取成员变量class中获取成员变量Field的四个方法Field[] getFields()获取所有 public成员变量包括父类的 public 字段Field[] getDeclaredFields()获取本类中全部成员变量private、protected、public、default 都拿到不包含父类的字段Field getField(String name)根据名字获取单个 public成员变量可找父类 publicField getDeclaredField(String name)根据名字获取本类中任意权限的单个成员变量private 也能拿到不找父类Field成员变量对象的作用反射拿到Field 对象就是把类的成员变量封装成一个对象在程序运行阶段动态读取 / 修改 一个对象的成员变量的值哪怕这个变量是private私有的。核心能力读取成员变量的值field.get(对象)传入对象拿到这个对象该成员变量的值修改成员变量的值field.set(对象, 新值)接上图Cat cat new Cat ;fName.set(cat,“加菲猫”)给对象的成员变量动态赋值突破访问权限限制field.setAccessible(true)开启暴力反射原本private私有变量也能读写只是关闭权限检查没有修改源码访问修饰符3.从 Class 对象获取成员方法核心作用:在程序运行阶段动态调用这个成员方法。getMethods()能拿到父类 publicgetDeclaredMethods()只能拿本类public/private/protected 都拿到父类方法拿不到。Method getMethod(String name,参数类型...)根据方法名 参数类型获取单个 public 方法可找父类 publicMethod getDeclaredMethod(String name,参数类型...)根据方法名 参数类型获取本类任意权限单个方法private 方法也可以不找父类调用:public Object invoke(Object obj, Object... args)执行方法的核心反射获取 Method 对象的目的。public void setAccessible(boolean flag)设置为 true禁止检查访问控制暴力反射获取 Class获取对应的 Constructor/Method/FieldClass 拿信息Constructor 造对象Field 读写变量Method 调用方法。私有成员setAccessible(true)开启暴力反射,关闭访问权限检查访问 private 成员不是修改权限执行对应的操作二. 反射的作用和应用场景反射Reflection正常编码编译期就知道Student s new Student()写死类名。反射运行时才拿到类动态创建对象、操作成员变量事先代码里不用写死new Student()/new Teacher()。1.反射核心作用运行时获取类的全部信息获取类名、成员变量 (Field)、构造器 (Constructor)、方法 (Method)包括private私有成员。动态创建对象运行时通过 Class 对象调用构造方法实例化对象不用编译期 new。动态读写成员变量的值即使属性是privatesetAccessible(true)可以绕过访问检查读取 / 修改对象属性。动态调用对象的方法运行时找到 Method执行方法不需要编译期写死调用。核心特点动态。编译期不知道要操作哪个类运行时才确定。✅优点灵活性强动态编程解耦框架底层实现基础。❌缺点性能较差破坏封装可以访问私有成员代码复杂异常在运行时才暴露。用反射做一个简易框架,对于任何一个对象,都可以将对象的字段和对应的值保存到文件中去2. 反射的应用场景面试重点① 框架底层最主要场景上面代码Framework就是简易框架思想SpringIOC 容器根据配置文件 / 注解通过反射创建对象注入属性MyBatis读取 xml 映射文件反射实例化实体类给对象字段赋值封装数据库查询结果框架不知道你将来会写什么实体类Student/Teacher只能靠反射动态操作。② 序列化 / 反序列化对象和文本、JSON 互转JSON 工具Jackson、FastJSON把对象转 JSON 字符串 / JSON 转对象底层都是反射扫描属性读写值。例如把对象所有字段保存到 data.txt就是简易序列化。③ 配置文件解析配置文件写类名程序读取配置反射加载类创建对象。例如data.txt写com.Student程序读到字符串用 Class.forName 加载这个类。④ 注解解析注解 反射配套使用自定义注解运行时通过反射判断类 / 字段上有没有注解做逻辑控制。例加注解NotSave框架反射扫描到这个注解就跳过该字段不保存到文件。⑤ 单元测试框架 JUnitJUnit 识别Test注解运行时通过反射找到标记的方法自动执行测试方法。三 . 注解1. 定义注解AnnotationJava 里一种标记写在类、方法、成员变量上面给程序附加额外信息。注解本身不会直接执行代码需要反射在运行时读取注解然后根据注解做对应的逻辑。2. 作用给编译器提示信息例如Override检测方法是否重写父类方法Deprecated标记方法过时。编译期生成代码比如 Lombok 的Data。运行时框架通过反射读取注解实现功能和刚才的 Framework 配套SpringComponent、AutowiredJUnitTest我们可以自定义注解标记某个字段不要保存到 txt 文件。3.自定义注解含义用interface声明搭配元注解Target指定使用位置(类、方法、成员变量 FIELD )Retention指定生命周期可以定义注解属性)。 如果需要运行时反射读取Retention 必须设置为RetentionPolicy.RUNTIME。Target指定使用位置(元注解的)-TYPE类、接口-FIELD成员变量字段-METHOD成员方法-PARAMETER方法参数-CONSTRUCTOR构造器-LOCAL_VARIABLE局部变量- 多个:Target({ElementType.FIELD,ElementType.METHOD})Retention指定注解保留到哪个阶段(元注解的)-RetentionPolicy.SOURCE源码阶段编译后丢弃-RetentionPolicy.CLASS保留到 class 字节码运行时丢失默认-RetentionPolicy.RUNTIME运行时保留可以通过反射读取框架必用3. 可以在注解内部定义属性。//元注解:描述注解的注解Target(ElementType.FIELD)//注解只能写在成员变量上Retention(RetentionPolicy.RUNTIME)//运行时保留反射才能读到publicinterfaceNotSave{//标记加上这个注解的字段不要保存到data.txt}使用:publicclassTeacher{privateStringname;NotSave//这个age字段不会写入txtprivateIntegerage;privateDoublesalary;}然后在Framework的 saveObject 里用反射判断字段上有没有NotSave有就跳过不输出。重点想用反射解析注解Retention 必须写 RUNTIME模拟Junit框架需求:定义若干个方法,只要加了注解AnnotationTest01,就会触发该方法执行(分析:1.定义一个自定义注解,只能注解方法,存活范围一直都在 2.部分方法不加自定义注解)四 . 什么是注解解析注解解析通过反射或者 APT 技术读取类、方法、属性上标记的注解信息根据注解中定义的参数执行对应的逻辑前提自定义注解必须加Retention (RetentionPolicy.RUNTIME)1. 基础前提最重要必背元注解Retention必须设置为RetentionPolicy.RUNTIME只有 RUNTIME注解保留到运行期JVM 加载字节码后反射才能读到注解如果是CLASS/SOURCE运行时拿不到注解解析失效Target限定注解能标注在什么位置类、字段、方法等所有能放注解的元素Class、Field、Method、Constructor都实现全部实现了AnnotatedElement接口这个接口提供注解解析方法2. AnnotatedElement 三大核心方法boolean isAnnotationPresent(注解.class)判断是否存在该注解返回 true/false ✅最常用判断有没有标记比如判断字段上有没有NotSavegetDeclaredAnnotation(注解.class)获取指定类型注解对象没有返回 null 获取注解读取注解里面的属性值Annotation[] getDeclaredAnnotations()获取当前元素上所有注解返回注解数组 遍历全部注解3. 注解解析核心思想要解析谁上面的注解就先拿到谁的对象。解析类上注解 → 获取Class对象解析成员变量注解 → 获取Field对象解析方法注解 → 获取Method对象解析构造器注解 → 获取Constructor对象Class、Field、Method、Constructor都实现了AnnotatedElement接口所以全都拥有解析注解的方法。黑马反射学习链接
返回列表