【Java SE】反射与枚举

news2024/12/23 19:19:56

目录

♫反射

♪什么是反射

♪与反射相关的类

♪什么是Class类

♪获取Class类

♪class类的常用方法

♪反射的使用

♪反射私有方法

♪反射的优缺点

♫枚举

♪什么是枚举

♪枚举的常用方法

♪枚举的构造方法

♫枚举与反射


♫反射

♪什么是反射

Java反射是Java语言的一个特性,它允许程序在运行时动态获取类的信息和调用类的方法。

Java 程序中许多对象在运行时会出现两种类型: 运行时类型 和编译时类型(如: Person p = new Student();这句代码中 p 在编译时类型为 Person ,运行时类型为 Student), 程序需要在运行时发现对象和类的真实信息,而通过使用反射程序就能判断出该对象和类属于哪些类。

♪与反射相关的类

类名描述
Class
代表类的实体,在运行的 Java 应用程序中表示类和接口
Field
代表类的成员变量 / 类的属性
Method
代表类的方法
Constructor类
代表类的构造方法

♪什么是Class类

class类表示类的实体,是反射机制的起源。Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件 ,被编译后的Java文件.class也被JVM解析为一个对象( java.lang.Class )。这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。

♪获取Class类

要使用反射首先得获取Class对象,获取Class对象可以通过类名、对象或者Class类的静态方法获取该类的Class对象:

定义一个Student类,用于后面的反射操作:

class Student{
    //私有属性name
    private String name = "李四";
    //公有属性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("i am eating");
    }
    
    public void sleep(){
        System.out.println("i am sleeping");
    }
    //私有方法
    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

♩通过类名获取:

        Student student1 = new Student();
        Class<?> c1 = student1.getClass();

♩通过对象获取:

        Class<?> c2 = Student.class;//说明任何一个类都有一个隐含的静态成员变量 class

♩通过Class类的静态方法获取(常用):

        Class<?> c3 = null;
        try {
            c3 = Class.forName("Student");//这里放的是全路径,如果有包需要加包的路径
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

注:一个类在 JVM 中只会有一个 Class 实例,即上面获取的c1,c2,c3均为一个对象。

♪class类的常用方法

方法描述
getClassLoader()
获得类的加载器
getDeclaredClasses()
返回一个数组,数组中包含该类中所有类和接口类的对象 ( 包括私有的 )
forName(String className)
根据类名返回类的对象
newInstance()
创建类的实例
getName()
获得类的完整路径名字
getField(String name)
获得某个公有的属性对象
getFields()
获得所有公有的属性对象
getDeclaredField(String name)
获得某个属性对象
getDeclaredFields()
获得所有属性对象
getAnnotation(Class annotation)
返回该类中与参数类型匹配的公有注解对象
getAnnotations()
返回该类所有的公有注解对象
getDeclaredAnnotation(Class   annotation)
返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()
返回该类所有的注解对象
getConstructor(Class...<?> parameterTypes)
获得该类中与参数类型匹配的公有构造方法
getConstructors()
获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes)
获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()
获得该类所有构造方法
getMethod(String name, Class...<?> parameterTypes)
获得该类某个公有的方法
getMethods()
获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes)
获得该类某个方法
getDeclaredMethods()
获得该类所有方法

♪反射的使用

♩反射对象:

public class Demo1 {
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        try {
            Student student = (Student)c1.newInstance();
            System.out.println(student);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果:

♩反射私有的构造方法:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo1 {
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        try {
            //注意传入对应的参数(类型.class)
            Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
            //设置为true后可修改访问权限
            constructor.setAccessible(true);
            //通过反射获取的构造方法创建对象
            Student student = (Student)constructor.newInstance("张三", 3);
            System.out.println(student);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果:

♩反射私有属性

import java.lang.reflect.Field;

public class Demo1 {
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        try {
            //注意参数为要属性名
            Field field = c1.getDeclaredField("name");
            field.setAccessible(true);
            //可以修改该属性的值
            Student student = (Student) c1.newInstance();
            field.set(student,"张三");
            System.out.println(student);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果:

♪反射私有方法

public class Demo1 {
    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        try {
            Method method = c1.getDeclaredMethod("function", String.class);
            method.setAccessible(true);
            Student student = (Student)c1.newInstance();
            //给私有方法传递一个参数
            method.invoke(student, "这是一个参数");
        } 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);
        }
    }
}

运行结果:

♪反射的优缺点

反射的优点:

  1. 在程序运行时可以动态获取和使用类的信息,增强了程序的灵活性。
  2. 可以在运行时对类的成员变量、方法等进行修改,实现对类的动态修改和扩展。
  3. 可以对一些没有接口的类进行操作和调用,提高了代码的可读性和可维护性。
  4. 可以通过反射实现一些通用的框架和工具,降低了代码的重复性。
  5. 可以通过反射实现一些高级操作,如序列化和反序列化等。

反射的缺点:

  1. 反射的性能比直接调用方法和访问属性的性能要差,因为它需要进行额外的操作和逻辑判断。
  2. 反射破坏了面向对象编程的封装性,对类的内部进行了不可控的操作,可能导致代码的不稳定性和安全性问题。
  3. 反射过多地依赖于类的内部实现,对于没有完全公开的类信息,无法进行反射操作。
  4. 反射会增加代码的复杂度,需要谨慎使用,以免导致代码可读性和可维护性的下降。

♫枚举

♪什么是枚举

枚举是一种特殊的数据类型,可以用来代替常量来表示固定的值集合,Java中的枚举类型是通过关键字“enum”来定义的:

public enum EnumTest {
    RED,BLACK,GREEN,WHITE;
}
注:枚举是java.lang.Enum的子类,它默认继承了这个类。

♪枚举的常用方法

方法描述
values()
以数组形式返回枚举类型的所有成员
ordinal()
获取枚举成员的索引位置
valueOf()
将普通字符串转换为枚举实例
compareTo()
比较两个枚举成员在定义时的顺序
public enum EnumTest {
    RED("张三", 3),BLACK,GREEN,WHITE;
    private String name;
    private int age;

    private EnumTest() {

    }
    private EnumTest(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        EnumTest[] enumTests = EnumTest.values();
        for (int i = 0; i < enumTests.length; i++) {
            System.out.println(enumTests[i] + " ori:" + enumTests[i].ordinal());
        }
        EnumTest enumTest = EnumTest.valueOf("WHITE");
        System.out.println(enumTest);
        System.out.println(RED.compareTo(GREEN));
    }
}

运行结果:

♪枚举的构造方法

枚举的构造方法默认是私有的,当枚举对象有参数时,需要提供相应的构造函数:

public enum EnumTest {
    RED("张三", 3),BLACK,GREEN,WHITE;
    private String name;
    private int age;

    private EnumTest() {

    }

    private EnumTest(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

♫枚举与反射

我们知道任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?

当我们用反射的方法获取构造方法:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public enum EnumTest {
    RED,BLACK,GREEN,WHITE;
    private EnumTest() {

    }

    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("EnumTest");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        try {
            Constructor<?> constructor = c1.getDeclaredConstructor();
            constructor.setAccessible(true);
            EnumTest enumTest = (EnumTest) constructor.newInstance();
            System.out.println(enumTest);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果会出现异常:

为什么会出现没有对应的构造方法的异常呢?这是因为为了实现枚举类型的安全性和可维护,Java枚举类型的构造方法还有两个隐藏参数(第一个隐藏参数表示枚举类型的名称,它是一个字符串类型的常量,由编译器自动生成;第二个隐藏参数是一个整型类型的常量,它表示枚举类型实例在枚举类型中的顺序,从0开始),但当我们加上这两个参数后:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public enum EnumTest {
    RED,BLACK,GREEN,WHITE;
    private EnumTest() {

    }

    public static void main(String[] args) {
        Class<?> c1 = null;
        try {
            c1 = Class.forName("EnumTest");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        try {
            Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true);
            EnumTest enumTest = (EnumTest) constructor.newInstance("x",2);
            System.out.println(enumTest);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

程序依然报错:

这里的Constructor.newInstance()报错是因为在这个方法的源码中,枚举被过滤了,所以我们 不能通过反射获取枚举类的实例

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

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

相关文章

【VUE复习·1】单向数据绑定v-bind;双向数据绑定v-model

总览 1.单向数据绑定&#xff1a;v-bind 2.双向数据绑定&#xff1a;v-model 一、v-bind 单向数据绑定 1.图解 data 中的值能够影响页面上的值&#xff0c;但是在页面上更改却不能影响 data 中的值。 2.用法说明 <div><input v-bind:value"name">&l…

ES查询数据的时报错:circuit_breaking_exception[[parent] Data too large

ES配置的官方网站&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/7.2/circuit-breaker.html 报错&#xff1a; circuit_breaking_exception[[parent] Data too large, data for [<transport_request>] would be [12318476937/11.2gb], which is…

Vue之ElementUI之动态树+数据表格+分页(项目功能)

目录 前言 一、实现动态树形菜单 1. 配置相应路径 2. 创建组件 3. 配置组件与路由的关系 index.js 4. 编写动态树形菜单 5. 页面效果演示 二、实现数据表格绑定及分页功能 1. 配置相应路径 2. 编写数据表格显示及分页功能代码 BookList.vue 3. 演示效果 总结 前言…

数据结构 - 泛型

目录 前言 1. 什么是泛型? 2. 为什么需要泛型? 引入泛型之前 引入泛型之后 3.泛型类 4.泛型的界限 1.上下界 2.通配符 前言 今天给大家介绍一下泛型的使用 1. 什么是泛型? 一般的类和方法&#xff0c;只能使用具体的类型: 要么是基本类型&#xff0c;要么是自定义…

抖音短视频seo矩阵系统源代码开发系统架构及功能解析

短视频seo源码&#xff0c;短视频seo矩阵系统底层框架上支持了从ai视频混剪&#xff0c;视频批量原创产出&#xff0c;云存储批量视频制作&#xff0c;账号矩阵&#xff0c;视频一键分发&#xff0c;站内实现关键词、短视频批量搜索排名&#xff0c;数据统计分类多功能细节深度…

在多台服务器上运行相同命令(二)、clush

介绍安装配置互信认证参数含义基本使用节点组拷贝文件 介绍 Clush&#xff08;Cluster Shell&#xff09;是一个用于管理和执行集群操作的工具&#xff0c;它允许你在多台远程主机上同时执行命令&#xff0c;以便批量管理服务器。Clush 提供了一种简单而强大的方式来管理大规模…

MySQL:远程连接数据库(2)

环境 两台centos7 其中一台安装MySQL数据库 192.168.254.1 另外一台安装mariadb最小化安装mysql仅供MySQL连接 或者安装完整版mysql也是可以的&#xff0c;只要支持mysql命令即可 192.168.254.2 开始部署 我们如果让那个用户可以远程连接&…

一步解决Android Studio没有提示的问题

问题 我们在使用安卓开发软件的时候常常会遇到没有提示或提示不全的情况&#xff0c;这样会很影响我们的学习效率,接下来我来教大家快速解决这个问题 解决办法 应用完成后点击ok&#xff0c;提示就可以出来了 如果有适配的版本到这里就可以了&#xff0c;没有的话可以继续往下看…

怎么删除文件夹?这才是正确操作方法!

“作为一名电脑小白&#xff0c;想问问大家&#xff0c;我想把文件夹一整个删除&#xff0c;但是没法操作是为什么呀&#xff1f;应该怎么正确删除文件夹呢&#xff1f;求解答&#xff01;” 在电脑使用过程中&#xff0c;有时我们需要删除不再需要的文件夹以释放磁盘空间或整理…

ISP图像信号处理——白平衡校正和标定介绍以及C++实现

从数码相机直接输出的未经过处理过的RAW图到平常看到的JEPG图有一系列复杂的图像信号处理过程&#xff0c;称作ISP&#xff08;Image Signal Processing&#xff09;。这个过程会经过图像处理和压缩。 参考文章1&#xff1a;http://t.csdn.cn/LvHH5 参考文章2&#xff1a;htt…

打造综合性品牌阵地 阿里元境引领元宇宙营销创新升级

9月21日&#xff0c;由中国商务广告协会主办&#xff0c;中国商务广告协会元宇宙营销应用研究工作委员会、南京大学中德社会计算研究所、南京大学中德数字营销实验室联合主办的“聚力共生”元宇宙学术及商业应用论坛启幕。阿里元境副总经理、市场负责人史敏君出席了论坛“多元聚…

SSE 推送技术

1、简介 Server-Sent Events&#xff08;SSE&#xff09;技术&#xff0c;它是一种用于实现服务器向客户端实时单向推送数据的Web技术。 SSE基于HTTP协议&#xff0c;允许服务器将数据以事件流&#xff08;Event Stream&#xff09;的形式发送给客户端。客户端通过建立持久的HT…

一些数学公式的几何意义

三角函数平方和公式&#xff1a; 三角函数中的平方和公式有三个形式&#xff1a; 第一种&#xff1a;&#xff1b; 接着两边同时除以可以得到第二种&#xff1a;; 或第一种同时除以可以得到第三种&#xff1a;。 首先我们做一个单位圆&#xff0c;我们学三角函数的时候应该…

当两界交汇:前端开发、后端开发与全栈开发的对比与选择

编程世界就像一座大城市&#xff0c;前端开发和后端开发就像城市的两个不同街区。在这两个街区&#xff0c;前端和后端开发都有自己的价值和机会。 一、引言 有些人更喜欢在前端创造令人印象深刻的用户界面&#xff0c;而有些人更喜欢处理数据和系统逻辑。在选择时&#xff…

Topaz Gigapixel AI6.3.2(图片无损放大)

Topaz Gigapixel AI是一款功能实用的图像无损放大工具。它的特色之处在于&#xff0c;通过使用先进的深度学习方法&#xff0c;它能够将照片放大高达600%&#xff0c;同时完美保留图像的质量。 此外&#xff0c;Topaz Gigapixel AI还具有一些其他的特色功能。 它能自动进行面…

ABB DDC779BE02 3BHE006805R0002 控制主板模块

ABB DDC779BE02 3BHE006805R0002 控制主板模块用于自动化和控制系统中&#xff0c;它们可能具有以下一些常见特点和功能&#xff1a; 处理能力&#xff1a;ABB DDC779BE02 3BHE006805R0002 控制主板模块通常具有强大的处理能力&#xff0c;可以执行复杂的控制算法和逻辑。 多种…

软件测试之网站测试怎么做?有什么作用?

网站测试是指对一个已经搭建好的网站进行功能、性能、安全等方面的测试。作为一家专注于软件测试的公司&#xff0c;我们清楚地知道网站测试在整个软件开发过程中的重要性。   一、网站测试怎么做?   1、确保测试环境的稳定和一致性&#xff0c;包括操作系统、浏览器版本等…

AnV-X6使用及总结

目录 1 简介2 安装3 基础概念3.1 画布Graph3.2 基类Cell3.3 节点Node3.4 边Edge 4 使用4.1 创建节点4.2 节点连线4.3 事件系统 5 总结 1 简介 AntV是一个数据可视化&#xff08;https://x6.antv.antgroup.com/&#xff09;的工具&#xff08;https://antv.vision/zh/ &#xf…

IPv6的主要优势有哪些?

第一&#xff0c;明显地扩大了地址空间。IPv6采用128位地址长度&#xff0c;几乎可以不受限制地提供IP地址&#xff0c;从而确保了端到端连接的可能性。 第二&#xff0c;提高了网络的整体吞吐量。由于IPv6的数据包可以远远超过64k字节&#xff0c;应用程序可以利用最大传输单元…

RFID技术引领汽车零部件加工新时代

RFID技术的兴起引领了汽车零部件加工领域的新时代&#xff0c;作为一种利用无线电频率进行自动识别的技术&#xff0c;RFID技术能够快速、准确地识别物体并获取相关数据&#xff0c;在汽车零部件加工中&#xff0c;RFID技术具有重要的应用价值&#xff0c;可以提高生产效率、降…