jdk1.8新特性

本文详细介绍了Java 8的新特性,包括Supplier接口、使用表达式返回数组元素最大值、Consumer接口、Function接口、Predicate接口、方法引用及其各种形式,Stream流的介绍和常用方法,如filter、map、sorted、reduce等。同时讲解了并行Stream流的效率对比、线程安全问题和Fork/Join原理。此外,还涉及了Optional类的使用和JDK 8的新日期和时间API,以及重复注解和类型注解的使用。
摘要由CSDN通过智能技术生成

Supplier接口

java.util.function.Supperlier<T>接口,它意味着“供给”,对应Lambda表达是需要“对外提供”一个符合泛型类型的对象数据

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

供给型接口,通过Supplier接口中的get 方法可以得到一个值,无参有返回的接口

使用Lombda表达式返回数组元素最大值

使用Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中最大值,提示:接口的泛型请使用java.lang.Integer
代码实例:

public class Demo01Test(){
   
	public static void main(){
   
			printMax(()  -> {
   
					int[]  arr = {
   10,20,30};
					//先排序,最后就是最大的
					Arrays.sort(arr);//生序排序
					return arr[arr.length - 1];//最后是最大的
			})
		}
	public static void printMax(Suppelier<Integer>  supplier) {
   
					int max = supplier.get();
					System.out.println("max=" + max );
				}
	}

Consumer接口

java.util.function.Consumer<T> 接口则正好相反,他不是生产一个数据,二是消费一个数据,其数据类型有泛型参数决定

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

使用Lambda表达式将一个字符串转换成大写和小写的字符串
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理,有参返回的接口,基本使用如:

public class DemoConsumer(){
   
	public static void main(String[] args){
   
		//Lambda表达式 先将一个字符串转成小写字符串,在转成大写
		test((String str) -> {
   
			System.out.println(s.toLowerCase());//小写
		},(String str) -> {
   
			System.out.println(str.toUpperCase()); //大写
		});
	}

	public static void test(Consumer<String> c1,Consumer<String> c2){
   
		System.out.println("aa");
		consumer.accept("HelloWord");
		//c1.accept(str);//helloword
		//c2.accept(str);//HELLOWORD
		//调用Consumer这个接口的andThen方法 会执行两个,一个是自己的Consumer的accept方法,还有一个是after.accept()方法,先执行自己的方法,在执行after.accept()方法
		c1.andThen(c2).accept(str);//先执行c1的accept方法,在执行c2的accept方法 效果等于上面两个注释的方法
	}
}

执行效果图:
在这里插入图片描述
在这里插入图片描述
默认方法:andThen
如果一个方法的参数和返回值全都是Consumer类型,那么可以就实现效果:消费一个数据的时候,首先做一个操作,然后在做一个操作,实现组合。而这个方法就是Consumer接口中的default 方法andThen 消费是JDK的源代码

Function接口

java.util.funtion.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。

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

使用Laambda表达式将字符串转成数字
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景例如:将String类型转换为Integer类型

public class DemoFunction(){
   
	public static void mian(String[] args){
   
			//lambda表达式
			只有一个Function参数的时候
			/*test ((String s)  -> {
					return Integer.parseInt(s);//10
			})*/
			test((String str) -> {
   
				return Integer.parseInt(s); //先把字符串的6转换成数字的6
			},(Integer i) -> {
   
			return i * 5; //在执行这个乘法操作输出结果
			})
			
	}
	public static void test(Function<String,Integer> function, Function<Integer,Integer> f2){
   
				Integer in1 = function.apply("10");
				Integer in2 = f2.apply(num);
				//System.out.println("in:" + (in + 5));
				Integer in3 = function.andThen(f2).apply("6");
				System.out.println("f2:" +f2);
	}
}

结果
在这里插入图片描述
默认方法:andThen
Function 接口中有一个默认的andThen 方法,用来进行组合操作,JDK源码如:
在这里插入图片描述

Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值的结果,这是可以使用java.util.function.Predicate<T>接口

@FucntionalInterface
public interface Predicate<T>{
   
	piublic abstart boolean test(T t);
}
Predicate 接口用户判断,返回boolean类型的值 接口的泛型就是参数的泛型

使用Lambda判断一个人如果超过3个字就认为是很长的名字
对test方法的参数T进行判断,返回boolean类型的结果,用于条件判断的场景:

public class DemoPridicate {
   
    //使用Lambda判断一个人如果超过3个就认为是很长的名字
    public static void main(String[] args) {
   
        isLongName((String name) ->{
   
            return name.length() > 3 ;
        });
    }
    public static void isLongName(Predicate<String> predicate){
   
        System.out.println("aa");
        boolean test = predicate.test("迪丽热巴");
        System.out.println("是否是长名字:" + test);
    }
}

判断条件的标准是传入的Lambda表达式逻辑,只要名称长度大于3则认为很长

## 默认方法:and
既然是条件判断。就会存在与,或,非三种常见的逻辑关系,其中将两个Predicate条件使用“与”逻辑连接起来实现并且的效果时,可以使用default方法and其JDK源码为:

default Predicate<T> and (Predicate<? super T>  other){
   
	Object.requreNonull(other);
	return (t)  ->(t)  && 	other.test(t);
}

使用Lambda表达式判断一个字符串中既包含W,也包含H
使用Lambda表达式判断一个字符串中包含W或者包含H
使用Lambda表达式判断一个字符串中即不包含W

如果要判断一个字符串纪要包含大写H又不包含W

public class Demo13Pridicate {
   
    //使用Lambda表达式判断一个字符串中既包含W,也包含H
    //使用Lambda表达式判断一个字符串中包含W或者包含H
    //使用Lambda表达式判断一个字符串中即不包含W
    public static void main(String[] args){
   
        test((String str) ->{
   
            return str.contains("W");
        },(String str) ->{
   
            return str.contains("H");
        });
    }
    public static void test(Predicate<String> p1, Predicate<String> p2){
   
        /*String str = "Hello World";
        boolean b1 = p1.test(str);
        boolean b2 = p2.test(str);
        if (b1 && b2){
            System.out.println("既包含H,又包含W");
        }*/
        String str = "Hello World";
        boolean test = p1.and(p2).test(str);
        if (test){
   
            System.out.println("既包含H,又包含W");
        }
        //使用Lambda表达式判断一个字符串中包含W或者包含H
        boolean b = p1.or(p2).test(str);
        if (b){
   
            System.out.println("包含H或者包含W  "+b);
        }
        //使用Lambda表达式判断一个字符串中即不包含W
        boolean b2 = p1.negate().test("Hello World"); //negate()取反
        if (b2){
   
            System.out.println("不包含W");
        }
    }
}

在这里插入图片描述

方法引用

目标:

了解Lambda表达式
掌握方法引用的格式
了解常见的方法引用方式

Lambda的冗余场景

使用Lambda表达式求一个数组的和

public class DemoMethodReflct {
   
    public static void getMax(int[] arr) {
   
        int sum = 0;
        for (int n : arr){
   
            sum += n ;
        }
        System.out.println(sum);
    }
    public static void main(String[] args){
   
        //使用Lambda表达式求一个数组的和
       /* printMax((int[] arr) ->{
            getMax(arr);
        });
*/
       //使用方法引用
       // //让指定的方法重写接口的抽象方法,到时候调用接口的抽象方法就是调用传递过去的方法
        printMax(DemoMethodReflct::getMax);

    }
    public static void printMax(Consumer<int[]> consumer ){
   
            int[] arr = {
   11,22,33,44,55};
            consumer.accept(arr);
    }
}

方法引用输出结果:
在这里插入图片描述

方法引用的格式

符号表示:::
符号说明:双冒号为方法运用运算符,而他所在的表达式被被称为方法引用
应用场景:如果Lambda所需要实现的方案,已经有其他方存在相同方案,那么则可以使用方法引用

常用的引用方式

方法引用在JDK 8 中使用方式相当灵活,有以下几种形式:

  1. instanceName : : methodName 对象::方法名
  2. ClassName::staticMethodName 类名::静态方法
  3. ClassName::methodName 类名::普通的方法(特殊手段)
  4. ClassName::new 类名::new 调用的构造器
  5. TypeName[]::new Sring[] :: new 调用数组的构造函数

对象名 :: 引用成员方法

这是最常见的一种用法,与上例相同,如果类中已经存在一个成员方法,即可以通过对象名引用成员方法,代码为:

//对象
@Test
public coid test01(){
   
	Date now = new Date();
	Supplier<Long> supp = () -> {
   
		return now.getTime();
	};
	Supplier<Long> supp2 = now::getTime;
	System.out.println();
]

方法引用注意事项

  1. 被引用的方法,参数要和接口中抽象方法的参数一样
  2. 当接口抽象方法有返回值时,被引用的方法也必须有返回值

lambda表达式相当于对这个抽象借口而的抽象方法的重写

类名::引用静态方法

由于java.lang.System类中已经存在了静态方法currentTimeMillis,所以当需要该方法时,可以使用方法引用,如下:

//类名:: 静态方法
@Test
public void test02(){
   
	Supplier<Long> su = () ->{
   
		return System.out.currentTimeMillis();
	};
	Long time = su.get();
	System.out.println("time = "+ time);
}

类名::引用实例方法

由于构造器的名臣与雷鸣完全一样,所以构造器引用使用类名称:: new的格式表示。首先Person类

//类名::实例方法
    @Test
    public void test03(){
   
       /* Function<String,Integer> function = (String str) ->{
          return str.length();
        };*/
       //类名::实例方法(注意:类名::类名::实例方法实际上会降低一个参数作为方法的调用者)
        Function<String,Integer> function = String::length;
        int length =function.apply("hello");
        System.out.println("length = "+length);
    }

在这里插入图片描述

//吧substring方法赋给Biunction接口里面的抽象方法apply方法,apply才会调用这个函数
        BiFunction<String,Integer,String> function1 = String::substring;
        String hellowrod = function1.apply("hellowrod", 3);
        System.out.println(hellowrod);//loworld

在这里插入图片描述

//类名::实例方法
    @Test
    public void test03(){
   
       /* Function<String,Integer> function = (String str) ->{
          return str.length();
        };*/
       //类名::实例方法
        Function<String,Integer> function = String::length;
        int length =function.apply("hello");
        System.out.println("length = "+length);

        //吧substring方法赋给Biunction接口里面的抽象方法apply方法,apply才会调用这个函数
        BiFunction<String,Integer,String> function1 = String::substring;
        //相当于这样的Lambda
        BiFunction<String,Integer,String> function2 = (String str,Integer index) ->{
   
            return str.substring(index);
        };
        System.out.println("lambda表达式"+function2.apply("helloworld",3));
        String hellowrod = function1.apply("hellowrod", 3);
        System.out.println(hellowrod);//loworld
    }
    ```
    **运行结果:**
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/74b9f709d8b34f4e8da2804bf0b428aa.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAcXFfNTg3NDY0NzU=,size_15,color_FFFFFF,t_70,g_se,x_16)

### 类名::new应用构造器
由于构造器的名称与雷鸣完全不一样,所以构造器引用使用类名称::new 的格式表示,首先是一个简单的Person类:
```java
package com.jdk8methodref;

import org.junit.Test;

import java.util.function.BiFunction;
import java.util.function.Supplier;

public class DemoTest {
   


    //类名::new 引用构造器
    @Test
    public void test04(){
   
       /* Supplier<Person> supplier =() ->{
          return new Person();
        };*/

       Supplier<Person> supplier = Person::new;

        Person person = supplier.get();
        System.out.println("person = "+person);

        /*BiFunction<String,Integer,Person> biFunction = (String name,Integer age) -> {
          return new Person(name,age);
        };*/

        BiFunction<String ,Integer,Person> biFunction = Person::new;

        Person zhangsan = biFunction.apply("zhangsan", 18);
        System.out.println(zhangsan);
    }
}

执行效果:
在这里插入图片描述
数组::new 引用数组构造器
数组也是Object的子类对象,所以同样具有构造器,只是语法稍有不同

 //类型::new 
    @Test
    public void test81(){
   
        /*Function<Integer,int[]> function = (Integer length) ->{
          return new int[length];
        };*/

        Function<Integer,int[]> function = int[]::new;

        int[] arr = function.apply(10); //给int数组附一个10位的长度
        System.out.println(Arrays.toString(arr));
    }

小结
方法引用是对Lambda表达式符合特定情况下,他使得我们的Lambda表达式四更加的精简,也可以理解为Lambda表达式的缩写形式,不过要住的是方法只能“引用”已经存在的方法。

Stream流介绍

在Java 8 中,得益于Lambda所带来恩函数式编程,引入一种全新的Stream流概念,用于解决已有集合类库既有的弊端。

stream流式思想概述

注意:Stream和io流(inputStream/outputStream)没有任何关系,请暂时忘记对传统IO流的固有印象
Stream流式思想类似于工厂车间“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数进行加工处理,Sream可以看做是流水线上第一个工序,在流水线上,通过多个工序让一个原材料加工成一个商品。
在这里插入图片描述
对罐子里的材料进行逐步的加工处理,最后得到需要的数据
在这里插入图片描述
Stream API 能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去重、统计、匹配和规约

public class DemoTest01 {
   

    public static void main(String[] args) {
   
        //一个ArrayList集合中存在一下数据:张无忌,周芷若,赵敏,张强,张三丰
        //需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");

        //1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
        list.stream()
                .filter((s) -> {
   
          return s.startsWith("张");
        })
                .filter((s) ->{
   
                    return s.length() == 3;
                })
                .forEach((s) ->{
   
                    System.out.println(s);
                });

        System.out.println("------------------");
        //1.拿到所有姓张的人
        ArrayList<String> zhangList = new ArrayList<>();
        for (String name:list) {
   
            if (name.startsWith("张")){
   
                zhangList.add(name);
            }
        }

        //2.拿到名字为三个字的人
        ArrayList<String> threeList = new ArrayList<>();
        for (String name: zhangList) {
   
            if (name.length() == 3){
   
                threeList.add(name);
            }
        }
        //3.打印这些数据
        for (String name: threeList
             ) {
   
            System.out.println(name);
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值