Java反射,枚举讲解

 💕"理想者最可能疯狂。"💕

作者:Mylvzi 

 文章主要内容:Java反射,枚举讲解 

"💕

作者:Mylvzi 

 文章主要内容:数据结构之Map/Set讲解+硬核源码剖析 

一.反射 

1.概念

Java的反射机制(reflection)是指在Java程序在运行状态过程中,能够拿到任意类的任意属性和任意方法,从而能够动态的调用,修改属性方法,这种机制就叫做"反射"

说明:

1.为什么强调"运行状态":在下面会讲到,.java程序经过编译,再到JVM上运行,最终每个.java文件都会生成一个类Class,我们的所有反射操作都是根据生成的Class类完成的

2.拿到任意类的任意属性和方法,包括他的私有属性和私有方法,这看起来和封装冲突了,因为我们之前讲过私有属性和私有方法只能在类内使用,类外无法使用,其实通过反射机制可以实现在类外使用私有属性和私有方法

2.反射用途 

  • 获取私有属性/方法 :在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统 应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
  • 开发框架:反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无 论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的 就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。

3.反射的相关类(最重要的)

3.1Class类

  Java文件被编译后会生成一个.class文件,这个.class文件会被JVM解析为一个类,这个类就是java.lang.Class,当程序运行时,对应的Java文件就是Class类的一个实例化对象,我们通过反射机制就能动态的调用这个对象的所有属性和方法

3.2 获取class对象的三种方式

1.全路径获取(最常用的方式)

  通过Class类对象的静态方法forName

Class.forName("反射对象的全路径");

 代码验证:

        Class c1;
        try {
            // forName这个方法会抛出异常 要去解决异常
            c1 = Class.forName("reflectDemo.Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

 

一定要记得处理异常

2.通过 反射对象.class获取
        // 2.使用.class方法
        Class c2 = Student.class;
3.通过getClass方法

先实例化出一个需要反射类的实例化对象,再通过此对象的getClass方法获取

        // 3.使用getClass方法  需要先实例化一个需要反射的对象
        Student student = new Student();
        Class c3 = student.getClass();

 以上三种方式都能获取到Class对象,那有人发现,我分别定义了三个变量来接受Class对象,分别是c1,c2,c3,那他们三个一定是不同的对象吧?实际上,这三个变量是同一个对象,因为Class对象只有一个,因为一个.java文件对应一个Class对象

        // 都输出true
        System.out.println(c1.equals(c2));
        System.out.println(c1.equals(c3));
        System.out.println(c2.equals(c3));

3.3 创建对象  

创建对象  即实例化一个反射获得对象,通过newInstance方法使用

newInstance()   创建类的实例

    // 创建对象  即实例化反射得到的Class对象
    public static void reflectNewInstance(){
        try {
            // 先获取一个Class对象
            Class<?> c1 = Class.forName("reflectDemo.Student");

            // 此处要进行类型转换
            Student student = (Student) c1.newInstance();
            System.out.println(student);

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

 

说明:

  1. newInstance方法的返回值是泛型,需要进行类型转换
  2.  newInstance方法会抛出异常,需要处理异常

3.4 获得构造方法

  获取到反射对象的私有构造方法

  • getConstructor(Class... parameterTypes) 获得该类中与参数类型匹配的公有构造方法
  • getConstructors() 获得该类的所有公有构造方法
  • getDeclaredConstructor(Class... parameterTypes) 获得该类中与参数类型匹配的构造方法
  • getDeclaredConstructors() 获得该类所有构造方法
    public static void reflectPrivateConstructor() {
        // 先获取一个Class对象
        try {

            // 1.先获取一个Class对象
            Class<?> c1 = Class.forName("reflectDemo.Student");

            // 2.获取私有的构造方法
            Constructor<Student> con = (Constructor<Student>) c1.getDeclaredConstructor(String.class,int.class);

            // 3.使用私有的构造方法需要添加"保险"
            con.setAccessible(true);

            // 4.创建一个反射的对象
            Student student = con.newInstance("张三",100);

            // 5.打印
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

 

3.5 获取属性Fileld

    public static void reflectPrivateField() {
        try {
            Class c1 = Class.forName("reflectDemo.Student");
            // 使用Field类接受一个私有属性               此处存放你要获取的私有属性的名称
            Field field = c1.getDeclaredField("name");
            // 私有的都需要添加保险
            field.setAccessible(true);
            // 创建一个类的实例
            Student student = (Student) c1.newInstance();
            // 改变私有属性的值
            // 第一个参数是你要修改的对象  第二个参数是你修改的值
            field.set(student,"zhizi");
            System.out.println(student);
            
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

 3.6 获取方法method

    public static void reflectPrivateMethod() {
        try {

            // 1.先创建一个对象
            Class c1 = Class.forName("reflectDemo.Student");

            // 获取私有的方法
            Method method = c1.getDeclaredMethod("function",String.class);
            
            // 私有的都需要添加保险
            method.setAccessible(true);

            // 创建一个类的实例
            Student student = (Student) c1.newInstance();
            
            // 修改方法
            method.invoke(student,"我是修改的私有构造方法");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

 

注意:方法的修改是通过method.invoke,属性的修改是field.set

4.反射的优点和缺点

优点:

  1. 对于任意一个类,都能获取其所有属性和方法
  2. 增加程序的灵活性,降低耦合性,提高自适应能力
  3. 开发框架

缺点:

  1. 使用反射会降低效率
  2. 反射技术绕过了源代码技术,带来维护相关的问题,同时也增加代码的复杂性

二.枚举

1.背景和定义

枚举是在JDK1.5以后引入的。和之前C语言中学习过的枚举常量类似,都是将一组常量有序组织起来的集合

在这之前定义常量的方式

public static final int RED = 1;
public static final int GREEN = 2;
public static final int BLACK = 3;

使用枚举常量 

    public enum Color{
        Black,Red,Blue;
    }

 

本质:是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了 这个类。

2.使用

1.switch语句

    public enum Color{
        Black,Red,Blue;
    }

    public static void main(String[] args) {
        Color color = Color.Black;
        switch (color) {
            case Red:
                System.out.println("Red");
                break;
            case Blue:
                System.out.println("Blue");
                break;
            case Black:
                System.out.println("Black");
                break;
            default:
                break;
        }
    }

2.枚举的定义

  直接将枚举类型定义为一个类

public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
    //拿到枚举实例BLACK
    TestEnum testEnum = TestEnum.BLACK;
    //拿到枚举实例RED
    TestEnum testEnum21 = TestEnum.RED;
}
}

3、常用方法 Enum 类的常用方法

  •  values() 以数组形式返回枚举类型的所有成员
  • ordinal() 获取枚举成员的索引位置
  • valueOf() 将普通字符串转换为枚举实例
  • compareTo() 比较两个枚举成员在定义时的顺序
    public enum Color{
        Black,Red,Blue;
    }

    public static void main(String[] args) {

        // 1.values方法  以数组形式返回枚举类型的所有成员
        Color[] colors = Color.values();
        for (Color color : colors) {
            System.out.println(color);
        }

        // 2.ordinal获取枚举成员的索引位置
        int index = Color.Red.ordinal();
        System.out.println(index);// 输出1  索引是从 0开始

        // 3.valueOf 将普通字符串转换为枚举实例
        Color color = Color.valueOf("Red");
        System.out.println(color);// 输出red

        // 4.compareTo
        // 计算:Red.ordinal - Black.ordinal
        System.out.println(Color.Red.compareTo(Color.Black));// 输出1  先定义Black
    }

 

注意:在Java的Enum的源码中,并不包好values方法,但却可以使用,这是为什么呢?

是因为values() 方法是由编译器在编译时自动添加到每个枚举类中的。

这个方法会返回一个包含枚举类型所有值的数组。虽然在 enum 类的源代码中看不到 values() 方法的定义,但编译器在生成字节码时会自动添加这个方法,因此你可以在代码中直接调用 YourEnumType.values() 来获取该枚举类型中所有的枚举常量值。

4.枚举的构造方法(重要)

枚举的构造方法是私有的

public enum TestEnum {
    RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
    private String name;
    private int key;
/**
* 1、当枚举对象有参数后,需要提供相应的构造函数
* 2、枚举的构造函数默认是私有的 这个一定要记住
* @param name
* @param key
*/
    private TestEnum (String name,int key) {
    this.name = name;
    this.key = key;
}

}

5.枚举的优缺点

优点

  1. 枚举常量跟简单,更安全
  2. 枚举内部有内置方法,代码更优雅  

缺点

  1. 不可继承,无法扩展

6.枚举和反射

  反射可以拿到任意类的任意属性和任意方法,对于枚举来说,其构造方法是私有的。是否也能通过反射拿到其构造方法呢?

public enum TestEnum {
    RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
    private String name;
    private int key;
    private TestEnum (String name,int key) {
        this.name = name;
        this.key = key;
    }

    public static void refletct() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class c1 = Class.forName("TestEnum");
        Constructor con = c1.getDeclaredConstructor(String.class,int.class);
        con.setAccessible(true);
        // 通过构造类con实例化一个对象
        TestEnum testEnum = (TestEnum) con.newInstance("绿色",666);
        System.out.println(testEnum);
    }
    
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        refletct();
    }
}

运行结果:
 java.lang.NoSuchMethodException指没有对应的构造方法

为什么会报这个错误呢?为什么说没有指定的构造方法呢?问题出现在哪里?我们之前说过。所有的枚举类都是继承于java.lang.Enum,既然是继承,肯定继承了除父类构造方法之外的所有的属性和方法,而且子类要先帮助父类进行构造,按理说我们自己写的enum类中应该有类似于super()这样的方法进行父类的构造,而实际上我们并没有写,是违反了继承的规则么?不是的,枚举类比较特殊,虽然我们写的是两个参数,但实际上他还默认添加了两个参数,请看源码:

虽然我们写了两个参数String和int,但实际上他还有两个默认的参数String和int,也就是构造方法中要有四个参数,下面修改一下构造方法

//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
Constructor<?> declaredConstructorStudent =
classStudent.getDeclaredConstructor(String.class,int.class,String.class,int.class)

运行结果:

 此处又出现了类型转换异常,定位到错误代码出,发现报错的代码竟然是

让我们再去看下newInstance的源码

 枚举类在上图画红线部分被过滤了,不能通过反射获取枚举类的实例!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

命令执行RCE及其绕过详细总结(17000+字数!)

目录 操作系统连接符&#xff1a; 常见函数&#xff1a; 绕过过滤&#xff1a; 空格过滤绕过&#xff1a; 1、大括号{}&#xff1a; 2、$IFS代替空格&#xff1a; 3、重定向字符<&#xff0c;<> 4、%09绕过&#xff08;相当于Tab键&#xff09; 文件名过滤绕过…

PyCharm连接远程服务器上Docker容器,使用远程服务器的python intercepter解释器和GPU资源 [本地调试深度学习代码]

概述 在编写常规深度学习代码时&#xff0c;总是需要使用服务器上的GPU资源&#xff0c;所以一般要写完代码&#xff0c;放到服务器&#xff0c;然后使用GPU运行。但是由于之前的习惯&#xff0c;总想本地调试一下或者本地直接跑测试结果&#xff0c;再放到服务器去跑。 网上…

linux sed批量修改替换文件中的内容/sed特殊字符

sed系列文章 linux常用命令(9)&#xff1a;sed命令(编辑/替换/删除文本)linux sed命令删除一行/多行_sed删除第一行/linux删除文件某一行linux sed批量修改替换文件中的内容/sed特殊字符 文章目录 sed系列文章一、sed替换文本语法1.1、基础语法1.2、高阶语法 二、实战/实例2.1…

土壤科学灌溉CG-36 土壤水势传感器

土壤科学灌溉CG-36 土壤水势传感器产品概述 土壤水势传感器可以很方便地插入到土壤剖面坑中&#xff0c;在其周围包裹上湿土即可。测定和记录非常简单。免维护、无需校准即可测量较大范围的土壤水势&#xff1b;无需灌水&#xff0c;大量程使得它成为测量自然系统水势的理想传…

JVM-接口响应时间很长解决办法

问题 在程序运行过程中&#xff0c;发现有几个接口的响应时间特别长&#xff0c;需要快速定位到是哪一个方法的代码执行过程中出现了性能问题。 解决思路 已经确定是某个接口性能出现了问题&#xff0c;但是由于方法嵌套比较深&#xff0c;需要借助于算法定位到具体的方法。 A…

Sui主网升级至V1.15.1版本

升级要点 Sui协议版本升至&#xff1a;32 #15080: 自己转移给自己的linter将不会在没有store能力的对象上触发&#xff0c;因此将生成较少的警告消息。为了避免冗余&#xff0c;还缩短了linter警告消息的长度。 #15096: 当给定对象的字段来自coin模块但不是Coin本身时&…

【rabbitMQ】springboot整合rabbitMQ模拟简单收发消息

目录 1.创建项目和模块 2.添加rabbitMQ依赖 3.启动rabbitMQ服务 4.引入rabbitMQ服务端信息 5.通过单元测试模拟业务发送消息 6. 接收消息 1.创建项目和模块 2.添加rabbitMQ依赖 <!-- rabbitmq依赖--> <dependency> <groupId>org.sp…

PCIe设备热插拔-理论篇

硬件层面理解热插拔 PRSNT1#和PRSNT2#信号与PCIe设备的热插拔相关。在基于PCIe总线的Add-in 卡中&#xff0c;PRSNT1# 和PRSNT2#信号直接相连&#xff0c;而在处理器主板中&#xff0c;PRSNT1#信号接地&#xff0c;而PRSNT2#信号通过上 拉电阻接为高。 不同的处理器系统处理PC…

刘家窑中医医院鲁卫星主任:冬季守护心脑血管,为社区居民送去健康关爱

随着冬季的来临&#xff0c;气温逐渐降低&#xff0c;心脑血管疾病的风险也随之增加。为了提高公众对心脑血管疾病的认知和预防意识&#xff0c;北京刘家窑中医医院于近日成功举办了冬季守护心脑血管公益义诊活动。 本次义诊活动主要针对社区居民中的中老年人&#xff0c;特别是…

微服务技术 RabbitMQ SpringAMQP P61-P76

B站学习视频https://www.bilibili.com/video/BV1LQ4y127n4?p61&vd_source8665d6da33d4e2277ca40f03210fe53a 文档资料: 链接&#xff1a;https://pan.baidu.com/s/1P_Ag1BYiPaF52EI19A0YRw?pwdd03r 提取码&#xff1a;d03r 一 初始MQ 1. 同步通讯 2. 异步通讯 3. MQ常…

聊聊Api接口优化的几个方法!

我负责的系统到2021年初完成了功能上的建设&#xff0c;开始进入到推广阶段。随着推广的逐步深入&#xff0c;收到了很多好评的同时也收到了很多对性能的吐槽。刚刚收到吐槽的时候&#xff0c;我们的心情是这样的&#xff1a; 当越来越多对性能的吐槽反馈到我们这里的时候&…

《对话品牌》——科技与时尚的融合

本期节目《对话品牌》栏目组邀请到了江西先禾服饰有限公司董事长吁火兰女士参加栏目录制&#xff0c;分享其企业故事&#xff0c;树立品牌形象&#xff0c;提升品牌价值&#xff01; 节目嘉宾&#xff1a;吁火兰 节目主持人&#xff1a;杨楠 节目播出平台&#xff1a;中央新…

MagicAnimate:Temporally consistent human image animation using diffusion model

1.Introduction 本文研究了任务形象动画人物&#xff0c;旨在根据特定的运动序列生成一个具有特定参考身份的视频。现有的人物图像动画的数据驱动方法可以基于所使用的生成主干模型分为两类&#xff0c;1.基于GAN&#xff0c;通常使用变形函数将参考图变形为目标姿态&#xff0…

AnimateAnything:Fine-grained open domain image animation with motion guidance

1.Introduction 本文旨在借助视频扩散模型的motion prior来解决开放领域图像动画问题&#xff0c;提出了一种可控扩散图像动画方法&#xff0c;能够在保留细节的同时对图像中的任意对象进行动画处理。为了增强用户对动画过程的控制能力&#xff0c;引入了motion area guidance和…

kafka支持外网访问

kafka支持外网访问 1.kafka正常部署之后如果不修改&#xff0c;外网是无法访问的&#xff0c;具体如下&#xff08;这里是单节点&#xff09; 2.这个时候需要修改kafka的config中的server.properties中的 listeners 修改为0.0.0.0 监控所有网卡&#xff0c;advertised.listene…

汽车软件大时代,如何提升软件工程创新力?

当前&#xff0c;传统汽车产业正加速数字化转型&#xff0c;“软件定义汽车”不断深化。在电动化、智能化和网联化趋势下&#xff0c;汽车软件已经成为汽车技术革新和发展的核心驱动力之一。根据亿欧智库发布的《2023中国智能电动汽车车载软件市场分析报告》&#xff0c;2022年…

Leetcode—783.二叉搜索树节点最小距离【简单】

2023每日刷题&#xff08;五十八&#xff09; Leetcode—783.二叉搜索树节点最小距离 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ void dfs(struct TreeNode* roo…

为什么要用向量检索

之前写过一篇文章&#xff0c;是我个人到目前阶段的认知&#xff0c;所做的判断。我个人是做万亿级数据的搜索优化工作的。一直在关注任何和搜索相关的内容。 下一代搜索引擎会什么&#xff1f;-CSDN博客 这篇文章再来讲讲为什么要使用向量搜索。 在阅读这篇文章之前呢&#xf…

云原生之深入解析网络服务Istio、eBPF和RSocket Broker

一、服务治理 ① “服务治理”简介 在微服务时代&#xff0c;一个复杂的应用程序被分解为多个组件化、协作和连接的单元&#xff0c;服务往往会承担越来越多的业务责任&#xff0c;这使得服务治理的难度前所未有&#xff0c;仅仅依靠微服务框架级的治理是不够的&#xff0c;构…

腾讯云Elasticsearch Service产品体验

基本介绍 产品概述 腾讯云 Elasticsearch Service&#xff08;ES&#xff09;是云端全托管海量数据检索分析服务&#xff0c;拥有高性能自研内核&#xff0c;集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群&#xff0c;也支持免运维、自动弹性、按需…