Java8新特性实战

        Java 8 作为一个里程碑式的版本,其中所做出的改变,在许多方面比Java历史上任何一次改变都深远。Java为什么会一直在改变,因为编程语言就像生态系统一样,更优秀语言的出现,落后的语言就会被取代,除非它们不断地演变和进步。Java 8 引入的核心新特性包括:Lambda表达式、函数式接口、Stream流API、方法引用/构造器引用等。

1. 什么是行为参数化

       行为参数化是Java 8 增加的一个编程概念,即把不同行为的代码通过参数传递给方法。对于初学者来说这可能不太好理解,我们需要记住的是Java 8 增加了把方法(你的代码)作为参数传递给另一个方法的能力。Java 8中的Lambda表达式和方法引用的语法就是使用了行为参数化的体现。

  • Lambda表达式,通过匿名函数传递代码。
package com.mj.lambda;/*** @author: gjm* @description: 常见的Lambda使用*/
public class UseLambda {public static void main(String[] args) {// Lambda表达式把一个匿名函数传递给方法new Thread(()-> System.out.println("t1 running"),"t1").start();}
}
  • 方法引用语法,表示传递此方法的代码。
package com.mj.method_ref;import java.util.function.Function;/*** @author: gjm* @description: 如何使用方法引用*/
public class UseMethodRef {public static void main(String[] args) {// 方法引用语法: (类::方法名),传递的是方法的代码getThreadInfo(new Thread(()->System.out.println("t1 running"),"t1"),Thread::getId,Thread::getName);}// 使用函数式接口接收public static void getThreadInfo(Thread t, Function<Thread, Long> f, Function<Thread, String> f1) {// 调用函数,传递的方法不同,获取的结果也不同Long threadId = f.apply(t);String threadName = f1.apply(t);System.out.println("threadId:"+threadId);System.out.println("threadName:"+threadName);}}

2. 方法引用的语法

      方法引用是Java 8 支持的新语法,使用 ": :" 的语法声明方法引用,可以引用静态方法、普通成员方法、构造器方法。

  • 语法一:使用类: :普通成员方法。
package com.mj.method_ref;import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;/*** @author: gjm* @description: 用户类*/
@Setter
@Getter
@AllArgsConstructor
@ToString
public class User {private long id;private String username;private int age;
}
 public static void main(String[] args) {List<User> users = new ArrayList<>();users.add(new User(1L,"张三", 30));users.add(new User(2L,"李四", 25));users.add(new User(3L,"王五", 35));// 可以按照语法理解,通过年龄从小到大排序,users.sort(Comparator.comparing(User::getAge));System.out.println(users.toString());}
  • 语法二:使用类: :静态成员方法。
package com.mj.method_ref;import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;/*** @author: gjm* @description: 用户类*/
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {private long id;private String username;private int age;// 按用户年龄降序排序public static int compareByAgeDesc(User u1, User u2){return u2.getAge() - u1.getAge();}
}
public static void main(String[] args) {List<User> users = new ArrayList<>();users.add(new User(1L,"张三", 30));users.add(new User(2L,"李四", 25));users.add(new User(3L,"王五", 35));// 使用工具类的排序方法,引用自定义排序规则的静态方法Collections.sort(users, User::compareByAgeDesc);System.out.println(users.toString());}
  • 语法三:对象: :普通成员方法。
 public int compareByAgeAsc(User u1, User u2){return u1.getAge() - u2.getAge();}
public static void main(String[] args) {List<User> users = new ArrayList<>();users.add(new User(1L,"张三", 30));users.add(new User(2L,"李四", 25));users.add(new User(3L,"王五", 35));// 使用工具类的排序方法,引用自定义排序规则的静态方法User user = new User();Collections.sort(users, user::compareByAgeAsc);System.out.println(users.toString());}
  • 语法四:构造器引用,类: :new。
package com.mj.method_ref;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;/*** @author: gjm* @description: 构造器引用*/
public class ConstructorRef {public static void main(String[] args) {List<String> names = new ArrayList<>();Collections.addAll(names,"11223","122","1222","12","21122","12aa");// 找到所有以12开头的字符串,并转换为数字List<Integer> integers = names.stream().filter(num -> {// 字符串只能是数字Pattern pattern = Pattern.compile("[0-9]+");return (pattern.matcher(num).matches() && num.startsWith("12"));} )// 把过滤出的数字字符串转换成 Integer类型,// Integer::new所对应构造器为 public Integer(String s).map(Integer::new).collect(Collectors.toList());System.out.println(integers.toString());}}

3. Lambda表达式

      Lambda表达式也是Java 8 支持的新语法,本质就是一个匿名函数,把函数当成参数传递给其它方法。前面的方法引用使用的是具体方法,也就是需要被提前声明出的静态方法或普通成员方法才能被引用。

  • 一个Lambda表达式包括了三个部分。

    1. 参数列表: 和函数一样支持无参(),一个参数(a),多个参数(a, b , ..),无需书写参数类型。

    2. 箭头: 箭头 —>把参数列表与Lambda主体分隔开。

    3. Lambda主体: 在大括号{ }中书写具体的执行代码,只有一行代码可以省略大括号。

  • Lambda表达式需匹配函数式接口才能使用。函数式接口就是只定义一个抽象方法的接口,具体说来,Lambda表达式是函数式接口一个具体实现的实例。
  • Lambda表达式主体内可以使用外部的变量,外部变量必须是最终变量或使用final修饰的实际最终变量。
  • 无参的Lambda表达式,可以有返回值和无返回值。
  // 无参无返回值,对应的是函数式接口Runnable      Runnable runnable = () -> { };// 无参有返回值,对应的是函数式接口Supplier<T>或者Callable<T>Supplier<String> supplier = () -> "Gets a result";// Callable一般用于多线程,可以拿到执行结果Callable<String> callable = () -> "Computes a result";
  • 一个参数的Lambda表达式,可以有返回值和无返回值。
// 一个参数无返回值,对应的是函数式接口Consumer<T>
Consumer<String> consumer = (a) -> { };// 一个参数有返回值,对应的是函数式接口Function<T, R>
Function<Integer, String> f = (a) -> a.toString();
  • 两个参数的Lambda表达式,可以有返回值和无返回值。
// 两个参数无返回值,对应的是函数式接口BiConsumer<T, U>
BiConsumer<String, Integer> biConsumer = (a, b) -> { };// 两个参数有返回值,返回类型为int,对应的是函数式接口Comparator<T>
Comparator<User> comparator = (u1, u2) -> u1.getAge() - u2.getAge();

 4. 函数式接口

       函数式接口也是Java 8 新增加的一种类型,函数式接口的定义是只有一个抽象方法的接口。函数式接口作为方法的参数时,可以使用Lambda表达式或者方法引用把代码作为参数值传递,这就是行为参数化。让方法通过接收多种不同的代码实现,来完成不同的行为,这也被称为函数式编程。

  • Predicate函数式接口。

         java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。我们可以使用一个具体的案例体验函数式编程的使用和强大之处,下面案例通过lambda表达式传入不同行为的代码,对过滤用户的方法完成了不一样的功能。这些代码必须匹配Predicate接口的抽象方法的定义规则,官方叫匹配函数式接口的函数描述符

package com.mj.lambda;import com.mj.method_ref.User;import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;/*** @author: gjm* @description: Predicate函数式接口使用*/
public class UsePredicate {public static void main(String[] args) {List<User> users = new ArrayList<>();users.add(new User(1L,"张三", 30));users.add(new User(2L,"李四", 25));users.add(new User(3L,"王五", 35));/***  filterUsers()方法的第二个参数可以使用Lambda表达式*  Lambda表达式必须匹配函数描述符: T-> boolean*  函数描述符: T-> boolean,表示接收一个T类型参数,返回boolean类型结果*/// 找出大于等于35岁的用户System.out.println(filterUsers(users, user -> user.getAge() >=30 ));// 找出姓张的用户System.out.println(filterUsers(users, user -> user.getUsername().startsWith("张") ));}/*** 过滤用户* @param user* @param p* @return*/public static List<User> filterUsers(List<User> users, Predicate<User> p) {List<User> result = new ArrayList<>();for (User user: users){// 执行方法,就是执行了lambda表达式中的代码if (p.test(user)) {result.add(user);}}return result;}
}
  • Consumer函数式接口。

         java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。由于只有输入参数,没有返回值,所以通常也叫消费型函数式接口。

package com.mj.lambda;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/*** @author: gjm* @description: Consumer函数式接口*/
public class UseConsumer {public static void main(String[] args) {List<String> names = new ArrayList<>();Collections.addAll(names,"刘备","关羽","张飞");/***  forEach()方法的参数就是Consumer函数式接口*  default void forEach(Consumer<? super T> action) {}*  forEach()方法也是jdk1.8新增加的接口中允许有实现的默认方法*/names.forEach(str -> System.out.println(str));}}
  • Supplier函数式接口。

        java.util.function.Supplier<T>接口定义了一个叫作get的方法,它不接收任何参数,只返回一个泛型T的对象。根据这个特性,通常也被称供给型函数式接口。

package com.mj.lambda;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.function.Supplier;/*** @author: gjm* @description: Supplier函数式接口*/
public class UseSupplier {public static void main(String[] args) {// 获取当前时间的字符串String currentDateTime = getDateTimeStr(LocalDateTime::now, "yyyy-MM-dd HH:mm:ss");System.out.println(currentDateTime);// 获取指定时间的字符串String dateTime =  getDateTimeStr(()->LocalDateTime.of(2022, Month.APRIL,1,12,30),"yyyy-MM-dd HH:mm:ss");System.out.println(dateTime);}/*** 获取LocalDateTime时间的字符串格式* @param supplier* @param pattern* @return*/public static String getDateTimeStr(Supplier<LocalDateTime> supplier,String pattern){LocalDateTime time = supplier.get();return time.format(DateTimeFormatter.ofPattern(pattern));}
}
  • Function函数式接口。

        java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。根据这个接口的方法特点,我们可以利用它实现类型转换的功能。

package com.mj.lambda;import com.mj.method_ref.User;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;/*** @author: gjm* @description: Function函数式接口*/
public class UseFunction {public static void main(String[] args) {List<User> users = new ArrayList<>();users.add(new User(1L,"张三", 30));users.add(new User(2L,"李四", 25));users.add(new User(3L,"王五", 35));List<Map> dataMap = map(users, user -> {// 转换为HashMap集合,以id为keyMap<Long, String> map = new HashMap<>();map.put(user.getId(),user.getUsername());return map;});System.out.println(dataMap.toString());}/*** 把T类型的对象转换为R类型* @param list* @param f* @param <T>* @param <R>* @return*/public static <T, R> List<R> map(List<T> list, Function<T, R> f) {List<R> result = new ArrayList<>();for(T s: list){result.add(f.apply(s));}return result;}}
  •  原始类型特化的函数式接口。

        Java 8为我们前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类型时避免自动装箱的操作。这些函数式接口对原始类型进行了扩展,比如我们使用Predicate时需要指定输入参数的泛型T,如果输入的参数类型是可以确定的,比如int、long、double,那么我们就可以使用IntPredicateLongPredicateDoublePredicate等接口,这样可以避免使用包装类型,产生自动装箱操作。

package com.mj.lambda;import java.util.function.IntPredicate;
import java.util.function.Predicate;/*** @author: gjm* @description: IntPredicate*/
public class UseIntPredicate {public static void main(String[] args) {IntPredicate intPredicate = (int a) -> a==0;intPredicate.test(0);            // 此时无自动装箱Predicate<Integer> predicate = (Integer a) -> a==0;predicate.test(0);               // 产生了自动装箱} }
  • 自定义函数式接口。

       某些时候我们在java.util.function包下没找到符合自己要求的函数式接口时,我们可以自定义满足自己要求的函数式接口。根据函数式接口只有一个抽象方法的定义,我们也可以实现自定义函数式接口,从而来满足自己的开发。注意函数式接口上的@FunctionalInterface注解是非必需的,只是用来标识这是一个函数式接口,我们建议加上让代码更容易理解。

package com.mj.lambda;import java.lang.FunctionalInterface;/*** @author: gjm* @description: 自定义函数式接口*/
@FunctionalInterface
public interface CustomFunInterface<T,U,X,R> {R compute(T t, U u, X x);
}
package com.mj.lambda;/*** @author: gjm* @description: 测试自定义函数式接口*/
public class UseCustomFunInterface {public static void main(String[] args) {Integer a = 90, b = 20, c = 30;// 计算出a,b,c的最大值,并返回StringCustomFunInterface<Integer, Integer, Integer, String> myFunInterface = (x, y, z)-> {Integer max = x;max = max > y ? max : y;max = max > z ? max : z;return max.toString();};String compute = myFunInterface.compute(a, b, c);System.out.println(compute);}}

 4. 什么是Stream流

      Stream流是Java 8 提供的新API,它主要是用来帮助我们简单和高效地处理集合数据的,Stream提供了集合处理的常用API。我们可以使用Stream的API配合前面学的函数式编程思想实现更简洁的声明式方法表达和几乎透明的并行处理。

     Java8之前如果我们想在集合中过滤掉并返回我们想要的数据,我们要对集合进行遍历,并书写自定义过滤逻辑处理再返回。这在代码层面往往不能够清晰地表达你所做的事情,Java8为我们提供了专门做这个事情的filter方法。

   List<Employee> employees = new ArrayList<>();employees.add(new Employee(1L,"张三", 30));employees.add(new Employee(2L,"李四", 25));employees.add(new Employee(3L,"王五", 35));List<Employee> old = new ArrayList<>();// Java8以前的写法for(Employee e: employees){if(e.getAge() >= 35)  old.add(e);}// 使用Stream API的写法old = employeeList.stream().filter(employee -> employee.getAge() >=35).collect(toList());
  • Stream的filter方法,通过名称就能知道它的作用就是用于过滤数据,它只有一个参数并且类型是Predicate<? super T> 的函数式接口,返回值为Stream<T>,意味着它可以继续调用其它方法处理更复杂的逻辑,就像流一样把结果流向下一个方法处理。
   List<Employee> employees = new ArrayList<>();employees.add(new Employee(1L,"张华", 35));employees.add(new Employee(2L,"王五", 35));employees.add(new Employee(3L,"张小樊", 21));employees.add(new Employee(4L,"李东东", 25));// 找到所有姓张的员工,并按年龄从小到大排序List<Employee> search = employeeList.stream().filter(employee -> employee.getName().startsWith("张")).sorted(Comparator.comparing(Employee::getAge)).collect(toList());
  •  Stream的map方法,map可以翻译为映射。这个名称比较有意思,我们可以这样理解,对于集合中的每一个元素,都可以通过一个函数的处理得到一个新的元素,这就叫映射。本质就是对流中每一个元素应用函数,使其做一种转换。它的方法参数是一个函数型的函数式接口Function<? super T, ? extends R>,这也体现了它适合做类型转换的特点。
   List<RoleRefResource> roleRefResources = new ArrayList<>();roleRefResources.add(new RoleRefResource(1L,"/api/employee","roleA"));roleRefResources.add(new RoleRefResource(2L,"/api/department","roleB"));roleRefResources.add(new RoleRefResource(3L,"/api/user","roleC"));roleRefResources.add(new RoleRefResource(4L,"/api/employee","roleD"));String resPath = "/api/employee";// 获取可访问该资源路径的所有角色列表List<String> roles = roleRefResources.stream()// 过滤路径.filter(rr -> resPath.equals(rr.getResPath()))// 映射为角色名称.map(rr -> rr.getRoleName() ).collect(toList());
  • Stream的flatMap方法。这个方法主要用来做流的扁平化处理,我们可以理解为把每一个处理结果所返回的流都合并为一个流。它和map的区别是多了一步flat扁平化的操作,我们通常可以利用flatMap来做一些合并操作。
   Integer[] a = new Integer[]{1,3,5,2};Integer[] b = new Integer[]{1,4,5,9};List<Integer[]> list = Arrays.asList(a,b);// 把a,b集合合并成一个新集合,并过滤重复元素List<Integer> uniqueInteger = list.stream().flatMap(x -> Arrays.stream(x))  // 把每个集合都转换成流,并且合并.distinct()   // 对合并的这个流过滤重复元素.sorted().collect(Collectors.toList());System.out.println(uniqueInteger);
  • java.util.stream.Stream中的Stream接口定义了许多流操作(方法)。Stream的流操作主要分为两类:中间操作终端操作,其中可以连接起来的流操作称为中间操作,关闭流的操作则称为终端操作。filtersorted等中间操作会返回另一个流,返回的是流意味着它能继续执行其它流操作,就像工厂流水线一样分工执行。终端操作会从流的流水线生成结果,这返回的结果不再是任何的流而是Java中的各种类型List、Integer,或者是void。
  long count = students.stream().filter(s -> s.getGrade() >= 60) // 中间操作,返回的是流.distinct()  // 中间操作,返回的是流.limit(50)   // 中间操作,返回的是流.count();    // 终端操作,返回执行结果System.out.println(count);
  • Stream接口的distinct方法,用于元素的去重,是一个中间操作。它会返回一个没有重复元素的流,当元素为对象类型时,需要根据流所生成元素的hashCode和equals方法判断是否重复。
 List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);numbers.stream().filter(i -> i % 2 == 0)  // 筛选偶数.distinct()               // 排除重复元素.forEach(System.out::println);  // 终端操作,打印元素
  • Stream接口的limit(n)方法,该方法用于截短流,限定生成流时的元素的个数是多少,也是一个中间操作。如果流是有序的,它会返回前n个元素;如果流是无序的,比如源是一个Set,它的limit结果也是无序的。
  Set<Student> students = new HashSet<>();students.add(new Student(10010, "李小军",77));students.add(new Student(10013, "赵平",89));students.add(new Student(10009, "刘小美",85));// 输出结果是自己内部顺序,非元素放入顺序students.stream().limit(5).forEach(System.out::println);
  • Stream接口的anyMatch方法,该方法用于至少匹配一个元素,只要有一个元素匹配就返回true,表示匹配成功。anyMatch方法返回一个boolean,因此是一个终端操作。
  Set<Student> students = new HashSet<>();students.add(new Student(10010, "李小军",77));students.add(new Student(10013, "赵平",89));students.add(new Student(10009, "刘小美",85));// 判断是否有李小军这个学生boolean b = students.stream().anyMatch(s -> "李小军".equals(s.getName()));System.out.println(b);
  • Stream接口的findAny方法,该方法会返回当前流中的任意一个元素。findAny方法的返回结果是Optional<T>类型,它可以帮我们处理结果为null值的情况,能够防止空指针。这个类也是JDK8新增加的容器类,后面也将会学习其使用。
  Set<Student> students = new HashSet<>();students.add(new Student(10010, "李小军",77));students.add(new Student(10013, "赵平",89));students.add(new Student(10009, "刘小美",85));// 返回任意一个学生,并打印Optional<Student> student = students.stream().findAny();System.out.println(student.get());

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

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

相关文章

Ionic4 生命周期钩子函数和angular生命周期钩子函数介绍

1、Ionic4 生命周期钩子函数 Ionic 4&#xff08;以及之后的 Ionic 版本&#xff09;使用了 Angular 生命周期钩子&#xff0c;因为 Ionic 是基于 Angular 构建的。因此&#xff0c;Ionic 4 中的生命周期与 Angular 组件生命周期非常相似。以下是一些常见的 Ionic 4 生命周期钩…

掌握Python爬虫实现网站关键词扩展提升曝光率

目录 一、关键词优化的重要性 二、关键词优化的基本方法 1、选择与网站内容相关的关键词 2、控制关键词的密度和分布 3、关键词的层次布局 三、Python爬虫实现网站关键词扩展 1、确定目标网站 2、分析目标网站的HTML结构 3、编写Python爬虫代码 4、分析爬取到的关键词…

BSPHP 未授权访问 信息泄露

漏洞描述 BSPHP 存在未授权访问 泄露用户 IP 和 账户名信息 漏洞复现 访问url&#xff1a; 构造payload访问&#xff1a; /admin/index.php?madmin&clog&atable_json&jsonget&soso_ok1&tuser_login_log&page1&limit10&bsphptime16004073…

25栈和队列-理解栈和队列

目录 LeetCode之路——232. 用栈实现队列 分析&#xff1a; LeetCode之路——225. 用队列实现栈 分析&#xff1a; 栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;是两种基本的数据结构&#xff0c;它们在计算机科学中用于不同的目的。以下是它们的定…

P4491 [HAOI2018] 染色

传送门:洛谷 解题思路: 写本题需要知道一个前置知识: 假设恰好选 k k k个条件的方案数为 f ( k ) f(k) f(k);先钦定选 k k k个条件,其他条件无所谓的方案数为 g ( k ) g(k) g(k) 那么存在这样的一个关系: g ( k ) ∑ i k n C i k f ( i ) g(k)\sum_{ik}^nC_{i}^kf(i) g(k)…

【windows下docker安装rocketMQ】

namesrv和broker安装就不说了&#xff0c;见如下博客 https://blog.csdn.net/Wonderful1025/article/details/107244434/ 安装rocketMQ-console docker run -d -e "JAVA_OPTS-Drocketmq.config.namesrvAddr192.168.65.2:9876 -Drocketmq.config.isVIPChannelfalse"…

12.SpringBoot之RestTemplate的使用

SpringBoot之RestTemplate的使用 初识RestTemplate RestTemplate是Spring框架提供用于调用Rest接口的一个应用&#xff0c;它简化了与http服务通信方式。RestTemplate统一Restfull调用的标准&#xff0c;封装HTTP链接&#xff0c;只要需提供URL及返回值类型即可完成调用。相比…

Spark中的Driver、Executor、Stage、TaskSet、DAGScheduler等介绍

工作流程&#xff1a; Driver 创建 SparkSession 并将应用程序转化为执行计划&#xff0c;将作业划分为多个 Stage&#xff0c;并创建相应的 TaskSet。Driver 将 TaskSet 发送给 TaskScheduler 进行调度和执行。TaskScheduler 根据资源情况将任务分发给可用的 Executor 进程执…

DAE转换GLB格式

1、DAE模型介绍 DAEA&#xff08;Deep Attentive and Ensemble Autoencoder&#xff09;模型是一种用于无监督学习的深度学习模型&#xff0c;由华为公司提出。DAEA模型结合了自编码器和深度注意力机制&#xff0c;能够对高维数据进行降维和特征提取&#xff0c;并且在处理大规…

博图数值按照特定格式(“T000000”)转换成字符串

一、前言 1.string to dint物流输送线往往需要通过扫码器读取托盘条码&#xff0c;一维码或者二维码​。 读取的数据需要解析才能正常使用。两种方式读取的数据直接是字符串&#xff0c;但当设备与上位机通信时&#xff0c; 字符串数据量太大&#xff0c;故可以通过算法转换成…

Ceph分布式存储的简单介绍与Ceph集群的部署搭建

文章目录 1. 存储的概述1.1 单机存储设备1.1.1 DAS&#xff08;直接附加存储&#xff09;1.1.2 NAS&#xff08;网络附加存储&#xff09;1.1.3 SAN&#xff08;存储区域网络&#xff09; 1.2 单机存储的缺陷1.3 分布式存储&#xff08;软件定义的存储 SDS&#xff09;1.4 分布…

unity ugui text 超链接和下划线,支持部分富文本格式

unity版本&#xff1a;2021.3.6f1 局限性&#xff1a; 1.测试发现不能使用 size 富文本标签, 2.同一文本不能设置不同颜色的超链接文本 其它&#xff1a;代码中注释掉使用innerTextColor的地方&#xff0c;可以使用富文本设置超链接颜色&#xff0c; 但是下划线是文本本身颜色 …

springboot中如何进行业务层测试事务回滚

业务层测试事务回滚 为测试用例添加事务&#xff0c;SpringBoot会对测试用例对应的事务提交操作进行回滚 SpringBootTest Transactional public class DaoTest { Autowired private BookService bookService;} 如果想在测试用例中提交事务&#xff0c;可以通过Rollback注…

windows部署django服务器

windows部署django服务器 1、安装IIS1.1 控制面板-----程序----程序和功能----启用或关闭windows功能1.2安装IIS服务器&#xff0c;完成后&#xff0c;重新进入&#xff0c;把CGI安装进系统 2、安装python与虚拟环境2.1 安装python2.2 安装virtualenv虚拟环境2.3 创建一个虚拟环…

求二叉树的高度——函数递归的思想

二叉树的高度&#xff1a;左右两个数最高的那个的1 int TreeHight(BTNode* root) {if (root NULL){return 0;}int lefhightTreeHight(root->left);int righthight TreeHight(root->right);return lefhight > righthight ? TreeHight(root->left) 1 : TreeHight…

mysql中遇到查询字段的别名与函数冲突问题

比如以下哎&#xff0c;我查询城市行业数量排名 select City, DENSE_RANK() over(ORDER BY COUNT(Id) DESC) rank, COUNT(Id) num,IndustrySubGroupName from base_companyinfo WHERE IndustrySubGroupName工业机器人 GROUP BY City 上面使用 DENSE_RANK() 函数来计算排名&am…

深入推荐引擎2:YouTube 视频推荐系统

深入推荐引擎2:YouTube 视频推荐系统 概述候选生成网络体系结构和功能表示训练排名网络特征选择架构迈向多任务排名系统多任务学习专家的多门混合学习选择偏差总结参考这篇博客将介绍 YouTube 视频推荐系统,YouTube 是世界上最大的创建、消费和分享视频内容的平台。他们的推荐…

想要精通算法和SQL的成长之路 - 连续的子数组和

想要精通算法和SQL的成长之路 - 连续的子数组和 前言一. 连续的子数组和1.1 最原始的前缀和1.2 前缀和 哈希表 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 连续的子数组和 原题链接 1.1 最原始的前缀和 如果这道题目&#xff0c;用前缀和来算&#xff0c;我们的思路…

雷电模拟器上使用第一个frida(三)简单的使用实例

经过前两篇 雷电模拟器上使用第一个frida&#xff08;一&#xff09;之安装-CSDN博客雷电模拟器上使用第一个frida&#xff08;二&#xff09;之su超级权限-CSDN博客 本篇开始记录如何使用frida进行hook。 一、先让手机模拟器端的frida server运行起来 虽然是让手机模拟器端…

Rust 流程控制

开发中最常见的用来控制执行流的结构是判断和循环。 判断 Rust 中的 if 表达式允许根据条件执行不同的代码分支&#xff0c;提供一个条件并表示 “如果条件满足&#xff0c;运行这段代码&#xff1b;如果条件不满足&#xff0c;不运行这段代码。” 需要注意的是&#xff0c;…