Java反射:如何正确理解,不如手写一个(反射包分析、楼主亲测)

2023-11-13

Java反射机制、动态代理是基于什么原理?

这个问题可谓是老生常谈的一个热门问题了,如果没有深入的思考还真的是很难回到上来。那么今天我们一起来看看,如何正确清晰的认识这个热门却又说简单又不简单说复杂又比较复杂的问题。

一、什么是反射

反射机制是Java语言提供的一种基础功能

这个功能能够赋予程序在运行时进行自省的能力。

通过反射我们可以直接操作类或者对象,比如:获取某个对象的类的定义、获取类声明的属性和方法、调用方法或者构造对象,甚至可以运行时修改类的定义。

二、认识反射的包

1、反射包

Java为我们准备一个专门的反射包java.lang.reflect

这里写图片描述

在这个包中中有3个类,FieldMethodConstructor分别作用于类的域(成员变量)、方法和构造器。

2、AccessibleObject

关于反射有一点需要特意注意一下,就是反射包提供的AccessibleObject类中的setAccessible方法。

这里写图片描述

他的子类很多也重写了这个方法,这里的所谓的setAccessible顾名思义,就是指的是成员变量前面的用于修饰的publicprotectedprivate,这个方法也就意味着我们可以在运行时通过反射去修改类的成员变量的访问限制。

setAccessible的应用场景非常广泛,各种框架:开发、测试、依赖注入。例如在数据库O/R Mapping框架中,我们在加载或者持久化数据的时候,框架通常会利用反射做这个事情,而不需要开发者自己去实现。

还有个典型的应用场景,就是绕过API的访问控制。我们在开发的过程中,有时候,可能需要调用内部的API去做一些事情,比如,自定义的高性能的NIO框架需要显示的释放DirectBuffer,使用反射绕开限制是一种常见的办法。

那么具体怎么实现呢?在后面的代码示例中会有说明。

3、Modifier

反射包中还有一个重要的类Modifier,该类是静态类,其中的方法也是静态方法。
Modifier.Class类中getModifiers()函数返回一个用于描述类,构造器,方法和域的修饰符的整形数值。、
调用Modifier.toString()方法将整型数值转变成字符串,也是就我们熟悉的public,private,static,final等修饰符。

三、反射包的详解与代码实践

package com.newframe.controllers.api;

import com.newframe.entity.test.TestUser;

import java.lang.reflect.*;

/**
 * @author:wangdong
 * @description:反射
 */

/**
 * 创建一个user类
 * 可不在与main方法同一个类中创建
 */
class User{

    //定一个成员变量:姓名
    private String name;

    //无参构造
    public User(){}
    //有参构造
    public User(String name) {
        this.name = name;
    }
    //get方法
    public String getName() {
        return name;
    }
    //set方法
    public void setName(String name) {
        this.name = name;
    }
}

/**
 * 测试Class类的函数和反射库中的函数
 */
public class TestReflectController {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {

        //获得User类的Class对象
        Class<?> cc = User.class;

        //下面这个是通过new出来的对象,并通过set给成员变量赋值
        User user1 = new User();
        user1.setName("hello");
        //下面这个是通过构造器的方式

        //1.有参构造器
        //先得到有参构造器的信息,再根据构造器的信息,由newInstance()函数创建一个User对象
        Constructor<?> constructor = cc.getConstructor(String.class);
        //这个constructor.newInstance方法就是反射包下java.lang.reflect.Constructor类下的newInstance方法
        User user2 = (User) constructor.newInstance("world");
        System.out.println(user1.getName());//hello
        System.out.println(user2.getName());//world

        //2.无参构造器
        Constructor<?> constructor1 = cc.getConstructor();
        //无参构造不能够像1一样赋值
        //User user3 = (User) constructor1.newInstance("熊本");
        User user3 = (User) constructor1.newInstance();
        user3.setName("熊本");
        System.out.println(user3.getName());//熊本

        //由无参构造器创建对象时,可不必获得构造器,直接由Class对象调用newInstance()方法。
        Class<?> cc2 = User.class;
        User user4 = (User) cc2.newInstance();
        user4.setName("同学");
        System.out.println(user4.getName());//同学

        //3.下面2个输出语句可看出cc保存类信息,输出的是“class + 类名”。cc.newInstance()是具体类的对象。
        System.out.println(cc);//class com.newframe.controllers.api.User
        System.out.println(cc.newInstance());//com.newframe.controllers.api.User@3a03464


        //4.AccessibleObject、Field类
        //首先得到有参构造函数的信息,然后根据构造函数实例化一个对象。
        //由getDeclaredField()函数得到类里面的私有成员变量,访问私有成员变量要用setAccessible()函数设置访问权限。
        //Field类对象得到成员变量后还可以设置该变量的值,使用set()方法。
        Constructor<?> constructor2 = cc.getConstructor(String.class);
        User user5 = (User) constructor2.newInstance("改变前:100");
        Field field = cc.getDeclaredField("name");
        field.setAccessible(true);
        field.set(user5,"改变后:50");
        System.out.println(user5.getName());//改变后:50

        //5.Method.invoke
        //首先根据获得的构造函数信息实例化一个对象
        //然后由函数名获得类中的公有函数,getMethod("函数名")
        //invoke()方法执行由getMethod()获得的函数,这里获得的函数是getter()
        //对于获得的有参函数,invoke(对象)里只添加对象名。
        Constructor<?> constructor3 = cc.getConstructor(String.class);
        User user6 = (User) constructor3.newInstance("你好世界");
        //getName是User实体类中的方法
        System.out.println(cc.getMethod("getName").invoke(user6));//你好世界

        //对于获得到的无参函数,在调用getMethod()函数时,要在getMethod()中指定被获得函数的"函数名"和"参数类型"
        //并且在执行该函数(即调用invoke()函数时),要指定对象和参数类型的具体实例。
        User user7 = (User) cc.newInstance();
        Method method = cc.getMethod("setName", String.class);
        method.invoke(user7,"你好哇,这个世界");
        System.out.println(user7.getName());//你好哇,这个世界

        //6.Modifier类
        //Class类中getModifiers()函数返回一个用于描述类,构造器,方法和域的修饰符的整形数值。、
        //调用Modifier.toString()方法将整型数值转变成字符串,也是就我们熟悉的public,private,static,final等修饰符。
        System.out.println(Modifier.toString(cc.getModifiers()));//无
        System.out.println(Modifier.toString(constructor.getModifiers()));//public
        System.out.println(Modifier.toString(field.getModifiers()));//private
        System.out.println(Modifier.toString(method.getModifiers()));//public

        //同时,Modifier类还有一些判断修饰符是不是某一类型的方法。
        System.out.println(Modifier.isPublic(cc.getModifiers()));
        System.out.println(Modifier.isPublic(constructor.getModifiers()));

    }
}

四、通过反射调用其他类的方法

下面以在另一个类的main中调用其他类的main方法。

首先要有一个被调用的类:TestPrint
package com.newframe.controllers.api;

/**
 * @author:wangdong
 * @description:反射测试的打印类
 */
public class TestPrint {

    public static void main(String[] args) {
        for (String arg: args) {
            System.out.println(arg);
        }
    }
}
需要有一个调用类去调用TestPrint类中的主方法
package com.newframe.controllers.api;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author:wangdong
 * @description:测试反射调用其他类的方法
 */
public class TestReflectInvoke {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //传统的方式调用
        TestPrint.main(new String[]{"hello","world"});//输出hello world

        //反射调用,路径为TestPrint的包路径
        Class<?> cc = Class.forName("com.newframe.controllers.api.TestPrint");
        Method method = cc.getMethod("main", String[].class);
        //这里需要用Object强转,实例化字符串数组对象时要在前面加(Object),不然会报参数个数不匹配的错误。
        method.invoke(null,(Object) new String[]{"你好","世界"});
    }
}

五、数组的反射

因为Array数组属于反射包java.lang.reflect.Array;

package com.newframe.controllers.api;

import java.lang.reflect.Array;

/**
 * @author:wangdong
 * @description:测试数组的反射
 */
public class TestReflectArray {

    public static void main(String[] args) throws ClassNotFoundException {
        //新建4个不同的数组
        //一维数组
        int[] a = new int[3];
        int[] b = new int[4];
        //二维数组
        int[][] c = new int[3][4];
        //String类型的数组
        String[] d = new String[3];

        //可以对比各个数组所属的类是否相同
        System.out.println(a.getClass() == b.getClass());//true
        //System.out.println(a.getClass() == c.getClass());不同类型的数组不同比较
        System.out.println(a.length == c.length);//但是可以比较长度,true
        System.out.println(a.getClass().getName());//[I
        System.out.println(d.getClass().getName());//[Ljava.lang.String;
        System.out.println(a.getClass().getSuperclass().getName());//java.lang.Object

        //利用反射生成一个数组
        int[] e = (int[]) Array.newInstance(int.class,3);
        //0为数组索引下标,1为数字值
        Array.set(e,0,1);
        Array.set(e,1,2);
        Array.set(e,2,3);
        System.out.println(Array.get(e,0));
        System.out.println(Array.get(e, 1));
        System.out.println(Array.get(e, 2));

        //利用反射获取数组类型常用的一些方式
        Class<?> cc = String[].class;
        Class<?> cc1 = Class.forName("[I");
        Class<?> cc2 = Class.forName("[Ljava.lang.String;");

        System.out.println(cc);
        System.out.println(cc1);
        System.out.println(cc2);

        //通过已有的对象获取数组的类型
        Class<?> cc3 = e.getClass();
        System.out.println(cc3);
        //根据类型信息获取数组内成员的类型
        Class<?> cc4 = cc3.getComponentType();
        System.out.println(cc4);//int
    }
}

六、友情索引

在知乎上有关于反射的讨论,可以参考一下:知乎反射

做技术,最重要的还是动手啊,很多概念,类似于反射啊、动态代理啊。
你不动手,你光看,肯定是不会深入理解的。
你动手敲一边代码,看看源码,立刻理解完全就不一样了啊。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java反射:如何正确理解,不如手写一个(反射包分析、楼主亲测) 的相关文章

  • 我是否需要安装 SQLite 才能使 SQLiteJDBC 正常工作?

    我想我只是没有 明白 如果我的计算机上尚未安装 SQLite 并且我想编写一个使用嵌入式数据库的 Java 应用程序 并且我将 SQLiteJDBC JAR 下载 导入到我的项目中 那么这就是我所需要的吗 或者 我是否需要先安装 SQLit
  • Netbeans 8.1 Gnome 3 GTK+ UI 字体和选项卡高度

    我刚刚在运行 GNOME 3 桌面的 Ubuntu 16 04 上安装了 NetBeans 8 1 如果可能的话 我想继续使用 IDE 的 GTK 外观和感觉 但 UI 上的字体 尤其是选项卡中的字体 太小且重叠 我尝试添加 fontsiz
  • Java Logger 未记录到 Netbeans 中的输出

    我正在 Netbeans 中使用 Maven 启动一个 Java 项目 我编写了一些代码来使用 Logger 类进行日志记录 但是 日志记录似乎不起作用 在程序开始时 我运行 Logger getLogger ProjectMainClas
  • Thymeleaf 3 Spring 5 映射加载字符串而不是 HTML

    我正在尝试将 Spring 5 和 Thymeleaf 3 一起配置 我正在 Eclipse 上工作 我使用 全新安装 构建并使用 springboot run 运行应用程序 我已经设置了一个控制器和几个模板 但 Thymeleaf 似乎找
  • java inputstream 打印控制台内容

    sock new Socket www google com 80 out new BufferedOutputStream sock getOutputStream in new BufferedInputStream sock getI
  • Runtime.exec 处理包含多个空格的参数

    我怎样才能进行以下运行 public class ExecTest public static void main String args try Notice the multiple spaces in the argument Str
  • 断言 Kafka 发送有效

    我正在使用 Spring Boot 编写一个应用程序 因此要写信给 Kafka 我这样做 Autowired private KafkaTemplate
  • Java 中如何将 char 转换为 int? [复制]

    这个问题在这里已经有答案了 我是Java编程新手 我有例如 char x 9 我需要得到撇号中的数字 即数字 9 本身 我尝试执行以下操作 char x 9 int y int x 但没有成功 那么我应该怎么做才能得到撇号中的数字呢 ASC
  • 如何在 ant 中为 junit 测试设置 file.encoding?

    我还没有完全完成file encoding 和 ant https stackoverflow com questions 1339352 how do i set dfile encoding within ants build xml
  • 如何在.NET中使用java.util.zip.Deflater解压缩放气流?

    之后我有一个转储java util zip Deflater 可以确认它是有效的 因为 Java 的Inflater打开它很好 并且需要在 NET中打开它 byte content ReadSample sampleName var inp
  • 如何在JPanel中设置背景图片

    你好 我使用 JPanel 作为我的框架的容器 然后我真的想在我的面板中使用背景图片 我真的需要帮助 这是我到目前为止的代码 这是更新 请检查这里是我的代码 import java awt import javax swing import
  • 使用 Elastic Beanstalk 进行 Logback

    我在使用 Elastic Beanstalk 记录应用程序日志时遇到问题 我正在 AWS Elastic Beanstalk 上的 Tomcat 8 5 with Corretto 11 running on 64bit Amazon Li
  • 不可变的最终变量应该始终是静态的吗? [复制]

    这个问题在这里已经有答案了 在java中 如果一个变量是不可变的并且是final的 那么它应该是一个静态类变量吗 我问这个问题是因为每次类的实例使用它时创建一个新对象似乎很浪费 因为无论如何它总是相同的 Example 每次调用方法时都会创
  • hibernate 6.0.2.Final 和 spring boot 2.7.0 的entityManagerFactory bean 未配置问题

    所以最近我想升级我的 Spring Boot 项目项目的一些依赖项 特别是这些组件 雅加达 EE 9 弹簧靴2 7 休眠 6 0 2 Final 完成此操作后 所有更新和代码折射 更新将 javax 导入到 jakarta 以及一些 hib
  • Spring @Cacheable 和 @Async 注解

    我需要缓存一些异步计算的结果 具体来说 为了克服这个问题 我尝试使用 Spring 4 3 缓存和异步计算功能 作为示例 我们采用以下代码 Service class AsyncService Async Cacheable users C
  • 子类构造函数(JAVA)中的重写函数[重复]

    这个问题在这里已经有答案了 为什么在派生类构造函数中调用超类构造函数时 id 0 当创建子对象时 什么时候在堆中为该对象分配内存 在基类构造函数运行之后还是之前 class Parent int id 10 Parent meth void
  • Android View Canvas onDraw 未执行

    我目前正在开发一个自定义视图 它在画布上绘制一些图块 这些图块是从多个文件加载的 并将在需要时加载 它们将由 AsyncTask 加载 如果它们已经加载 它们只会被绘制在画布上 这工作正常 如果加载了这些图片 AsyncTask 就会触发v
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • MiniDFSCluster UnsatisfiedLinkError org.apache.hadoop.io.nativeio.NativeIO$Windows.access0

    做时 new MiniDFSCluster Builder config build 我得到这个异常 java lang UnsatisfiedLinkError org apache hadoop io nativeio NativeIO
  • 由 Servlet 容器提供服务的 WebSocket

    上周我研究了 WebSockets 并对如何使用 Java Servlet API 实现服务器端进行了一些思考 我没有花费太多时间 但在使用 Tomcat 进行一些测试时遇到了以下问题 如果不修补容器或至少对 HttpServletResp

随机推荐

  • CORE-ESP32C3

    目录 参考博文 源于网友oled eink aht10项目 源代码修改及复现说明 主要修改 显示效果 编辑硬件准备 软件版本 日志及soc下载工具 软件使用 接线说明 天气显示屏 硬件接线 温度采集 日期温度显示屏 正常初始化LOG 示例代
  • Spring Boot跨域问题简介

    什么是跨域问题 在Web开发中 跨域指的是在浏览器中访问一个不同于当前域名的资源 浏览器出于安全考虑 限制了这种跨域资源的访问 具体来说 当浏览器使用XMLHttpRequest或Fetch API发送跨域请求时 目标服务器必须在响应头中包
  • Python爬虫-11-response.text出现乱码的解决方案

    代码如下 这里是封装的一个下载url页面的方法 import requests def download page url user Agent None referer None print Downloading url headers
  • 前端xp单位和数值批量转换插件 编辑器正则匹配搜索

    因为要使手机端app自适应ipad端 所以要把项目中部分使用px的固定单位的改为相对单位 uniapp中规定了页面的宽度为750rpx 所以改起来还是很简单的 但是使用正则匹配修改px单位为rpx 编辑器可以按照正则匹配 但是因为没有运算功
  • 怎么禁用Windows Defender?

    如果你没有安装第三方杀毒软件 Windows10会自动激活其内置的Window Defender杀毒软件 虽然Windows Defender是Windows内置的 但是杀毒能力只能算比较平庸 并且在很多操作步骤和使用方法都不太符合用户的习
  • C++11Lambda表达式

    Lambda表达式 定义 可以理解为一个匿名函数 和函数一样 lambda表达式具有一个返回类型 一个参数列表和一个函数体 语法 capture list parameter list gt return type function bod
  • 使用tensorrt对keras-yolov3 模型进行低精度量化相关报错

    基本错误都是环境引起的 所以环境很重要 环境 python3 5 cuda10 0 cudnn 7 5 0 TensorRT 6 0 1 onnx 1 3 0 相关错误 错误1 NoneType object has no attribut
  • C++11--constexpr关键字

    关键字 constexpr 是在 C 11 中引入的 并在 C 14 中进行了改进 作用 它是用于表示 constant 常量 表达式的 常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式 使用常量表达式可以提高程序的执行效率
  • kali linux渗透测试之漏洞扫描

    主题内容就是进行漏洞扫描 文章目录 前言 一 Nikto 1 Nikto漏洞扫描介绍 2 Nikto使用 二 Nessus 1 Nessus介绍 2 安装nessus 3 nessus的简单使用 3 nessus扫描之advanced sc
  • 从浏览器地址栏输入url到显示页面的过程

    基本流程 1 用户在浏览器中输入url地址 2 浏览器解析域名得到服务器ip地址 浏览器会首先从缓存中找是否存在域名 如果存在就直接取出对应的ip地址 如果没有就开启一个DNS域名解析器 DNS域名解析器会首先访问顶级域名服务器 将对应的i
  • python编程入门书-最适合Python初学者的6本书籍推荐「必须收藏」

    Python是一种通用的解释型编程 主要用于Web开发 机器学习和复杂数据分析 Python对初学者来说是一种完美的语言 因为它易于学习和理解 随着这种语言的普及 Python程序员的机会也越来越大 如果你想学习Python编程 市场上就有
  • 2020年tensorflow定制训练模型笔记(1)——object detection的安装

    自己看着网上的很多教程摸索了好几天 终于能够自己训练 事实上 网上关于这个API的教程还是非常多的 但我实际做起来发现其实在某些关键部分缺少点步骤 会把我这样的小白搞得一头雾水 无从下手 最后在无穷无尽的报错中崩溃 所以我决定写这篇笔记 一
  • QT控件之(Lable)中的文字对齐方式

    今天才发现lable一般拖动到界面中 拉大之后 界面效果就是如下 是靠左显示出来的 但是我们想让它进行居中的显示效果 就需要选择如下的居中效果 然后就发现它可以达到居中的效果了 学习推荐 百度云盘 链接 https pan baidu co
  • 2023年数学建模排队论及Matlab实战案例

    订阅专栏后9月比赛期间会分享思路及Matlab代码 目录 实战案例 银行窗口服务模型 1 问题建模
  • pub get一直卡在Resolving dependencies...

    添加pubspec yaml配置 pub get一直卡在Resolving dependencies 原因 访问国外下载很慢 我们需要配置镜像 解决方法 官方提供的国内镜像 export PUB HOSTED URL https pub f
  • linux安装nginx服务器并配置支持php LNMP搭建

    本机环境 centos7 4 64bit 概要 使用yum安装nginx 源码编译安装php fpm 配置让服务器能支持解析php 友情连接 LAMP搭建 https blog csdn net Dong Alex article deta
  • 修改数据库表结构(SQLserver)

    1 添加表字段 alter table 表名 add 字段名 类型 值 示例 alter table tableName add columnName varchar 40 2 删除表字段 alter table 表名 drop colum
  • 建站系列(五)--- 前端开发语言之HTML、CSS、JavaScript

    目录 相关系列文章 前言 一 前端开发与后端开发 二 前端语言简介 一 HTML 二 CSS 三 JavaScript 三 学习指导 一 开发环境 二 第一个Hello world 相关系列文章 建站系列 一 网站基本常识 建站系列 二 域
  • SpringMVC requestBody和responseBody重复获取

    SpringMVC requestBody和responseBody重复获取 最近有个需求 要根据响应结果来判断 是否需要保存requestBody的内容到数据库 想法使用拦截器HandlerInterceptor 拦截需要处理的url 根
  • Java反射:如何正确理解,不如手写一个(反射包分析、楼主亲测)

    Java反射机制 动态代理是基于什么原理 这个问题可谓是老生常谈的一个热门问题了 如果没有深入的思考还真的是很难回到上来 那么今天我们一起来看看 如何正确清晰的认识这个热门却又说简单又不简单说复杂又比较复杂的问题 一 什么是反射 反射机制是