Java函数式编程+Lambda表达式

news2025/1/13 9:44:48

文章目录

  • 函数式编程介绍
  • 纯函数
  • Lambda表达式基础
    • Lambda的引入
      • 传统方法
        • 1. 顶层类
        • 2. 内部类
        • 3. 匿名类
      • Lambda
  • 函数式接口(Functional Interface)
        • 1. **函数式接口的定义**
          • 示例:
        • 2. **函数式接口与Lambda表达式的关系**
          • 关联逻辑:
          • 示例:
        • 3. **JDK内置的函数式接口**
          • 常见接口:
        • 4. **Lambda表达式使用函数式接口的典型例子**
  • **方法引用(Method Reference)详解**
    • **1. 方法引用的基本概念**
        • **方法引用的语法结构**:
    • **2. 方法引用的四种形式**
      • **2.1 引用静态方法**
      • **2.2 引用特定对象的实例方法**
      • **2.3 引用构造方法**
    • **3. 方法引用与函数式接口的关系**
    • **4. 方法引用的实际应用**
  • 常用的函数式接口
    • **1. Consumer接口(级联多个Consumer)**
        • **Consumer的基本用法**
        • **级联Consumer - andThen方法**
        • **更复杂的级联示例**
    • **2. Predicate接口与对象查找**
        • **Predicate的基本概念**
        • **对象查找中的应用**
    • **3. Function接口及相关接口**
        • **Function的基本概念**
        • **示例代码**
        • **相关接口**

函数式编程介绍

Java8新引入函数式编程方式,大大的提高了编码效率。
从面向对象编程视角来看,程序中使用的变量,其实只是一个值的容器,这个容器中,可以放置不同的值。
从函数式编程视角,变量其实只是值的一个“别名”,值本身是不能改的
采用函数式编程思想设计类,强调“类”所封装的数据,应该只被初始化一次,之后就不再更改。
JDK中的String类型,就是用“函数式编程”风格设计出来的一个例子。

Java 8引入了Lambda表达式特性,提供了Stream API,其内置的函数支持动态组合和级联调用,能够方便地实现“声明式”的编程方式。

函数式与面向对象编程的基本构造元素:
面向对象编程,编程的基本单元是“类”,函数必须放在类中,从属于“类”。
函数式编程,以“函数”作为编程的基本构造块,函数之间可以相互协作和动态组合。

函数式编程能很容易地实现“行为的参数化”
函数封装了“行为”,在函数式编程中,是“把函数作为值”来看待的。
既然“函数是值”,那么它就可以作为另一个函数的参数或返回值,这个就是“行为的参数化”。
Java使用Lambda表达式来代表那些需要反复传递的行为,将其作为函数参数或返回值,从而实现了“行为参数化”。
这样讲有点抽象ヽ(´¬`)ノ,下面进行详细介绍。
在这里插入图片描述

纯函数

函数式编程中的“函数”,强调要消除“副作用”。
所谓“副作用”,就是指:
(1)函数的执行,受到其“运行上下文”的影响,在不同的运行环境中执行,会得到不同的结果。
(2)函数执行之后,会修改外界的数据,从而对外界的状态有所影响。
没有“副作用”的函数,可以放心地让多个线程调用。
抛出异常的函数,不满足“函数式编程”的要求

“纯函数”——没有副作用的函数:
一个方法是不是“Pure Function(纯函数)”,关键就是它的运行,是不是有“副作用(Side Effect)”。纯函数是没有副作用的,只要输入参数值一定,它的结果总是一致的,从而可以安全地被跨线程调用而无需考虑线程同步问题。


public class SideEffectIllustration {

    // 没有副作用的方法
    public int f1(int x) {
        return x * 2;
    }

    private int state = 0;
    // 有副作用的方法
    public int f2(int x) {
        state++;
        return x * 2 + state;
    }

    public static void main(String[] args) {
        SideEffectIllustration obj = new SideEffectIllustration();
        //创建10个线程,每个线程都调用f1或f2方法,观察多线程环境下
        //Pure Function的输出与有副作用的方法的输出有何区别
        Thread[] theads = new Thread[10];

        for (int i = 0; i < theads.length; i++) {
            final int index = i;
            theads[i] = new Thread(() -> {
                // Note:切换以下两句的注释,观察输出的结果
                System.out.println(String.format("第%d次,结果为:%d", index + 1, obj.f1(5)));
                //System.out.println(String.format("第%d次,结果为:%d", index + 1, obj.f2(5)));
            });
            theads[i].start();
        }
    }


}

在实际开发中,推荐尽量编写“纯函数”。

Lambda表达式基础

Lambda的引入

我们把只定义有一个抽象方法的接口,称为“单一抽象方法(SAM:Single Abstract Method)”的接口,在开发中可以有三种方式实现它:

传统方法

1. 顶层类
interface MyInterface {
    void func();
}

class MyClass implements MyInterface {
    @Override
    public void func() {
        System.out.println("MyClass's func()");
    }
}

上述代码定义了一个MyInterface接口(它只定义了一个抽象方法,所以是SAM接口),接着,写了一个MyClass类实现这个接口,在这里,MyClass是一个顶层类。

然后,写了一个静态方法调用接口定义的方法:

    public static void doWithMyInterface(MyInterface obj) {
        obj.func();
    }

使用传统编程方式,上面的代码是这样被调用的:

        //传统方法,定义一个类实现接口,创建这个类的对象,
        //再把它传给doWithMyInterface()方法
        MyClass obj = new MyClass();
        doWithMyInterface(obj);
2. 内部类

除了使用顶层类,也可以使用内部类实现接口:
内部类的适用场景,主要是“仅在本类内部使用”,不需要被外界调用,并且代码比较简短。

        class MyInnerClass implements MyInterface{
            @Override
            public void func() {
                System.out.println("本地内部类,实现接口");
            }
        }
        //实例化本地内部类对象,传给示例方法
        doWithMyInterface(new MyInnerClass());
3. 匿名类

如果代码仅在特定方法内部调用,并且代码量也不大,可以直接使用匿名内部类实现接口:


        doWithMyInterface(new MyInterface() {
            @Override
            public void func() {
                System.out.println("使用匿名内部类,实现接口");
            }
        });

Lambda

前面的代码是传统的经典的Java面向对象编程代码, 在开发中使用接口,所有代码可以很明确地分为“第一步、第二步、第三步……”,很易于理解,也很规范,但每次都需要手工做这么多的事,似乎有点过于麻烦了。
为了便捷性考虑,Java设计者借鉴其他编程语言,把Lambda特性引入到了Java中。
就可以把上述的代码写成这样的形式。

        //定义一个Lambda表达式,将其引用保存到变量中,
        //再把它传给doWithMyInterface()方法
        //从而可以节省下新定义一个类的工作任务
        MyInterface lambdaObj = () -> {
            System.out.println("Explicit Define Lambda object's func()");
        };
        doWithMyInterface(lambdaObj);

可以更进一步地简化为:

        //直接把一个Lambda表达式作为doWithMyInterface()方法的参数
        //不仅不需要定义一个单独的类,甚至不再需要定义一个变量
        doWithMyInterface(() -> {
            System.out.println("inline lambda object's func()");
        });

** 直接执行Lambda表达式:**
要“执行”一个Lambda表达式所封装的代码,需要使用关联接口所定义的方法:

        MyInterface lambdaObj2 = () -> {
            System.out.println("另一个Lambda表达式");
        };
        //Lambda表达式,也可以直接执行
        lambdaObj2.func();

在函数式编程代码中,函数与其它数据类型一样,也可以进行“赋值”和“传送”,具体来说,就是可以定义“函数类型”的变量,函数可以成为另一个函数的“参数”,函数也可以返回“另一个函数”。
可以把Lambda表达式理解为一种简洁的可传递匿名函数:它没有名称,但有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常列表。

Lambda表达式格式:(如果方法体只有单行的话,可以把大括号去掉)

(参数列表) -> { 方法体 }

这是一些常见的Lambda使用例子:
在这里插入图片描述
再举一个完整的例子:


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class UseComparator {

    public static void main(String args[]) {

        List<String> strings = new ArrayList<String>();

        strings.add("CCC");
        strings.add("ddd");
        strings.add("EEE");
        strings.add("AAA");
        strings.add("bbb");
        //使用Lambda表达式重写上述代码段
        Comparator<String> comparator = (str1, str2) -> {
            return str1.compareToIgnoreCase(str2);
        };
        strings.sort(comparator);
        System.out.println("Sort with comparator");

        //输出排序结果
        for (String str : strings) {
            System.out.println(str);
        }
    }
}

我们可以用Lambda表达式来写comparator方法,并在sort中利用这个方法进行排序。

现在知道了如何编写Lambda表达式,但在哪里使用呢?可以通过函数式接口来使用Lambda表达式。
Java通过“函数式接口”+ Lambda表达式,实现函数式编程。

函数式接口(Functional Interface)

能接收一个Lambda表达式的变量,必须是接口类型,并且这种接口,还必须是一种“函数式接口(functional interface)”。

所谓“函数式接口”,就是“只定义有一个抽象方法的接口”,在Java 8之前,这种接口被称为“SAM:Single Abstract Method”接口。

Java 8中,使用“@FunctionalInterface”标识一个“函数式接口”。

函数式接口 是 Java 8 引入的一个概念,指 只有一个抽象方法 的接口。
它是 Lambda 表达式的基础,Lambda 表达式可以直接替代函数式接口的实现。


1. 函数式接口的定义

函数式接口在语法上与普通接口无异,但必须确保只有一个抽象方法。

示例:
@FunctionalInterface
public interface MyFunction {
    int apply(int x, int y); // 唯一的抽象方法
}

注意:

  • Java 8 提供了 @FunctionalInterface 注解,用于显式声明一个接口为函数式接口。
  • 如果有多个抽象方法,编译器会报错。

即使不加 @FunctionalInterface,接口只有一个抽象方法时,依然可以作为函数式接口使用。


2. 函数式接口与Lambda表达式的关系

Lambda表达式的本质 是一种简化语法,用来表示函数式接口的实例。
当一个 Lambda 表达式被传递时,JVM 自动将其映射为对应函数式接口的实现。

关联逻辑:
  1. 函数式接口提供了唯一的抽象方法
  2. Lambda 表达式的代码实现对应函数式接口的唯一抽象方法。
示例:
@FunctionalInterface
interface MyFunction {
    int apply(int x, int y);
}

// 使用Lambda表达式实现MyFunction接口
MyFunction add = (x, y) -> x + y;

System.out.println(add.apply(3, 5)); // 输出: 8

解释:

  • MyFunction 是一个函数式接口。
  • Lambda表达式 (x, y) -> x + y 实现了 apply 方法。

3. JDK内置的函数式接口

Java 提供了许多内置的函数式接口,位于 java.util.function 包中。这些接口可以直接配合 Lambda 表达式使用。

常见接口:
  1. Predicate<T>:接收一个参数,返回布尔值。

    Predicate<Integer> isEven = n -> n % 2 == 0;
    System.out.println(isEven.test(4)); // 输出: true
    
  2. Consumer<T>:接收一个参数,无返回值。

    Consumer<String> print = s -> System.out.println(s);
    print.accept("Hello, Lambda!"); // 输出: Hello, Lambda!
    
  3. Function<T, R>:接收一个参数,返回一个结果。

    Function<Integer, String> intToString = n -> "Number: " + n;
    System.out.println(intToString.apply(10)); // 输出: Number: 10
    
  4. Supplier<T>:无参数,返回一个结果。

    Supplier<Double> random = () -> Math.random();
    System.out.println(random.get()); // 输出: 随机数
    
  5. BiFunction<T, U, R>:接收两个参数,返回一个结果。

    BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
    System.out.println(multiply.apply(3, 4)); // 输出: 12
    

4. Lambda表达式使用函数式接口的典型例子
  1. 线程启动:使用 Runnable 接口

    // 传统写法
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Thread running...");
        }
    }).start();
    
    // 使用Lambda
    new Thread(() -> System.out.println("Thread running...")).start();
    
  2. 集合操作:Comparator接口

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    
    // 使用Lambda表达式简化排序
    names.sort((a, b) -> a.compareTo(b));
    System.out.println(names);
    
  3. 事件处理:ActionListener接口

    JButton button = new JButton("Click Me");
    button.addActionListener(e -> System.out.println("Button clicked!"));
    

在这里插入图片描述

方法引用(Method Reference)详解

方法引用 是 Java 8 引入的一种简化 Lambda 表达式的语法,允许开发者通过直接引用已有方法来实现函数式接口的抽象方法,从而使代码更简洁、更可读。
Lambda表达式可以进一步简化为方法引用,直接使用现有方法实现接口的抽象方法。

每个方法引用都存在一个等效的 lambda 表达式

1. 方法引用的基本概念

  • 方法引用是 Lambda 表达式的简化形式。
  • 它使用双冒号 :: 操作符来引用方法。
  • 适用于 Lambda 表达式仅调用一个已有方法的场景。
方法引用的语法结构
ClassName::methodName

例如:

// Lambda 表达式
Function<String, Integer> lambda = s -> Integer.parseInt(s);

// 方法引用
Function<String, Integer> methodRef = Integer::parseInt;

在这两个例子中,Integer::parseInts -> Integer.parseInt(s) 的简化形式。


2. 方法引用的四种形式

2.1 引用静态方法

适用于 Lambda 表达式调用某个类的静态方法。

语法:

ClassName::staticMethod

示例:

// Lambda 表达式
Function<String, Integer> lambda = s -> Integer.parseInt(s);

// 方法引用
Function<String, Integer> methodRef = Integer::parseInt;

System.out.println(methodRef.apply("123")); // 输出: 123

分析:

  • Lambda 表达式 s -> Integer.parseInt(s) 调用的是 Integer 类的静态方法 parseInt
  • 通过 Integer::parseInt 简化了代码。

2.2 引用特定对象的实例方法

适用于 Lambda 表达式调用特定对象的实例方法。

语法:

instance::instanceMethod

示例1:

// 特定对象
String str = "Hello";

// Lambda 表达式
Supplier<Integer> lambda = () -> str.length();

// 方法引用
Supplier<Integer> methodRef = str::length;

System.out.println(methodRef.get()); // 输出: 5

分析:

  • Lambda 表达式 () -> str.length() 调用的是 str 对象的 length 方法。
  • 通过 str::length 简化代码。

示例2:

// Lambda 表达式
BiFunction<String, String, Boolean> lambda = (s1, s2) -> s1.equals(s2);

// 方法引用
BiFunction<String, String, Boolean> methodRef = String::equals;

System.out.println(methodRef.apply("abc", "abc")); // 输出: true
System.out.println(methodRef.apply("abc", "def")); // 输出: false

分析:

  • Lambda 表达式 (s1, s2) -> s1.equals(s2) 调用的是 String 类实例的 equals 方法。
  • 通过 String::equals 进一步简化。

2.3 引用构造方法

适用于 Lambda 表达式用于创建对象的场景。

语法:

ClassName::new

示例:

// Lambda 表达式
Supplier<List<String>> lambda = () -> new ArrayList<>();

// 方法引用
Supplier<List<String>> methodRef = ArrayList::new;

List<String> list = methodRef.get();
System.out.println(list); // 输出: []

带参数的构造方法引用:

// Lambda 表达式
Function<String, Integer> lambda = s -> new Integer(s);

// 方法引用
Function<String, Integer> methodRef = Integer::new;

System.out.println(methodRef.apply("123")); // 输出: 123

3. 方法引用与函数式接口的关系

方法引用本质上是对函数式接口的实现。

  • 函数式接口 要求实现唯一的抽象方法。
  • 方法引用 的方法与该抽象方法的签名必须一致。

示例:

@FunctionalInterface
interface MyFunction {
    void print(String s);
}

// 使用 Lambda 表达式
MyFunction lambda = s -> System.out.println(s);

// 使用方法引用
MyFunction methodRef = System.out::println;

lambda.print("Hello, Lambda!");   // 输出: Hello, Lambda!
methodRef.print("Hello, Method Reference!"); // 输出: Hello, Method Reference!

4. 方法引用的实际应用

  1. 集合排序
List<String> names = Arrays.asList("Bob", "Alice", "Charlie");

// 使用 Lambda 表达式
names.sort((a, b) -> a.compareTo(b));

// 使用方法引用
names.sort(String::compareTo);

System.out.println(names); // 输出: [Alice, Bob, Charlie]
  1. 流处理(Stream API)
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 使用 Lambda 表达式
names.stream().map(name -> name.toUpperCase()).forEach(name -> System.out.println(name));

// 使用方法引用
names.stream().map(String::toUpperCase).forEach(System.out::println);

常用的函数式接口

1. Consumer接口(级联多个Consumer)

Consumer的基本用法

Consumer<T> 接口用于接收一个输入参数并对其执行某些操作,但不会返回结果。常见用法是打印日志、更新状态、操作集合元素等。

  • 核心方法:
    void accept(T t);
    
    • 接收一个参数 t,对其执行操作。
    • 没有返回值。
级联Consumer - andThen方法

Consumer 提供了默认方法 andThen,可以将多个 Consumer 串联起来,按照顺序依次执行每个 Consumer 的逻辑。

  • 方法定义:

    default Consumer<T> andThen(Consumer<? super T> after)
    
    • 参数 after: 另一个 Consumer,会在当前 Consumer 执行完之后被调用。
    • 返回一个新的 Consumer,依次执行两个 Consumer 的操作。
  • 示例代码:

    public static void main(String[] args) {
        Consumer<String> consumer1 = str -> System.out.println("Consumer 1: " + str);
        Consumer<String> consumer2 = str -> System.out.println("Consumer 2: " + str.toUpperCase());
    
        // 将两个Consumer级联
        Consumer<String> combinedConsumer = consumer1.andThen(consumer2);
    
        combinedConsumer.accept("hello");
    }
    
    • 输出:
      Consumer 1: hello
      Consumer 2: HELLO
      

更复杂的级联示例

可以串联多个 Consumer 来实现更复杂的操作。

public static void main(String[] args) {
    Consumer<String> consumer1 = str -> System.out.println("Step 1: " + str.trim());
    Consumer<String> consumer2 = str -> System.out.println("Step 2: " + str.toLowerCase());
    Consumer<String> consumer3 = str -> System.out.println("Step 3: " + str.toUpperCase());

    // 级联三个Consumer
    Consumer<String> combinedConsumer = consumer1.andThen(consumer2).andThen(consumer3);

    combinedConsumer.accept("  HeLLo WoRLd  ");
}
  • 输出:
    Step 1: HeLLo WoRLd
    Step 2: hello world
    Step 3: HELLO WORLD
    

2. Predicate接口与对象查找

Predicate的基本概念

Predicate<T> 接口主要用于定义一个“判断规则”,用于表示一个“布尔值”函数。它接收一个输入参数,并返回一个布尔值,用于条件判断。
在这里插入图片描述

  • 核心方法:

    boolean test(T t);
    
    • 参数 t: 输入值。
    • 返回值:布尔值,用于判断输入是否满足条件。
  • 默认方法:

    • and: 将多个 Predicate 串联,所有条件均为 true 时返回 true
    • or: 至少一个条件为 true 时返回 true
    • negate: 对当前 Predicate 结果取反。
    • isEqual: 检查对象是否相等。

对象查找中的应用

Predicate 通常用于过滤集合、查找符合条件的对象。

  • 示例代码(查找满足条件的对象):

    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob", "Charlie", "David");
    
        // 定义Predicate,查找长度大于3的名字
        Predicate<String> lengthPredicate = name -> name.length() > 3;
    
        // 查找符合条件的名字
        names.stream()
             .filter(lengthPredicate)
             .forEach(System.out::println);
    }
    
    • 输出:
      Alice
      Charlie
      David
      
  • 级联使用多个Predicate:

  • 在这里插入图片描述

    Predicate<String> startsWithA = name -> name.startsWith("A");
    Predicate<String> lengthPredicate = name -> name.length() > 3;
    
    // 组合条件:以A开头且长度大于3
    Predicate<String> combinedPredicate = startsWithA.and(lengthPredicate);
    
    names.stream()
         .filter(combinedPredicate)
         .forEach(System.out::println);
    
    • 输出:
      Alice
      

3. Function接口及相关接口

Function的基本概念

Function<T, R> 是 Java 8 中的函数式接口,此接口定义了一个apply方法,它接收一个T类型的对象,返回一个R类型的对象:
在这里插入图片描述

  • 核心方法:

    R apply(T t);
    
    • 参数 t: 输入值。
    • 返回值:类型为 R 的结果。
  • 默认方法:

    • andThen: 先执行当前 Function,再将结果传给另一个 Function
    • compose: 先执行参数指定的 Function,再将结果传递给当前 Function

示例代码
  • 基本用法:

    public static void main(String[] args) {
        Function<String, Integer> stringLength = str -> str.length();
    
        System.out.println(stringLength.apply("Hello")); // 输出 5
    }
    
  • 使用 andThencompose:

    public static void main(String[] args) {
        Function<String, Integer> stringLength = str -> str.length();
        Function<Integer, Integer> square = num -> num * num;
    
        // andThen: 先计算长度,再平方
        System.out.println(stringLength.andThen(square).apply("Hello")); // 输出 25
    
        // compose: 先平方长度,再计算平方
        System.out.println(square.compose(stringLength).apply("Hello")); // 输出 25
    }
    

相关接口
  1. BiFunction<T, U, R>
    作为Function接口的特例,有一个BiFunction<T, U, R>接口,它所定义的apply方法接收两个参数:T和U类型的,然后返回一个R类型的对象。
    在这里插入图片描述

    • 表示一个接收两个参数的函数,返回一个结果。

    • 核心方法:

      R apply(T t, U u);
      
    • 示例:

      BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
      System.out.println(add.apply(5, 10)); // 输出 15
      
  2. UnaryOperator

    • Function<T, T> 的子接口,表示输入和输出类型相同的函数。其实就是Function<T,T>的简写。
      在这里插入图片描述

    • 示例:

      UnaryOperator<Integer> square = x -> x * x;
      System.out.println(square.apply(5)); // 输出 25
      
  3. BinaryOperator

    • BiFunction<T, T, T> 的子接口,等价于BiFunction<T,T,T>,表示两个相同类型参数的函数,并返回相同类型的结果。
      在这里插入图片描述

    • 示例:

      BinaryOperator<Integer> multiply = (a, b) -> a * b;
      System.out.println(multiply.apply(2, 3)); // 输出 6
      
  4. IntToDoubleFunction

    • 表示接收一个 int 类型参数并返回一个 double 类型结果的函数。
    • 示例:
      IntToDoubleFunction half = x -> x / 2.0;
      System.out.println(half.applyAsDouble(10)); // 输出 5.0
      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2249683.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DI依赖注入详解

DI依赖注入 声明了一个成员变量&#xff08;对象&#xff09;之后&#xff0c;在该对象上面加上注解AutoWired注解&#xff0c;那么在程序运行时&#xff0c;该对象自动在IOC容器中寻找对应的bean对象&#xff0c;并且将其赋值给成员变量&#xff0c;完成依赖注入。 AutoWire…

自动化运维(k8s)之微服务信息自动抓取:namespaceName、deploymentName等全解析

前言&#xff1a;公司云原生k8s二开工程师发了一串通用性命令用来查询以下数值&#xff0c;我想着能不能将这命令写成一个自动化脚本。 起初设计的 版本一&#xff1a;开头加一条环境变量&#xff0c;执行脚本后&#xff0c;提示输入&#xff1a;需要查询的命名空间&#xff0c…

[Python/网络安全] Git漏洞之Githack工具基本安装及使用详析

前言 本文仅分享Githack工具基本安装及使用相关知识&#xff0c;不承担任何法律责任。 Git是一个非常流行的开源分布式版本控制系统&#xff0c;它被广泛用于协同开发和代码管理。许多网站和应用程序都使用Git作为其代码管理系统&#xff0c;并将其部署到生产环境中以维护其代…

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统 国内某水库安全监测项目需要监测点分散&#xff0c;且无外接供电。项目年限为4年&#xff0c;不允许使用太阳能电板。因此&#xff0c;我们需要设备具备低功耗且内置电池的功能。为了满足客户的要求&#xff0c;…

蓝桥杯c++算法秒杀【6】之动态规划【上】(数字三角形、砝码称重(背包问题)、括号序列、组合数问题:::非常典型的必刷例题!!!)

下将以括号序列、组合数问题超级吧难的题为例子讲解动态规划 别忘了请点个赞收藏关注支持一下博主喵&#xff01;&#xff01;&#xff01;! ! ! ! &#xff01; 关注博主&#xff0c;更多蓝桥杯nice题目静待更新:) 动态规划 一、数字三角形 【问题描述】 上图给出了…

AD软件如何快速切换三维视图,由2D切换至3D,以及如何恢复

在Altium Designer软件中&#xff0c;切换三维视图以及恢复二维视图的操作相对简单。以下是具体的步骤&#xff1a; 切换三维视图 在PCB设计界面中&#xff0c;2D切换3D&#xff0c;快捷键按住数字键盘中的“3”即可切换&#xff1b; 快捷键ctrlf&#xff08;或者vb快捷键也…

学习threejs,使用CubeCamera相机创建反光效果

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️CubeCamera 立方体相机 二、…

长时间无事可做是个危险信号

小马加入的是技术开发部&#xff0c;专注于Java开发。团队里有一位姓隋的女同事&#xff0c;是唯一的web前端工程师&#xff0c;负责页面开发工作&#xff0c;比小马早两个月入职。公司的项目多以定制化OA系统为主&#xff0c;后端任务繁重&#xff0c;前端工作相对较少。在这样…

Llama模型分布式训练(微调)

1 常见大模型 1.1 参数量对照表 模型参数量发布时间训练的显存需求VGG-19143.68M2014~5 GB&#xff08;单 224x224 图像&#xff0c;batch_size32&#xff09;ResNet-15260.19M2015~7 GB&#xff08;单 224x224 图像&#xff0c;batch_size32&#xff09;GPT-2 117M117M2019~…

Linux 子进程 -- fork函数

子进程 什么是子进程? 子进程指的是由一个已经存在的进程&#xff08;称为父进程或父进程&#xff09;创建的进程. 如: OS (操作系统) 就可以当作是一个进程, 用来管理软硬件资源, 当我点击浏览器, 想让浏览器运行起来时, 实际上是由 OS 接收指令, 然后 OS 帮我们将浏览器运行…

DataLoade类与list ,iterator ,yield的用法

1 问题 探索DataLoader的属性&#xff0c;方法 Vscode中图标含意 list 与 iterator 的区别&#xff0c;尤其yield的用法 2 方法 知乎搜索DataLoader的属性&#xff0c;方法 pytorch基础的dataloader类是 from torch.utils.data.dataloader import Dataloader 其主要的参数如下&…

C++入门——“C++11-lambda”

引入 C11支持lambda表达式&#xff0c;lambda是一个匿名函数对象&#xff0c;它允许在函数体中直接定义。 一、初识lambda lambda的结构是&#xff1a;[ ] () -> 返回值类型 { }。从左到右依次是&#xff1a;捕捉列表 函数参数 -> 返回值类型 函数体。 以下是一段用lam…

【Linux网络编程】第二弹---Socket编程入门指南:从IP、端口号到传输层协议及编程接口全解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、Socket 编程预备 1.1、理解源 IP 和目的 IP 1.2、认识端口号 1.2.1、端口号范围划分 1.2.2、理解 &q…

《用Python实现3D动态旋转爱心模型》

简介 如果二维的爱心图案已经无法满足你的创意&#xff0c;那今天的内容一定适合你&#xff01;通过Python和matplotlib库&#xff0c;我们可以实现一个动态旋转的3D爱心模型&#xff0c;充满立体感和动感。# 实现代码&#xff08;完整代码底部名片私信&#xff09; 以下是完…

shell-函数调用进阶即重定向

shell-函数调用进阶 声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷…

【高等数学学习记录】微分中值定理

一、知识点 &#xff08;一&#xff09;罗尔定理 费马引理 设函数 f ( x ) f(x) f(x) 在点 x 0 x_0 x0​ 的某邻域 U ( x 0 ) U(x_0) U(x0​) 内有定义&#xff0c;并且在 x 0 x_0 x0​ 处可导&#xff0c;如果对任意的 x ∈ U ( x 0 ) x\in U(x_0) x∈U(x0​) &#xff0…

【vue-router】vue-router如何实现动态路由

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Web前端技术浅谈CooKieAG网址漏洞与XSS攻防策略

随着互联网技术的飞速发展,Web前端开发已经成为构建网站和应用程序的重要环节。然而,Web前端开发中存在许多安全问题,这些问题不仅会影响用户体验,还可能给企业和个人带来严重的经济损失。但是web前端安全方面技术包含的东西较多&#xff0c;我们这里着重聊一聊关于XSS 的危害与…

关于VNC连接时自动断联的问题

在服务器端打开VNC Server的选项设置对话框&#xff0c;点左边的“Expert”&#xff08;专家&#xff09;&#xff0c;然后找到“IdleTimeout”&#xff0c;将数值设置为0&#xff0c;点OK关闭对话框。搞定。 注意,服务端有两个vnc服务,这俩都要设置ide timeout为0才行 附件是v…

51c自动驾驶~合集35

我自己的原文哦~ https://blog.51cto.com/whaosoft/12206500 #纯视觉方案的智驾在大雾天还能用吗&#xff1f; 碰上大雾天气&#xff0c;纯视觉方案是如何识别车辆和障碍物的呢&#xff1f; 如果真的是纯纯的&#xff0c;特头铁的那种纯视觉方案的话。 可以简单粗暴的理解为…