Java——反射&枚举

简介: 本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。

🍁1. 反射

在程序运行时,可以动态地创建对象、调用方法、访问和修改字段,以及获取类的各种属性信息(如成员变量、方法、构造函数等),这种机制就称为反射

类名

用途

Class类

代表类的实体,在运行的Java应用程序中表示类和接口

Field类

代表类的成员变量 / 类的属性

Method类

代表类的方法

Constructor类

代表类的构造方法

🍁1.1 反射获取Class对象

获取字节码文件对象

1. Class.forName(全类名) 包名 + 类名

2. 类名.class

3. 对象名.getClass()

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取字节码文件对象的三种方式
        // Class.forName(全类名)包名 + 类名(常用)
        Class clazz1 = Class.forName("reflect.Student");
        //类名.class
        Class clazz2 = Student.class;
        //对象名.getClass()
        Student student = new Student();
        Class clazz3 = student.getClass();
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz2 == clazz3);
    }
}

🍁1.2 反射获取构造方法

🍁1.2.1 获取构造方法的方式

方法

用途

getConstructor(Class...<?> parameterTypes)

获得该类中与参数类型匹配的公有构造方法

getConstructors()

获得该类的所有公有构造方法对象的数组

getDeclaredConstructor(Class...<?> parameterTypes)

获得该类中与参数类型匹配的构造方法

getDeclaredConstructors()

获得该类所有构造方法对象的数组

使用 getConstructors()时,只会获取类中的公共的构造方法

可以看出,使用getDeclaredConstructors()时,不论构造方法的权限修饰符是什么,都可以获取到

来看获取单个构造方法的例子,在调用方法的时候,传入方法里面的参数要和需要获取的构造方法的参数一致

getConstructor 只能获取 public 修饰的构造方法,getDeclaredConstructor可以获取任意修饰符修饰的构造方法,所以如果要获取的构造方法如果不是 public 修饰的,但是使用了getConstructor获取,就会报错

public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取class字节码文件对象
        Class clazz = Class.forName("com.li.reflect.Student");
        //获取构造方法
        Constructor[]  cons1 = clazz.getConstructors();
        for (Constructor con : cons1) {
            System.out.println(con);
        }
        Constructor[] cons2 = clazz.getDeclaredConstructors();
        for (Constructor con2 : cons2) {
            System.out.println(con2);
        }
        //获取单个的构造方法
        Constructor con1 = clazz.getDeclaredConstructor();
        System.out.println(con1);
        Constructor con2 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con2);
        Constructor con3 = clazz.getDeclaredConstructor(String.class,int.class);
        System.out.println(con3);
        Constructor con4 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con2);
        
    }
}

student类:

public class Student {
    private String name;
    private int age;
    public Student() {
    }
    private Student(int age){
        this.age = age;
    }
    protected Student(String name){
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

🍁1.2.2 获取构造方法的用途

通过反射,可以获取到构造方法的所有内容

首先第一个,就是可以获取到构造方法的权限修饰符

//获取权限修饰符,这里获取的是其对应的常量值
int modifiers = con1.getModifiers();
System.out.println(modifiers); //public 对应的常量是1

idea中的提示功能就是使用了反射的机制,获取到了构造方法的权限修饰符,这里只提示了除空参构造之外,可以使用的两种构造方法,private由于在其他类中不能调用,这里就没有显示

之后,还可以获取到构造方法的参数

//获取参数
Parameter[] parameters = con3.getParameters();
System.out.println(Arrays.toString(parameters));//[java.lang.String arg0, int arg1]

应用场景还是上面的代码提示功能

既然可以获取构造方法的所有内容,那以此来创建一个对象也是可以的:

//创建对象
        Student student1 = (Student) con3.newInstance("张三", 18);
        System.out.println(student1);
        //获取到private修饰的构造方法时
        con4.setAccessible(true); //表示临时取消权限校验
        Student student2 = (Student) con4.newInstance(18);
        System.out.println(student2);

用这种方式创建对象需要注意:

传进去的参数要和获取到的构造方法的参数一致

如果获取到 private修饰的构造方法时,需要临时取消权限校验

🍁1.3 反射获取成员变量

🍁1.3.1 获取成员变量的方式

方法

用途

getField(String name)

获得某个公有的属性对象

getFields()

获得所有公有的属性对象的数组

getDeclaredField(String name)

获得某个属性对象

getDeclaredFields()

获得所有属性对象的数组

public class Demo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //获取字节码文件的对象
        Class clazz = Class.forName("com.li.reflect.Student");
        //获取所有的成员变量
        Field[] fields1 = clazz.getFields();
        System.out.println(Arrays.toString(fields1));
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //获取单个成员变量
        Field res1 = clazz.getField("gender");
        System.out.println(res1);
        Field res2 = clazz.getDeclaredField("name");
        System.out.println(res2);
    }
}

这里的方法的区别对比着获取构造方法的区别是一样的

在获取单个的成员变量时,传入的参数是要获取的变量名

🍁1.3.2 获取成员变量的用途

可以获取到成员变量的权限修饰符,名称,数据类型

//获取成员变量的权限修饰符
        int modifiers = res1.getModifiers();
        System.out.println(modifiers);
        //获取成员变量的名称
        String name = res1.getName();
        System.out.println(name);
        //获取成员变量的数据类型
        Class<?> type = res1.getType();
        System.out.println(type);

此外,还可以获取到当前成员变量记录的值

//获取成员变量记录的值
        Student student = new Student("张三",18,"男");
        //临时取消权限校验
        res1.setAccessible(true);
        Object value = res1.get(student);
        System.out.println(value);

这里需要注意的是,如果获取的值是private修饰的,还是需要设置临时取消权限校验

获取到值之后,还可以进行修改

//修改
res1.set(student,"女");
System.out.println(student);

🍁1.4 反射获取成员方法

🍁1.4.1 获取成员方法的方式

方法

用途

getMethod(String name, Class...<?> parameterTypes)

获得该类某个公有的方法

getMethods()

获得该类所有公有的方法的数组,包括继承的

getDeclaredMethod(String name, Class...<?> parameterTypes)

获得该类某个方法

getDeclaredMethods()

获得该类所有方法的数组,不包括继承的

getMethods()  获取的是该类所有公有的方法的数组,包括继承的,所以显示出了很多Person类本身没有定义的方法

getDeclaredMethods()获取的是该类所有方法的数组,不包括继承的,

获取指定的方法:

在使用getDeclaredMethod获取指定方法时,第一个参数需要指定方法的名称,第二个参数需要指定方法的形参,如果是空参,那么第二个参数就空着不写

//获取指定方法
        Method m = clazz.getDeclaredMethod("eat",String.class);
        System.out.println(m);

🍁1.4.2 获取成员方法的用途

可以获取方法的修饰符,名字,方法的形参,以及方法抛出的异常

//获取方法修饰符
        int modifiers = m.getModifiers();
        System.out.println(modifiers);
        //获取方法的名字
        String name = m.getName();
        System.out.println(name);
        //获取方法的形参
        Parameter[] parameters = m.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }
        //获取方法抛出的异常
        Class[] exceptionTypes = m.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }

获取到的方法也可以通过调用 invoke 方法运行

/*Object invoke(Object obj,Object...args):运行方法
          参数一:用obj对象调用该方法
          参数二:调用方法的传递的参数(如果没有就不写)
          返回值:方法的返回值(如果没有就不写)
        * */
        Person person = new Person();
        m.setAccessible(true);
        //如果有返回值可以接收
        Object res = m.invoke(person, "饭");
        //打印返回值
        System.out.println(res);

🍁2. 枚举

枚举是一种特殊的类,枚举中的每个元素都是该类的一个唯一实例,主要用途是把一组常量组织起来

在之前我们定义常量的时候是这样定义的:

public static final int RED = 1;

但是如果恰好此时有一个数字1,就可能被误认为是RED,这时就可以通过枚举来组织常量

在创建类时选择Enum类进行创建

🍁2.1 枚举的使用

Enum类中的常用方法

方法名称

描述

values()

以数组形式返回枚举类型的所有成员

ordinal()

获取枚举成员的索引位置

valueOf()

将普通字符串转换为枚举实例

compareTo()

比较两个枚举成员在定义时的顺序

public enum EnumDemo1 {
    RED, BLACK, YELLO, BLUE;
    public static void main(String[] args) {
        EnumDemo1[] enumDemo1s = EnumDemo1.values();
        for (int i = 0; i < enumDemo1s.length; i++) {
            System.out.println(enumDemo1s[i] + " " + enumDemo1s[i].ordinal());
        }
        //根据字符串返回类中的实例
        EnumDemo1 res = EnumDemo1.valueOf("RED");
        System.out.println(res);
        //比较定义顺序
        System.out.println(RED.compareTo(BLUE));
    }
}

可以和switch语句一起使用:

public enum EnumDemo1 {
    RED, BLACK, YELLO, BLUE;
    public static void main(String[] args) {
        EnumDemo1 e1 = RED;
        switch (e1) {
            case RED:
                System.out.println("RED");
                break;
            case BLACK:
                System.out.println("BLACK");
            default:
                System.out.println("无法匹配");
        }
    }
}

🍁2.2 枚举类的构造方法

在之前提到过,枚举本身是一个类,如果想要类里面的实例对象有参数的话,需要提供相应的构造方法,并且构造方法默认是私有的

public enum EnumDemo2 {
    RED(0, "red"), BLACK(1, "black"),
    YELLO(2, "yello"), BLUE(3, "blue");
    private int ordinal;
    private String color;
    EnumDemo2() {
    }
    private EnumDemo2(int ordinal, String color) {
        this.ordinal = ordinal;
        this.color = color;
    }
}

如果将构造方法设置为除private外的其他类型的话就会报错

🍁2.3 枚举在反射中的特殊情况

还按照之前反射的方法获取构造方法并创建对象时,会发现报错了,并且给出的异常是没有找到这个构造方法,但是我们的EnumDemo2类中是存在这个方法的

自己定义的枚举类是默认继承Enum类的,会优先代用父类的构造方法,所以就需要加上父类的参数,此时就解决了第一个异常

此时通过查看newInstance的源码发现,只要是枚举类,就会报错,所以说枚举类是比较安全的


相关文章
|
3天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
29天前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19264 29
|
30天前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18803 20
|
29天前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17508 13
Apache Paimon V0.9最新进展
|
1月前
|
存储 人工智能 前端开发
AI 网关零代码解决 AI 幻觉问题
本文主要介绍了 AI Agent 的背景,概念,探讨了 AI Agent 网关插件的使用方法,效果以及实现原理。
18694 15
|
29天前
|
人工智能 自然语言处理 搜索推荐
评测:AI客服接入钉钉与微信的对比分析
【8月更文第22天】随着人工智能技术的发展,越来越多的企业开始尝试将AI客服集成到自己的业务流程中。本文将基于《10分钟构建AI客服并应用到网站、钉钉或微信中》的解决方案,详细评测AI客服在钉钉和微信中的接入流程及实际应用效果,并结合个人体验分享一些心得。
9910 9
|
1月前
|
消息中间件 弹性计算 关系型数据库
函数计算驱动多媒体文件处理解决方案体验评测
从整体解读到部署体验,多方位带你了解如何利用函数计算驱动多媒体文件处理,告别资源瓶颈。
10441 13
|
23天前
|
存储 JSON Serverless
西游再现,函数计算一键部署 Flux 超写实文生图模型部署
参与体验活动生成西游人物图像,既有机会赢取好礼!本次实验在函数计算中内置了flux.1-dev-fp8大模型,通过函数计算+Serverless应用中心一键部署Flux模型,快速生成超写实图像。首次开通用户可领取免费试用额度,部署过程简单高效。完成部署后,您可以通过修改提示词生成各种风格的图像,体验Flux模型的强大绘图能力。
西游再现,函数计算一键部署 Flux 超写实文生图模型部署
|
1天前
|
Java 应用服务中间件 测试技术
Maven学习笔记(一):Maven基础(基于命令行的学习和应用)
Maven 是一款 Java 项目构建工具,主要用于管理 jar 包及其依赖关系。 本文主要了解Maven基础知识及基础应用,旨在为之后的进一步学习奠定基础。 内容上几近全为学习《尚硅谷2022版Maven教程》整理所得。 仅供参考。
127 80
Maven学习笔记(一):Maven基础(基于命令行的学习和应用)
|
1天前
|
缓存 前端开发 JavaScript
终极 Nginx 配置指南(全网最详细)
本文详细介绍了Nginx配置文件`nginx.conf`的基本结构及其优化方法。首先通过删除注释简化了原始配置,使其更易理解。接着,文章将`nginx.conf`分为全局块、events块和http块三部分进行详细解析,帮助读者更好地掌握其功能与配置。此外,还介绍了如何通过简单修改实现网站上线,并提供了Nginx的优化技巧,包括解决前端History模式下的404问题、配置反向代理、开启gzip压缩、设置维护页面、在同一IP上部署多个网站以及实现动静分离等。最后,附上了Nginx的基础命令,如安装、启动、重启和关闭等操作,方便读者实践应用。
128 77
终极 Nginx 配置指南(全网最详细)