2023.12.12 关于 Java 反射详解

news2024/11/18 1:36:53

目录

基本概念

定义

用途

反射相关的类

反射基本原理

Class 类中的相关方法

常用获得类相关的方法

常用获得类中属性相关的方法

常用获得类中构造器相关的方法

常用获得类中方法相关的方法

实例理解

反射优缺点


基本概念

定义

  • Java 的反射(reflection)机制是一种强大功能,它可以让我们在运行时动态地获取和操作 类 或 对象 的信息

实例理解

  • 我们可以通过反射机制来创建一个类的对象,而不需要使用 new 关键字
  • 我们也可以通过反射机制来访问或修改一个对象的私有属性或方法,而不需要遵循封装原则
  • 我们还可以通过反射机制来调用一个对象的任意方法,而不需要知道它的参数类型或返回值类型

用途

典型用途一:

  • 在开发第三方应用时,我们可能会遇到一些类的成员变量、方法或属性是私有的,或者只对系统应用开放,这就意味着我们不能直接访问这些成员或方法
  • 这时我们便可以在运行时 通过 Java 的反射机制 来动态地访问和操作类的内部成员,包括私有成员和方法

典型用途二:

  • 反射在开发通用框架 Spring 中起着重要的作用
  • 在Spring 框架中,所有类(Bean)都由 Spring 容器进行管理,这些 Bean 可以通过 XML 配置或注解来配置
  • 当我们从容器中获取Bean 以进行依赖注入时,容器会读取配置信息,这些配置信息包含了类的信息,比如类的名称、属性、方法等
  • Spring根据这些信息 动态地创建这些类的实例,这个过程就是所谓的 依赖注入,该过程中,反射起到了关键作用
  • Spring 使用反射来动态地创建类的实例,调用方法,以及设置属性值

反射相关的类

反射基本原理

  • Java 的反射机制是基于 java.lang.Class 类实现的
  • 当我们编译一个 Java 文件时,会生成一个 .class 文件
  • 当 JVM 加载这个 .class 文件时,会将其解析为一个 java.lang.Class 类的对象
  • 在程序运行时,每个 Java 文件都会被 JVM 解析为一个 Class 类的实例
  • 这个 Class 类的实例包含了该 Java 文件中所定义类的所有信息,包括类的名称、属性、方法等
  • 我们可以通过 Java 的反射机制来操作这个 Class 类的实例
  • 具体来说,我们可以使用反射来获取类的属性和方法,甚至可以添加或修改类的属性和方法
  • 这使得我们可以在运行时动态地操作类,使其成为一个 动态的类
类名用途
Class 类代表类的实体,在运行的 Java 应用程序中表示类和接口
Field 类代表类的成员变量、类的属性
Method 类代表类的方法
Constructor 类代表类的构造方法

Class 类中的相关方法

常用获得类相关的方法

方法用途
getClassLoader()获得类的加载器
getDeclaredClasses()返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的)
forName(String className)根据类名返回类的对象
newInstance()创建类的实例
getName()获得类的完整路径名字

常用获得类中属性相关的方法

方法用途
getField(String name)获得某个公有的属性对象
getFields()获得所有公有的属性对象
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象

常用获得类中构造器相关的方法

方法用途
getConstructor(Class <?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class <?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法

常用获得类中方法相关的方法

方法用途
getMethod(String name, Class <?> parameterTypes) 获得该类某个公有的方法
getMethods()获得该类所有公有的方法
getDeclaredMethod(String name, Class <?> parameterTypes) 获得该类某个方法
getDeclaredMethods()获得该类所有方法

实例理解

  • 此处我们先创建一个 Student 类
class Student{
    //私有属性name
    private String name = "master";
    //公有属性age
    public int age = 18;
    //不带参数的构造方法
    public Student(){
        System.out.println("Student()");
    }
    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }
    private void eat(){
        System.out.println("make hamburger!");
    }
    public void sleep(){
        System.out.println("go to bed!");
    }
    private void function(String str) {
        System.out.println(str);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 此处介绍三种方式来获取 Student 的 Class 对象

public class Demo1 {
    public static void main(String[] args) {
//        有三种方式可以获取 Class 对象
        Student student1 = new Student();
//        1、通过对象的 getClass() 方法
        Class<?> c1 = student1.getClass();
//        2、通过类名 .class 获取
        Class<?> c2 = Student.class;
//        3、通过调用 Class.forName() 方法获取了 Student 类的 Class 对象
//        Class.forName() 方法需要一个类的全限定名(包括 包名和类名) 作为参数
//        此处的 ? 是一个通配符,用于表示未知类型
//        当我们声明一个泛型变量时,如果我们不确定或不关心实际的类型参数,我们可以使用 ? 来表示
        Class<?> c3 = null;
        try {
            c3 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
//        此处证明通过上述三种方式所获取的 Class 对象 都是同一个
        System.out.println((c1.equals(c2) && c1.equals(c3) && c2.equals(c3)) ? "true" : "false");
    }
}
  • 此处我们通过反射机制创建一个对象

import java.lang.reflect.InvocationTargetException;

public class ReflectClassDemo {

//  通过反射创建一个对象
    public static void reflectNewInstance() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Class<?> c3 = Class.forName("Student");
//        通过调用 Class 类的 newInstance() 方法来创建一个 c3 对应类的新实例
//        newInstance() 方法调用的是这个类的无参构造函数
//        如果这个类没有无参构造函数,或者无参构造函数是私有的,那么 newInstance 会抛出一个异常
//        因为 newInstance() 方法返回的类型为 Object 类 所以需要类型转换,此处转换为 Student 类
        Student student = (Student) c3.newInstance();
        System.out.println(student);
    }

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

运行结果:

  • 此处我们通过反射机制获取私有的构造方法
import java.lang.reflect.Constructor;

public class ReflectClassDemo {

//  通过反射获取私有的构造方法
    public static void  reflectPrivateConstructor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> c3 = Class.forName("Student");
        Constructor<?> constructor = c3.getDeclaredConstructor(String.class,int.class);
//        注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的构造方法为私有的
        constructor.setAccessible(true);
//        此处利用构造方法 修改年龄和性别
        Student student = (Student)constructor.newInstance("xiaolin",20);
        System.out.println(student);
    }

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

运行结果:

  • 此处我们通过反射机制获取私有属性
import java.lang.reflect.Field;

public class ReflectClassDemo {

//  通过反射获取私有属性
    public static void reflectPrivateField() throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        Class<?> c3 = Class.forName("Student");
        Field field = c3.getDeclaredField("name");
//        注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的 name 属性是私有的
        field.setAccessible(true);
        Student student = (Student) c3.newInstance();
//        此处修改私有属性
        field.set(student,"haoran");
        System.out.println(student);
    }

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

运行结果:

  • 此处我们通过反射机制获取私有方法
import java.lang.reflect.Method;

public class ReflectClassDemo {

//  通过反射获取私有方法
    public static void reflectPrivateMethod() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> c3 = Class.forName("Student");
        Method method1 = c3.getDeclaredMethod("function", String.class);
        Method method2 = c3.getDeclaredMethod("sleep");
//        注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的 function 方法是私有的
        method1.setAccessible(true);
        Student student = (Student) c3.newInstance();
//        此处给 function 方法传参
        method1.invoke(student,"此处利用反射机制给 function 方法传个字符串参数");
//        此处调用 sleep 方法,该方法为 public 无需额外打开权限,直接调用即可
        method2.invoke(student);
    }

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

运行结果:

反射优缺点

优点

  • 对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个类都能调用它的任意一个方法
  • 增加程序的灵活性和扩展性,降低耦合性,提高自适应性
  • 反射已经运用在很多流行框架,典型代表为 Spring

缺点

  • 使用反射会有效率问题,会导致程序效率降低
  • 反射技术绕过了源代码的技术,因而会带来维护问题
  • 反射代码比相应的直接代码更复杂

总结:

  • 虽然反射非常强大,但也需要谨慎使用
  • 我们需要在反射带来的灵活性和可扩展性与其带来的性能开销、维护问题和代码复杂性之间找到一个平衡
  • 某些情况下,比如开发通用的框架,使用反射式非常有价值的
  • 但在其他情况下,我们可能更倾向于使用更简单、更直接的代码

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

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

相关文章

Unity 置顶OpenFileDialog文件选择框

置顶文件选择框 &#x1f32d;处理前&#x1f959;处理后 &#x1f32d;处理前 &#x1f959;处理后 解决方案

持续集成交付CICD:CentOS 7 安装SaltStack

目录 一、理论 1.SaltStack 二、实验 1.主机一安装master 2.主机二安装第一台minion 3.主机三安装第二台minion 4.测试SaltStack 一、理论 1.SaltStack &#xff08;1&#xff09;概念 SaltStack是基于python开发的一套C/S自动化运维工具&#xff0c;通信采用了zerom…

【谭浩强C语言:前八章编程题(多解)】

文章目录 第一章1. 求两个整数之和(p7) 第二章2. 求三个数中的较大值&#xff08;用函数&#xff09;(p14、p107)3.求123...n(求n的阶乘&#xff0c;用for循环与while循环)(P17)1.循环求n的阶乘2.递归求n的阶乘(n< 10) 4.有M个学生&#xff0c;输出成绩在80分以上的学生的学…

【JVM从入门到实战】(六)类加载器的双亲委派机制

一、双亲委派机制 在Java中如何使用代码的方式去主动加载一个类呢&#xff1f; 方式1&#xff1a;使用Class.forName方法&#xff0c;使用当前类的类加载器去加载指定的类。 方式2&#xff1a;获取到类加载器&#xff0c;通过类加载器的loadClass方法指定某个类加载器加载。 …

VR虚拟现实的七大应用领域

一、工业领域 园区利用虚拟现实技术优化生产管理与节能减排&#xff0c;实现提质增效降本。发展支持多人协作和模拟仿真的虚拟现实开放式服务平台&#xff0c;打通产品设计与制造环节&#xff0c;构建虚实融合的远程运维新型解决方案&#xff0c;适配各类先进制造技术的员工技…

Linux——MySQL数据库的使用

访问MySQL数据库 MySOL数据库系统也是一个典型的C/S&#xff08;客户端/服务器&#xff09;架构的应用&#xff0c;要访问MySOL数据库 需要使用专门的客户端软件&#xff0c;在Linux系统中&#xff0c;最简单、易用的MySQL.客户端软件是其自带的mysql 命令工具。 登录到MySQL服…

经典基本电路

USB电路 USB差分走线的阻抗为90欧:差分对10mil宽的走线以及5mil的间距,两边包地15/20mil以上厚度(SI9000计算阻抗) USB2.0接口电路&#xff1a; USB3.0接口电路&#xff1a; USB HUB电路: HDMI电路 HDMI差分走线的阻抗为100欧:差分对6mil宽的走线以及5mil的间距,两边包地15/20…

05 python数据容器

5.1 数据容器认识 5.2 python列表 5.2.1 列表的定义 演示数据容器之&#xff1a;list 语法&#xff1a;[元素&#xff0c;元素&#xff0c;....] #定义一个列表List List [itheima,uityu,gsdfg] List1 [itheima,6666,True] print(List) print(List1) print(type(List)) pr…

stm32F407-GPIO的使用——点亮LED并且讲解各个寄存器

stm32F407-GPIO的使用——点亮LED并且讲解各个寄存器 本文为stm32GPIO的介绍与使用&#xff0c;例子是简单的LED点亮。 一、 GPIO GPIO&#xff08;General Purpose I/O Ports&#xff09;意思为通用输入/输出端口&#xff0c;通俗地说&#xff0c; 就是一些引脚&#xff0c;可…

关于AISD300系列三相智能安全配电装置的详细介绍-安科瑞 蒋静

1概述 AISD300系列三相智能安全配电装置是安科瑞专为低压配电侧开发的一款智能安全配电产品&#xff0c;本产品主要针对低压配电系统人身触电、线路老化、短路、漏电等原因引起电气安全问题而设计。 产品主要应用于学校、加油站、医院、银行、疗养院、康复中心、敬老院、酒店…

Python 自动化之收发邮件(一)

imapclient / smtplib 收发邮件 文章目录 imapclient / smtplib 收发邮件前言一、基本内容二、发送邮件1.整体代码 三、获取邮件1.整体代码 总结 前言 简单给大家写个如何用Python进行发邮件和查看邮件教程&#xff0c;希望对各位有所帮助。 一、基本内容 本文主要分为两部分…

动画制作与动画控制器的使用_unity基础开发教程

动画制作与动画控制器的使用 导入素材创建动画控制器制作人物动画 前面我们讲过2D游戏中环境地图的制作&#xff0c;这里教大家使用动画控制器的使用 导入素材 先导入一下素材 选择window&#xff0c;点击Asset Store 点击Search online 搜索栏输入Sunny&#xff0c;然后回车…

2022年AMC8数学竞赛真题的典型考点和解析

大家好&#xff01; 从战争中学习战争。 在2024年AMC8考前一个多月的时间中&#xff0c;孩子除了完成学校作业外&#xff0c;备考AMC8的有效方法就是做往年真题&#xff0c;通过往年真题进一步了解AMC8的考点、题型&#xff0c;建立自己的解题策略和方法。并且查漏补缺&#…

Docker容器如何优雅地访问宿主机网络

# 前言 某些时候&#xff0c;我们会有在容器内容访问宿主机某个服务的需求&#xff0c;比如现在 openai 无法直接访问&#xff0c;需要给项目添加代理&#xff0c;我的 chatgpt-dingtalk (opens new window) 项目支持了通过环境变量指定代理地址。 添加方式如下&#xff1a; …

使用对象处理流ObjectOutputStream读写文件

注意事项: 1.创建的对象必须实现序列化接口,如果属性也是类&#xff0c;那么对应的类也要序列化 2.读写文件路径问题 3.演示一个例子 &#xff08;1&#xff09;操作的实体类FileModel&#xff0c;实体类中有Map,HashMap这些自带的本身就实现了序列化。 public class File…

c语言printf函数与==和=

介绍&#xff1a; 本篇文章 是一些 关于c语言的 细节代码讲解 代码与讲解&#xff1a; 代码&#xff1a; #include <stdio.h>int main(void) {int a 1, b 2, c 3;printf("%d %d %d %d\n", a b c, a b c, a (b c), a (b c));return 0; } 大家先自…

玉米浸泡液回收植酸,植酸回收树脂

植酸为环己六醇六磷酸&#xff0c;分子量为660&#xff0c;植酸钠为环己六醇六磷酸钠&#xff08;Na12C6H6O24P6&#xff09;分子量为924。 植酸用途&#xff1a;在食品工业中植酸钠可用作食品添设剂&#xff0c;菲丁&#xff08;植酸钙&#xff09;可用于生产肌醇&#xff0c…

02.尚医通 Mybatis-Plus

1、前期准备 a. 创建数据库 CREATE TABLE USER (id BIGINT(20)NOT NULL COMMENT 主键ID,NAME VARCHAR(30)NULL DEFAULT NULL COMMENT 姓名,age INT(11)NULL DEFAULT NULL COMMENT 年龄,email VARCHAR(50)NULL DEFAULT NULL COMMENT 邮箱,PRIMARY KEY (id) );INSERT INTO user…

jdk+zookeeper+kafka 搭建kafka集群

环境准备 环境资源包&#xff1a; jdk-8u341-linux-x64.tar.gz kafka_2.12-2.2.0.tgz zookeeper-3.4.14.tar.gz server-idip状态server110.206.120.10leaderserver210.206.120.2followerserver310.206.120.3follower 一、安装jdk 因为kafka需要Java环境&#xff0c;所以优先…

Liunx高级系统设计9-线程间同步与互斥

同步与互斥的概念 互斥&#xff1a;同一时间&#xff0c;只能有一个任务&#xff08;进程或线程&#xff09;执行&#xff0c;谁先执行不确定。 同步&#xff1a;同一时间&#xff0c;只能有一个任务&#xff08;进程或线程&#xff09;执行&#xff0c;有顺序的执行。 同步…