JDK1.8常用新特性,你想看的都在这

概述

Java8 是自 java5之后最重要的版本,这个版本包含语言、编译器、库、工具、JVM等方面的十多个新特性。
Jdk8新增的特性如下:

  • Lambda表达式 类似于ES6中的箭头函数
  • 接口的默认方法和静态方法
  • 新增方法引用格式
  • 新增Stream类
  • 新的日期API,Datetime,更方便对日期的操作
  • 引入Optional,在SpringData中使用较多,然后再通过get获取值,可以防止空指针异常
  • 支持Base64
  • 注解相关的改变
  • 支持并行(parallel)数组
  • 对并发类(Concurrency)的扩展。
  • JavaFX

一:接口新特性

1、接口默认方法

接口中的方法默认都是抽象方法,在jdk8之前,每个实现这个接口的类都要重写所继承接口的所有方法。

假如某个框架中对一个接口增加一个新的方法,那么实现这个接口的所有类都不得不重写接口中的方法,显然这不是我们想要的。

在jdk8中提供了 default 关键字,可以对接口中的方法进行修饰,使用 default 关键字修饰的方法子类不一定要去实现,但必须要有自己的方法体

public interface UserService {

    default void test(){
        System.out.println("测试接口中方法的默认实现");
    }

}
2、接口静态方法

在接口中,可以使用 static 修饰方法,该方法必须要有自己的实现,且只能通过 接口名.方法名 进行调用,不能通过实现类进行调用,实现类不可以重写该方法

public interface UserService {

    static void test(){
        System.out.println("这是接口中的静态方法");
    }

}
3、函数式接口

函数式接口在Java中是指:有且仅有一个抽象方法的接口,可以有默认方法和静态方法。

而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
可以通过@FunctionalInterface注解对接口进行代码规范
@FunctionalInterface 注解与 @Override 一样,都只是在编译期起作用,所谓编译期就是你加不加都是不影响最终的运行效果,但你加了之后,代码会显得更加清晰。

@FunctionalInterface
public interface UserService {

    void test();

}

二:lambda表达式

语法

三要素:参数、箭头、代码

格式:(参数类型 参数1, 参数类型 参数2…) – > {代码}

  1. 如果参数有多个,那么使用逗号分隔。如果参数没有,则留空
  2. 箭头是固定写法
  3. 大括号相当于方法体。

Lambda 省略规则

  1. 参数类型可以省略。但是只能同时省略所有参数的类型,或者同时都不省略。
  2. 如果参数有且仅有一个,那么小括号可以省略。
  3. 如果大括号内的语句有且仅有一条,那么无论是否有返回值,return、大括号、分号都可以省略

Lamdba对函数式接口的省略demo

  1. 无参数的情况
@Test
public void testRunnable(){
    new Thread(() -> System.out.println("开启了子线程")).start();
}
  1. 一个参数没返回值的情况
@Test
public void test1(){
    interOneImpl("ssss",str -> System.out.println(str));
}
private void interOneImpl(String str,InterOne interOne){
    interOne.printStr(str);
}

@FunctionalInterface
interface InterOne{
    void printStr(String str);
}
  1. 多个参数有返回值的情况
@Test
public void test2(){
    // 参数类型要么都省略,要么都不省略
    interTwoImpl(5,8,(a,b) -> a+b );
}

private void interTwoImpl(int a,int b,InterTwo interTwo){
    int i = interTwo.addNum(a, b);
    System.out.println("执行结果:"+i);
}

@FunctionalInterface
interface InterTwo{
    int addNum(int a,int b);
}

延迟加载
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。

性能浪费的案例

public class Demo{
    private static void log(int level, String msg) {
        if (level == 1) {
            System.out.println(msg);
        }
    }

    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
        log(1, msgA + msgB + msgC);
    }
}

这段代码存在问题:无论级别是否满足要求,作为 log 方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。

Lambda的优化写法

@FunctionalInterface
public interface MessageBuilder {
    String buildMessage();
}
// ................
public class DemoLambda {
    private static void log(int level, MessageBuilder builder) {
        if (level == 1) {
            System.out.println(builder.buildMessage());
        }
    }

    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
        log(1, () -> msgA + msgB + msgC );
    }
}

这样一来,只有当满足条件的时候才会进行三个字符串的拼接。否则不会拼接。

三:Stream流操作

1、 获取Stream流的方式

java.util.stream.Stream 是Java 8新加入的流接口。(不是一个函数式接口) 通过里面的默认方法 stream 可以获取流,所以其实现类均可获取流。

  1. 所有的 Collection 集合都可以通过 stream 默认方法获取流;
  2. Stream 接口的静态方法 of 可以获取数组对应的流。
//        list
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
//        set
        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();
//        Map,需要分key、value或entry等情况
        Map<String,String> map = new HashMap<>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
//        数组,通过静态方法Stream.of()获取
        String[] strings = {"111","222"};
        Stream<String> stream3 = Stream.of(strings);
2、常用方法

forEach、filter、map、count、limit、skip、concat

    public static void main(String[] args) {

        List<String> list = getList();
//        遍历:forEach
        list.stream().forEach(System.out::println);
//        过滤:filter,根据返回Boolean决定是否返回元素
//        获取以张开头的,并打印
        list.stream().filter(e ->e.startsWith("张"))
                .forEach(System.out::println);
//        map:将流中的元素映射到另一个流中,返回要映射的元素
        Stream<String> streamString = list.stream().map(e -> e.substring(1));
        streamString.forEach(System.out::println);
//        统计个数
//        long count = streamString.count();
//        System.out.println(count);
        System.out.println("--------------------------------");
//        分页,limit(取几个)、skip(跳过几个)
//        每页两个数据,第二页
        Stream<String> page = list.stream().skip(1*2).limit(2);
        page.forEach(System.out::println);
        System.out.println("----------------------------------");
//        Stream.concat:合并流
        List<String> list1 = new ArrayList<>();
        list1.add("111");
        list1.add("222");
        List<String> list2 = new ArrayList<>();
        list1.add("333");
        list1.add("444");
        Stream<String> concat = Stream.concat(list1.stream(), list2.stream());
        concat.forEach(System.out::println);
    }

    public static List<String> getList(){
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("张三丰");
        list.add("周芷若");
        list.add("赵敏");
        list.add("杨过");
        return list;
    }

附:JDK1.8中提供的函数式接口

在这里插入图片描述

Supplier

java.util.function.Supplier :无参有返回值。
抽象方法:T get():用来获取一个泛型参数指定类型的对象数据。

//  通过Supplier接口实现字符串拼接
    @Test
    public void test1(){
        String str1 = "Hello";
        String str2 = "World";
        String res = test1Handler(() -> str1 + str2);
        System.out.println(res);
    }

    private String test1Handler(Supplier<String> supplier){
        return supplier.get();
    }

//    使用Supplier接口实现求数组中的最小值
    @Test
    public void test2(){
        int[] nums = {7,3,22,15,1,85,89};
        int res = test2Handler(() -> {
            int min = nums[0];
            for (int num : nums) {
                if (num < min) {
                    min = num;
                }
            }
            return min;
        });
        System.out.println(res);
    }
    private int test2Handler(Supplier<Integer> supplier){
        return supplier.get();
    }
Consumer

java.util.function.Consumer:有参无返回值。 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据。
抽象方法:void accept(? t),意为消费一个指定泛型的数据
默认方法:Consumer<?> andThen(Consumer<?> t),执行完A的accept方法,然后再执行B的accept方法,类比迭代执行

	// 打印格式化信息
	public static void handle(Consumer<String> one,Consumer<String> two,String[] arr){
		for(String item:arr){
			// 执行完1的accept方法,然后再执行B的accept方法
			one.andThen(two).accept(item);
		}
	}

	public static void main(String[] args) {
		String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};
		handle(e -> System.out.println("姓名" + e.split(",")[0]), 
                          e -> System.out.println("性别:"+e.split(",")[1]), array);
	}
Predicate

java.util.function.Predicate:有参有返回值。
抽象方法:boolean test(T t):有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。
默认方法:Predicate and(Predicate p)、Predicate or(Predicate p)、Predicate negate(Predicate p)

	// 通过Predicate实现一个字符串中以"好"开头,并且包含"坏"字
	public static boolean handler(Predicate<String> one,Predicate<String> two,String str){
		// 两个为true,才返回true
		return one.and(two).test(str);
	}
	
	public static void main(String[] args) {
		String str = "好xxx坏反反复复";
		boolean res = handler(e->e.startsWith("好"), e->e.contains("坏"), str);
		System.out.println(res);
	}
Function

java.util.function.Function<T,R>:有参有返回值。用来根据一个类型的数据(T)得到另一个类型的数据(R)。
抽象方法:R apply(T t),接受类型为T的参数,返回类型为R的参数。
默认方法:Function<T,R> andThen(Function<T,R> f);

//	1. 将字符串截取数字年龄部分,得到字符串; 
//	2. 将上一步的字符串转换成为int类型的数字; 
//	3. 将上一步的int数字累加100,得到结果int数字。 
	public static int handler(String str,Function<String,String> one,Function<String,Integer> two,Function<Integer,Integer> three){
		return one.andThen(two).andThen(three).apply(str);
	}
	public static void main(String[] args) {
		String str = "张三,18";
	 	int res = handler(str,e->e.split(",")[1],e->Integer.parseInt(e),e->e+100);
	 	System.out.println(res);
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Stream流是在Java 8之后新增的一个特性,它是用来操作集合(Collection)的工具。Stream流提供了一种简洁、高效的方式来对集合进行处理和操作。Stream流可以用于遍历集合中的元素、进行过滤、映射、排序、聚合等各种操作。 在使用Stream流时,常用的方法之一是forEach()方法,它用来遍历Stream流中的数据。这个方法是一个终结方法,一旦调用了forEach()方法,就不能再继续调用Stream流中的其他方法了。例如,下面的代码演示了如何使用forEach()方法遍历一个Stream流并打印其中的元素: ``` List<String> list = Arrays.asList("字节","网易","百度", "美团", "阿里", "字节"); Stream<String> stream = list.stream(); stream.forEach(System.out::println); ``` 执行以上代码会依次打印出集合中的元素:"字节"、"网易"、"百度"、"美团"、"阿里"、"字节"。 除了forEach()方法之外,Stream流还提供了许多其他常用的方法,比如filter()用于过滤元素、map()用于映射元素、sorted()用于排序元素、reduce()用于聚合元素等等。这些方法可以根据具体的需求来选择使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [JDK1.8常见的Stream流](https://blog.csdn.net/TFHoney/article/details/129687717)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值