【数据结构】(12) 反射、枚举、lambda 表达式

news/2025/2/25 20:58:02

一、反射

1、反射机制定义及作用

        反射是允许程序在运行时检查和操作类、方法、属性等的机制,能够动态地获取信息、调用方法等。换句话说,在编写程序时,不需要知道要操作的类的具体信息,而是在程序运行时获取和使用。

2、反射机制的原理

        程序运行时,JVM编译好的 .class 文件(代表一个类)解析为 java.lang.Class 类的实例,这个实例包含了该类的所有信息。通过反射机制,可以用到这个实例,来获取该类的信息并进行操作

        下面介绍反射的相关类。以 Student 类为例子:

java">public 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 eat");
    }

    public void sleep(){
        System.out.println("i am pig");
    }

    private void function(String str) {
        System.out.println(str);
    }

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

        Class 在 java.lang 中,不需要导包。其它三个都在 java.lang.reflect 中。

3、Class 类

        代表类的实体

        获取 JVM 给类解析的 Class 对象:

        其它方法:

4、Field 类

        代表类的属性

  • getField(String name):获得指定公有属性对象。
  • getField():获得所有公有属性对象。
  • getDeclaredField(String name):获得某个属性对象。(不限于公有)
  • getDeclaredField():获得所有属性对象。

        属性对象.get(对象) 表示获取指定对象的某属性,静态属性不需对象,参数填 null。

5、Method 类

        代表类的方法

6、Constructor 类

        代表类的构造方法

        利用构造方法对象获得类的实例:

7、使用反射动态加载类并创建对象

        编写一个程序,在运行时根据用户输入的类名来动态加载类并创建对象,调用其 draw 方法。

java">import java.util.Scanner;

// 定义一个 Shape 接口
interface Shape {
    void draw();
}

// 定义 Circle 类实现 Shape 接口
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制一个圆形");
    }
}

// 定义 Rectangle 类实现 Shape 接口
class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制一个矩形");
    }
}

public class DynamicClassLoadingExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要加载的类名(如 Circle 或 Rectangle):");
        String className = scanner.nextLine();

        try {
            // 根据用户输入的类名获取 Class 对象
            Class<?> clazz = Class.forName(className);
            // 创建该类的实例
            Shape shape = (Shape) clazz.getDeclaredConstructor().newInstance();
            // 调用 draw 方法
            shape.draw();
        } catch (Exception e) {
            e.printStackTrace();
        }
        scanner.close();
    }
}

反射优点:让程序更灵活,动态加载类和方法、可以访问和修改私有成员。

反射缺点:性能开销大、破坏封装性和安全性、程序不易读。

二、枚举

1、定义及作用

        一组常量,比如颜色,它们是同一类,我们想把这组常量组织在一起,就用枚举。作用是与没有意义的数字区分开来(如果定义一组常量,就要用数字表示它们)。例子:

java">public enum Color {
    RED, GREEN, BLUE;
    public static void main(String[] args) {
        Color color = Color.BLUE;
        switch (color) {
            case RED:
                System.out.println("RED");
                break;
            case GREEN:
                System.out.println("GREEN");
                break;
            case BLUE:
                System.out.println("BLUE");
                break;
            default:
                System.out.println("default");
                break;
        }
    }
}

        枚举类默认继承了 java.lang.Enum。枚举常量的默认是 public static final 修饰

2、常用方法

        枚举类型能够使用这些方法,是因为默认继承了 Enum 类:

        但是可以发现,Enum 类中没有 values 方法。这是因为自定义枚举类型中枚举常量的数量、类型都是不确定的,如果 values 封装在 Enum 类中,就需要在运行时动态确定每个枚举常量(需要用到反射,破坏了封装性、增强了复杂性等)。实际上,编译器编译时会为每个枚举类型自动加上 values 方法,返回的数组在编译时就确定

3、构造方法

        枚举相当于是类,可以有属性、构造方法、方法。构造方法默认私有,且只能。这样保证了枚举实例的唯一性。

 查看构造函数的实际参数有哪些:

        多了 String 和 int 类型参数,因为枚举类默认继承了 Enum,其本身有 name 和 ordinal 属性,隐藏了 super(name, ordinal) ,所以 Color 构造函数会多 2 个参数

4、枚举和反射

        通过反射获取枚举类的实例:

         出现错误:

       查看 newInstance 源码:

        因此,不能通过反射创建枚举类的实例。这样设计的目的是让枚举类只有一个实例防止反射攻击由私有构造函数、防反射攻击这两个特点得知,枚举实现单例模式(一种创建型模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例)是安全的

枚举优点:简单安全。

枚举缺点:不能继承

5、枚举实现单例模式

        后续学了单例模式补充。

三、Lambda 表达式

1、定义及作用

        相当于方法,但是比方法更简化。语法:

java">(形参列表)->表达式

(形参列表)->{代码块}

// 参数类型可以省略(要省全都省),只有一个参数可以省略圆括号。

// 返回一个值或者不返回值

// 只有一条 return 语句,可省略 return

2、函数式接口

  • 函数式接口:一个接口只有一个抽象方法
  • @FunctionalInterface 注解:以函数式接口的标准(只有一个抽象方法)要求接口,检查作用。
  • lambda 可简化匿名内部,实现函数式接口

        几个函数式接口代码:

java">//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

        使用匿名内部类实现接口,重写方法:

        使用 lambda 实现接口,重写方法:

java">NoParameterNoReturn noParameterNoReturn = ()->{System.out.println("hello");};

3、变量捕获

        匿名内部类中可捕获外部变量。如果想修改捕获的变量,那该变量必须被 final 修饰,否则报错。lambda 表达式同理

java">        int a = 10;
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
//                a = 99;  不允许修改未被 final 修饰的外部变量
                System.out.println(a); // 变量 a 捕获
                return o1.compareTo(o2);
            }
        });

4、Lambda 在集合中的应用

        lambda 表达式作为以上方法的参数,实现接口中的方法。 

4.1、Collection 接口

        以 forEach 为例:

        源码如下:Consumer 是函数式接口,accept 是实现的抽象方法。

        实现函数式接口:

4.2、List 接口

        以 sort 为例:

        源码:

        在 Java 中,所有类都直接或间接地继承自 Object 类,因此,Comparator 接口继承了 Object 类的 equals 方法。它不是 Comparator 接口自身定义的抽象方法,因此,不违反函数式接口的定义规则。

java">    @FunctionalInterface
    public interface Comparator<T> {
        int compare(T o1, T o2);

        boolean equals(Object obj); // 继承自 Object,不影响函数式接口
        
        ......
    }

        实现:

4.3、Map 接口

        以 forEach 为例:

        源码:

        实现:

5、总结:

优点:语句简洁、方便函数式编程(替换匿名内部类)、改善集合操作。

缺点:可读性差、不易调试。


http://www.niftyadmin.cn/n/5865938.html

相关文章

Linux | man 手册使用详解

注&#xff1a;本文为 “Linux man 手册” 相关文章合辑。 略作重排。 man 手册常用命令 1. 查看和搜索手册页 查看特定软件包的手册页&#xff0c;并使用 grep 命令过滤出包含特定关键字的行&#xff1a; man <package> | grep <keyword>在整个系统的手册页中…

七.智慧城市数据治理平台架构

一、整体架构概览 智慧城市数据治理平台架构描绘了一个全面的智慧城市数据治理平台&#xff0c;旨在实现城市数据的统一管理、共享和应用&#xff0c;为城市运行、管理和决策提供数据支撑。整体架构呈现出分层、模块化、集约化的特点&#xff0c;并强调数据安全和标准规范。 智…

linux--多进程基础(2)GDB多进程调试(面试会问)

将其中的命令记住就行。 总结&#xff1a;GDB下默认调试父进程&#xff0c;可以设置调试父进程还是子进程&#xff0c;也可以设置调试模式&#xff0c;调试模式默认是on即一个在调试另一个直接运行&#xff0c;off就是另一个进程挂起&#xff0c;最后可以查看调试进程 一般默认…

R Excel 文件:高效数据处理的利器

R Excel 文件:高效数据处理的利器 在数据分析领域,R语言因其强大的统计分析和可视化功能而备受推崇。而R Excel文件,作为R语言与Excel的桥梁,使得数据在R和Excel之间的高效转换成为可能。本文将详细介绍R Excel文件的概念、应用场景以及操作方法。 一、R Excel文件的概念…

cline通过硅基流动平台接入DeepSeek-R1模型接入指南

为帮助您更高效、安全地通过硅基流动平台接入DeepSeek-R1模型&#xff0c;以下为优化后的接入方案&#xff1a; DeepSeek-R1硅基流动平台接入指南 &#x1f4cc; 核心优势 成本低廉&#xff1a;注册即送2000万Tokens&#xff08;价值约14元&#xff09;高可用性&#xff1a;规…

2.3 变量

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 变量是用来存放某个值的数据&#xff0c;它可以表示一个数字、一个字符串、一个结构、一个类等。变量包含名称、类型和值。在代码中…

嵌入式学习|C语言篇进程间通信(IPC)全面解析与示例

一、进程通信基础概念 1.1 进程隔离原理 现代操作系统通过虚拟内存技术为每个进程创建独立的地址空间&#xff0c;这种隔离机制保障了系统的安全性&#xff0c;但也导致进程无法直接访问彼此的内存数据。进程间通信&#xff08;IPC&#xff09;正是为解决这一矛盾而设计的核心…

历史数据清洗(数据治理)需要关注的问题

历史数据清洗&#xff08;数据治理&#xff09; 如果有基本了解的可以直接看总结 事务与幂等性 使用事务&#xff08;Transaction&#xff09; 若数据量大且涉及多表更新&#xff0c;尽量使用 批量提交&#xff08;如每 1000 条提交一次&#xff09;来提高效率&#xff0c;避…