数据结构--9:反射、枚举以及lambda表达式(了解即可)

属于JAVA语法 与数据结构关系不大】

1.反射

Java的反射(reflection)机制是在运行时检查、访问和修改类、接口、字段和方法的机制;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。

用途(了解) :框架开发、注解处理、动态代理、配置文件解析等等







java 复制代码
package last;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Student {
    private int id = 10;
    private String name;

    public void print() {
        System.out.println("id = " + id + ", name = " + name);
    }

    public void print(String prefix) {
        System.out.println(prefix + "id = " + id + ", name = " + name);
    }
}

public class Test2 {
    private static void test1() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        // 通过反射, 获取到 Student 类中的 私有属性 .

        // 1. 获取到 Student 类的 Class 对象
        Class studentClass = Class.forName("last.Student");
        // 2. 拿到上面的 id 属性
        Field idField = studentClass.getDeclaredField("id");

        // 3. 创建一个对象出来. 怎么创建无所谓.
        Student student = new Student();
        // 直接这样访问, 由于 id 是 private, 肯定不行的.
        // System.out.println(student.id);

        // 4. 借助反射完成上述的操作. 相当于 "开锁"
        idField.setAccessible(true);

        // 先设置一下这里的值.
        idField.setInt(student, 100);

        // 再获取一下值.
        int id = idField.getInt(student);
        System.out.println(id);
    }

    private static void test2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 通过反射调用 Student 的方法
        // Class studentClass = Class.forName("last.Student");
        // 这个写法也能拿到 类对象. 更简单; 失去了 类名 的动态能力.
        Class studentClass = Student.class;

        // 1. 拿到方法对象
        Method printMethod = studentClass.getDeclaredMethod("print", String.class);
        // 2. 创建对象
        Student student = new Student();
        // 3. 设置可访问 (如果本来是 public 的, 这里可以不用设置)
        printMethod.setAccessible(true);
        // 4. 调用方法
        printMethod.invoke(student, "Hello, ");
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        test2();
    }
}

2.枚举

2.1 概念

枚举是在JDK1.5以后引入的,**主要用途是:将一组常量组织起来,**在这之前表示⼀组常量通常使用定义常量的方式:

java 复制代码
package last;

public class Test3 {
    public static final int FEMALE = 0;
    public static final int MALE = 1;

    public static void main(String[] args) {
        Gender gender = Gender.FEMALE;
        if (gender == Gender.FEMALE) {
            // 针对女性的处理
        } else if (gender == Gender.MALE) {
            // 针对男性的处理
        }

        // 上述代码可能会被误用.
        if (Gender.FEMALE + Gender.MALE == Gender.MALE) {
            // 从逻辑上来说, 不合理!! 性别不能进行 算术运算.
            // 但是这个逻辑从语法上来说, 不会出现任何报错!!
        }
    }
}

但是常量举例有不好的地方,例如:可能碰巧有个数字1,但是他有可能误会为是RED,**现在我们可以直接用枚举来进行组织,这样⼀来,就拥有了类型,枚举类型,**而不是普通的整形。



**优点:**将常量组织起来统一进行管理

**场景:**错误状态码,消息类型,颜色的划分,状态机等等....

**本质:**是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了这个类。

2.2 使用

刚刚说过,在Java当中枚举实际上就是⼀个类。所以我们在定义枚举的时候,还可以这样定义和使用枚举。

重要:枚举的构造方法默认是私有的

java 复制代码
package last;

public enum Color {
    RED("红色", 0xff0000), GREEN("绿色", 0x00ff00), BLUE("蓝色", 0x0000ff);

    // 实际开发中, 绝大部分的情况, 不需要使用属性方法的..... 核心就是上个例子, 定义枚举类型/枚举值.
    private String name;
    private int value;

    // 构造方法, 指定枚举的属性值.
    Color(String name, int value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public int getValue() {
        return value;
    }
}

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

枚举是否可以通过反射,拿到实例对象呢? ( 原版问题是:为什么枚举实现单例模式是安全的?


总结:

1、枚举本身就是⼀个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum。

2、枚举可以避免反射和序列化问题

3、枚举的优点和缺点

面试问题(单例模式学完后可以回顾)

3.Lambda表达式

3.1 概念

lambda表达式允许你通过表达式来代替功能接口 。lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体 (body,可以是一个表达式或一个代码块)。Lambda表达式(Lambda expression)

基本语法:(parameters) -> expression 或 (parameters) -> {statements;}

Lambda表达式由三部分组成:

**(1)paramaters:**类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有⼀个推断类型时可以省略掉圆括号。

**(2)->:**可理解为"被用于"的意思

**(3)方法体:**可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不返回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不返回。



3.2 函数式接口

java 复制代码
package last;

// 形如这样的接口, 只包含一个抽象方法的, 就是函数式接口.
@FunctionalInterface
interface MyFuncInterface {
    void print(String s);
    // void doSomething();
}



public class Test6 {
    public static void main(String[] args) {
//        MyFuncInterface myFuncInterface = new MyFuncInterface() {
//            @Override
//            public void print(String s) {
//                System.out.println(s);
//            }
//        };

        MyFuncInterface myFuncInterface = (s) -> System.out.println(s);

        myFuncInterface2.print("Hello, world!", 100);
    }
}

3.3 变量捕获

Lambda表达式中存在变量捕获,了解了变量捕获之后,我们才能更好的理解Lambda表达式的作用域。

Java当中的匿名类中,会存在变量捕获。

(1)匿名内部类

匿名内部类就是没有名字的内部类,我们这里只是为了说明变量捕获。

java 复制代码
package last;

// 形如这样的接口, 只包含一个抽象方法的, 就是函数式接口.
@FunctionalInterface
interface MyFuncInterface {
    void print(String s);
    // void doSomething();
}

interface MyFuncInterface2 {
    void print(String s, int i);
}

public class Test6 {
    public static void main(String[] args) {
//        MyFuncInterface myFuncInterface = new MyFuncInterface() {
//            @Override
//            public void print(String s) {
//                System.out.println(s);
//            }
//        };

        // MyFuncInterface myFuncInterface = (s) -> System.out.println(s);
        MyFuncInterface myFuncInterface = s -> System.out.println(s);
        MyFuncInterface2 myFuncInterface2;
        {
            int num = 10000;
            myFuncInterface2 = (String s, int i) -> {
                System.out.println(num);
            };
            // num = 20000;
        }
        myFuncInterface2.print("Hello, world!", 100);
    }
}

3.4 Lambda在集合当中的使用

java 复制代码
package last;

import java.util.ArrayList;
import java.util.List;

public class Test8 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i) * 2);
        }

        // 使用 forEach 方法来实现.
        // Java 中有一组 Streaming API 就是类似于这种编码风格.
        // 模仿了函数式编程的风格.
        list.forEach(i -> System.out.println(i * 2));
    }

}

3.5 总结

Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。

**优点:**1.代码简洁,开发迅速 2.方便函数式编程 3.非常容易进行并行计算 4.Java引入Lambda,改善了集合操作。

**缺点:**1.代码可读性变差 2.在非并行计算中,很多计算未必有传统的for性能要高 3.不容易进行调试

相关推荐
青衫码上行2 小时前
【Java Web学习 | 第12篇】JavaScript(6)DOM
java·开发语言·前端·javascript·学习
njxiejing2 小时前
Python pandas基础:Series数据操作详解
数据结构·pandas
TDengine (老段)2 小时前
TDengine 字符串函数 POSITION 用户手册
android·java·大数据·数据库·物联网·时序数据库·tdengine
弘毅 失败的 mian2 小时前
C++、Java 还是测试开发?
java·c++·经验分享·笔记·测试开发·技术方向·就业
杜子不疼.2 小时前
【C++】 set/multiset底层原理与逻辑详解
java·开发语言·c++
q***31892 小时前
Spring Boot--@PathVariable、@RequestParam、@RequestBody
java·spring boot·后端
Macbethad3 小时前
如何用WPF做工控设置界面
java·开发语言·wpf
CodeAmaz3 小时前
使用责任链模式设计电商下单流程(Java 实战)
java·后端·设计模式·责任链模式·下单
喝养乐多长不高3 小时前
Rabbit MQ:概述
java·rabbitmq·mq·amqp