JavaSE学习笔记——Java8-17新特性

Java8-17新特性

1 Java8新特性

1.1 Lambda表达式

Lambda表达式使用举例

public class LambdaTest{
    @Test
    public void test1(){
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("我爱北京天安门");
            }
        };
        r1.run();

        System.out.println("***********************");

        //使用Lambda表达式
        Runnable r2 = () -> System.out.println("我爱北京故宫");
        r2.run();
    }

    @Test
    public void test2(){
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };

        int compare1 = com1.compare(12,21);
        System.out.println(compare1);

        //使用Lambda表达式
        System.out.println("***********************");

        Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);

        int compare2 = com1.compare(32,21);
        System.out.println(compare2);
        //方法引用
        System.out.println("***********************");

        Comparator<Integer> com3 = Integer :: compare;

        int compare3 = com1.compare(32,21);
        System.out.println(compare3);

    }


}

Lambda表达式格式举例

/*
lambda形参列表 -> lambda体

-> :lambda操作符
-> 的左边 lambda形参列表,对应着要重写的接口方法的形参列表
-> 的右边 lamda体,对应着接口的实现类要重写的方法的方法体
*/

  1. Lambda表达式的使用,分六种情况介绍

    public class LambdaTest1 {
        //语法格式1 无参无返回值
        @Test
        public void test1() {
            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    System.out.println("我爱北京天安门");
                }
            };
            r1.run();
    
            System.out.println("***********************");
    
            //使用Lambda表达式
            Runnable r2 = () -> System.out.println("我爱北京故宫");
            r2.run();
        }
    
        //语法格式 2 Lambda需要1个参数,但是没有返回值
        @Test
        public void test2() {
    
            Consumer<String> con = new Consumer<String>() {
                @Override
                public void accept(String s) {
                    System.out.println(s);
                }
            };
    
            con.accept("谎言和誓言的区别是什么?");
    
            System.out.println("***********************");
    
            //使用Lambda表达式
            Consumer<String> con1 = (String s) -> System.out.println(s);
    
            con1.accept("一个是说的人当真了,一个是听的人当真了");
        }
    
        //语法格式3 数据类型可以省略,可以由编译器推断得出,称为"类型推断"
        @Test
        public void test3() {
            Consumer<String> con1 = (String s) -> System.out.println(s);
    
            con1.accept("如果大学可以重来,最想重来的事是啥?");
    
            Consumer<String> con2 = (s) -> System.out.println(s);
    
            con1.accept("好好努力考研!");
        }
    
        //语法格式4 Lmabda只需要一个参数的时候,小括号可以省略
        @Test
        public void test4() {
            Consumer<String> con1 = (s) -> System.out.println(s);
    
            con1.accept("世界那么大,我想去看看");
    
            System.out.println("***********************");
    
            Consumer<String> con2 = s -> System.out.println(s);
    
            con1.accept("世界那么大,我想去看看");
        }
    
        //语法格式5 Lmabda需要两个以上的参数,多条执行语句,并且能够有返回值
        @Test
        public void test5() {
            Comparator<Integer> com1 = new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    System.out.println(o1);
                    System.out.println(o2);
                    return o1.compareTo(o2);
                }
            };
    
            System.out.println(com1.compare(12,21));
    
            System.out.println("***********************");
    
            Comparator<Integer> com2 = (o1,o2) -> {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            };
    
            System.out.println(com2.compare(12,21));
        }
    
        //语法格式6 Lmabda体只有一条语句,return 与 大括号若有,都可省略
        @Test
        public void test6() {
            Comparator<Integer> com1 = (o1,o2) -> {
                return o1.compareTo(o2);
            };
    
            System.out.println(com1.compare(12,6));
    
    
            Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
        }
    
    }
    
  2. Lambda表达式的本质:

    • 一方面,Lambda表达式作为接口实现类的对象。
    • 另一方面,Lambda表达式是一个匿名函数。

1.2 函数式接口

  1. 如果接口中只声明一个抽象方法,那么此接口称为函数式接口。
  2. 只有函数式接口提供实现类的对象,才能使用Lambda表达式。
//定义函数式接口
@FunctionalInterface
public interface MyFunctionalInterfaceTest {
    void method();

}
//测试
public class MyFunctionalInterfaceTest {

    @Test
    public void test1(){
        MyFunctionalInterface m = () -> System.out.println("hello");
        m.method();
    }

}

四个基本函数接口

函数式接口 称谓 参数类型 用途
Consumer 消费型接口 T 对类型为T的对象应用操作,包含方法: void accept(T t)
Supplier 供给型接口 返回类型为T的对象,包含方法:T get()
Function 函数型接口 T 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate 判断型接口 T 确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t)

1.3 方法引用

Lambda表达式是可以简化函数式接口的变量或形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。

方法引用格式

  • 格式:使用方法引用操作符 “::” 将类(或对象) 与 方法名分隔开来。

    • 两个:中间不能有空格,而且必须英文状态下半角输入
  • 如下三种主要使用情况:

    • 情况1:对象 :: 实例方法名
    • 情况2:类 :: 静态方法名
    • 情况3:类 :: 实例方法名

方法引用使用举例

public class MethodRefTest {
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)    
/*函数式接口总的抽象方法a与内部实现时调用的对象的某个方法b的形参列表和返回值类型都相同(或一致),此时可以考虑方法b对方法a的替换、覆盖。即为方法引用 --- b为非静态方法,需要对象调用*/
@Test
public void test1() {
	Consumer<String> con1 = str -> System.out.println(str);
	con1.accept("hello");

	System.out.println("*******************");
	PrintStream ps = System.out;
	Consumer<String> con2 = ps::println;
	con2.accept("hello");
    
}

//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
	Employee emp = new Employee(1001,"Tom",23,5600);

	Supplier<String> sup1 = () -> emp.getName();
	System.out.println(sup1.get());

	System.out.println("*******************");
	Supplier<String> sup2 = emp::getName;
	System.out.println(sup2.get());

}

// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
/*
函数式接口总的抽象方法a与内部实现时调用的类的某个静态方法b的形参列表和返回值类型都相同(或一致),此时可以考虑方法b对方法a的替换、覆盖。即为方法引用 --- b为静态方法,需要类调用
*/
@Test
public void test3() {
	Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
	System.out.println(com1.compare(12,21));

	System.out.println("*******************");

	Comparator<Integer> com2 = Integer::compare;
	System.out.println(com2.compare(12,3));

}

//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
	Function<Double,Long> func = new Function<Double, Long>() {
		@Override
		public Long apply(Double d) {
			return Math.round(d);
		}
	};

	System.out.println("*******************");

	Function<Double,Long> func1 = d -> Math.round(d);
	System.out.println(func1.apply(12.3));

	System.out.println("*******************");

	Function<Double,Long> func2 = Math::round;
	System.out.println(func2.apply(12.6));
}

// 情况三:类 :: 实例方法  (有难度)
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
/*函数式接口总的抽象方法a与内部实现时调用的对象的某个方法b的返回值类型都相同(或一致),同时抽象方法a有n个参数,方法b中有n-1个参数,且抽象方法a的第一个参数作为方法b的调用者,且抽象方法a的后n-1个参数,与方法b的n-1个参数类型相同或一致。此时可以考虑方法b对方法a的替换、覆盖。即为方法引用 --- b为非静态方法,需要对象调用。形式上是使用对象a所属的类*/
@Test
public void test5() {
	Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
	System.out.println(com1.compare("abc","abd"));

	System.out.println("*******************");

	Comparator<String> com2 = String :: compareTo;
	System.out.println(com2.compare("abd","abm"));
}

//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
	BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
	System.out.println(pre1.test("abc","abc"));

	System.out.println("*******************");
	BiPredicate<String,String> pre2 = String :: equals;
	System.out.println(pre2.test("abc","abd"));
}

// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
	Employee employee = new Employee(1001, "Jerry", 23, 6000);
    Function<Employee,String> func1 = e -> e.getName();
	System.out.println(func1.apply(employee));

	System.out.println("*******************");
	Function<Employee,String> func2 = Employee::getName;
	System.out.println(func2.apply(employee));
}
}

1.4 构造器引用

当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表,就可以使用构造器引用。

格式:类名::new

构造器引用举例

public class ConstructorRefTest {
	//构造器引用
    //Supplier中的T get()
    //Employee的空参构造器:Employee()
@Test
public void test1(){  
Supplier<Employee> sup = new Supplier<Employee>() {
        @Override
        public Employee get() {
            return new Employee();
        }
    };
    System.out.println("*******************");

    Supplier<Employee>  sup1 = () -> new Employee();
    System.out.println(sup1.get());

    System.out.println("*******************");
//构造器引用
    Supplier<Employee>  sup2 = Employee :: new;
    System.out.println(sup2.get());
}

//Function中的R apply(T t)
@Test
public void test2(){
    Function<Integer,Employee> func1 = id -> new Employee(id);
    Employee employee = func1.apply(1001);
    System.out.println(employee);

    System.out.println("*******************");

    Function<Integer,Employee> func2 = Employee :: new;
    Employee employee1 = func2.apply(1002);
    System.out.println(employee1);

}

//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
    BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
    System.out.println(func1.apply(1001,"Tom"));

    System.out.println("*******************");

    BiFunction<Integer,String,Employee> func2 = Employee :: new;
    System.out.println(func2.apply(1002,"Tom"));

}
}

1.5 数组引用

当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度,就可以数组构造引用。

格式:数组类型名::new

数组引用举例

//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
    Function<Integer,String[]> func1 = length -> new String[length];
    String[] arr1 = func1.apply(5);
    System.out.println(Arrays.toString(arr1));
System.out.println("*******************");

Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
    }

1.6 Stream API

1.Stream API 关注的是多个数据的计算,面向CPU;

2.集合关注的是数据的存储,面向内存的;

3.Stream API–>集合 == SQL -->数据表的查询;

Stream的操作三个步骤

1- 创建 Stream(实例化)
一个数据源(如:集合、数组),获取一个流

2- 中间操作
每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象。因此中间操作可以是个操作链,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。

3- 终止操作(终端操作)
终止操作的方法返回值类型就不再是Stream了,因此一旦执行终止操作,就结束整个Stream操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream。

1.6.1 创建Stream实例

方式一:通过集合

Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:

  • default Stream stream() : 返回一个顺序流

  • default Stream parallelStream() : 返回一个并行流

@Test
public void test01(){
    List<Integer> list = Arrays.asList(1,2,3,4,5);
//JDK1.8中,Collection系列集合增加了方法
Stream<Integer> stream = list.stream();
}

方式二:通过数组

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

  • static Stream stream(T[] array): 返回一个流
  • public static IntStream stream(int[] array)
  • public static LongStream stream(long[] array)
  • public static DoubleStream stream(double[] array)
@Test
public void test02(){
    String[] arr = {"hello","world"};
    Stream<String> stream = Arrays.stream(arr); 
}

@Test
public void test03(){
    int[] arr = {1,2,3,4,5};
    IntStream stream = Arrays.stream(arr);
}

方式三:通过Stream的of()

可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。

  • public static Stream of(T… values) : 返回一个流
@Test
public void test04(){
    Stream<Integer> stream = Stream.of(1,2,3,4,5);
    stream.forEach(System.out::println);
}

1.6.2 一系列中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

1-筛选与切片

方 法 描 述
filter(Predicatep) 接收 Lambda , 从流中排除某些元素
distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。
若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补

2-映 射

方法 描述
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

3-排序

方法 描述
sorted() 产生一个新流,其中按自然顺序排序
sorted(Comparator com) 产生一个新流,其中按比较器顺序排序

代码举例:

public class StreamAPITest {

    //1- 筛选与切片
    @Test
    public void test1(){
//        filter(Predicate p) 接收 Lambda,从流中排除某些元素
        //遍历员工表薪资大于7000的员工信息
        List<Employee> list = EmployeeData.getEmployees();
        Stream<Employee> stream = list.stream();
        stream.filter(emp -> emp.getSalary() > 7000).forEach(System.out::println);

        System.out.println();
//        limit(long maxSize) 截断流,使其元素不超过给定数量
        list.stream().limit(4).forEach(System.out::println);
        System.out.println();
//        skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足n个,则返回一个空流。与 limit(n) 互补
        list.stream().skip(5).forEach(System.out::println);
        System.out.println();
//        distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
        list.add(new Employee(1009, "马斯克", 35, 2500.32));
        list.add(new Employee(1009, "马斯克", 35, 2500.32));
        list.add(new Employee(1009, "马斯克", 35, 2500.32));
        list.add(new Employee(1009, "马斯克", 35, 2500.32));

        list.stream().distinct().forEach(System.out::println);

    }

    //2 - 映射
    @Test
    public void test2(){
//        map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
        //转化为大写
        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
        list.stream().map(String::toUpperCase).forEach(System.out::println);

        //获取员工姓名长度大于3的员工
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().filter(emp -> emp.getName().length() > 3).forEach(System.out::println);

        //获取员工姓名长度大于3的员工姓名
        employees.stream().filter(emp -> emp.getName().length() > 3).map(emp -> emp.getName()).forEach(System.out::println);

    }

    //3 - 排序
    @Test
    public void test3(){

        //sorted 自然排序
        Integer[] arr = new Integer[]{213,434,2,232,0,-434,432,43,21};
        String[] arr1 = new String[]{"GG","DD","MM","SS","JJ"};

        Arrays.stream(arr).sorted().forEach(System.out::println);
        System.out.println("***********************************");
        Arrays.stream(arr1).sorted().forEach(System.out::println);




        //sorted(Comparator com) 定制排序
        List<Employee> list = EmployeeData.getEmployees();
        list.stream().sorted((e1,e2) -> e1.getAge() - e2.getAge()).forEach(System.out::println);

        //针对字符串从大到小排序
        Arrays.stream(arr1).sorted((s1,s2) -> -s1.compareTo(s2)).forEach(System.out::println);

    }
}

1.6.3 终止操作

  • 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

  • 流进行了终止操作后,不能再次使用。

1-匹配与查找

方法 描述
allMatch(Predicate p) 检查是否匹配所有元素
**anyMatch(Predicate p) ** 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。
相反,Stream API 使用内部迭代——它帮你把迭代做了)

2-归约

方法 描述
reduce(T identity, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional

备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。

3-收集

方 法 描 述
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,
用于给Stream中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。

另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

方法 返回类型 作用
toList Collector 把流中元素收集到List
List<Employee> emps= list.stream().collect(Collectors.toList());
方法 返回类型 作用
toSet Collector 把流中元素收集到Set
Set<Employee> emps= list.stream().collect(Collectors.toSet());
方法 返回类型 作用
toCollection Collector 把流中元素收集到创建的集合
Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
方法 返回类型 作用
counting Collector 计算流中元素的个数
long count = list.stream().collect(Collectors.counting());
方法 返回类型 作用
summingInt Collector 对流中元素的整数属性求和
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
方法 返回类型 作用
averagingInt Collector 计算流中元素Integer属性的平均值
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
方法 返回类型 作用
summarizingInt Collector 收集流中Integer属性的统计值。如:平均值
int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
方法 返回类型 作用
joining Collector 连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
方法 返回类型 作用
maxBy Collector 根据比较器选择最大值
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
方法 返回类型 作用
minBy Collector 根据比较器选择最小值
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
方法 返回类型 作用
reducing Collector 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
方法 返回类型 作用
collectingAndThen Collector 包裹另一个收集器,对其结果转换函数
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
方法 返回类型 作用
groupingBy Collector> 根据某属性值对流分组,属性为K,结果为V
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
方法 返回类型 作用
partitioningBy Collector> 根据true或false进行分区
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));

举例:

public class StreamAPITest2 {

    //1 匹配与查找
    @Test
    public void test1(){

//        allMatch(Predicate p) 检查是否匹配所有元素

        //练习:是否所有员工的年龄都大于18

        List<Employee> list = EmployeeData.getEmployees();
        System.out.println(list.stream().allMatch(emp -> emp.getAge() > 18));
        System.out.println(list.stream().anyMatch(emp -> emp.getAge() > 18));
//        anyMatch(Predicate p) 检查是否至少匹配一个元素
        //练习:是否存在员工的工资大于 10000

        System.out.println(list.stream().anyMatch(emp -> emp.getSalary() > 10000));

//        findFirst() 返回第一个元素
        System.out.println(list.stream().findFirst().get());

    }

    @Test
    public void test2(){

//        count() 返回流中元素总数
        List<Employee> list = EmployeeData.getEmployees();
        System.out.println(list.stream().filter(emp -> emp.getSalary() > 7000).count());
//        max(Comparator c) 返回流中最大值
        //返回最高的工资

        System.out.println(list.stream().map(emp -> emp.getSalary()).max((salary1, salary2) -> Double.compare(salary1, salary2)));
        System.out.println(list.stream().map(emp -> emp.getSalary()).max(Double::compare).get());

//        min(Comparator c) 返回流中最小值

        //返回最低的工资的员工
        System.out.println(list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));

//        forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
        list.stream().forEach(System.out::println);
    }

    //2 - 归约
    @Test
    public void test3(){

//        reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
        //练习 计算1-10的自然数的和
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println(list.stream().reduce(0, (x1, x2) -> x1 + x2));
        System.out.println(list.stream().reduce(10, (x1, x2) -> x1 + x2));

//        reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional


        //备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google用它来进行网络搜索而出名。

        //计算公司所有员工的工资总和
        List<Employee> employeeList = EmployeeData.getEmployees();
        System.out.println(employeeList.stream().map(emp -> emp.getSalary()).reduce((salary1, salary2) -> Double.sum(salary1, salary2)));
        System.out.println(employeeList.stream().map(emp -> emp.getSalary()).reduce(Double::sum));
    }

    //3-收集
    @Test
    public void test4(){
        List<Employee> list = EmployeeData.getEmployees();
//        collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法

        //练习1 查找工资大于6000的员工 结果返回List或者Set
        List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList());
        list1.forEach(System.out::println);
        System.out.println("*************************************");
        list.forEach(System.out::println);


        //练习2 按照员工年龄进行排序,返回一个新的List中
        System.out.println("*************************************");
        List<Employee> list2 = list.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).collect(Collectors.toList());
        list2.forEach(System.out::println);
    }
}

2 Java9新特性

2.1 Jshell

Java 终于拥有了像Python 和 Scala 之类语言的REPL工具(交互式编程环境,read - evaluate - print - loop):jShell。以交互式的方式对语句和表达式进行求值。即写即得快速运行

利用jShell在没有创建类的情况下,在命令行里直接声明变量,计算表达式,执行语句。无需跟人解释”public static void main(String[] args)”这句"废话"。

  1. 命令行中通过指令jshell
  2. 常用指令
    • /help: 获取jshell工具信息
    • /help intro: jshell工具的简介
    • /list:列出当前有效代码片段
    • /vars:查看当前创建过的变量
    • /methods:查看当前所有创建过的方法
    • /imports :列出导入的包
    • /history:输入内容的历史记录
    • /edit:使用外部代码编辑器编写Java代码
    • /exit:退出

2.2 try-catch新特性

try的前面可以定义流对象,try后面的()中可以直接引用流对象的名称。在try代码执行完毕后,流对象也可以释放掉,也不用写finally了。

格式:

A a = new A();
B b = new B();
try(a;b){
    可能产生的异常代码
}catch(异常类名 变量名){
    异常处理的逻辑
}

举例:

@Test
public void test04() {
    InputStreamReader reader = new InputStreamReader(System.in);
    OutputStreamWriter writer = new OutputStreamWriter(System.out);
    try (reader; writer) {
        //reader是final的,不可再被赋值
        //   reader = null;

    } catch (IOException e) {
        e.printStackTrace();
    }
}

3 Java10新特性

3.1局部变量类型推断

局部变量的显示类型声明,常常被认为是不必须的,给一个好听的名字反而可以很清楚的表达出下面应该怎样继续。本新特性允许开发人员省略通常不必要的局部变量类型声明,以增强Java语言的体验性、可读性。(var)

  • 使用举例

    //1.局部变量的实例化
    var list = new ArrayList<String>();
    
    var set = new LinkedHashSet<Integer>();
    
    //2.增强for循环中的索引
    for (var v : list) {
        System.out.println(v);
    }
    
    //3.传统for循环中
    for (var i = 0; i < 100; i++) {
        System.out.println(i);
    }
    
    //4. 返回值类型含复杂泛型结构
    var iterator = set.iterator();
    //Iterator> iterator = set.iterator();
    
    /*
    不适用场景
    - 声明一个成员变量
    - 声明一个数组变量,并为数组静态初始化(省略new的情况下)
    - 方法的返回值类型
    - 方法的参数类型
    - 没有初始化的方法内的局部变量声明
    - 作为catch块中异常类型
    - Lambda表达式中函数式接口的类型
    - 方法引用中函数式接口的类型
    */
    

4 Java12新特性

4.1 Switch新特性

JDK12中预览特性:

  • Java 12将会对switch声明语句进行扩展,使用case L ->来替代以前的break;,省去了 break 语句,避免了因少写 break 而出错。
  • 同时将多个 case 合并到一行,显得简洁、清晰,也更加优雅的表达逻辑分支。
  • 为了保持兼容性,case 条件语句中依然可以使用字符 : ,但是同一个 switch 结构里不能混用 -> : ,否则编译错误。
public class SwitchTest2 {
    public static void main(String[] args) {
        Fruit fruit = Fruit.GRAPE;
        int numberOfLetters = switch(fruit){
            case PEAR -> 4;
            case APPLE,MANGO,GRAPE -> 5;
            case ORANGE,PAPAYA -> 6;
            default -> throw new IllegalStateException("No Such Fruit:" + fruit);
        };
        System.out.println(numberOfLetters);
    }
}

JDK13中二次预览特性:

JDK13中引入了yield语句,用于返回值。这意味着,switch表达式(返回值)应该使用yield,switch语句(不返回值)应该使用break。

yield和return的区别在于:return会直接跳出当前循环或者方法,而yield只会跳出当前switch块。

@Test
public void testSwitch2(){
    String x = "3";
    int i = switch (x) {
        case "1" -> 1;
        case "2" -> 2;
        default -> {
            yield 3;
        }
    };
    System.out.println(i);
}

//或者
@Test
public void testSwitch3() {
    String x = "3";
    int i = switch (x) {
        case "1":
            yield 1;
        case "2":
            yield 2;
        default:
            yield 3;
    };
    System.out.println(i);
}

JDK17的预览特性:switch的模式匹配

旧写法:

static String formatter(Object o) {
    String formatted = "unknown";
    if (o instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (o instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (o instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (o instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}

模式匹配新写法:

static String formatterPatternSwitch(Object o) {
    String formatted =  switch (o) {
        case Integer i :
            yield "int" + i;
        case Long l:
            yield "long"+ l;
        case Double d:
            yield "double"+ d ;
        case String s:
            yield "String" + s;
        default:
            yield o.toString();
    };
    return formatted;
}
/*
直接在 switch 上支持 Object 类型,这就等于同时支持多种类型,使用模式匹配得到具体类型,大大简化了语法量,这个功能很实用。
*/

5 Java13新特性

5.1 文本块

使用"""作为文本块的开始符和结束符,在其中就可以放置多行的字符串,不需要进行任何转义。因此,文本块将提高Java程序的可读性和可写性。

使用:

"""
line1
line2
line3
"""
//相当于
"line1\nline2\nline3\n"
    
"line1\n" +
"line2\n" +
"line3\n"

JDK14中二次预览特性

JDK14的版本主要增加了两个escape sequences,分别是 \(取消换行)\s (空格)

6 Java14新特性

6.1 instanceof的模式匹配

instanceof 模式匹配通过提供更为简便的语法,来提高生产力。有了该功能,可以减少Java程序中显式强制转换的数量,实现更精确、简洁的类型安全的代码。instanceof 模式匹配通过提供更为简便的语法,来提高生产力。有了该功能,可以减少Java程序中显式强制转换的数量,实现更精确、简洁的类型安全的代码。JDK16中转正。

Java 14之前旧写法:

if(obj instanceof String){
    String str = (String)obj; //需要强转
    .. str.contains(..)..
}else{
    ...
}

Java 14新特性写法:

if(obj instanceof String str){//加变量名,不用显示强转
    .. str.contains(..)..
}else{
    ...
}

6.2 record

record是一种全新的类型,它本质上是一个final类,同时所有的属性都是final修饰,它会自动编译出public gethashcodeequalstoString`、构造器等结构,减少了代码编写量。

具体来说:当你用record 声明一个类时,该类将自动拥有以下功能:

  • 获取成员变量的简单方法。注意区别于我们平常getter()的写法。
  • 一个 equals 方法的实现,执行比较时会比较该类的所有成员属性。
  • 重写 hashCode() 方法。
  • 一个可以打印该类所有成员属性的 toString() 方法。
  • 只有一个构造方法。

此外:

  • 还可以在record声明的类中定义静态字段、静态方法、构造器或实例方法。

  • 不能在record声明的类中定义实例字段;类不能声明为abstract;不能声明显式的父类等。

record的设计目标是提供一种将数据建模为数据的好方法。它也不是 JavaBeans 的直接替代品,因为record的方法不符合 JavaBeans 的 get 标准。另外 JavaBeans 通常是可变的,而记录是不可变的。尽管它们的用途有点像,但记录并不会以某种方式取代 JavaBean。


7 Java15新特性

7.1 密封类

在 Java 中如果想让一个类不能被继承和修改,这时我们应该使用 final 关键字对类进行修饰。不过这种要么可以继承,要么不能继承的机制不够灵活,有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承,是做不到的。Java 15 尝试解决这个问题,引入了 sealed 类,被 sealed 修饰的类可以指定子类。这样这个类就只能被指定的类继承。

JDK15的预览特性:

通过密封的类和接口来限制超类的使用,密封的类和接口限制其它可能继承或实现它们的其它类或接口。

具体使用:

  • 使用修饰符sealed,可以将一个类声明为密封类。密封的类使用保留关键字permits列出可以直接扩展(即extends)它的类。

  • sealed 修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰,且只能是 finalsealednon-sealed 三者之一。

public abstract sealed class Shape permits Circle, Rectangle, Square {...}//sealed指定类才可被继承

public final class Circle extends Shape {...} //final表示Circle不能再被继承了

public sealed class Rectangle extends Shape permits TransparentRectangle{...}//sealed只能被指定的类继承

public non-sealed class Square extends Shape {...} //non-sealed表示可以允许任何类继承

8 API的变化

8.1 Optional类

到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google在著名的Guava项目引入了Optional类,通过检查空值的方式避免空指针异常。受到Google的启发,Optional类已经成为Java 8类库的一部分。

Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象。

/*
1.为什么需要Optional类?
为了避免空指针异常

2.如何实例化
static  Optional ofNullable(T value) :用来创建一个Optional实例,value可能是空,也可能非空

3.常用方法
T orElse(T other) :orElse(T other) 与ofNullable(T value)配合使用,如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替
*/

public class OptionalTest {
    @Test
    public void test() {

        String value = "小明";
        value = null;

        //Optional类避免空指针异常
        //1.实例化
        //ofNullable(T value) :用来创建一个Optional实例,value可能是空,也可能非空
        Optional<String> optional = Optional.ofNullable(value);

        //orElse(T other) 与ofNullable(T value)配合使用,
        // 如果Optional的value不为null,就返回所value,如果为空,就返回other

        String other = "小洪";


        String s = optional.orElse(other);
        System.out.println(s.toString());
    }

    @Test
    public void test2(){
        String value = "小明";
        Optional<String> optional = Optional.ofNullable(value);
        //get()取出value值
        System.out.println(optional.get());
    }

}

8.2 String

String、StringBuffer 与 StringBuilder 再也不用 char[] 来存储,改成了 byte[] 加上编码标记,节约了一些空间。

JDK11新特性:新增了一系列字符串处理方法

描述 举例
判断字符串是否为空白 " ".isBlank(); // true
去除首尾空白 " Javastack ".strip(); // “Javastack”
去除尾部空格 " Javastack “.stripTrailing(); // " Javastack”
去除首部空格 " Javastack ".stripLeading(); // "Javastack "
复制字符串 “Java”.repeat(3);// “JavaJavaJava”
行数统计 “A\nB\nC”.lines().count(); // 3

JDK12新特性:String 实现了 Constable 接口

Keep Going!

你可能感兴趣的:(学习,java,开发语言)