Java8新特性

一、Lambda表达式

1、基本语法

(parameters) -> expression
(parameters) -> { statements; }

ThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
// Lambda表达式取代匿名函数
executor.execute(() -> {
    System.out.println("线程执行事务");
});

executor.execute(() -> System.out.println("线程执行事务"));

2、函数式接口

在接受函数式接口的地方才可以使用Lambda表达式。Java8自带一些常用的函数式接口,在java.util.function包中:

// Predicate接口定义一个test的抽象方法,接受泛型T对象,并返回一个boolean
Predicate<String> predicate = (str) -> str.contains("a");
System.out.println(predicate.test("abc"));

// Consumer接口定义一个accept的抽象方法,接受泛型T对象,没有返回值
Consumer<String> consumer = (str) -> System.out.println(str);
consumer.accept("abc");

// Function接口定义一个apply的抽象方法,接受泛型T对象,返回泛型R对象
Function<String, String> function = (str) -> str.toUpperCase();
System.out.println(function.apply("abc"));

// Supplier接口定义一个get的抽象方法,没有参数,返回泛型R对象
Supplier<String> supplier = () -> "abc";
System.out.println(supplier.get());

// 另外,还专门提供给原始类型的接口,避免自动装箱,如:IntPredicate、IntConsumer、IntFunction、IntSupplier
// 接受两个参数的接口,如:BiPredicate、BiConsumer、BiFunction

有需要三个参数以上的接口,可以自行定义新的函数式接口:

public interface MyFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}

// 使用自行定义的函数式接口
MyFunction<String, String, String, String> myFunction = (str1, str2, str3) -> str1 + str2 + str3;
System.out.println(myFunction.apply("a", "b", "c"));

3、方法引用

// 相当于 (String str) -> str.toUpperCase()
String::toUpperCase

// 相当于 (User user) -> user.getName()
User::getName

4、环绕执行模式的例子

public class JedisClient {

    public String set(String key, String value) {
       return getClientAndAutoClose((jedis) -> {
          return jedis.set(key, value);
       });
    }

    public String get(String key) {
       return getClientAndAutoClose((jedis) -> {
          return jedis.get(key);
       });
    }
    
    // 统一进行资源关闭
    private <T> T getClientAndAutoClose(Function<Jedis, T> function) {
       Jedis jedis = RedisManager.getJedisPool().getResource();
       try {
          return function.apply(jedis);
       }finally {
          if (jedis != null) {
             jedis.close();
          }
       }
    }
}

二、stream流

List<String> list = Lists.newArrayList("abc", "efg", "cba", "wa", "ba");
list.stream()
        .filter(s -> s.contains("a"))
        .map(String::toUpperCase)
        .limit(3)
        .sorted()
        .forEach(System.out::println);

1、中间操作

中间操作形成一条流水线,此时还不会执行任何处理。类似builder模式。
filter:筛选出满足条件的数据。
distinct:去重,根据对象的hashCode和equals方法。
limit:截取指定数量的数据。
skip:忽略前面n个数据。
map:对数据做映射,例如使用User::getName,从user对象转成name。

2、终端操作

终端操作会触发执行流水线,并能生成结果。
forEach:对流中每个数据遍历进行处理。
collect:收集,把流转成一个对应的输出,如集合:collect(Collectors.toList())。
count:统计流中数据的个数。
allMatch、anyMatch、noneMatch:匹配Lambda条件,返回boolean。
findAny、findFirst:查找满足条件的数据,返回Optional。
reduce:规约,reduce接受两个参数:一个是初始值,一个是BinaryOperator<T>来将两个数据合成一个,如累计:reduce(0, (a, b) -> a + b)。

3、Collectors静态方法

Collectors提供了很多静态方法,可在stream().collect中使用。

List<String> list = Lists.newArrayList("abc", "efg", "cba", "wa", "ba");
// 将流的数据收集到list
List<String> list1 = list.stream().collect(Collectors.toList());
// 将流的数据收集到set,删除重复项
Set<String> set = list.stream().collect(Collectors.toSet());
// 将流的数据进行分组,可以按对象某个属性,此处用String的对象本身
Map<String, List<String>> group = list.stream().collect(Collectors.groupingBy(Function.identity()));
// 将流的数据收集到map,key为toString生成的字符串,value为对象本身,遇到重复的key则取第一个值
Map<String, String> map = list.stream().collect(Collectors.toMap(String::toString, Function.identity(), (v1, v2) -> v1));
// 分隔符连接对象的toString生成的字符串
String join = list.stream().collect(Collectors.joining(","));
// 统计流的数据个数
Long count = list.stream().collect(Collectors.counting());
// 统计流的数据某个整数属性的总和
Integer summing = list.stream().collect(Collectors.summingInt(String::length));

三、其他类库的更新

1、Optional

// 可以为不存在的对象提供默认值
User user = null;
Optional<User> userOpt = Optional.ofNullable(user);
User user2 = userOpt.orElse(new User(0, "DEFAULT"));
System.out.println(user2.getName());
// 通过方法提供默认值
User user3 = userOpt.orElseGet(() -> new User(0, "DEFAULT")); 
System.out.println(user3.getName());

2、CompletableFuture

// 异步执行api,使用默认线程池ForkJoinPool,不推荐
CompletableFuture.runAsync(() -> {
    
});

// 推荐使用自定义线程池,隔离资源,按需分配线程数
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                ThreadPoolConfig.corePoolSize,
                ThreadPoolConfig.maximumPoolSize,
                ThreadPoolConfig.keepAliveTime,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>(ThreadPoolConfig.queueSize), 
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {  }, threadPoolExecutor);

3、新的日期和时间API

// 获取当前日期
LocalDate now = LocalDate.now();
// 获取昨天的日期
LocalDate yestday = now.minusDays(1);
// 日期转换
LocalDate date = LocalDate.parse("2024-01-01", DateTimeFormatter.ISO_DATE);
// 获取当前时间
LocalTime localTime = LocalTime.now();
// 时间转换
LocalTime time = LocalTime.parse("12:00:00", DateTimeFormatter.ISO_TIME);
// 获取当前日期时间
LocalDateTime localDateTime = LocalDateTime.now();
// 获取昨天的日期时间
LocalDateTime beforeDatetime = localDateTime.minusDays(1);
// 便于机器使用的日期和时间
Instant instant = Instant.now();
// 时间间隔
Duration duration = Duration.between(beforeDatetime, localDateTime);
System.out.println(duration.getSeconds() + "秒");
// 日期间隔
Period period = Period.between(yestday, now);
System.out.println(period.getDays() + "天");

4、集合

Map<String, Integer> map = new HashMap<>();
// 获取时指定默认值
Integer value = map.getOrDefault("a", 0);
// 若map中不存在键"a",则添加键值对("a", 1)
map.putIfAbsent("a", 1);
// 若map中不存在键"b",则添加键值对("b", 2)
map.computeIfAbsent("b", v -> 2);
// 若map中存在键"b",则将其对应的值加1
map.computeIfPresent("b", (k, v) -> v + 1);
System.out.println(map);

List<String> list = Lists.newArrayList("abc", "bcd", "cde");
// 所有元素替换
list.replaceAll(String::toUpperCase);
// 根据Lambda条件删除元素
list.removeIf(s -> s.contains("B"));
System.out.println(list);

5、并发

// 部分上锁,线程安全
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

AtomicInteger atomicInteger = new AtomicInteger(0);
// 更新,返回更新前的值
int before = atomicInteger.getAndUpdate(i -> i + 1);
System.out.println(before);
// 更新,返回更新后的值
int after = atomicInteger.updateAndGet(i -> i + 1);
System.out.println(after);
// 更新,取最大值,返回更新前的值
int before2 = atomicInteger.getAndAccumulate(10, Integer::max);
System.out.println(before2);
// 更新,取最大值,返回更新后的值
int after2 = atomicInteger.accumulateAndGet(20, Integer::max);
System.out.println(after2);

// 支持多线程中累加
LongAdder longAdder = new LongAdder();
longAdder.add(1);
longAdder.add(2);
long sum = longAdder.sum();
System.out.println(sum);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • JAVA8新特性 速度更快 Lambda表达式 Stream API 便于并行 最大化的减少空指针异常 速度更快 ...
    Levi_wen阅读 1,261评论 0 0
  • 目录结构 介绍 Java语言的新特性2.1 Lambdas表达式与Functional接口2.2 接口的默认与静态...
    夜风月圆阅读 3,411评论 0 2
  • Java 8自Java 5(发行于2004)以来最具革命性的版本。Java 8 为Java语言、编译器、类库、开发...
    huoyl0410阅读 3,856评论 1 2
  • 2019年9月19日java13已正式发布,感叹java社区强大,经久不衰。由于国内偏保守,新东西总要放一放,让其...
    CodingAndLiving阅读 4,256评论 0 1
  • 为什么使用 Lambda 表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传...
    强某某阅读 14,975评论 0 15