目录

前言

一、反射机制:运行时类型探索

1.1 反射的定义与本质

1.2 反射的核心类

1.3 获取Class对象的三种方式

1.4 反射实战应用

1.4.1 创建对象实例

1.4.2 访问和修改私有成员

1.5 反射的优缺点分析

二、枚举类型:类型安全的常量集合

2.1 枚举的演进与定义

2.2 枚举的基本用法

2.3 枚举的核心方法

2.4 枚举与switch语句

2.5 枚举与反射的深入分析

2.5.1 枚举的本质

2.5.2 枚举防反射机制

2.6 枚举实现单例模式

2.7 枚举的优缺点总结

三、Lambda表达式:函数式编程的入口

3.1 Lambda表达式背景与语法

3.2 函数式接口

3.3 内置函数式接口

3.4 Lambda表达式实战

3.4.1 集合遍历与处理

3.4.2 集合排序

3.5 变量捕获机制

3.6 Stream API与Lambda结合

3.7 Lambda表达式的性能考量

3.8 Lambda表达式的优缺点

四、综合应用与设计模式

4.1 策略模式与Lambda

4.2 观察者模式与Lambda

4.3 模板方法模式与Lambda

五、最佳实践与注意事项

5.1 反射使用规范

5.2 枚举设计原则

5.3 Lambda最佳实践

总结


前言

在Java开发中,反射、枚举和Lambda表达式是三个重要的高级特性,它们分别代表了Java语言在不同发展阶段的重要创新。反射赋予了程序在运行时动态分析类和对象的能力,枚举提供了类型安全的常量组织方式,而Lambda表达式则引入了函数式编程范式,使代码更加简洁优雅。掌握这些特性不仅能提升代码质量,还能帮助解决复杂的设计问题。


一、反射机制:运行时类型探索

1.1 反射的定义与本质

反射(Reflection)是Java在运行时获取类信息并操作类对象的能力。通过反射,程序可以:

  • 动态获取类的属性和方法

  • 在运行时创建对象并调用方法

  • 访问和修改私有成员

  • 实现通用框架和工具

// 反射的核心:运行时类型识别(RTTI)
Person p = new Student();  // 编译时类型Person,运行时类型Student

1.2 反射的核心类

Java反射API围绕以下几个核心类构建:

类名 用途 示例方法
Class 类的元数据表示 forName()newInstance()
Field 类的字段/属性 getField()setAccessible()
Method 类的方法 getMethod()invoke()
Constructor 类的构造方法 getConstructor()newInstance()

1.3 获取Class对象的三种方式

// 方式1:Object.getClass() - 适用于已有对象
Student s1 = new Student();
Class c1 = s1.getClass();

// 方式2:类名.class - 最安全可靠
Class c2 = Student.class;

// 方式3:Class.forName() - 最常用,需处理异常
Class c3 = Class.forName("com.example.Student");

1.4 反射实战应用

1.4.1 创建对象实例
public class ReflectDemo {
    // 1. 通过无参构造创建对象
    public static void reflectNewInstance() {
        try {
            Class<?> clazz = Class.forName("Student");
            Student student = (Student) clazz.newInstance();
            System.out.println("创建对象: " + student);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 2. 访问私有构造方法
    public static void reflectPrivateConstructor() {
        try {
            Class<?> clazz = Class.forName("Student");
            // 获取私有构造方法
            Constructor<?> constructor = clazz.getDeclaredConstructor(
                String.class, int.class
            );
            constructor.setAccessible(true);  // 突破访问限制
            Student student = (Student) constructor.newInstance("张三", 20);
            System.out.println("通过私有构造创建: " + student);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
1.4.2 访问和修改私有成员
public class ReflectDemo {
    // 访问私有字段
    public static void reflectPrivateField() {
        try {
            Class<?> clazz = Class.forName("Student");
            Student student = (Student) clazz.newInstance();
            
            // 获取私有字段
            Field nameField = clazz.getDeclaredField("name");
            nameField.setAccessible(true);  // 设置可访问
            
            // 修改字段值
            nameField.set(student, "李四");
            System.out.println("修改后的name: " + nameField.get(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 调用私有方法
    public static void reflectPrivateMethod() {
        try {
            Class<?> clazz = Class.forName("Student");
            Student student = (Student) clazz.newInstance();
            
            // 获取私有方法
            Method method = clazz.getDeclaredMethod("privateMethod", String.class);
            method.setAccessible(true);
            
            // 调用方法
            method.invoke(student, "私有方法参数");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1.5 反射的优缺点分析

优点

  1. 动态性:运行时获取类型信息,实现动态创建对象和调用方法

  2. 灵活性:可以访问和操作私有成员,突破访问限制

  3. 框架支持:Spring、Hibernate等主流框架的核心技术

缺点

  1. 性能开销:反射操作比直接调用慢1-2个数量级

  2. 安全限制:可能破坏封装性,引发安全问题

  3. 维护困难:反射代码可读性差,调试困难

  4. 内部依赖:可能因为JDK版本变化而失效

// 性能对比示例
public class PerformanceTest {
    public static void main(String[] args) throws Exception {
        int iterations = 1000000;
        
        // 直接调用
        long start = System.nanoTime();
        Student s = new Student();
        for (int i = 0; i < iterations; i++) {
            s.publicMethod();
        }
        long directTime = System.nanoTime() - start;
        
        // 反射调用
        start = System.nanoTime();
        Class<?> clazz = Class.forName("Student");
        Method method = clazz.getMethod("publicMethod");
        Object obj = clazz.newInstance();
        for (int i = 0; i < iterations; i++) {
            method.invoke(obj);
        }
        long reflectTime = System.nanoTime() - start;
        
        System.out.println("直接调用: " + directTime + "ns");
        System.out.println("反射调用: " + reflectTime + "ns");
        System.out.println("性能差距: " + (reflectTime / directTime) + "倍");
    }
}

二、枚举类型:类型安全的常量集合

2.1 枚举的演进与定义

枚举(Enumeration)在JDK 1.5引入,解决了传统常量定义的缺陷:

// 传统方式 - 存在类型安全问题
public static final int RED = 1;
public static final int GREEN = 2;
public static final int BLUE = 3;

// 枚举方式 - 类型安全
public enum Color {
    RED, GREEN, BLUE  // 每个都是Color类型的实例
}

2.2 枚举的基本用法

public enum StatusCode {
    // 枚举实例
    SUCCESS(200, "成功"),
    NOT_FOUND(404, "未找到"),
    SERVER_ERROR(500, "服务器错误");
    
    // 枚举字段
    private final int code;
    private final String message;
    
    // 枚举构造方法(默认private)
    StatusCode(int code, String message) {
        this.code = code;
        this.message = message;
    }
    
    // 枚举方法
    public int getCode() { return code; }
    public String getMessage() { return message; }
    
    // 根据code查找枚举
    public static StatusCode fromCode(int code) {
        for (StatusCode status : values()) {
            if (status.code == code) {
                return status;
            }
        }
        throw new IllegalArgumentException("未知状态码: " + code);
    }
}

2.3 枚举的核心方法

方法 描述 示例
values() 返回所有枚举值数组 StatusCode.values()
valueOf() 根据名称获取枚举 StatusCode.valueOf("SUCCESS")
ordinal() 获取枚举序数 SUCCESS.ordinal()
name() 获取枚举名称 SUCCESS.name()
compareTo() 比较枚举顺序 SUCCESS.compareTo(NOT_FOUND)

2.4 枚举与switch语句

public class EnumSwitchDemo {
    public static void handleStatus(StatusCode status) {
        switch (status) {
            case SUCCESS:
                System.out.println("操作成功");
                break;
            case NOT_FOUND:
                System.out.println("资源不存在");
                break;
            case SERVER_ERROR:
                System.out.println("服务器错误");
                break;
            default:
                System.out.println("未知状态");
        }
    }
    
    // Java 12+ 增强的switch表达式
    public static String getStatusMessage(StatusCode status) {
        return switch (status) {
            case SUCCESS -> "操作成功";
            case NOT_FOUND -> "资源不存在";
            case SERVER_ERROR -> "服务器错误";
            // 不需要default,因为枚举已涵盖所有情况
        };
    }
}

2.5 枚举与反射的深入分析

2.5.1 枚举的本质

所有枚举类都隐式继承自java.lang.Enum,编译器会进行特殊处理:

// 我们编写的枚举
public enum Color { RED, GREEN, BLUE }

// 编译器处理后(简化版)
public final class Color extends Enum<Color> {
    public static final Color RED = new Color("RED", 0);
    public static final Color GREEN = new Color("GREEN", 1);
    public static final Color BLUE = new Color("BLUE", 2);
    
    private Color(String name, int ordinal) {
        super(name, ordinal);
    }
}
2.5.2 枚举防反射机制

枚举通过反射创建实例时受到限制,这是实现单例安全的关键:

public enum EnumReflectionTest {
    INSTANCE;
    
    private String value = "初始值";
    
    public static void testReflection() {
        try {
            Class<?> clazz = EnumReflectionTest.class;
            
            // 尝试通过反射创建枚举实例
            Constructor<?> constructor = clazz.getDeclaredConstructor(
                String.class, int.class  // Enum父类的构造参数
            );
            constructor.setAccessible(true);
            
            // 这里会抛出IllegalArgumentException
            Object obj = constructor.newInstance("NEW_INSTANCE", 1);
            System.out.println("反射创建成功: " + obj);
            
        } catch (IllegalArgumentException e) {
            System.out.println("禁止反射创建枚举实例: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        testReflection();
    }
}

JDK源码分析Constructor.newInstance()方法中对枚举进行了特殊检查:

// JDK源码片段(简化)
public T newInstance(Object... initargs) {
    // 检查是否为枚举类
    if ((clazz.getModifiers() & Modifier.ENUM) != 0) {
        throw new IllegalArgumentException("Cannot reflectively create enum objects");
    }
    // ... 其他代码
}

2.6 枚举实现单例模式

枚举是实现线程安全单例的最佳方式,避免了反射和序列化攻击:

// 1. 饿汉式单例(传统方式,有反射漏洞)
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}

// 2. 枚举单例(推荐方式)
public enum EnumSingleton {
    INSTANCE;
    
    // 单例的业务方法
    public void businessMethod() {
        System.out.println("枚举单例的业务方法");
    }
    
    // 使用示例
    public static void main(String[] args) {
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;
        
        System.out.println("是否为同一实例: " + (instance1 == instance2));  // true
        instance1.businessMethod();
    }
}

2.7 枚举的优缺点总结

优点

  1. 类型安全:编译时类型检查,避免错误赋值

  2. 代码清晰:常量有明确含义,提高可读性

  3. 线程安全:枚举实例天生是线程安全的

  4. 防反射攻击:无法通过反射创建新实例

  5. 序列化安全:自动处理序列化和反序列化

缺点

  1. 不可继承:枚举类不能被继承

  2. 内存占用:每个枚举实例都是静态常量

  3. 扩展困难:无法动态添加枚举值

三、Lambda表达式:函数式编程的入口

3.1 Lambda表达式背景与语法

Lambda表达式是Java 8引入的函数式编程特性,用于简化匿名内部类的编写:

// 语法格式:(参数列表) -> { 方法体 }
// 简化规则:参数类型可推导则省略;单参数可省略括号;单行代码可省略大括号

// 完整形式
(int a, int b) -> { return a + b; }

// 简化形式
(a, b) -> a + b

3.2 函数式接口

函数式接口是只有一个抽象方法的接口,是Lambda表达式的基础:

// 1. 自定义函数式接口
@FunctionalInterface  // 注解确保接口符合函数式接口规范
interface MathOperation {
    int operate(int a, int b);
    
    // 可以有默认方法
    default void printResult(int result) {
        System.out.println("结果: " + result);
    }
    
    // 可以有静态方法
    static MathOperation getDefault() {
        return (a, b) -> a + b;
    }
}

// 2. 使用示例
public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // Lambda实现函数式接口
        MathOperation addition = (a, b) -> a + b;
        MathOperation subtraction = (a, b) -> a - b;
        MathOperation multiplication = (a, b) -> a * b;
        
        // 使用
        System.out.println("10 + 5 = " + addition.operate(10, 5));
        System.out.println("10 - 5 = " + subtraction.operate(10, 5));
        System.out.println("10 × 5 = " + multiplication.operate(10, 5));
    }
}

3.3 内置函数式接口

Java 8提供了丰富的内置函数式接口:

接口 方法签名 用途
Function<T,R> R apply(T t) 转换函数
Consumer<T> void accept(T t) 消费函数
Supplier<T> T get() 供应函数
Predicate<T> boolean test(T t) 断言函数
BiFunction<T,U,R> R apply(T t, U u) 二元转换

import java.util.function.*;

public class BuiltInFunctionalInterfaces {
    public static void main(String[] args) {
        // 1. Function - 转换
        Function<String, Integer> strToInt = Integer::parseInt;
        System.out.println("字符串转数字: " + strToInt.apply("123"));
        
        // 2. Consumer - 消费
        Consumer<String> printer = System.out::println;
        printer.accept("Hello Lambda");
        
        // 3. Supplier - 供应
        Supplier<Double> randomSupplier = Math::random;
        System.out.println("随机数: " + randomSupplier.get());
        
        // 4. Predicate - 断言
        Predicate<String> isNotEmpty = s -> !s.isEmpty();
        System.out.println("字符串非空: " + isNotEmpty.test("Hello"));
        
        // 5. BiFunction - 二元函数
        BiFunction<Integer, Integer, Integer> adder = Integer::sum;
        System.out.println("两数之和: " + adder.apply(10, 20));
    }
}

3.4 Lambda表达式实战

3.4.1 集合遍历与处理
import java.util.*;

public class LambdaWithCollections {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
        
        // 传统方式
        for (String name : names) {
            System.out.println(name);
        }
        
        // Lambda方式
        names.forEach(name -> System.out.println(name));
        
        // 方法引用(更简洁)
        names.forEach(System.out::println);
    }
}
3.4.2 集合排序
public class LambdaSorting {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 20)
        );
        
        // 传统Comparator
        Collections.sort(people, new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return Integer.compare(p1.getAge(), p2.getAge());
            }
        });
        
        // Lambda表达式
        Collections.sort(people, (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
        
        // 方法引用
        Collections.sort(people, Comparator.comparingInt(Person::getAge));
        
        // 链式比较
        Collections.sort(people, 
            Comparator.comparing(Person::getName)
                     .thenComparingInt(Person::getAge));
    }
    
    static class Person {
        private String name;
        private int age;
        
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public String getName() { return name; }
        public int getAge() { return age; }
    }
}

3.5 变量捕获机制

Lambda表达式可以捕获外部变量,但有限制:

public class VariableCapture {
    public static void main(String[] args) {
        // 有效最终变量(effectively final)
        int x = 10;  // 隐式final
        
        Runnable r = () -> {
            // 可以读取x,但不能修改
            System.out.println("捕获的变量x: " + x);
            // x = 20;  // 编译错误:Variable used in lambda expression should be final or effectively final
        };
        
        new Thread(r).start();
        
        // 数组或对象引用可以修改内容
        int[] counter = {0};
        Runnable incrementer = () -> {
            counter[0]++;  // 允许,修改数组元素
            System.out.println("Counter: " + counter[0]);
        };
        
        new Thread(incrementer).start();
    }
}

3.6 Stream API与Lambda结合

import java.util.*;
import java.util.stream.*;

public class StreamLambdaDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // 1. 过滤偶数并求和
        int sumOfEvens = numbers.stream()
            .filter(n -> n % 2 == 0)      // 过滤偶数
            .mapToInt(Integer::intValue)  // 转换为int
            .sum();                       // 求和
        System.out.println("偶数之和: " + sumOfEvens);
        
        // 2. 查找最大最小值
        Optional<Integer> max = numbers.stream()
            .max(Integer::compare);
        max.ifPresent(m -> System.out.println("最大值: " + m));
        
        // 3. 分组统计
        Map<String, List<Integer>> grouped = numbers.stream()
            .collect(Collectors.groupingBy(
                n -> n % 2 == 0 ? "偶数" : "奇数"
            ));
        System.out.println("分组结果: " + grouped);
        
        // 4. 并行流处理
        long count = numbers.parallelStream()
            .filter(n -> n > 5)
            .count();
        System.out.println("大于5的元素个数: " + count);
    }
}

3.7 Lambda表达式的性能考量

public class LambdaPerformance {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            list.add(i);
        }
        
        // 测试1:传统for循环
        long start = System.currentTimeMillis();
        int sum1 = 0;
        for (int i : list) {
            sum1 += i;
        }
        long time1 = System.currentTimeMillis() - start;
        
        // 测试2:Stream串行流
        start = System.currentTimeMillis();
        int sum2 = list.stream()
            .mapToInt(Integer::intValue)
            .sum();
        long time2 = System.currentTimeMillis() - start;
        
        // 测试3:Stream并行流
        start = System.currentTimeMillis();
        int sum3 = list.parallelStream()
            .mapToInt(Integer::intValue)
            .sum();
        long time3 = System.currentTimeMillis() - start;
        
        System.out.println("传统循环: " + time1 + "ms, 结果: " + sum1);
        System.out.println("串行流: " + time2 + "ms, 结果: " + sum2);
        System.out.println("并行流: " + time3 + "ms, 结果: " + sum3);
    }
}

3.8 Lambda表达式的优缺点

优点

  1. 代码简洁:大幅减少样板代码

  2. 函数式编程:支持高阶函数和流式操作

  3. 并行友好:易于实现并行计算

  4. 集合操作:强大的Stream API支持

缺点

  1. 调试困难:Lambda调用栈不直观

  2. 性能开销:首次调用有初始化成本

  3. 学习曲线:需要理解函数式编程概念

  4. 过度使用:可能使代码难以理解

四、综合应用与设计模式

4.1 策略模式与Lambda

// 传统策略模式
interface ValidationStrategy {
    boolean execute(String s);
}

class IsAllLowerCase implements ValidationStrategy {
    public boolean execute(String s) {
        return s.matches("[a-z]+");
    }
}

class IsNumeric implements ValidationStrategy {
    public boolean execute(String s) {
        return s.matches("\\d+");
    }
}

// Lambda简化策略模式
public class StrategyPattern {
    public static void main(String[] args) {
        // 传统方式
        ValidationStrategy lowerCase = new IsAllLowerCase();
        ValidationStrategy numeric = new IsNumeric();
        
        // Lambda方式
        ValidationStrategy lowerCaseLambda = s -> s.matches("[a-z]+");
        ValidationStrategy numericLambda = s -> s.matches("\\d+");
        
        // 使用
        System.out.println(lowerCaseLambda.execute("hello"));
        System.out.println(numericLambda.execute("123"));
    }
}

4.2 观察者模式与Lambda

// 传统观察者接口
interface Observer {
    void notify(String tweet);
}

// Lambda简化
public class ObserverPattern {
    public static void main(String[] args) {
        // 传统方式需要创建多个类实现Observer接口
        
        // Lambda方式
        List<Observer> observers = new ArrayList<>();
        
        observers.add(tweet -> {
            if (tweet.contains("money")) {
                System.out.println("Breaking news: " + tweet);
            }
        });
        
        observers.add(tweet -> {
            if (tweet.contains("queen")) {
                System.out.println("Royal news: " + tweet);
            }
        });
        
        // 通知所有观察者
        String news = "The queen has a lot of money!";
        observers.forEach(observer -> observer.notify(news));
    }
}

4.3 模板方法模式与Lambda

// 传统模板方法
abstract class OnlineBanking {
    public void processCustomer(int id) {
        Customer c = Database.getCustomerWithId(id);
        makeCustomerHappy(c);
    }
    
    abstract void makeCustomerHappy(Customer c);
}

// Lambda方式
public class TemplateMethodPattern {
    public static void processCustomer(int id, Consumer<Customer> makeCustomerHappy) {
        Customer c = Database.getCustomerWithId(id);
        makeCustomerHappy.accept(c);
    }
    
    public static void main(String[] args) {
        // 使用Lambda提供不同实现
        processCustomer(1337, customer -> 
            System.out.println("Hello " + customer.getName()));
        
        processCustomer(1337, customer -> 
            customer.setBalance(customer.getBalance() + 100));
    }
}

五、最佳实践与注意事项

5.1 反射使用规范

  1. 最小化使用:仅在必要时使用反射

  2. 缓存结果:缓存Class对象和Method对象

  3. 安全检查:使用SecurityManager限制反射

  4. 异常处理:妥善处理反射相关异常

public class ReflectionBestPractice {
    // 缓存Class对象
    private static final Map<String, Class<?>> CLASS_CACHE = new HashMap<>();
    
    public static Class<?> getCachedClass(String className) {
        return CLASS_CACHE.computeIfAbsent(className, name -> {
            try {
                return Class.forName(name);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("类未找到: " + name, e);
            }
        });
    }
    
    // 安全检查
    public static void safeReflection() {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
        }
    }
}

5.2 枚举设计原则

  1. 单一职责:每个枚举类型只表示一种概念

  2. 不可变设计:枚举字段应为final

  3. 行为封装:将与枚举相关的行为封装在枚举内

  4. 模式匹配:优先使用switch而非if-else

// 良好的枚举设计
public enum Planet {
    MERCURY(3.303e+23, 2.4397e6),
    VENUS(4.869e+24, 6.0518e6),
    EARTH(5.976e+24, 6.37814e6);
    
    private final double mass;   // 质量(千克)
    private final double radius; // 半径(米)
    
    private Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    
    // 计算表面重力
    public double surfaceGravity() {
        return G * mass / (radius * radius);
    }
    
    // 计算物体重量
    public double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
    
    // 通用常量
    public static final double G = 6.67300E-11;
}

5.3 Lambda最佳实践

  1. 保持简短:Lambda表达式应简洁明了

  2. 方法引用:优先使用方法引用

  3. 避免副作用:纯函数式编程思想

  4. 类型推断:让编译器推导类型

public class LambdaBestPractice {
    // 1. 使用方法引用
    List<String> names = Arrays.asList("Alice", "Bob");
    names.forEach(System.out::println);  // 优于 names.forEach(s -> System.out.println(s))
    
    // 2. 保持简短
    // 差:过于复杂
    Function<String, String> badLambda = s -> {
        String trimmed = s.trim();
        String lower = trimmed.toLowerCase();
        return lower.substring(0, Math.min(lower.length(), 10));
    };
    
    // 好:提取为方法
    Function<String, String> goodLambda = this::processString;
    
    private String processString(String s) {
        String trimmed = s.trim();
        String lower = trimmed.toLowerCase();
        return lower.substring(0, Math.min(lower.length(), 10));
    }
    
    // 3. 组合函数
    public static void functionComposition() {
        Function<String, Integer> strToInt = Integer::parseInt;
        Function<Integer, Integer> square = x -> x * x;
        Function<String, Integer> squareOfInt = strToInt.andThen(square);
        
        System.out.println(squareOfInt.apply("5"));  // 25
    }
}

总结

特性 核心价值 适用场景 注意事项
反射 运行时类型信息获取 框架开发、动态代理 性能开销、安全问题
枚举 类型安全常量组织 状态机、错误码、配置 不可继承、内存占用
Lambda 函数式编程简化 集合处理、回调函数 调试困难、学习曲线

更多推荐