反射简介及简单使用

1、反射的定义及作用

在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取类的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。在Java运行时环境中,对于任意一个类的对象,可以通过反射获取这个类的信息。

反射的作用:Java反射机制允许程序在运行时透过Reflection APIs取得任意一个已知名称的class的内部信息,包括modifiers(如public、static等)、superclass(如Object)、实现的interfaces(如Serializable)、fields(属性)和methods(方法)(但不包括methods定义),可于运行时改变fields的内容,也可调用methods。

2Class类的API简介

由类的加载可知,类加载的最终产品是位于方法区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序提供了访问方法区内的数据结构的接口。.class文件在硬盘中时是一个文件,当载入到内存中,可以认为是一个对象,是java.lang.Class类的对象。(在java中一切皆对象,类也是对象)

想要使用反射机制,就必须要先获取到该类的字节码文件对象,通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件,也就对应着一个Class类型的对象,也就是字节码文件对象。

获取字节码文件对象的三种方式。

1、Class clazz1 = Class.forName("全限定类名");

通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。

2、Class clazz2  = 类名.class;

当类被加载成.class文件时,再获取该字节码文件对象,该类处于字节码阶段

3、Class clazz3 = 对象.getClass();

通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段。(java.lang.Object类(所有类的超类)定义了getClass()方法,任意一个Java对象都可以通过此方法获得它的Class对象。)

有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用上面哪种方式获取字节码对象合理,视不同情况而定。

Java Reflection API简介

在JDK中,主要由以下类实现Java反射机制:

①Class类:代表一个类;

②Field类:代表类的成员变量(属性);

③Method类:代表类的方法;

④Constructor类:代表类的构造方法;

⑤Array类:提供了动态创建数组,以及访问数组元素的静态方法;

以上类中,Class类在java.lang包,其余位于java.lang.reflect包。

Class类是Reflection API中的核心类,主要有以下方法:

①getName():获取类的名字;

②getFields():获取类中public类型的属性;

③getDeclaredFields():获取类的所有属性(包括public、protected、default、private);

④getMethods():获取类中public类型的方法;

⑤getDeclaredMethods():获取类的所有方法;

⑥getMethod(String name,Class[] parameterTypes):获取类的指定方法,name:指定方法的名字,parameterType:指定方法的参数类型;

⑦getConstrutors():获取类中所有构造方法(包括private修饰的);

⑧getConstrutor(Class[] parameterTypes):获取类的指定构造方法,parameterTypes:指定构造方法的参数类型;

⑨newInstance():通过类默认的不带参数的构造方法创建该类的一个对象;

注:在访问私有属性和私有方法时,需要对访问的私有属性或方法设置setAccessible(true)使被反射的类抑制java的访问检查机制。否则会报IllegalAccessException异常。

3、反射的使用

创建一个java bean(如User类)和一个测试类(如ReflectTest类),如下所示(下面所有测试方法都是在ReflectTest类中定义的)。

User

package com.test; public class User { private String name = "init"; private int age; public User() {} public User(String name, int age) { super(); this.name = name; this.age = age; }private String getName() { return name; }private void setName(String name) { this.name = name; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }@Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } 
}

测试类

public class ReflectTest { private static Logger logger = Logger.getLogger(ReflectTest.class); //获取Class对象,在下面获取类名、构造函数、属性、方法等都需要该对象private static Class<User> userClass = User.class; //ReflectTest测试类中定义的测试方法在下面分别进行讲解,此处省略不写了
}

3.1、获取Class对象

在使用反射之前,必须获取类的字节码文件对象(此处是User.class文件对应的字节码文件对象),即Class类的对象(字节码文件对象都是Class类型的对象)。

由上面可知,获取class对象的方式主要有三种:

根据类名获取:类名.class

根据对象获取:对象.getClass()

根据全限定类名获取:Class.forName(“全限定类名”)

@Test 
public void getClassTest() throws Exception { // 获取Class对象的三种方式,在ReflectTest类中就是使用第一种方式获取的logger.info("根据类名: \t" + User.class); logger.info("根据对象: \t" + new User().getClass()); logger.info("根据全限定类名:\t" + Class.forName("com.test.User")); 
}

输出结果:

根据类名: class com.test.User

根据对象: class com.test.User

根据全限定类名: class com.test.User

3.2、获取类名

@Test 
public void classTest() throws Exception { // 常用的方法 logger.info("获取全限定类名:\t" + userClass.getName()); logger.info("获取类名:\t" + userClass.getSimpleName()); logger.info("实例化:\t" + userClass.newInstance()); 
}

输出结果:

获取全限定类名: com.test.User

获取类名: com.test.User

实例化: User [name=init, age=0]

Class类的newInstance()方法是使用该类无参的构造函数创建对象,如果一个类没有无参的构造函数,就不能这样创建,可以调用Class类的getConstructor()方法获取一个指定的构造函数,然后再调用Constructor类的newInstance()方法创建对象,如下所示。

3.3、获取构造函数

@Test 
public void constructorTest() throws Exception { // 获取全部的构造函数 Constructor<?>[] constructors = userClass.getConstructors(); //取消安全性检查,设置后才可以使用private修饰的构造函数,也可以单独对某个构造函数进行设置 	Constructor.setAccessible(constructors, true); for (int i = 0; i < constructors.length; i++) { //获取构造函数中参数类型的字节码文件对象数组Class<?> parameterTypesClass[] = constructors[i].getParameterTypes(); System.out.print("第" + i + "个构造函数:\t ("); for (int j = 0; j < parameterTypesClass.length; j++) { System.out.print(parameterTypesClass[j].getName() + (j == parameterTypesClass.length - 1 ? "" : "\t")); } logger.info(")"); } // 调用构造函数,实例化对象 logger.info("实例化,调用无参构造:\t" + constructors[0].newInstance()); logger.info("实例化,调用有参构造:\t" + constructors[1].newInstance("韦德", 35));
}

输出结果:

0个构造函数: ()

1个构造函数: (java.lang.String int)

实例化,调用无参构造: User [name=init, age=0]

实例化,调用有参构造: User [name=韦德, age=35]

3.4、获取属性

@Test 
public void fieldTest() throws Exception { //使用User类的无参构造函数创建User对象User user = userClass.newInstance(); // 获取当前类所有属性 Field fields[] = userClass.getDeclaredFields(); // 获取公有属性(包括父类) // Field fields[] = userClass.getFields(); // 取消安全性检查,设置后才可以获取或者修改private修饰的属性,也可以单独对某个属性进行设置 Field.setAccessible(fields, true); for (Field field : fields) { // 获取属性名、属性值、属性类型 logger.info("属性名:" + field.getName() + "\t属性值:" + field.get(user) + " \t属性类型:" + field.getType()); } // 获取具体某个属性名,此处获取"name"属性Field fieldUserName = userClass.getDeclaredField("name"); // 取消安全性检查,设置后才可以获取或者修改private修饰的属性,也可以批量对所有属性进行设置 fieldUserName.setAccessible(true); fieldUserName.set(user, "韦德"); logger.info("name属性的值:" + fieldUserName.get(user));// 可以直接对 private 的属性赋值 logger.info("修改属性后对象:\t" + user); 
}

输出结果:

属性名:name 属性值:init 属性类型:class java.lang.String

属性名:age 属性值:0 属性类型:int

name属性的值:韦德

修改属性后对象: User [name=韦德, age=0]

Class.getField("属性名称")方法可以获取类中的指定属性(public修饰的),如果获取私有属性,可以用getDeclaedField("属性名称")方法获取。通过获取的属性的set(对象, "属性值")方法可以设置指定对象上该属性的值,如果是私有的,需要先调用setAccessible(true)设置访问权限,用获取的属性调用get(对象)可以获取指定对象中该属性的值。

3.5、获取方法

@Test 
public void methodTest() throws Exception { User user = userClass.newInstance(); // 获取当前类的所有方法 Method[] methods = userClass.getDeclaredMethods(); // 获取公有方法(包括父类) // Method[] methods = userClass.getMethods(); // 取消安全性检查,设置后才可以调用private修饰的方法,也可以单独对某个方法进行设置 Method.setAccessible(methods, true); for (Method method : methods) { // 获取方法名和返回类型,获取参数类型:getParameterTypes logger.info("方法名:" + method.getName() + " \t返回类型:" + method.getReturnType().getName()); } // 获取无参方法 Method getMethod = userClass.getDeclaredMethod("getName"); // 取消安全性检查,设置后才可以调用private修饰的方法,也可以批量对所有方法进行设置 getMethod.setAccessible(true); // 调用无参方法 logger.info("调用getName方法:" + getMethod.invoke(user)); // 获取有参方法 Method setMethod = userClass.getDeclaredMethod("setName", String.class); // 取消安全性检查,设置后才可以调用private修饰的方法,也可以批量对所有方法进行设置 setMethod.setAccessible(true); // 调用有参方法 logger.info("调用setName方法:" + setMethod.invoke(user, "韦德")); logger.info("通过set方法修改属性后对象:\t" + user); 
}

输出结果:

方法名:toString 返回类型:java.lang.String

方法名:setAge 返回类型:void

方法名:getAge 返回类型:int

方法名:getName 返回类型:java.lang.String

方法名:setName 返回类型:void

调用getName方法:init

调用setName方法:null

通过set方法修改属性后对象: User [name=韦德, age=0]

Class.getMethod("方法名",方法参数类型的Class类型)和Class.getDeclaredMethod("方法名",方法参数类型的Class类型)方法可以获取类中的指定方法,如果为私有方法,则需要使用setAccessible(true)打开一个权限。例如,User类中的private void setName(String name)方法,方法名是setName,方法参数类型是String类型,参数类型的Class类型就是String.class,而且是private私有方法,所以使用反射获取该方法的代码如下所示:Method getMethod = userClass.getDeclaredMethod("setName", String.class)

用获取的方法名调用invoke(方法所属的对象, 参数值)方法可以对该方法操作,如果方法没有参数值,可以不写参数值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/333.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

iOS 18.2 可让欧盟用户删除App Store、Safari、信息、相机和照片应用

升级到 iOS 18.2 之后&#xff0c;欧盟的 iPhone 用户可以完全删除一些核心应用程序&#xff0c;包括 App Store、Safari、信息、相机和 Photos 。苹果在 8 月份表示&#xff0c;计划对其在欧盟的数字市场法案合规性进行更多修改&#xff0c;其中一项更新包括欧盟用户删除系统应…

基于Spring Boot的信息学科平台系统开发与优化

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

Unity——如何自然采摘果实(进行中)

2023.3.9 思路&#xff1a;把苹果挂在树上&#xff0c;开始苹果没有重力&#xff0c;当用手摘下后&#xff0c;有重力&#xff0c; 此时树枝播放颤动动画。 目前&#xff1a;树枝使用网格碰撞器。 当与其他碰撞体接触并离开后&#xff0c;播放动画。 private void OnTrigge…

局部加权回归

2. 局部加权回归 (Loess / Lowess) 局部加权回归是一种非参数回归方法&#xff0c;可以自适应地拟合数据的弧度。它对每个点应用加权回归&#xff0c;以根据数据的局部趋势产生一条平滑曲线。这种方法特别适合捕捉数据中较小的曲率变化。 优点&#xff1a;能够很好地拟合微小…

湘潭市学生公交卡线上申领流程及一寸照片自拍方法

在湘潭市&#xff0c;学生公交卡的线上申领流程已经非常便捷&#xff0c;同时&#xff0c;为了满足学生公交卡申领时所需的一寸照片要求&#xff0c;本文将详细介绍整个申领流程以及如何使用手机自拍并制作线上申领学生公交卡所需的一寸照片电子版。 一、湘潭市学生公交卡线上申…

STM32HAL-最简单的长、短、多击按键框架(多按键)

概述 本文章使用最简单的写法实现长、短、多击按键框架,非常适合移植各类型单片机,特别是资源少的芯片上。接下来将在stm32单片机上实现,只需占用1个定时器作为时钟扫描按键即可。 一、开发环境 1、硬件平台 STM32F401CEU6 内部Flash : 512Kbytes,SARM …

研究了100个小绿书十万加之后,我们发现2024小绿书独家秘籍就是:在于“先抄后超,持续出摊,量大管饱”!

小绿书作为今年最大的红利&#xff0c;很多人已经吃到了螃蟹。看——&#xff1a; 今天我们总结了100个10万爆款&#xff0c;我们发现要在这个平台上脱颖而出&#xff0c;找到属于自己的方法尤为重要。在这里分享一个主题——小绿书的秘诀就是“先抄后超&#xff0c;持续出摊”…

鸿蒙原生应用开发及部署:首选华为云,开启HarmonyOS NEXT App新纪元

目录 前言 HarmonyOS NEXT&#xff1a;下一代操作系统的愿景 1、核心特性和优势 2、如何推动应用生态的发展 3、对开发者和用户的影响 华为云服务在鸿蒙原生应用开发中的作用 1、华为云ECS C系列实例 &#xff08;1&#xff09;全维度性能升级 &#xff08;2&#xff…

[mysql]数据定义语言DDL和数据操作语言DCL

目录 前文提要 数据定义语言DDL 数据操作语言DML 数据控制语言DCL 基础知识: 标识符(命名规则): 数据定义语言DDL 创建和管理数据库.: 管理数据库 切换数据库 修改数据库 更改数据库字符集 删除数据库 如何创建表 方式1:”白手起家的方式”创建表 方式2:已经有…

redis:基本全局命令-键管理(1)

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》《网络》 《redis学习笔记》 文章目录 前言KEYSEXISTSDELEXPIRETTLTYPE 总结 前言 本文是作为基本全局命令-键管理的学习笔记 redis 是按照键值对的方式存储数据的&#xff0c;red…

【工具变量】大数据管理机构改革DID(2007-2023年)

数据简介&#xff1a;数字ZF是指以新一代信息技术为支撑&#xff0c;重塑政务信息化管理架构、业务架构、技术架构的现代化治理模式。随着数字政府的建设&#xff0c;特别是借助大数据等新一代数字技术&#xff0c;极大地提升了政府的治理能力&#xff0c;从而起到辅助监管机构…

自编以e为底的对数函数ln,性能接近标准库函数

算法描述&#xff1a; (1). 先做自变量x的范围检查&#xff0c;不能出现负数和0. 自己使用时&#xff0c;如果能通过其它途径保证自变量为正&#xff0c;那么可以省略这两个判断&#xff0c;提高速度。 (2). 根据IEEE 754浮点数的格式&#xff0c;&#xff0c;则 ln(x)kln(2)ln…

[vulnhub] billu: b0x

https://www.vulnhub.com/entry/billu-b0x,188/ 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的&#xff0c;所以靶机IP是168 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-28 18:54 CST Nmap scan report for 192.…

《机器人SLAM导航核心技术与实战》第1季:第10章_其他SLAM系统

视频讲解 【第1季】10.第10章_其他SLAM系统-视频讲解 【第1季】10.1.第10章_其他SLAM系统_RTABMAP算法-视频讲解 【第1季】10.2.第10章_其他SLAM系统_VINS算法-视频讲解 【第1季】10.3.第10章_其他SLAM系统_机器学习与SLAM-视频讲解 第1季&#xff1a;第10章_其他SLAM系统 …

JDK8---Stream流详解

Stream流 一.概述二.数据准备二.Stream流的创建2.1 单列集合创建Stream流.2.2 数组创建Stream流2.3 双列集合创建Stream流 三. 中间操作.3.1 filter(过滤操作)3.2 map(计算或者转换)3.3 distinct(去重操作)3.4 sorted(排序操作)3.5 limit (设置流的长度)3.6 skip(跳过前n个元素…

tcp shutdown, fin_wait1, fin_wait2, close_wait, last_ack, 谢特!

TCP 作为双向传输协议&#xff0c;如果你想只收不发&#xff0c;可以单向关掉发&#xff0c;shutdown(socket.SHUT_WR)&#xff0c;但不建议这么做。 看以下代码&#xff1a; #!/Users/zhaoya/myenv/bin/python3 # client import socketclient_socket socket.socket(socket.…

算法详解——线段树

1. 线段树介绍 线段树是一个高度平衡二叉树&#xff0c;它主要用来高效动态地管理一个序列。线段树叶子结点存储序列元素值&#xff0c;分支结点存储一个连续地子区间的某种聚合信息&#xff0c;例如最值、均值等信息。 如图所示&#xff1a; 用这样一个树状结构来管理序列…

XXL-JOB

Github 地址&#xff1a; https://github.com/xuxueli/xxl-job/ 。 官⽅介绍&#xff1a; https://www.xuxueli.com/xxl-job/ 。 XXL-JOB 于 2015 年开源&#xff0c;是⼀款优秀的轻量级分布式任务调度框架&#xff0c;⽀持任务可视化管理、弹性 扩容缩容、任务失败重试和告…

基于 Python 的 Django 框架开发的电影推荐系统

项目简介&#xff1a;本项目是基于 Python 的 Django 框架开发的电影推荐系统&#xff0c;主要功能包括&#xff1a; 电影信息爬取&#xff1a;获取并更新电影数据。数据展示&#xff1a;提供电影数据的列表展示。推荐系统&#xff1a;基于协同过滤算法实现个性化推荐。用户系…

服务器的免密登录和文件传输

在天文学研究中&#xff0c;通常会采用ssh登录服务器&#xff0c;把复杂的计算交给服务器&#xff0c;但是如果你没有进行额外的配置&#xff0c;那么登录服务器&#xff0c;以及和服务器进行文件传输&#xff0c;每次都要输入账号和密码&#xff0c;比较不方便&#xff0c;Win…