• Java反射、枚举、Lambda表达式


    反射

    Java 中的反射

    Java 反射(Reflection)是一种强大的机制,允许程序在运行时检查和操作类、方法、字段等程序结构的信息。通过反射,你可以动态地获取类的信息、创建对象、调用方法、访问字段等,而不需要在编译时确定这些信息。

    主要涉及的类在 java.lang.reflect 包中,其中包括以下 4 个类:

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

    反射的使用

    常用获得类相关的方法

    方法解释
    ClassLoader getClassLoader()获得类的加载器
    Class[] getDeclaredClasses()返回一个数组,数组中包含该类中所有类和接口类的对象
    Class forName(String className)根据类名返回类的对象
    T newInstance()创建类的实例
    String getName()获得类的完整路径名

    常用获得类中属性相关的方法

    方法解释
    Field getField(String name)获得某个公有的属性对象
    Field[] getFields()获得所有公有的属性对象
    Field getDeclaredField(String name)获得某个属性对象
    Field[] getDeclaredFields()获得所有属性对象

    获得类中注解相关的方法

    方法解释
    A getAnnotation(Class annotationClass)返回该类中与参数类型匹配的公有注解对象
    Annotation[] getAnnotations()返回该类所有的公有注解对象
    A getDeclaredAnnotation(Class annotationClass)返回该类中与参数类型匹配的所有注解对象
    Annotation[] getDeclaredAnnotations()返回该类所有的注解对象

    获得类中构造器相关的方法

    方法解释
    Constructor getConstructor(Class... parameterTypes)获得该类中与参数类型匹配的公有构造方法
    Constructor[] getConstructors()获得该类的所有公有构造方法
    Constructor getDeclaredConstructor(Class... parameterTypes)获得该类中与参数类型匹配的构造方法
    Constructor[] getDeclaredConstructors()获得该类的所有构造方法

    获得类中方法相关的方法

    方法解释
    Method getMethod(String name, Class... parameterTypes)获得该类某个公有方法
    Method[] getMethods()获得该类所有公有方法
    Method getDeclaredMethod(String name, Class... parameterTypes)获得该类某个方法
    Method[] getDeclaredMethods()获得该类所有方法

    以Student类为例:

    class Student {
        public String name;
        private int age;
    
        public Student() {}
        private Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        private void fun(String message) {
            System.out.println(message);
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    获取 Class 对象的三种方式

    public static void main(String[] args) {
        Student student = new Student();
        Class<?> c1 = student.getClass();
        Class<?> c2 = Student.class;
        Class<?> c3 = null;
        try {
            c3 = Class.forName("reflectdemo.Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
    }
    /* 输出:
    true
    true
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    反射创建类的实例:

    public class ReflectClassDemo {
        public static void reflectNewInstance() {
            Class<?> c3 = null;
            try {
                c3 = Class.forName("reflectdemo.Student");
                Student student = (Student) c3.newInstance();
                System.out.println(student);
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    
        public static void main(String[] args) {
            reflectNewInstance();
        }
    }
    /* 输出:
    reflectdemo.Student@1b6d3586
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    反射获取私有的构造方法:

    public static void reflectPrivateConstructor() {
        Class<?> c3 = null;
        try {
            c3 = Class.forName("reflectdemo.Student");
            Constructor<?> constructor = c3.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true); // 设置为true可修改访问权限
            Student student = (Student) constructor.newInstance("zhangsan", 23);
            System.out.println(student);
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException |
                 IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void main(String[] args) {
        reflectPrivateConstructor();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    反射获取私有属性

    public static void reflectPrivateField() {
        Class<?> c3 = null;
        try {
            c3 = Class.forName("reflectdemo.Student");
            Student student = (Student) c3.newInstance();
            Field field = c3.getDeclaredField("age");
            field.setAccessible(true);
            field.set(student, 99);
            System.out.println(student);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    反射调用私有方法:

    public static void reflectPrivateMethod() {
        Class<?> c3 = null;
        try {
            c3 = Class.forName("reflectdemo.Student");
            Student student = (Student) c3.newInstance();
            Method method = c3.getDeclaredMethod("fun", String.class);
            method.setAccessible(true);
            method.invoke(student, "hello");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException |
                 InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    反射的优缺点

    优点

    • 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
    • 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
    • 反射已经运用在很多流行框架:Struts、Hibernate、Spring 等等。

    缺点

    • 使用反射会有效率问题,导致程序效率降低
    • 反射技术绕过了源代码的技术,因而会带来维护问题,反射代码比相应的直接代码更复杂。

    枚举

    定义枚举类型

    public enum Day {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
    }
    
    • 1
    • 2
    • 3
    • 其中的 SUNDAY, MONDAY,… 都是枚举常量,每个枚举常量都是枚举类型的一个实例。
    • 枚举常量会被隐式声明为public static final
    • 枚举类型中的常量是通过枚举类型名称后的点表示的。例如,Day.MONDAY表示星期一。
    • 其本质是 java.lang.Enum 的子类。

    方法和属性:你可以为枚举类型添加方法和属性。每个枚举常量都是枚举类型的一个实例,并且可以有自己的方法和属性。

    public enum Day {
        SUNDAY("Sun"), MONDAY("Mon"), TUESDAY("Tue"), WEDNESDAY("Wed"),
        THURSDAY("Thu"), FRIDAY("Fri"), SATURDAY("Sat");
    
        private final String abbreviation;
    
        Day(String abbreviation) {
            this.abbreviation = abbreviation;
        }
    
        public String getAbbreviation() {
            return abbreviation;
        }
    }
    // 每个枚举常量都有一个与之关联的缩写属性,并且有一个获取缩写的方法。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意

    • 构造方法默认是私有的,且只能是私有,这是为了确保枚举常量只能在枚举类型内部进行创建。
    • 枚举常量在类外,即使是通过反射也无法创建

    常用方法

    方法解释
    values()以数组形式返回枚举类型的所有成员
    int ordinal()获取枚举成员的索引位置
    valueOf()将普通字符串转换为枚举实例
    int compareTo(E o)比较两个枚举成员的序号

    遍历枚举:通过values()方法获取枚举类型的所有常量,并使用foreach循环遍历它们。

    public static void main(String[] args) {
        for (Day day : Day.values()) {
            System.out.println(day);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    switch语句: 枚举类型在使用switch语句时非常有用。每个枚举常量都可以作为一个分支。

    Day today = Day.MONDAY;
    
    switch (today) {
        case MONDAY:
            System.out.println("It's Monday!");
            break;
        case TUESDAY:
            System.out.println("It's Tuesday!");
            break;
        // ... 其他星期的情况
        default:
            System.out.println("It's not a weekday.");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Lambda 表达式

    语法

    (parameters) -> expression
    
    • 1

    或者:

    (parameters) -> { statements; }
    
    • 1

    Lambda 表达式主要由以下几个部分组成:

    1. 参数列表(Parameters): 与普通方法的参数列表类似,可以为空或非空,如果只有一个参数,可以省略 ()。参数的类型在可推导的时候可能省略
    2. 箭头符号(Arrow): 使用 -> 符号表示从参数到表达式或语句的"流向"。
    3. 表达式(Expression): 单行表达式作为 Lambda 的主体。在这种情况下,表达式的值将被返回。注意,这种情况下,不需要使用 return 关键字。
    4. 代码块(Block): 多行代码块作为 Lambda 的主体。在这种情况下,如果需要返回值,则需要使用 return 关键字。
    // 不需要参数,返回值为2
    () -> 2
    // 接收一个参数(数字类型),返回其2倍的值
    x -> 2 * x
    // 接收2个参数(数字类型),返回它们的和
    (x, y) -> x + y
    // 接收2个int,返回它们的积
    (int x, int y) -> x * y
    // 接收一个String,打印,无返回值
    (String s) -> System.out.println(s)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    函数式接口与Lambda表达式使用

    函数式接口是一种只包含一个抽象方法的接口

    • 函数式接口中可以有多个默认方法和静态方法,但只能有一个抽象方法。这个抽象方法就是Lambda表达式的目标。
    • Java 8引入了 @FunctionalInterface 注解,用于显式声明一个接口是函数式接口。编译器会检查带有这个注解的接口,确保其满足函数式接口的定义。
    • Lambda表达式本质就是一个匿名内部类,实现了函数式接口,重写了其中的方法

    例:

    @FunctionalInterface
    interface MyFunctionalInterface {
        void myMethod();
    }
    
    public class Test {
        public static void main(String[] args) {
            MyFunctionalInterface myFunc = () -> System.out.println("Hello, Lambda!");
            myFunc.myMethod();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    其等价于使用匿名内部类:

    @FunctionalInterface
    interface MyFunctionalInterface {
        void myMethod();
    }
    
    
    public class Test {
        public static void main(String[] args) {
            MyFunctionalInterface myFunc = new MyFunctionalInterface() {
                @Override
                public void myMethod() {
                    System.out.println("Hello, Lambda!");
                }
            };
            myFunc.myMethod();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    捕获

    在Java中,匿名内部类和Lambda表达式都可以用来捕获变量

    匿名内部类捕获变量:

    public static void main(String[] args) {
        int a = 10;
        MyFunctionalInterface myFunc = new MyFunctionalInterface() {
            @Override
            public void myMethod() {
                System.out.println(a); // 10
            }
        };
        myFunc.myMethod();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Lambda 表达式捕获:

    public static void main(String[] args) {
        int a = 10;
        MyFunctionalInterface myFunc = () -> System.out.println(a); // 10
        myFunc.myMethod();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在集合中的使用

    在集合中,也新增了部分接口,以便于 Lambda 表达式的使用

    函数式接口方法
    CollectionremoveIf() spliterator() stream() parallelStream() forEach()
    ListreplaceAll() sort()
    MapgetOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()

    Collection 的 forEach() 方法是从接口 java.lang.Iterable 拿过来的

  • 相关阅读:
    【PCB学习笔记】基础概念及操作
    同步辐射散射测试中影响效果的原因有哪些?
    Android 11.0 mt6771新增分区功能实现三
    飞书项目发布3个月,已签约理想汽车、安克创新等100余家公司
    CSS元素浮动
    ComfyUI 安装
    数据结构——线性表的类型定义
    2022京东双十一全品类销售额变化情况一览:50%增长,50%下滑
    校园门禁升级改造,终于看到了希望!
    基础算法--双指针算法
  • 原文地址:https://blog.csdn.net/CegghnnoR/article/details/134469355