Java 中的反射和枚举

简介: Java 中的反射和枚举

引言



在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量或成员方法是私有的或只对系统应用开放,这时候就可以利用 Java 的反射机制通过反射来获取所需的私有成员或方法,当然,公开的成员变量和方法显然也能通过反射获取得到。


反射最重要的用途就是开发各种通用框架,比如在 spring 中,我们将所有的类Bean 交给 spring 容器管理,无论是 XML 配置 Bean 还是注解配置,当我们从容器中获取 Bean 来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring 根据这些信息,需要创建 Bean,spring 动态的这些类。


一、什么是反射



Java 的反射机制表示在运行状态中,对于任意一个类或者说对于任意一个对象,都能够调用它的任意方法和属性(主要解决调用私有方法和私有属性的问题)。这样一来,我们就可以修改部分类型信息,而这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。


二、通过反射获取对象的三种方式



通过反射机制获取对象的三种方式中,方式一用的最多。


创建一个包 demo1,在包中创建一个 java 文件,里面放着两个类,即类 Student 和 类 Test1,如下所示:


那么下面的方式一需要指明类的全路径,如果有包需要加包的路径。


Class<?> c1 = Class.forName("demo1.Student"); //用的情况最多


/**
 * 演示 获取对象的三种方式
 */
class Student{
    //私有属性name
    private String name = "bit";
    //公有属性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 +
                '}';
    }
}
public class Test {
    public static void main(String[] args) throws ClassNotFoundException{
        //获得 Class 对象的三种方式
        //不管使用哪种方式来获取 Class 对象,此时对象只有一个
        //方式一
        Class<?> c1 = Class.forName("demo1.Student"); //用的情况最多
        //方式二
        Class<?> c2 = Student.class;
        //方式三
        Student student = new Student();
        Class<?> c3 = student.getClass();
        System.out.println(c1==c2);
        System.out.println(c1==c3);
        System.out.println(c2==c3);
    }
}


输出结果:


c3a9b62834d74bd0a2632dfd4f09650c.png


三、演示反射机制的一些方法



在刚刚的包 demo1 中,我们创建第二个 java 文件,里面创建四个类,用来测试反射机制来拿到 Student 类中的相关信息。


接下来我们测试反射机制常用的四个方法:


① 通过 Class 类的 newInstance( )方法获取学生实例

② 通过 Constructor 类 的 getDeclaredConstructor( )方法,获得私有的构造方法(公开的构造方法也可通过反射获得)

③ 通过 Field 类的 getDeclaredField( )方法获得对象中私有的成员变量(公有的成员变量也可通过反射获得)

④ 通过 Method类的 getDeclaredMethod( )方法获得对象中私有的成员方法(公有的成员变量也可通过反射获得)


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectClass {
    /**
     * 通过 Class 类的 newInstance()方法 获取学生实例
     */
    public static void reflectNewInstance() {
        try {
            //1. 拿到 Class 对象
            Class<?> c1 = Class.forName("demo1.Student");
            Student student = (Student) c1.newInstance();
            //2. 直接打印
            System.out.println(student);
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 反射私有的构造方法(公开私有皆可)
     */
    public static void reflectPrivateConstructor(){
        try {
            //1. 拿到 Class 对象
            Class<?> c1 = Class.forName("demo1.Student");
            //2. 获得该类中与参数类型匹配的构造方法
            //例如:Student 类的公开和私有的构造方法都可以获取到
            Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true); // 设置为true后可修改访问权限
            Student student = (Student) constructor.newInstance("linxi", 13);
            System.out.println(student);
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException
                 | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 反射私有的成员变量(公开私有皆可)
     */
    public static void reflectPrivateField() {
        try {
            //1. 拿到 Class 对象
            Class<?> c1 = Class.forName("demo1.Student");
            Student student = (Student) c1.newInstance();
            //2. 获得对象中私有的成员变量并给其赋值(公有也可进行操作)
            Field field = c1.getDeclaredField("name");
            field.setAccessible(true);
            field.set(student,"Jack");//给指定对象私有的成员变量赋值
            System.out.println(student);
        } catch (ClassNotFoundException | InstantiationException
                 | IllegalAccessException | NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 反射私有的成员方法(公开私有皆可)
     */
    public static void reflectPrivateMethod() {
        try {
            //1. 拿到 Class 对象
            Class<?> c1 = Class.forName("demo1.Student");
            Student student = (Student) c1.newInstance();
            //2. 获得对象中私有的成员方法并给其赋值(公有也可进行操作)
            Method method = c1.getDeclaredMethod("function", String.class);
            method.setAccessible(true);
            method.invoke(student,"拿到私有方法");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                 | NoSuchMethodException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        reflectNewInstance();
        reflectPrivateConstructor();
        reflectPrivateField();
        reflectPrivateMethod();
    }
}


输出结果:


174509e1390a41f5a2bd037f1f25cb58.png


四、枚举



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


源代码中的 Enum 类是抽象类,其作用就是用来被继承。


fac92dfbdda241c68a79a556e06ce19b.png


1. 语法


public enum TestEnum {
  RED,BLACK,GREEN; //列举某个事物的颜色
}


2. 枚举的一些方法


5360d1814a6d4ac3aa33c039fc9cd30b.png


3. 演示1


public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        TestEnum[] testEnums = TestEnum.values();
        for (int i = 0; i < testEnums.length; i++) {
            System.out.println(testEnums[i] + " -> " + testEnums[i].ordinal() );
        }
        System.out.println("-----------------");
        System.out.println(BLACK.compareTo(RED)); //依照索引下标比较
        System.out.println(BLACK.compareTo(WHITE));
    }
}


输出结果:


8313cad24b0e40928996df6427ef2492.png


4. 演示2


public enum TestEnum2 {
    RED("red",1),BLACK("black",2),
    WHITE("white",3),GREEN;
    public String color;
    public int ordinal;
    //1. 当枚举对象有参数后,需要提供相应的构造函数
    //2. 枚举的构造函数默认是私有的
    TestEnum2 (String color,int ordinal) { //private
        this.color = color;
        this.ordinal = ordinal;
    }
    TestEnum2(){
    }
    public static void main(String[] args) {
    }
}


5. 枚举的优缺点


优点:

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


缺点:

不可被继承,无法扩展


6. 枚举与反射


枚举非常安全,不能通过反射来获取到枚举的实例对象


7. 学完线程再回过头看问题


如何实现一个线程安全的单例模式(只能获取一个实例对象)?


通过枚举实现。


目录
相关文章
|
4天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
10天前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
29天前
|
Java
Java枚举使用的基本案例
这篇文章是关于Java枚举的基本使用,通过一个指令下发的代码案例,展示了如何定义枚举、使用枚举以及如何通过枚举实现指令的匹配和处理。
|
30天前
|
Java 开发者
在Java编程中,if-else与switch作为核心的条件控制语句,各有千秋。if-else基于条件分支,适用于复杂逻辑;而switch则擅长处理枚举或固定选项列表,提供简洁高效的解决方案
在Java编程中,if-else与switch作为核心的条件控制语句,各有千秋。if-else基于条件分支,适用于复杂逻辑;而switch则擅长处理枚举或固定选项列表,提供简洁高效的解决方案。本文通过技术综述及示例代码,剖析两者在性能上的差异。if-else具有短路特性,但条件增多时JVM会优化提升性能;switch则利用跳转表机制,在处理大量固定选项时表现出色。通过实验对比可见,switch在重复case值处理上通常更快。尽管如此,选择时还需兼顾代码的可读性和维护性。理解这些细节有助于开发者编写出既高效又优雅的Java代码。
23 2
|
21天前
|
安全 Java 编译器
java枚举
java枚举
13 0
|
2月前
|
安全 Java 测试技术
day26:Java零基础 - 反射
【7月更文挑战第26天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
29 5
|
28天前
|
缓存 安全 Java
【Java 第十篇章】反射
Java 反射技术让程序能在运行时动态获取类信息并操作对象,极大提升了灵活性与扩展性。本文将介绍反射的基本概念、原理及应用,包括如何使用 `Class`、`Field`、`Method` 和 `Constructor` 类进行动态操作。此外,还将探讨反射在动态加载、框架开发与代码测试中的应用场景,并提醒开发者注意性能与安全方面的问题,帮助你更合理地运用这一强大工具。
14 0
|
2月前
|
存储 缓存 Java
java枚举消除冗余代码问题之findByName和findByValue方法工作时的问题如何解决
java枚举消除冗余代码问题之findByName和findByValue方法工作时的问题如何解决
|
1月前
|
Java
【Java】内部类、枚举、泛型
【Java】内部类、枚举、泛型
|
2月前
|
安全 Java
Java进阶之枚举
【7月更文挑战第11天】Java枚举是Java 5引入的特性,用于定义固定常量集合,如星期。枚举是继承自`java.lang.Enum`的特殊类,编译后成为final类,每个枚举值是静态final实例。定义枚举用`enum`关键字,如`public enum Weekday {MONDAY, TUESDAY, ...}`。枚举可包含方法和变量,能实现接口但不能继承其他类。例如,`Weekday`枚举可实现`Describe`接口,提供`describe()`方法。在实际应用中,枚举常用于表示如响应状态等固定选项,便于类型安全和代码阅读。
31 8