当前位置: 首页 > news >正文

Lambda表达式详解

文章目录

  • 1、Lambda表达式简介
  • 2、如何使用Lambda表达式
  • 3、在哪里使用Lambda表达式
    • 3.1 函数式接口
    • 3.2函数描述符
  • 4、四大核心函数式接口
    • 4.1 Predicate
    • 4.2 Consumer
    • 4.3 Function
    • 4.4 Supplier
  • 5、方法引用
    • 5.1 方法引用的使用情况
  • 6、构造器引用
  • 7、数组引用
  • 8、复合Lambda表达式的有用方法
  • 9、Lambda表达式的作用域

1、Lambda表达式简介

在开发中,利用行为参数化来传递代码有助于应对不断变化的需求。大白话就是将方法作为参数传来传递,这样可以让我们的代码变得更灵活。Lambda表达式就是让方法作为参数传递变得更简洁明了。 下面通过一个简单的示例来了解Lambda表达式。

一个简单的示例:
从一个果篮中去获取指定的水果
1、首先创建一个水果类

/*** 水果类*/
public class Fruits {/*** 名称 eg: 香蕉、苹果、橙子*/String name;/*** 重量 单位:克(g)*/Integer weight;public Integer getWeight() {return weight;}public String getName() {return name;}public Fruits(String name, Integer weight) {this.name = name;this.weight = weight;}@Overridepublic String toString() {return "Fruits{" +"name='" + name + '\'' +", weight=" + weight +'}';}
}

2、使用最基础的方式来获取”苹果“

// 通过if语句来判断是否为苹果
private static List<Fruits> getApples(List<Fruits> list) {List<Fruits> listApple = new ArrayList<>();for (Fruits fruits : list) {if ("苹果".equals(fruits.name)) {listApple.add(fruits);}}return listApple;
}

3、使用上面这种是最简单的,但是却是最不容易扩展的。
在软件工程中,一个众所周知的问题就是,不管你做什么,用户的需求肯定会变的。如果此时让我们来获取”香蕉“、获取”香蕉“和”苹果“,甚至要重量作为筛选条件呢?我们就需要写许多的代码,而且这些代码是高度相似的,这样的代码是打破DRY(Don’t Repeat Yourself,不要重复自己)的软件工程原则的。

	// 筛选苹果和香蕉private static List<Fruits> getApplesAndBanana(List<Fruits> list) {List<Fruits> listApple = new ArrayList<>();for (Fruits fruits : list) {if ("苹果".equals(fruits.name) || "香蕉".equals(fruits.name)) {listApple.add(fruits);}}return listApple;}// 筛选苹果、香蕉和重量超过150g的苹果private static List<Fruits> getApplesAndBananaAndWeight(List<Fruits> list) {List<Fruits> listApple = new ArrayList<>();for (Fruits fruits : list) {if ("苹果".equals(fruits.name) && fruits.weight > 150) {listApple.add(fruits);}if ("香蕉".equals(fruits.name)) {listApple.add(fruits);}}return listApple;}

4、写出上面这样的代码是令人有点失望的。
让我们后退一步来看看更高层次的抽象,我们可以定义一个接口来实现这个获取动作。如果有新的需求就实现这个接口来定义实现方法。
a、定义接口

public interface FruitsPredicate {// 过滤水果boolean filter(Fruits fruits);
}

b、选择不同水果的策略
不同策略
c、具体类实现

public class ApplesPredicate implements FruitsPredicate{@Overridepublic boolean filter(Fruits fruits) {return "苹果".equals(fruits.name) ? true : false;}
}
public class ApplesAndBananaPredicate implements FruitsPredicate{@Overridepublic boolean filter(Fruits fruits) {return "苹果".equals(fruits.name) || "香蕉".equals(fruits.name) ? true : false;}
}
public class ApplesAndBananaAndWeightPredicate implements FruitsPredicate{@Overridepublic boolean filter(Fruits fruits) {if ("苹果".equals(fruits.name) && fruits.weight > 150) {return true;}if ("香蕉".equals(fruits.name)) {return true;}return false;}
}

d、过滤方法

private static List<Fruits> filterFruits(List<Fruits> list, FruitsPredicate fruitsPredicate) {List<Fruits> listApple = new ArrayList<>();for (Fruits fruits : list) {if (fruitsPredicate.filter(fruits)) {listApple.add(fruits);}}return listApple;
}

5、这里值得小小地庆祝一下。
上面的代码比我们第一次尝试的时候灵活多了,读起来、用起来也更容易。filterFruits方法的行为取决于你通过FruitsPredicate对象传递的代码,我们已经实现了filterFruits方法的行为参数化了。
但是上面的方式我们不得不声明好几个实现FruitsPredicate接口的类,然后实例化好几个只会提到一次的FreitsPredicate对象。这是很啰嗦的,很费时间。所以我们想到了可以使用Java的匿名类来进一步改善代码。:

List<Fruits> oranges = filterFruits(list, new FruitsPredicate() {@Overridepublic boolean filter(Fruits fruits) {return "橙子".equals(fruits.name);}
});

6、使用Lambda表达式。
好的代码应该是一目了然的,即使匿名类处理在某种程度上改善了为一个接口声明好几个实体类的啰嗦问题,但它仍不能令人满意,需要书写很多重复的代码。所以Java8的语言设计者通过引入Lambda表达式来解决这个问题。

// 获取所有苹果
List<Fruits> apples = filterFruits(list, fruits -> "苹果".equals(fruits.name));// 获取所有苹果和香蕉
List<Fruits> applesAndBananas = filterFruits(list, fruits -> "苹果".equals(fruits.name) || "香蕉".equals(fruits.name));// 获取重量大于150g的苹果和所有香蕉
List<Fruits> applesAndBananasAndWeight = filterFruits(list, fruits -> {
if ("苹果".equals(fruits.name) && fruits.weight > 150) {return true;
}
if ("香蕉".equals(fruits.name)) {return true;
}return false;
});

使用Lambda表达式看起来是不是非常的灵活和简单,这就是Lambda表达式的优点。现在你还不需要学会使用Lambda表达式,只需要认识到Lambda的优点。后面会详细介绍如何使用。

2、如何使用Lambda表达式

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

Lambda主要又三部分组成:参数列表、箭头和Lambda主体。
Lambda表达式
其实Lambda就是匿名类的简化,我们可以将这个匿名类实现出来进行比较,发现它将匿名类的许多代码都进行了省略,只保存了最重要的部分

list.sort(new Comparator<Fruits>() {@Overridepublic int compare(Fruits f1, Fruits f2) {return f1.weight - f2.weight;}
});

下面对每个语法格式的特征进行举例说明:

(1)、语法格式一:无参,无返回值,Lambda主体只有一条语句。

Runnable runnable = () -> System.out.println("Hello World!");

(2)、语法格式二:有一个参数,无返回值,Lambda主体只有一条语句。

Consumer<String> consumer = (x) -> System.out.println(x);

只有一个参数时参数的小括号可以省略

Consumer<String> consumer = x -> System.out.println(x);

(3)、语法格式三:两个参数及以上,有返回值,Lambda主体只有一条语句。
Lambda没有return语句,因为已经隐含了return。

Comparator<Integer> com=(x, y)-> Integer.compare(x,y);

(4)、语法格式四:两个参数及以上,有返回值,Lambda主体有多条语句。

Comparator<Integer> com=(x, y)->{System.out.println("x的值为:"+x);System.out.println("y的值为:"+y);return x*y;
};

(5)、Lambda表达式的参数列表数据类型可以省略不写,因为JVM可以通过上下文推断出数据类型。

Comparator<Integer> com=(Integer x,Integer y)-> Integer.compare(x,y);
Comparator<Integer> com=(x,y)-> Integer.compare(x,y);

3、在哪里使用Lambda表达式

函数式接口上使用Lambda表达式,当方法的参数是一个函数式接口时就可以使用Lambda表达式。

3.1 函数式接口

函数式接口就是只定义一个抽象方法的接口。
例如我们写的FruitsPredicate接口:

@FunctionalInterface
public interface FruitsPredicate {/*** 过滤水果** @param fruits* @return*/boolean filter(Fruits fruits);
}

注意:

1、函数式接口可以使用 @FunctionalInterface 注解来声明。
这个注解只是用来表明这是一个函数式接口,并不是没有这个注解就不是函数式接口了。
有这个注解的话,如果它不是函数式接口的话,编译器将报错。

2、Java8在接口中可以增加默认方法了。哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

3.2函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作
函数描述符。例如,Runnable接口可以看作一个什么也不接受什么也不返回(void)的函数的
签名,因为它只有一个叫作run的抽象方法,这个方法什么也不接受,什么也不返回(void)。

() -> void  // 这个函数描述符代表了参数列表为空,且返回void的函数。Runnable就是这种。

其他常见的函数式接口和对应的函数描述符:

函数式接口函数描述符
Preicate<T>T -> boolean
Consumer<T>T -> void
Function<T,R>T -> R
Supplier<T>() -> T
UnaryOperator<T>T -> T
BinaryOperator<T>(T,T) -> T
BiPredicate<L,R>(L,R) -> boolean
BiConsumer<T,U>(T,U) -> void
BiFunction<T,U,R>(T,U) -> R

4、四大核心函数式接口

Java 8的库设计师在java.util.function包中引入了几个新的函数式接口,最核心的四个函数式接口分别为Predicate<T>、Consumer<T>、Function<T,R>和Supplier<T>。下面我们来一一介绍它们。

4.1 Predicate

源码:

public interface Predicate<T> {boolean test(T t);
}

java.util.function.Predicate<T>接口定义了一个test的抽象方法,他接受泛型T对象,并返回一个boolean值。是一个断定型接口
使用:

// 优化获取所有果篮中苹果的方法,使用Predicate接口
private static List<Fruits> filterApples(List<Fruits> list, Predicate<Fruits> predicate) {List<Fruits> listApple = new ArrayList<>();for (Fruits fruits : list) {if (predicate.test(fruits)) {listApple.add(fruits);}}return listApple;
}// 使用filterApples方法来获取结果
filterApples(list,s -> "苹果".equals(s.name) ? true : false);

4.2 Consumer

源码:

@FunctionalInterface
public interface Consumer<T> {void accept(T t);
}

java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T
的对象,没有返回(void)。是一个消费型接口
使用:

// 去消费果篮中的水果,【可根据不同的消费行为来消费】
private static void printFruits(List<Fruits> list, Consumer<Fruits> consumer) {for (Fruits fruits : list) {consumer.accept(fruits);}
}
// 输出果篮中全部水果名称和重量
printFruits(list,s -> System.out.println("水果名称:" + s.name + ";" + "水果重量:" + s.weight));// 输出果篮中苹果和重量
printFruits(list,s -> {if ("苹果".equals(s.name)) {System.out.println("水果名称:" + s.name + ";" + "水果重量:" + s.weight);}
});

4.3 Function

源码:

@FunctionalInterface
public interface Function<T, R> {R apply(T t);
}

java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个
泛型T的对象,并返回一个泛型R的对象。是一个函数型接口
使用:

/*** 此方法将果篮中的水果一一检查执行对应的操作* * 使用了Java泛型,进一步抽象方法。* 在Java 8中泛型的类型编译器可以自行推断出来*/
private static <T,R> List<R> getAllFruits(List<T> list, Function<T,R> function) {List<R> result = new ArrayList<>();for (T t : list) {result.add(function.apply(t));}return result;
}
// 将果篮中的所有水果贴上标签
List<FruitsLabel> allFruits = getAllFruits(list, t -> {FruitsLabel fruitsLabel = new FruitsLabel();fruitsLabel.name = t.name;fruitsLabel.weight = t.weight;fruitsLabel.label = "新鲜";return fruitsLabel;
});
System.out.println(allFruits);

4.4 Supplier

源码:

@FunctionalInterface
public interface Supplier<T> {T get();
}

java.util.function.Supplier<T>接口定义了一个叫作get的方法,它不需要接受参数,即可返回一个泛型T的对象。是一个供给型接口
使用:

private static <T> List<T> addFruits(List<T> list, Supplier<T> supplier) {list.add(supplier.get());return list;
}
// 向果篮中放一个橙子
addFruits(list,() -> new Fruits("橙子", 100));

其他的函数式接口我们就不在过多的介绍,如果大家后续有用到可以自行查阅。

5、方法引用

当要传递给Lambda体的操作,已经有实现的方法了,就可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用的参数列表一致,方法的返回值也必须一致,即方法的签名一致)。方法引用可以理解为方法引用是Lambda表达式的另一种表现形式。

方法引用的语法:使用操作符"::"将对象或类和方法名分隔开。

我们使用一个实例来进行演示:

// 根据水果的重量来排序
list.sort(Comparator.comparing(s -> s.getWeight()));

我们发现后面的Lambda表达式其实就是调用getWeight()方法来获取Fruits类的重量,这个方法已经存在Fruits类中了,所以我们可以直接使用这个方法,这就是方法引用。

list.sort(Comparator.comparing(Fruits::getWeight));

5.1 方法引用的使用情况

方法引用的使用情况共分为以下三种:

  • 对象::实例方法名
  • 类::静态方法名
  • 类::实例方法名

使用实例:
1、对象::实例方法名

  public void test(){PrintStream out = System.out;// 使用Lambda实现Consumer<String> consumer = s -> out.println(s);// 使用方法引用实现相同效果Consumer<String> consumer1 = out::println;// 测试consumer.accept("hello world");consumer1.accept("hello world");}

2、类::静态方法名

  public void test(){// 使用Lambda实现Comparator<Integer> comparable=(x,y)->Integer.compare(x,y);//使用方法引用实现相同效果Comparator<Integer> integerComparable=Integer::compare;// 测试System.out.println(integerComparable.compare(4,2));//结果:1System.out.println(comparable.compare(4,2));//结果:1}

3、类::实例方法名

  public void test(){// 使用Lambda实现BiPredicate<String,String> bp=(x,y)->x.equals(y);//使用方法引用实现相同效果BiPredicate<String,String> bp2=String::equals;// 测试System.out.println(bp.test("1","2"));//结果:falseSystem.out.println(bp2.test("1","2"));//结果:false}

注意:

对象::实例化方法类::实例化方法可能乍看起来有点儿晕。类似于String::equals的方法引
用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数

6、构造器引用

对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个对象。需要注意构造器参数列表要与接口中抽象方法的参数列表一致。
格式:类名::new

演示实例:

public class Employee {private Integer id;private String name;private Integer age;@Overridepublic String toString() {return "Employee{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}public Employee(){}public Employee(Integer id) {this.id = id;}public Employee(Integer id, Integer age) {this.id = id;this.age = age;}public Employee(int id, String name, int age) {this.id = id;this.name = name;this.age = age;}
}
public void test(){//引用无参构造器Supplier<Employee> supplier=Employee::new;System.out.println(supplier.get());//引用有参构造器Function<Integer,Employee> function=Employee::new;System.out.println(function.apply(21));BiFunction<Integer,Integer,Employee> biFunction=Employee::new;System.out.println(biFunction.apply(8,24));
}输出结果:Employee{id=null, name='null', age=null}Employee{id=21, name='null', age=null}Employee{id=8, name='null', age=24}

7、数组引用

数组引用格式:type[]::new

使用示例:

public void test02(){Function<Integer,String[]> function=String[]::new;String[] apply = function.apply(10);System.out.println(apply.length); //结果:10
}

8、复合Lambda表达式的有用方法

Java 8的好几个函数式接口都有为方便而设计的复合方法。例如Comparator、Function和Predicate都提供了允许你进行复合的方法。
这是什么意思呢?在实践中,这意味着你可以把多个简单的Lambda复合成复杂的表达式。比如,
你可以让两个谓词之间做一个or操作,组合成一个更大的谓词。而且,你还可以让一个函数的结
果成为另一个函数的输入。
你可能会想,函数式接口中怎么可能有更多的方法呢?(毕竟,这违背了函数式接口的定义啊!)窍门在于,Java 8中增加了默认方法。它们不是抽象方法。

比较器复核

之前我们使用了方法引用对果篮中的水果进行排序:

list.sort(Comparator.comparing(Fruits::getWeight));

这个顺序是从小到大排序的,那么如何让它从大到小排序呢?我们需要去创建另一个方法吗?这样岂不是麻烦了?
在Comparator的接口中,提供了一个默认方法reversed可以对比较器逆序,使用如下:

list.sort(Comparator.comparing(Fruits::getWeight).reversed());

如果两个水果重量相同,我们又想按照名称排序怎么办?
在Comparator接口中提供了一个方法thenComParing方法可以接受另一个比较器。使用如下:

list.sort(Comparator.comparing(Fruits::getWeight).thenComparing(Fruits::getName));

谓词复合

谓词接口包括三个方法:negate、and和or,已知的Predicate接口就实现了这样的方法,我们测试一下:
1、negate表示取相反的值。例如我们之前从果篮中选取苹果的逻辑,我们如果对Predicate接口加上negate方法则表示取不是苹果的其他水果。

   // 得到所有苹果Predicate<Fruits> getApples = s -> "苹果".equals(s.name) ? true : false;// 得到所有除苹果外其他水果Predicate<Fruits> getNotApples = getApples.negate();

2、and方法相当于&&

例如我们之前逻辑选取苹果且重量大于150g的逻辑,我们可以这样写代码

   // 得到所有重量大于150g的苹果getApples.and(s -> s.getWeight() > 150 ? true : false);

3、or方法相当于||

例如我们可以选取果篮中的苹果和香蕉

	// 得到香蕉和苹果getApples.or(s -> "香蕉".equals(s.name) ? true : false);

注意:
and和or方法在表达式中的优先级是从左向右确定优先级的,例如a.or(b).and(c)可以看作(a||b)&&c

函数复核

Function接口有andThen和compose两个默认方法,它们都会返回Function的一个实例。我们可以先定义两个Function,后面将会依赖Funtion来进行演示。

// f = x + 1
Function<Integer, Integer> f = x -> x + 1;
// f = x * 2
Function<Integer, Integer> g = x -> x * 2; 

1、andThen方法等价于 g(f(x))

// 相当于 (x+1)*2
Function<Integer, Integer> g = x -> x * 2; 
int result = h.apply(1); // 返回4

2、compose方法等价于 f(g(x))

// 相当于 (x*2)+1
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1); // 返回3

9、Lambda表达式的作用域

Lambda表达式可以看作是匿名内部类实例化的对象,Lambda表达式对变量的访问限制和匿名内部类一样,因此Lambda表达式可以访问局部变量、局部引用,静态变量,实例变量。

1、访问局部变量

在Lambda表达式中规定只能引用标记了final的外层局部变量。我们不能在lambda 内部修改定义在域外的局部变量,否则会编译错误。

public class TestFinalVariable {interface VarTestInterface{Integer change(String str);}public static void main(String[] args) {//局部变量不使用final修饰Integer tempInt = 1;VarTestInterface var = (str -> Integer.valueOf(str+tempInt));//再次修改,不符合隐式final定义tempInt =2;Integer str =var.change("111") ;System.out.println(str);}
}

编译会报错:
报错

2、访问局部引用、静态变量、实例变量

Lambda表达式不限制访问局部引用变量,静态变量,实例变量。代码测试都可正常执行,代码:

public class LambdaScopeTest {/*** 静态变量*/private static String staticVar;/*** 实例变量*/private static String instanceVar;@FunctionalInterfaceinterface VarChangeInterface{Integer change(String str);}/*** 测试引用变量*/private void  testReferenceVar(){ArrayList<String> list = new ArrayList<>();list.add("111");//访问外部引用局部引用变量VarChangeInterface varChangeInterface = ((str) -> Integer.valueOf(list.get(0)));//修改局部引用变量list.set(0,"222");Integer str =varChangeInterface.change("");System.out.println(str);}/*** 测试静态变量*/void testStaticVar(){staticVar="222";VarChangeInterface varChangeInterface = (str -> Integer.valueOf(str+staticVar));staticVar="333";Integer str =varChangeInterface.change("111") ;System.out.println(str);}/*** 测试实例变量*/void testInstanceVar(){instanceVar="222";VarChangeInterface varChangeInterface = (str -> Integer.valueOf(str+instanceVar));instanceVar="333";Integer str =varChangeInterface.change("111") ;System.out.println(str);}public static void main(String[] args) {new LambdaScopeTest().testReferenceVar();new LambdaScopeTest().testStaticVar();new LambdaScopeTest().testInstanceVar();}
}

注意:
Lambda表达式里不允许声明一个与局部变量同名的参数或者局部变量。

	//编程报错Integer tempInt = 1;VarTestInterface varTest01 = (tempInt -> Integer.valueOf(tempInt));VarTestInterface varTest02 = (str -> {Integer tempInt = 1;Integer.valueOf(str);});

3、Lambda表达式访问局部变量作限制的原因

Lambda表达式不能访问非final修饰的局部变量的原因是,局部变量是保存在栈帧中的。而在Java的线程模型中,栈帧中的局部变量是线程私有的,如果允许Lambda表达式访问到栈帧中的变量地址(可改变的局部变量),则会可能导致线程私有的数据被并发访问,造成线程不安全问题。

基于上述,对于引用类型的局部变量,因为Java是值传递,又因为引用类型的指向内容是保存在堆中,是线程共享的,因此Lambda表达式中可以修改引用类型的局部变量的内容,而不能修改该变量的引用。

对于基本数据类型的变量,在 Lambda表达式中只是获取到该变量的副本,且局部变量是线程私有的,因此无法知道其他线程对该变量的修改,如果该变量不做final修饰,会造成数据不同步的问题。

但是实例变量,静态变量不作限制,因为实例变量,静态变量是保存在堆中(Java8之后),而堆是线程共享的。在Lambda表达式内部是可以知道实例变量,静态变量的变化。

相关文章:

Lambda表达式详解

文章目录1、Lambda表达式简介2、如何使用Lambda表达式3、在哪里使用Lambda表达式3.1 函数式接口3.2函数描述符4、四大核心函数式接口4.1 Predicate4.2 Consumer4.3 Function4.4 Supplier5、方法引用5.1 方法引用的使用情况6、构造器引用7、数组引用8、复合Lambda表达式的有用方…...

网关的通用设计框架

概念 网关&#xff0c;很多地方将网关比如成门&#xff0c; 没什么问题&#xff0c; 但是需要区分网关与网桥的区别。 网桥:工作在数据链路层&#xff0c;在不同或相同类型的LAN之间存储并转发数据帧&#xff0c;必要时进行链路层上的协议转换。可连接两个或多个网络&#xf…...

API 接口应该如何设计?如何保证安全?如何签名?如何防重?

说明&#xff1a;在实际的业务中&#xff0c;难免会跟第三方系统进行数据的交互与传递&#xff0c;那么如何保证数据在传输过程中的安全呢&#xff08;防窃取&#xff09;&#xff1f;除了https的协议之外&#xff0c;能不能加上通用的一套算法以及规范来保证传输的安全性呢&am…...

LeetCode-131. 分割回文串

目录题目思路回溯题目来源 131. 分割回文串 题目思路 切割问题类似组合问题。 例如对于字符串abcdef&#xff1a; 组合问题&#xff1a;选取一个a之后&#xff0c;在bcdef中再去选取第二个&#xff0c;选取b之后在cdef中再选取第三个…。切割问题&#xff1a;切割一个a之后&…...

【C++】string类的基本使用

层楼终究误少年&#xff0c;自由早晚乱余生。你我山前没相见&#xff0c;山后别相逢… 文章目录一、编码&#xff08;ascll、unicode字符集、常用的utf-8编码规则、GBK&#xff09;1.详谈各种编码规则2.汉字在不同的编码规则中所占字节数二、string类的基本使用1.string类的本质…...

【第一章 - 绪论】- 数据结构(近八千字详解)

目录 一、 数据结构的研究内容 二、基本概念和术语 2.1 - 数据、数据元素、数据项和数据对象 2.2 - 数据结构 2.2.1 - 逻辑结构 2.2.2 - 存储结构 2.3 - 数据类型和抽象数据类型 三、抽象数据类型的表现与实现 四、算法和算法分析 4.1 - 算法的定义及特性 4.2 - 评价…...

QIfw制作软件安装程序

前言 Qt Installer Framework是Qt默认包的发布框架。它很方便,使用静态编译Qt制作而成。从Qt的下载地址中下载Qt Installer Framework,地址是:http://download.qt.io/official_releases/qt-installer-framework/ 。支持我们自定义一些我们需要的东西包括页面、交互等。 框…...

【C++】C++入门(上)

前言&#xff1a; C是在C语言的基础上不断添加东西形成的一门语言&#xff0c;在C语言的基础上引入了面向对象的思想。因此C既是面向对象的语言&#xff0c;也是面向过程的语言。因为C是以C语言为基础的&#xff0c;所以基本上C兼容所有的C语言。目前最常用的版本是C98和C11这两…...

5. Kimball维度建模常用术语及概念(一)

文章目录维度建模过程相关概念1. 收集业务需求与数据实现2. 协作维度建模研讨3. 四步骤维度设计过程4. 业务过程5. 粒度6. 描述环境的维度7. 用于度量的事实8. 维度模型事实表技术术语1. 事实表结构2. 可加、半可加、不可加事实3. 事实表中的空值4. 一致性事实5. 事务事实表6. …...

内核调试之Panic-Oops日志分析

这部分我们接着之前的思考&#xff0c;看看内核异常日志的分析。 1 Panic 调试 2 Oops调试 内核出现Panic或Oops错误&#xff0c;如何分析定位问题原因&#xff1f; 首先&#xff0c;保留现场&#xff0c;如下所示为一次非法虚拟地址访问错误。 EXT4-fs (sdc3): recovery c…...

论文解读 | [AAAI2020] 你所需要的是边界:走向任意形状的文本定位

目录 1、研究背景 2、研究的目的 3、方法论 3.1 Boundary Point Detection Network(BPDN) 3.2 Recognition Network 3.3 Loss Functions 4、实验及结果 论文连接&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/6896 1、研究背景 最近&#xff0c;旨在…...

数据挖掘流程简单示例10min

数据挖掘流程简单示例10min 套路&#xff1a; 准备数据实现算法测试算法 任务1&#xff1a;亲和性分析 如果一个顾客买了商品X&#xff0c;那么他们可能愿意买商品Y衡量方法&#xff1a; 支持度support : 所有买X的人数 置信度confidence : 所有买X和Y的人数所有买X的人数…...

KDJB1200六相继电保护测试仪

一、概述 KDJB1200继电保护测试仪是在参照电力部颁发的《微机型继电保护试验装置技术条件(讨论稿)》的基础上&#xff0c;广泛听取用户意见&#xff0c;总结目前国内同类产品优缺点&#xff0c;充分使用现代新的的微电子技术和器件实现的一种新型小型化微机继电保护测试仪。可…...

从WEB到PWA 开发-发布-安装

见意如题&#xff01;本文主要来说说PWA开发&#xff01;作为一个前端程序员&#xff0c;在没有任何Android/IOS的开发情况下&#xff0c;想想我们有多少种方法来开发一个原生移动应用程序&#xff01;我们可以有非原生、混合开发&#xff0c;PWA等等手段。类似uniapp&#xff…...

FPGA纯vhdl实现MIPI CSI2 RX视频解码输出,OV13850采集,提供工程源码和技术支持

目录1、前言2、Xilinx官方主推的MIPI解码方案3、纯Vhdl方案解码MIPI4、vivado工程介绍5、上板调试验证6、福利&#xff1a;工程代码的获取1、前言 FPGA图像采集领域目前协议最复杂、技术难度最高的应该就是MIPI协议了&#xff0c;MIPI解码难度之高&#xff0c;令无数英雄竞折腰…...

《NFL橄榄球》:卡罗来纳黑豹·橄榄1号位

卡罗来纳黑豹&#xff08;英语&#xff1a;Carolina Panthers&#xff09;是一支位于北卡罗来纳州夏洛特的职业美式橄榄球球队。他们是国家美式橄榄球联合会的南区其中一支球队。他们与杰克逊维尔美洲虎在1995年加入NFL&#xff0c;成为扩充球队。 2018年球队市值为23亿美元&am…...

我说我为什么抽不到SSR,原来是这段代码在作祟...

本文是龚国玮所写&#xff0c;熊哥有所新增修改删减&#xff0c;原文见文末。 我说我为什么抽不到SSR&#xff0c;原来是加权随机算法在作祟 阅读本文需要做好心理准备&#xff0c;建议带着深究到底的决心和毅力进行学习&#xff01; 灵魂拷问 为什么有 50% 的几率获得金币&a…...

MySQL MGR 集群新增节点

前言 服务器规划现状&#xff08;CentOS7.x&#xff09; IP地址主机名部署角色192.168.x.101mysql01mysql192.168.x.102mysql02mysql192.168.x.103mysql03mysql192.168.x.104proxysql01proxysql、keepalived192.168.x.105proxysql02proxysql、keepalived 新增服务器IP&#x…...

【单目标优化算法】蜣螂优化算法(Dung beetle optimizer,DBO)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

【C++】类和对象入门必知

面向过程和面向对象的初步认识类的引入类的定义类的访问限定符封装类的作用域类的实例化类对象模型this指针C语言和C实现Stack的对比面向过程和面向对象的初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解…...

day38 动态规划 | 509、斐波那契数 70、爬楼梯 746、使用最小花费爬楼梯

题目 509、斐波那契数 斐波那契数&#xff0c;通常用 F(n) 表示&#xff0c;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n - 2)&#xff0c;其…...

2023年备考软考必须知道的6件事

不知不觉&#xff0c;距离2023年上半年软考也只有不到100天的时间了&#xff0c;报名入口也将在3月13日正式开通&#xff0c;你是正在犹豫是否参加考试? 还是已经开始着手准备复习? 关于软考考试你还有哪些疑问? 2023年备考软考必须知道的6件事&#xff0c;建议收藏&#xf…...

GLOG如何控制输出的小数点位数

1 问题 在小白的蹩脚翻译演绎型博文《GLOG从入门到入门》中&#xff0c;有位热心读者提问说&#xff1a;在保存日志时&#xff0c;浮点型变量的小数位数如何设置&#xff1f; 首先感谢这位“嘻嘻哈哈的地球人”赏光阅读了小白这不太通顺的博客文章&#xff0c;并提出了一个很…...

2022年全国职业院校技能大赛(中职组)网络安全竞赛试题A(6)

目录 模块A 基础设施设置与安全加固 一、项目和任务描述&#xff1a; 二、服务器环境说明 三、具体任务&#xff08;每个任务得分以电子答题卡为准&#xff09; A-1任务一&#xff1a;登录安全加固&#xff08;Windows&#xff09; 1.密码策略 a.密码策略必须同时满足大小…...

Safety-Gym环境配置与安

官网&#xff1a; https://github.com/openai/safety-gym https://github.com/openai/safety-starter-agents 一、安装依赖环境配置 建议使用python 3.7及以下环境&#xff0c;因为官方的safety-rl是基于tensorflow1.13.1实现&#xff0c;而tensorflow1.13.1只能支持python…...

3月再不跳槽,就晚了

从时间节点上来看&#xff0c;3月、4月是每年跳槽的黄金季&#xff01; 以 BAT 为代表的互联网大厂&#xff0c;无论是薪资待遇、还是平台和福利&#xff0c;都一直是求职者眼中的香饽饽&#xff0c;“大厂经历” 在国内就业环境中无异于一块金子招牌。在这金三银四的时间里&a…...

HTTP cookie格式与约束

cookie是前端编程当中经常要使用到的概念&#xff0c;我们可以使用cookie利用浏览器来存放用户的状态信息保存用户做了一些什么事情。session是服务器端维护的状态。session又是如何和cookie关联起来。后面介绍cookie和session的使用。Cookie 是什么&#xff1f;RFC6265, HTTP …...

docker基础

docker基础 docker概述 docker的出现&#xff1f;docker解决思想docker历史docker链接docker能干什么&#xff1f;开发-运维 docker安装 镜像(image)容器(container)仓库(repository)底层原理 docker命令 帮助命令镜像命令 docker-images查看所有本地主机上的镜像docker-searc…...

【微信小程序】--JSON 配置文件作用(三)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…...

EDA-课设

EDA-课程设计-电子闹钟 一、实验目的 1.掌握多层电路在 QuartusII 集成开发环境中的实现&#xff1b; 2.熟练掌握基于 QuartusII 集成开发环境的组合逻辑电路设计流程&#xff1b; 3.掌握基于 QuartusII 集成开发环境的时序逻辑电路设计流程&#xff1b; 4.理解有限状态机设计…...