什么是反射

反射(Reflection)是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。

通过反射机制,可以在运行时访问 Java 对象的属性,方法,构造方法等。

反射的应用场景

反射的主要应用场景有:

开发通用框架 - 反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 JavaBean、Filter 等),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

动态代理 - 在切面编程(AOP)中,需要拦截特定的方法,通常,会选择动态代理方式。这时,就需要反射技术来实现了。

注解 - 注解本身仅仅是起到标记作用,它需要利用反射机制,根据注解标记去调用注解解释器,执行行为。如果没有反射机制,注解并不比注释更有用。

可扩展性功能 - 应用程序可以通过使用完全限定名称创建可扩展性对象实例来使用外部的用户定义类。

反射的缺点

性能开销 - 由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。因此,反射操作的性能要比非反射操作的性能要差,应该在性能敏感的应用程序中频繁调用的代码段中避免。

破坏封装性 - 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

内部曝光 - 由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,所以反射的使用可能会导致意想不到的副作用,这可能会导致代码功能失常并可能破坏可移植性。反射代码打破了抽象,因此可能会随着平台的升级而改变行为。

反射机制类加载过程

类加载的完整过程如下:

(1)在编译时,Java 编译器编译好 .java 文件之后,在磁盘中产生 .class 文件。.class 文件是二进制文件,内容是只有 JVM 能够识别的机器码。

(2)JVM 中的类加载器读取字节码文件,取出二进制数据,加载到内存中,解析.class 文件内的信息。类加载器会根据类的全限定名来获取此类的二进制字节流;然后,将字节流所代表的静态存储结构转化为方法区的运行时数据结构;接着,在内存中生成代表这个类的 java.lang.Class 对象。

(3)加载结束后,JVM 开始进行连接阶段(包含验证、准备、初始化)。经过这一系列操作,类的变量会被初始化。

Class 对象

要想使用反射,首先需要获得待操作的类所对应的 Class 对象。Java 中,无论生成某个类的多少个对象,这些对象都会对应于同一个 Class 对象。这个 Class 对象是由 JVM 生成的,通过它能够获悉整个类的结构。所以,java.lang.Class 可以视为所有反射 API 的入口点。

反射的本质就是:在运行时,把 Java 类中的各种成分映射成一个个的 Java 对象。

举例来说,假如定义了以下代码:

User user = new User();

步骤说明:

JVM 加载方法的时候,遇到 new User(),JVM 会根据 User 的全限定名去加载 User.class 。

JVM 会去本地磁盘查找 User.class 文件并加载 JVM 内存中。

JVM 通过调用类加载器自动创建这个类对应的 Class 对象,并且存储在 JVM 的方法区。注意:一个类有且只有一个 Class 对象。

使用反射

java.lang.reflect 包

Java 中的 java.lang.reflect 包提供了反射功能。java.lang.reflect 包中的类都没有 public 构造方法。

java.lang.reflect 包的核心接口和类如下:

Member 接口 - 反映关于单个成员(字段或方法)或构造函数的标识信息。

Field 类 - 提供一个类的域的信息以及访问类的域的接口。

Method 类 - 提供一个类的方法的信息以及访问类的方法的接口。

Constructor 类 - 提供一个类的构造函数的信息以及访问类的构造函数的接口。

Array 类 - 该类提供动态地生成和访问 JAVA 数组的方法。

Modifier 类 - 提供了 static 方法和常量,对类和成员访问修饰符进行解码。

Proxy 类 - 提供动态地生成代理类和类实例的静态方法。

获得 Class 对象

获得 Class 的三种方法:

(1)使用 Class 类的 forName 静态方法

示例:

package io.github.dunwu.javacore.reflect;public class ReflectClassDemo01 { public static void main(String[] args) throws ClassNotFoundException { Class c1 = Class.forName("io.github.dunwu.javacore.reflect.ReflectClassDemo01"); System.out.println(c1.getCanonicalName()); Class c2 = Class.forName("[D"); System.out.println(c2.getCanonicalName()); Class c3 = Class.forName("[[Ljava.lang.String;"); System.out.println(c3.getCanonicalName()); }}//Output://io.github.dunwu.javacore.reflect.ReflectClassDemo01//double[]//java.lang.String[][]

使用类的完全限定名来反射对象的类。常见的应用场景为:在 JDBC 开发中常用此方法加载数据库驱动。

(2)直接获取某一个对象的 class

示例:

public class ReflectClassDemo02 { public static void main(String[] args) { boolean b; // Class c = b.getClass(); // 编译错误 Class c1 = boolean.class; System.out.println(c1.getCanonicalName()); Class c2 = java.io.PrintStream.class; System.out.println(c2.getCanonicalName()); Class c3 = int[][][].class; System.out.println(c3.getCanonicalName()); }}//Output://boolean//java.io.PrintStream//int[][][]

3)调用 Object 的 getClass 方法,示例:

Object 类中有 getClass 方法,因为所有类都继承 Object 类。从而调用 Object 类来获取

示例:

package io.github.dunwu.javacore.reflect;import java.util.HashSet;import java.util.Set;public class ReflectClassDemo03 { enum E {A, B} public static void main(String[] args) { Class c = "foo".getClass(); System.out.println(c.getCanonicalName()); Class c2 = ReflectClassDemo03.E.A.getClass(); System.out.println(c2.getCanonicalName()); byte[] bytes = new byte[1024]; Class c3 = bytes.getClass(); System.out.println(c3.getCanonicalName()); Set set = new HashSet<>(); Class c4 = set.getClass(); System.out.println(c4.getCanonicalName()); }}//Output://java.lang.String//io.github.dunwu.javacore.reflect.ReflectClassDemo.E//byte[]//java.util.HashSet

判断是否为某个类的实例

判断是否为某个类的实例有两种方式:

用 instanceof 关键字

用 Class 对象的 isInstance 方法(它是一个 Native 方法)

示例:

public class InstanceofDemo { public static void main(String[] args) { ArrayList arrayList = new ArrayList(); if (arrayList instanceof List) { System.out.println("ArrayList is List"); } if (List.class.isInstance(arrayList)) { System.out.println("ArrayList is List"); } }}//Output://ArrayList is List//ArrayList is List

创建实例

通过反射来创建实例对象主要有两种方式:

用 Class 对象的 newInstance 方法。

用 Constructor 对象的 newInstance 方法。

示例:

public class NewInstanceDemo { public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class> c1 = StringBuilder.class; StringBuilder sb = (StringBuilder) c1.newInstance(); sb.append("aaa"); System.out.println(sb.toString()); //获取String所对应的Class对象 Class> c2 = String.class; //获取String类带一个String参数的构造器 Constructor constructor = c2.getConstructor(String.class); //根据构造器创建实例 String str2 = (String) constructor.newInstance("bbb"); System.out.println(str2); }}//Output://aaa//bbb

Field

Class 对象提供以下方法获取对象的成员(Field):

getFiled - 根据名称获取公有的(public)类成员。

getDeclaredField - 根据名称获取已声明的类成员。但不能得到其父类的类成员。

getFields - 获取所有公有的(public)类成员。

getDeclaredFields - 获取所有已声明的类成员。

Method

Class 对象提供以下方法获取对象的方法(Method):

getMethod - 返回类或接口的特定方法。其中第一个参数为方法名称,后面的参数为方法参数对应 Class 的对象。

getDeclaredMethod - 返回类或接口的特定声明方法。其中第一个参数为方法名称,后面的参数为方法参数对应 Class 的对象。

getMethods - 返回类或接口的所有 public 方法,包括其父类的 public 方法。

getDeclaredMethods - 返回类或接口声明的所有方法,包括 public、protected、默认(包)访问和 private 方法,但不包括继承的方法。

获取一个 Method 对象后,可以用 invoke 方法来调用这个方法。

Constructor

Class 对象提供以下方法获取对象的构造方法(Constructor):

getConstructor - 返回类的特定 public 构造方法。参数为方法参数对应 Class 的对象。

getDeclaredConstructor - 返回类的特定构造方法。参数为方法参数对应 Class 的对象。

getConstructors - 返回类的所有 public 构造方法。

getDeclaredConstructors - 返回类的所有构造方法。

获取一个 Constructor 对象后,可以用 newInstance 方法来创建类实例。

Array

数组在 Java 里是比较特殊的一种类型,它可以赋值给一个对象引用。其中的 Array 类为 java.lang.reflect.Array 类。我们通过 Array.newInstance 创建数组对象。

动态代理

动态代理是反射的一个非常重要的应用场景。动态代理常被用于一些 Java 框架中。例如 Spring 的 AOP ,Dubbo 的 SPI 接口,就是基于 Java 动态代理实现的。

静态代理

静态代理其实就是指设计模式中的代理模式。

代理模式为其他对象提供一种代理以控制对这个对象的访问。

说明:

静态代理模式固然在访问无法访问的资源,增强现有的接口业务功能方面有很大的优点,但是大量使用这种静态代理,会使我们系统内的类的规模增大,并且不易维护;并且由于 Proxy 和 RealSubject 的功能本质上是相同的,Proxy 只是起到了中介的作用,这种代理在系统中的存在,导致系统结构比较臃肿和松散。

动态代理

为了解决静态代理的问题,就有了创建动态代理的想法:

在运行状态中,需要代理的地方,根据 Subject 和 RealSubject,动态地创建一个 Proxy,用完之后,就会销毁,这样就可以避免了 Proxy 角色的 class 在系统中冗杂的问题了。

Java 动态代理基于经典代理模式,引入了一个 InvocationHandler,InvocationHandler 负责统一管理所有的方法调用。

动态代理步骤:

获取 RealSubject 上的所有接口列表;

确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;

根据需要实现的接口信息,在代码中动态创建 该 Proxy 类的字节码;

将对应的字节码转换为对应的 class 对象;

创建 InvocationHandler 实例 handler,用来处理 Proxy 所有方法调用;

Proxy 的 class 对象 以创建的 handler 对象为参数,实例化一个 proxy 对象。

从上面可以看出,JDK 动态代理的实现是基于实现接口的方式,使得 Proxy 和 RealSubject 具有相同的功能。

但其实还有一种思路:通过继承。即:让 Proxy 继承 RealSubject,这样二者同样具有相同的功能,Proxy 还可以通过重写 RealSubject 中的方法,来实现多态。CGLIB 就是基于这种思路设计的。

在 Java 的动态代理机制中,有两个重要的类(接口),一个是 InvocationHandler 接口、另一个则是 Proxy 类,这一个类和一个接口是实现我们动态代理所必须用到的。

每一个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了一个 Handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。

投稿作者:一行万里

原文:https://blog.csdn.net/qq_41770757/article/details/90479852

javabean反射改字段内容_BAT程序员编写:深入理解 Java 反射和动态代理源码分析...相关推荐

  1. javabean反射改字段内容_java反射机制给实体类相同字段自动赋值实例

    一.封装一个工具类 1.简易版 package net.aexit.construct.acceptance.websky.utils; import java.lang.reflect.Field; ...

  2. 【本人秃顶程序员】深入理解Java——ConcurrentHashMap源码的分析(JDK1.8)

    ←←←←←←←←←←←← 快!点关注 一.前提 在阅读这篇博客之前,希望你对HashMap已经是有所理解的,如果你对java的cas操作也是有一定了解的,因为在这个类中大量使用到了cas相关的操作来保 ...

  3. javabean反射改字段内容_JSP第三篇「JavaBean的介绍、JSP的行为--JavaBean」(修订版)...

    什么是javaBean JavaBean就是一个普通的java类,也称之为简单java对象--POJO(Plain Ordinary Java Object),是Java程序设计中一种设计模式,是一种 ...

  4. java占位符填充_程序员:深入理解Java虚拟机,对象的内存布局

    在 HotSpot 虚拟机中,对象在内存中存储的布局分为 3 块区域:对象头 ( Header ) .实例数据 ( InstanceData ) 和对齐填充 (Padding) . 一.对象的内存布局 ...

  5. 黑马程序员,黑马论坛 ------JAVA中的反射机制

    文章来源:黑马程序员,黑马论坛 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法 ...

  6. python后端开发学路线_【后端开发】Python要学哪些内容?Python程序员学习路线图...

    很多零基础入门学习python不知道学习什么?也不知道Python要学哪些内容?下ki4网为您总结一下Python程序员学习路线图. python的应用范围是很广泛的,例如一些网络的爬虫,和web的开 ...

  7. 一年经验的java程序员薪资有多少?java收入分级

    Java编程可以应用到网站建设.游戏开发.移动系统开发等多个领域.所以随着科学技术进步电子产品不断更新问世,越来越成为企业重视的开发人才.这也是Java编程在近几年越发火爆的原因.一年经验的java程 ...

  8. 上帝视角:程序员为什么需要理解 CPU?

    来源 | 码农的荒岛求生(ID:escape-it) 可能有的同学会问,程序员写代码就好了,为什么需要去理解CPU啊?不嫌累啊?啊?啊?你倒是说啊. 计算机系统 != 汉堡包 在之前的文章中我把计算机 ...

  9. 程序员的视角:java GC

    GC(Garbage Collection 垃圾回收)的概念随着 java 的流行而被人们所熟知. 实际 GC 最早起源于20世纪60年代的 LISP 语言,是一种自动的内存管理机制. GC 要解决的 ...

最新文章

  1. 【CCNA考试】2010-06-17-杭州-1000(PASS)
  2. npoi 执行公式_生成excel文件时NPOI无法计算公式
  3. 多版本php共存 linux,linux下多版本php共存的原理、方法
  4. Mybatis(20)注解实现二级缓存
  5. iOS原生推送(APNS)进阶iOS10推送图片、视频、音乐
  6. Postman脚本发送请求pm.sendRequest
  7. 文件服务器配件,文件服务器 硬件配置
  8. 运行elasticsearch时报错:could not find java; set JAVA_HOME or ensure java is in PATH
  9. win10电脑打开计算机快捷键,运行快捷键,教您win10打开运行快捷键是什么
  10. 相关常用单位转换 mil 英里 英尺 .......
  11. Qt 5.12--color
  12. java qq验证_用Java代码来校验QQ号
  13. Proxmox VE 7.2 使用qemu-img转换磁盘格式
  14. python画拓扑图权值是线条粗细_拓扑图线条流动效果
  15. 基于JDBC的JavaWeb开发项目之——网上教务系统
  16. 如何快速开发一个自己的微信小程序
  17. 小米2020校招笔试题及答案
  18. 利用opencv-python对绿色植物的颜色进行提取
  19. IOS开发基础 · SwiftUI · StanfordCS193p Lecture1-2
  20. SQL数据库——分组查询GROUP BY

热门文章

  1. IOS开发系列--IOS程序开发概览
  2. 使用StateServer机制来解决session丢失而造成用户验证失败
  3. .net中存储过程的应用
  4. oracle spatial(一)开端
  5. [洛谷P1268]树的重量
  6. 感恩节QAD带你“吃鸡”
  7. 打开AD组策略编辑器提示“strings区段项目太长被截断”的解决
  8. 消灭Bug!推荐7款优秀的开源Bug跟踪工具
  9. 后赛门铁克时代Veritas加强数据保护应对欧盟法规
  10. c实现的trim函数