首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >反射,枚举&lambda表达式

反射,枚举&lambda表达式

作者头像
Han.miracle
发布2025-12-22 15:00:30
发布2025-12-22 15:00:30
80
举报

1. 反射

1.1 定义

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

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

1.2反射相关的类(重要)

1.3 Class类(反射机制的起源)

Class帮助文档代表类的实体,在**运行**的Java应用程序中表示类和接口。 Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件,被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是java.lang.Class。这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。

1.2Class类中的相关方法(方法的使用方法在后边的示例当中)
• (重要)常用的获得类的相关方法

getClassLoaded() 有三个类的加速器,这个是看是哪个加速去

(重要)常用获得类中属性相关的方法(以下方法返回值为[Field]相关)
• (了解)获得类中注解相关的方法
• (重要)获得类中构造器相关的方法(以下方法返回值为Constructor相关)
• (重要)获得类中方法相关的方法(以下方法返回值为Method相关)

1.3.2 反射示例

1.3.2.1 获得Class对象的三种方式

在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的Class对象,然后通过Class对象的核心方法,达到反射的目的,即:在运行状态中,对于任意⼀个类,都能够知道这个类的所有属性和方法;对于任意⼀个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息。 第⼀种,使用 Class.forName("类的全路径名");静态方法。 前提:已明确类的全路径名。 第二种,使用.class方法。 说明:仅适合在编译前就已经明确要操作的Class 第三种,使用类对象的getClass()方法

和普通的类不一样,一个类只有一个class,和类是对应的

newInstance() 方法的作用是创建对应类的一个新实例(对象)

具体来说:

  • 它是 Class 类的一个方法,调用时会调用对应类的无参构造方法来创建对象。
第一种
第二种
  • String.class 表示获取 String 类的字节码对象(Class<String> 类型);

<?> ... 可变参数

把这个保险打开 setAccessible()

代码语言:javascript
复制
class Student {
    // 私有属性
    private String name = "bit";
    // 公有属性
    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, int)");
    }

    // 私有方法
    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 +
                '}';
    }
}

public class TestDemo {
    public static void main(String[] args) {
        /*
         1. 通过 getClass() 方法获取 Class 对象
         */
        Student s1 = new Student();
        Class c1 = s1.getClass();

        /*
         2. 直接通过 类名.class 方式获取
            该方法安全可靠,程序性能更高,任何类都有隐含的静态成员变量 class
         */
        Class c2 = Student.class;

        /*
         3. 通过 Class 类的 forName() 静态方法获取(最常用)
            需传入类的全路径(含包名,此处无包则直接写类名)
            可能抛出 ClassNotFoundException 异常
         */
        Class c3 = null;
        try {
            c3 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 验证:一个类在 JVM 中只有一个 Class 实例,三者相等
        System.out.println(c1.equals(c2)); // true
        System.out.println(c1.equals(c3)); // true
        System.out.println(c2.equals(c3)); // true
    }
}
第三种

三种方法都在下面的使用中了

1.3.2.2反射的使用

接下来我们开始使用反射,我们依旧反射上面的Student类,把反射的逻辑写到另外的类当中进行理解

注意:所有和反射相关的包都在 import java.lang.reflect 包下面。

代码语言:javascript
复制
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java java.lang.reflect.Method;

public class ReflectClassDemo {
    // 反射创建对象(使用无参构造方法)
    public static void reflectNewInstance() {
        try {
            // 获取Student类的Class对象
            Class<?> classStudent = Class.forName("Student");
            // 通过无参构造方法创建实例(需类有public无参构造)
            Object objectStudent = classStudent.newInstance();
            Student student = (Student) objectStudent;
            System.out.println("获得学生对象:" + student);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    // 反射访问私有构造方法
    public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("Student");
            // 获取私有构造方法(参数为String和int类型)
            Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class, int.class);
            // 突破访问权限(私有成员需设置为可访问)
            declaredConstructorStudent.setAccessible(true);
            // 通过私有构造方法创建实例并传参
            Object objectStudent = declaredConstructorStudent.newInstance("高博", 15);
            Student student = (Student) objectStudent;
            System.out.println("获得私有构造方法创建的对象(修改姓名和年龄):" + student);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    // 反射访问并修改私有属性
    public static void reflectPrivateField() {
        try {
            Class<?> classStudent = Class.forName("Student");
            // 获取私有属性name
            Field field = classStudent.getDeclaredField("name");
            // 突破访问权限
            field.setAccessible(true);
            // 创建实例(使用无参构造)
            Object objectStudent = classStudent.newInstance();
            Student student = (Student) objectStudent;
            // 修改私有属性值
            field.set(student, "小明");
            // 获取修改后的属性值
            String name = (String) field.get(student);
            System.out.println("反射私有属性修改了name:" + name);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    // 反射访问私有方法
    public static void reflectPrivateMethod() {
        try {
            Class<?> classStudent = Class.forName("Student");
            // 获取私有方法function(参数为String类型)
            Method methodStudent = classStudent.getDeclaredMethod("function", String.class);
            System.out.println("私有方法的方法名为:" + methodStudent.getName());
            // 突破访问权限
            methodStudent.setAccessible(true);
            // 创建实例
            Object objectStudent = classStudent.newInstance();
            Student student = (Student) objectStudent;
            // 调用私有方法并传参
            methodStudent.invoke(student, "我是给私有的function函数传的参数");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 可根据需要取消注释测试不同反射功能
        // reflectNewInstance();       // 测试反射创建对象
        // reflectPrivateConstructor(); // 测试反射私有构造方法
        // reflectPrivateField();      // 测试反射私有属性
        reflectPrivateMethod();     // 测试反射私有方法
    }
}

1.4 反射的优点和缺点

一、优点
  1. 可全面获取类与对象信息:对于任意一个类,能知晓其所有属性和方法;对于任意一个对象,能调用其任意一个方法。
  2. 提升程序灵活性与扩展性:增加程序的灵活性和扩展性,降低代码间的耦合性,提高程序的自适应能力。
  3. 广泛应用于主流框架:反射技术已在 Struts、Hibernate、Spring 等众多流行框架中得到应用。
二、缺点
  1. 存在效率问题:使用反射会导致程序效率降低,具体可参考文章:http://www.imooc.com/article/293679
  2. 增加维护难度:反射技术绕过了源代码层面的常规流程,可能带来维护问题,且反射代码通常比对应的直接代码更复杂。

1.5 重点总结

  1. 明确反射的意义,理解其在动态操作类与对象中的核心作用。
  2. 掌握反射的关键类:包括 Class 类、Field 类、Method 类、Constructor 类。
  3. 合理且安全地使用反射:学会根据实际需求合理利用反射,且必须在安全环境下使用该技术。

2. 枚举的使用

2.1 背景及定义

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

代码语言:javascript
复制
 public static  final int RED = 1;
 public static  final int GREEN = 2;
 public static  final int BLACK = 3;

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

代码语言:javascript
复制
 public enum TestEnum {
    RED,BLACK,GREEN;
 }

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

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

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

2.2使用

1. switch语句
代码语言:javascript
复制
public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        TestEnum testEnum2 = TestEnum.BLACK;
        switch (testEnum2) {
            case RED:
                System.out.println("red");
                break;
            case BLACK:
                System.out.println("black");
                break;
            case WHITE:
                System.out.println("WHITE");
                break;
            case GREEN:
                System.out.println("black");
                break;
            default:
                break;
        }
    }
 }

2.常用方法

Enum 类的常用方法

示例一:

代码语言:javascript
复制
 public enum TestEnum {
 RED,BLACK,GREEN,WHITE;
 public static void main(String[] args) {
 TestEnum[] testEnum2 =  TestEnum.values();
 for (int i = 0; i < testEnum2.length; i++) {
 System.out.println(testEnum2[i] + " " + testEnum2[i].ordinal());
 }
 System.out.println("=========================");
 System.out.println(TestEnum.valueOf("GREEN"));
 }
 }

示例二

代码语言:javascript
复制
public enum TestEnum {
 RED,BLACK,GREEN,WHITE;
 public static void main(String[] args) {
 //拿到枚举实例BLACK 
TestEnum testEnum = TestEnum.BLACK;
 //拿到枚举实例RED 
}
 }
 TestEnum testEnum21 = TestEnum.RED;
 System.out.println(testEnum.compareTo(testEnum21));
 System.out.println(BLACK.compareTo(RED));
 System.out.println(RED.compareTo(BLACK))

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

重要:枚举的构造方法默认是私有的
代码语言:javascript
复制
 public enum TestEnum {
 RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
 private  String name;
 private  int key;
 /**
 * 1、当枚举对象有参数后,需要提供相应的构造函数
 * 2、枚举的构造函数默认是私有的这个⼀定要记住
 
* @param name
 * @param key
 */
 private TestEnum (String name,int key) {
 this.name = name;
 this.key = key;
 }
 public static TestEnum getEnumKey (int key) {
 for (TestEnum t: TestEnum.values()) {
 if(t.key == key) {
 return t;
 }
 }
 return null;
 }
 public static void main(String[] args) {
 System.out.println(getEnumKey(2));
 }
 }

2.3 枚举优点缺点

优点:

1. 枚举常量更简单安全。

2. 枚举具有内置方法,代码更优雅

缺点:

1. 不可继承,无法扩展

1. 方法的生成机制

Java 编译器在处理枚举类(如TestEnum)时,会自动为其隐式生成values()方法。这个方法并非从父类Enum继承而来,而是编译器针对枚举类型的特殊 “语法糖” 实现。

values()方法的核心作用是返回当前枚举类中所有枚举常量的数组。例如在代码中,TestEnum.values()会返回一个包含TestEnum所有枚举实例的TestEnum[]数组,从而支持遍历、统计等操作。

2.4 枚举和反射

2.4.1 枚举是否可以通过反射,拿到实例对象呢?

我们刚刚在反射里面看到了,任何⼀个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?接下来,我们来实验⼀下: 同样利用上述提供的枚举类来进行举例:

代码语言:javascript
复制
public enum TestEnum {
    RED("red", 1), BLACK("black", 2), WHITE("white", 3), GREEN("green", 4);
    private String name;
    private int key;

    /**
     * 1、当枚举对象有参数后,需要提供相应的构造函数
     * 2、枚举的构造函数默认是私有的 这个一定要记住
     * @param name
     * @param key
     */
    private TestEnum(String name, int key) {
        this.name = name;
        this.key = key;
    }

    public static TestEnum getEnumKey(int key) {
        for (TestEnum t : TestEnum.values()) {
            if (t.key == key) {
                return t;
            }
        }
        return null;
    }

    public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("TestEnum");
            //注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
            Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class, int.class);
            //设置为true后可修改访问权限
            declaredConstructorStudent.setAccessible(true);
            Object objectStudent = declaredConstructorStudent.newInstance("绿色", 666);
            TestEnum testEnum = (TestEnum) objectStudent;
            System.out.println("获得枚举的私有构造函数: " + testEnum);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        reflectPrivateConstructor();
    }
}

输出结果:

代码语言:javascript
复制
java.lang.NoSuchMethodException: TestEnum.<init>(java.lang.String, int)
 at java.lang.Class.getConstructor0(Class.java:3082)
 at java.lang.Class.getDeclaredConstructor(Class.java:2178)
 at TestEnum.reflectPrivateConstructor(TestEnum.java:40)
 at TestEnum.main(TestEnum.java:54)

我们提供的枚举的构造方法就是两个参数分别是 String 和 int ,这里的异常信息是 java.lang.NoSuchMethodException: TestEnum.(java.lang.String, int) 枚举比较特殊,虽然我们写的是两个,但是默认他还添加了两个参数,哪两个参数呢?我们看⼀ 下Enum类的源码

代码语言:javascript
复制
 protected Enum(String name, int ordinal) {
 this.name = name;
 this.ordinal = ordinal;
 }

也就是说,我们自己的构造函数有两个参数⼀个是String⼀个是int,同时他默认还会给两个参数,⼀ 个是String⼀个是int。也就是说,这里我们正确给的是4个参数:

代码语言:javascript
复制
public static void reflectPrivateConstructor() {
 try {
 Class<?> classStudent = Class.forName("TestEnum");

//注意传⼊对应的参数,获得对应的构造⽅法来构造对象,当前枚举类是提供了两个参数分别String和int。
 
Constructor<?> declaredConstructorStudent = 
classStudent.getDeclaredConstructor(String.class,int.class,String.class,int.cla
 ss);
 //设置为true后可修改访问权限
 
declaredConstructorStudent.setAccessible(true);
//后两个为⼦类参数,⼤家可以将当前枚举类的key类型改为double验证
 
Object objectStudent = declaredConstructorStudent.newInstance("⽗类参数",666,"⼦类参数",888);
 TestEnum testEnum = (TestEnum) objectStudent;
 System.out.println("获得枚举的私有构造函数:"+testEnum);
 } catch (Exception ex) {
 ex.printStackTrace();
 }
 }

此时运行程序结果是:

代码语言:javascript
复制
 java.lang.IllegalArgumentException: Cannot reflectively create enum objects
 at java.lang.reflect.Constructor.newInstance(Constructor.java:416)
 at TestEnum.reflectPrivateConstructor(TestEnum.java:46)
 at TestEnum.main(TestEnum.java:55)

这次就是我想要的结果!此时的异常信息显示,是我的⼀个方法这个方法是 newInstance() 报错问题就是这里,我们来看⼀下这个方法的源码,为什么会抛出 java.lang.IllegalArgumentException: 异常呢? 源码显示 newInstance() 报错

是的,枚举在这里被过滤了,你不能通过反射获取枚举类的实例!这道题是2017年阿里巴巴曾经问到 的⼀个问题,不看不知道,⼀看吓⼀跳!同学们记住这个坑。原版问题是:为什么枚举实现单例模式 是安全的?希望同学们记住这个问题!

1. 枚举单例的安全性
  • JVM 保障:枚举类的实例是由 JVM 在类加载阶段(初始化阶段)创建的,这个过程是线程安全的,天然避免了多线程下的实例创建冲突。
  • 防止反射破坏:Java 反射机制在尝试通过Constructor.newInstance()创建枚举实例时,会触发IllegalArgumentException(因为枚举的构造方法被编译器特殊处理,反射创建会被明确禁止)。
  • 防止序列化破坏:枚举类的序列化由 JVM 特殊处理,反序列化时不会创建新实例,而是直接返回已存在的枚举常量。
2. 反射无法创建枚举实例的原理

Java 的Enum类的构造方法被设计为只能由 JVM 调用,且反射框架在检测到要创建枚举实例时,会主动抛出异常。例如,当你通过反射获取枚举的构造方法并尝试调用newInstance()时,会触发IllegalArgumentException,明确禁止这种操作。

如果你是枚举

2.5 总结

1、枚举本身就是⼀个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum
2、枚举可以避免反射和序列化问题
3、枚举的优点和缺点

2.6 面试问题

1. 写一个单例模式。
代码语言:javascript
复制
public class Singleton {
 private volatile static Singleton uniqueInstance;
 private Singleton() {}
 public static Singleton getInstance() {
 if (uniqueInstance == null) {
 synchronized (Singleton.class){

                if(uniqueInstance == null){//进⼊区域后,再检查⼀次,如果仍是null,才创建实例
 
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
 }
2.用静态内部类实现⼀个单例模式
代码语言:javascript
复制
class Singleton {
    /** 
私有化构造器
 */ 
    private Singleton() {
    }
    /** 
对外提供公共的访问⽅法
 */ 
    public static Singleton getInstance() {
        return UserSingletonHolder.INSTANCE;
    }
    /** 
写⼀个静态内部类,⾥⾯实例化外部类
 */ 
    private static class UserSingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
 }
 public class Main {
    public static void main(String[] args) {
        Singleton u1 = Singleton.getInstance();
        Singleton u2 = Singleton.getInstance();
        System.out.println("
两个实例是否相同:
"+ (u1==u2));
    }
 }
3.用枚举实现⼀个单例模式
代码语言:javascript
复制
public enum TestEnum {
    INSTANCE;
    public TestEnum getInstance(){
        return INSTANCE;
    }
    public static void main(String[] args) {
 TestEnum singleton1=TestEnum.INSTANCE;
 TestEnum singleton2=TestEnum.INSTANCE;
 System.out.println("两个实例是否相同:"+(singleton1==singleton2));
 }
 }

3.Lambda 表达式

3.1背景

Lambda表达式是JavaSE8中⼀个重要的新特性。lambda表达式允许你通过表达式来代替功能接 口。lambda表达式就和⽅法⼀样,它提供了⼀个正常的参数列表和⼀个使用这些参数的主体(body,可 以是⼀个表达式或⼀个代码块)。Lambda表达式(Lambdaexpression),基于数学中的λ演算得 名,也可称为闭包(Closure)。

3.1.1 Lambda表达式的语法

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

Lambda表达式由三部分组成:

1. paramaters:类似方法中的形参列表,这⾥的参数是函数式接口里的参数。这⾥的参数类型可以 明确的声明也可不声明⽽由JVM隐含的推断。另外当只有⼀个推断类型时可以省略掉圆括号。 2.-> :可理解为“被用于”的意思

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

3.1.2 函数式接口

要了解Lambda表达式,首先需要了解什么是函数式接口,函数式接口定义:

⼀个接口有且只有⼀个抽象方法。

注意:

1. 如果⼀个接口只有⼀个抽象方法,那么该接口就是⼀个函数式接口

2. 如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接 ⼝的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来 说,只要你保证你的接口中只有⼀个抽象方法,你可以不加这个注解。加上就会自动进行检测的。

代码语言:javascript
复制
 @FunctionalInterface
 interface NoParameterNoReturn {
 //注意:只能有⼀个⽅法
 
void test();
 }

但是这种方式也是可以的:

代码语言:javascript
复制
@FunctionalInterface
 interface NoParameterNoReturn {
 void test();
 default void test2() {
 }
 }
 System.out.println("JDK1.8新特性,default默认⽅法可以有具体的实现");

3.2 Lambda表达式的基本使用

首先,我们事先准备好好几个接口:

代码语言:javascript
复制
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
    void test(int a);
}

//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
    void test(int a, int b);
}

//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
    int test();
}

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int test(int a);
}

//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
    int test(int a, int b);
}

我们在上面提到过,Lambda可以理解为:Lambda就是匿名内部类的简化,实际上是创建了⼀个类, 实现了接口,重写了接口的方法。

没有使用lambda表达式的时候的调用方式:

代码语言:javascript
复制
 NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
 @Override
 public void test() {
 System.out.println("hello");
 }
 };
 noParameterNoReturn.test();

具体使用见以下示例代码:

代码语言:javascript
复制
public class TestDemo {
    public static void main(String[] args) {
        NoParameterNoReturn noParameterNoReturn = ()->{
            System.out.println("无参数无返回值");
        };
        noParameterNoReturn.test();

        OneParameterNoReturn oneParameterNoReturn = (int a)->{
            System.out.println("一个参数无返回值: " + a);
        };
        oneParameterNoReturn.test(10);

        MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->{
            System.out.println("多个参数无返回值: "+a+" "+b);
        };
        moreParameterNoReturn.test(20,30);

        NoParameterReturn noParameterReturn = ()->{
            System.out.println("有返回值无参数! ");
            return 40;
        };
        int ret = noParameterReturn.test();
        System.out.println(ret);

        OneParameterReturn oneParameterReturn = (int a)->{
            System.out.println("有返回值有一个参数! ");
            return a;
        };
        ret = oneParameterReturn.test(50);
        System.out.println(ret);

        MoreParameterReturn moreParameterReturn = (int a,int b)->{
            System.out.println("有返回值多个参数! ");
            return a+b;
        };
        ret = moreParameterReturn.test(60,70);
        System.out.println(ret);
    }
}
3.2.1语法精简

1.参数类型可以省略,如果需要省略,每个参数的类型都要省略。

2.参数的小括号里面只有⼀个参数,那么小括号可以省略

3.如果方法体当中只有⼀句代码,那么大括号可以省略

4.如果方法体中只有⼀条语句,且是return语句,那么大括号可以省略,且去掉return关键字。

示例代码:

代码语言:javascript
复制
public static void main(String[] args) {
    // 无返回值多个参数,省略参数类型
    MoreParameterNoReturn moreParameterNoReturn = (a, b) -> {
        System.out.println("无返回值多个参数,省略参数类型:" + a + " " + b);
    };
    moreParameterNoReturn.test(20, 30);

    // 无返回值一个参数,小括号可省略
    OneParameterNoReturn oneParameterNoReturn = a -> {
        System.out.println("无返回值一个参数,小括号可省略:" + a);
    };
    oneParameterNoReturn.test(10);

    // 无参数无返回值,方法体只有一行代码(省略大括号)
    NoParameterNoReturn noParameterNoReturn = () -> System.out.println("无参数无返回值,方法体中只有一行代码");
    noParameterNoReturn.test();

    // 方法体只有一条return语句,省略return和大括号
    NoParameterReturn noParameterReturn = () -> 40;
    int ret = noParameterReturn.test();
    System.out.println(ret);
}

3.3 变量捕获

Lambda表达式中存在变量捕获,了解了变量捕获之后,我们才能更好的理解Lambda表达式的左右域。Java当中的匿名类中,会存在变量捕获。

3.3.1 匿名内部类

匿名内部类就是没有名字的内部类。我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用就好,那么下面我们来,简单的看看匿名内部类的使用就好了。 具体想详细了解的同学戳这里:匿名内部类我们通过简单的代码来学习⼀下

代码语言:javascript
复制
 class Test {
 public void func(){
 System.out.println("func()");
 }
 }
 public class TestDemo {
 public static void main(String[] args) {
 new Test(){
 @Override
            public void func() {
                System.out.println("我是内部类,且重写了func这个⽅法!");
            }
        };
    }
 }

在上述代码当中的main函数当中,我们看到的就是⼀个匿名内部类的简单的使用

3.3.2匿名内部类的变量捕获
代码语言:javascript
复制
class Test {
    public void func(){
        System.out.println("func()");
    }
 }
 public class TestDemo {
    public static void main(String[] args) {
        int a = 100;
        new Test(){
            @Override
            public void func() {
                System.out.println("我是内部类,且重写了func这个⽅法!");               System.out.println("我是捕获到变量 a == "+a
                        +" 我是⼀个常量,或者是⼀个没有改变过值的变量!");
            }
        };
    }
 }

在上述代码当中的变量a就是,捕获的变量。这个变量要么是被final修饰,如果不是被final修饰的你 要保证在使用之前,没有修改。如下代码就是错误的代码。

代码语言:javascript
复制
public class TestDemo {
    public static void main(String[] args) {
        int a = 100;
        new Test() {
            @Override
            public void func() {
                a = 99;
                System.out.println("我是内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量 a = " + a 
                        + " 我是一个常量,或者是一个没有改变过值的变量!");
            }
        };
    }
}

该代码直接编译报错

3.3.3 Lambda的变量捕获

在Lambda当中也可以进⾏变量的捕获,具体我们看⼀下代码

代码语言:javascript
复制
@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

public static void main(String[] args) {
    int a = 10;
    NoParameterNoReturn noParameterNoReturn = ()->{
        // a = 99; error
        System.out.println("捕获变量: "+a);
    };
    noParameterNoReturn.test();
}

3.4 Lambda在集合当中的使用

为了能够让Lambda和Java的集合类集更好的⼀起使⽤,集合当中,也新增了部分接⼝,以便与 Lambda表达式对接。

以上方法的作用可自行查看我们发的帮助手册。我们这里会示例⼀些方法的使用。

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

3.4.1 Collection接口

for Each() 方法演示 该方法在接口 Iterable 当中,原型如下

代码语言:javascript
复制
 default void forEach(Consumer<? super T> action) {
 Objects.requireNonNull(action);
 for (T t : this) {
 action.accept(t);
 }
 }

该方法表示:对容器中的每个元素执行action指定的动作。

代码语言:javascript
复制
public static void main(String[] args) {
 ArrayList<String> list = new ArrayList<>();
 list.add("Hello");
 list.add("bit");
 list.add("hello");
 list.add("lambda");
 list.forEach(new Consumer<String>(){
 @Override
 public void accept(String str){
 //简单遍历集合中的元素。
  System.out.print(str+" ");
}
 });
 }

输出结果:Hello bit hello lambda

我们可以修改为如下代码:

代码语言:javascript
复制
 public static void main(String[] args) {
 ArrayList<String> list = new ArrayList<>();
 list.add("Hello");
 list.add("bit");
 list.add("hello");
 list.add("lambda");
 list.forEach(s -> {
 System.out.println(s);
 });
 }

输出结果:Hello bit hello lambda

3.4.2 List接口

sort()方式的演示

sort方法源码:该方法根据c指定的比较规则对容器元素进行排序。

代码语言:javascript
复制
public void sort(Comparator<? super E> c) {
 final int expectedModCount = modCount;
 Arrays.sort((E[]) elementData, 0, size, c);
 if (modCount != expectedModCount) {
 throw new ConcurrentModificationException();
 }
 modCount++;
 }

使用示例:

代码语言:javascript
复制
 public static void main(String[] args) {
 ArrayList<String> list = new ArrayList<>();
 list.add("Hello");
 list.add("bit");
 list.add("hello");
 list.add("lambda");
 list.sort(new Comparator<String>() {
 @Override
 public int compare(String str1, String str2){
 //注意这⾥⽐较⻓度
 
}
 return str1.length()-str2.length();
 }
 });
 System.out.println(list);

输出结果:bit,Hello,hello,lambda

修改为lambda表达式:

代码语言:javascript
复制
public static void main(String[] args) {
 ArrayList<String> list = new ArrayList<>();
 list.add("Hello");
 list.add("bit");
 list.add("hello");
 list.add("lambda");
 //调⽤带有2个参数的⽅法,且返回⻓度的差值
 
list.sort((str1,str2)-> str1.length()-str2.length());
 System.out.println(list);
 }

输出结果:bit,Hello,hello,lambda

3.4.3 Map接口

HashMap 的 forEach() 该方法原型如下:

代码语言:javascript
复制
 default void forEach(BiConsumer<? super K, ? super V> action) {
 Objects.requireNonNull(action);
 for (Map.Entry<K, V> entry : entrySet()) {
 K k;
 V v;
 try {
 k = entry.getKey();
 v = entry.getValue();
 } catch(IllegalStateException ise) {
 // this usually means the entry is no longer in the map.
 throw new ConcurrentModificationException(ise);
 }
 action.accept(k, v);
 }
 }

作⽤是对Map中的每个映射执执行action指定的操作。

代码示例:

代码语言:javascript
复制
public static void main(String[] args) {
 HashMap<Integer, String> map = new HashMap<>();
 map.put(1, "hello");
 map.put(2, "bit");
 map.put(3, "hello");
 map.put(4, "lambda");
 map.forEach(new BiConsumer<Integer, String>(){
 @Override
 public void accept(Integer k, String v){
 System.out.println(k + "=" + v);
 }
 });
 }

输出结果:

1=hello

2=bit

3=hello

4=lambda

使用lambda表达式后的代码:

代码语言:javascript
复制
 public static void main(String[] args) {
 HashMap<Integer, String> map = new HashMap<>();
 map.put(1, "hello");
 map.put(2, "bit");
 map.put(3, "hello");
 map.put(4, "lambda");
 map.forEach((k,v)->  System.out.println(k + "=" + v));
 }

输出结果:

1=hello

2=bit

3=hello

4=lambda

3.5 总结

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

优点

1. 代码简洁,开发迅速

2. 方便函数式编程

3. 非常容易进行并行计算

4. Java引入Lambda,改善了集合操作

缺点:

1. 代码可读性变差

2. 在非并行计算中,很多计算未必有传统的for性能要高

3. 不容易进行调试

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 反射
    • 1.1 定义
    • 1.2反射相关的类(重要)
  • 1.3 Class类(反射机制的起源)
    • 1.2Class类中的相关方法(方法的使用方法在后边的示例当中)
  • 1.3.2 反射示例
    • 1.3.2.1 获得Class对象的三种方式
    • 1.3.2.2反射的使用
  • 1.4 反射的优点和缺点
    • 一、优点
    • 二、缺点
  • 1.5 重点总结
  • 2. 枚举的使用
    • 2.1 背景及定义
    • 2.2使用
      • 1. switch语句
      • 重要:枚举的构造方法默认是私有的
    • 2.3 枚举优点缺点
      • 1. 方法的生成机制
    • 2.4 枚举和反射
      • 2.4.1 枚举是否可以通过反射,拿到实例对象呢?
      • 1. 枚举单例的安全性
      • 2. 反射无法创建枚举实例的原理
    • 2.5 总结
      • 1、枚举本身就是⼀个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum
      • 2、枚举可以避免反射和序列化问题
      • 3、枚举的优点和缺点
    • 2.6 面试问题
      • 1. 写一个单例模式。
      • 2.用静态内部类实现⼀个单例模式
      • 3.用枚举实现⼀个单例模式
  • 3.Lambda 表达式
    • 3.1背景
      • 3.1.1 Lambda表达式的语法
      • 3.1.2 函数式接口
    • 3.2 Lambda表达式的基本使用
      • 3.2.1语法精简
    • 3.3 变量捕获
      • 3.3.1 匿名内部类
      • 3.3.2匿名内部类的变量捕获
    • 3.3.3 Lambda的变量捕获
    • 3.4 Lambda在集合当中的使用
      • 3.4.1 Collection接口
      • 3.4.2 List接口
      • 3.4.3 Map接口
    • 3.5 总结
      • 优点
      • 缺点:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档