New features of JDK8 (Lambda expression and Stream streaming programming)

Table of contents

1: New features of JDK8

1. The development history of Java SE

2. Understand Open JDK and Oracle JDK

3. New features of JDK 8

3.1 Lambda expression (emphasis)

3.2 Enhancement of interface

3.3 Functional Interface

3.4 Method References

3.5 Collection Stream operation (emphasis)

3.6 New time and date API


1: New features of JDK8

1. The development history of Java SE

        Sun established a project called the Green Project in 1991, led by Dr. James Gosling (Gosling). The purpose of the Green Project is to develop an , radio, etc.) The product of this project is the predecessor of the Java language: Oak (oak tree). Oak was not considered successful in the consumer goods market at that time, but with the rise of the Internet trend in 1995, Oak quickly found the most suitable market position for its own development.

Now let's introduce the updated version and naming of JDK:

JDK Beta - 1995 JDK 1.0 - January 1996 (the real first stable version JDK 1.0.2, known as Java 1) JDK 1.1 - February 1997

J2SE 1.2 - December 1998

J2ME (Java 2 Micro Edition, a micro version of the Java 2 platform), used in mobile, wireless and limited resource environments

J2SE (Java 2 Standard Edition, the standard version of the Java 2 platform), applied to the desktop environment.

J2EE (Java 2 Enterprise Edition, Enterprise Edition of the Java 2 platform), used in Java-based application servers.

J2SE 1.3 - May 2000

J2SE 1.4 - February 2002

J2SE 5.0 - September 2004

Java SE 6 - December 2006

Java SE 7 - July 2011

Java SE 8 (LTS) - March 2014

Java SE 9 - September 2017

Java SE 10 (18.3) - March 2018

Java SE 11 (18.9 LTS) - September 2018

Java SE 12 (19.3) - March 2019

Java SE 13 (19.9) - September 2019

........................................................

2. Understand Open JDK and Oracle JDK

(1) Open JDK source

        Java was invented by Sun, and Open JDK is a project formed by Sun to open source Java at the end of 2006. That is to say, Open JDK is an open source and free implementation of the Java SE platform version. It is supported by SUN and the Java community. In 2009, Oracle acquired Sun. Since then, SUN, one of the maintainers of Java, has also become Oracle.

(2) The relationship between Open JDK and Oracle JDK

        Most JDKs are further written and implemented on the basis of Open JDK, such as IBM J9, Oracle JDK and Azul Zulu, Azul Zing. Oracle JDK is fully developed by Oracle Corporation, and Oracle JDK is a commercial version based on the Open JDK source code; in addition, it contains closed source components. Oracle JDK is licensed under the Binary Code License Agreement, and public updates to Oracle Java SE 8 released after January 2019 will not be available for commercial or production use without a commercial license. But Open JDK is completely open source and can be used freely.

 (3) Open JDK official website introduction

Open JDK official website: https://openjdk.org/

JDK Enhancement Proposals (JDK Enhancement Proposals); popularly speaking, JEP is a new feature of JDK! 

3. New features of JDK 8

3.1 Lambda expression (emphasis)

(1) Problems with anonymous inner classes

We first write the implementation of a thread, through the anonymous inner class; due to the object-oriented syntax requirements, first create an anonymous inner class object of the Runnable interface to specify the task content to be performed by the thread, and then hand it over to a thread to start .

public class Test {
    public static void main(String[] args) {
        // 匿名内部类
        new Thread(new Runnable() {
            public void run() {
                System.out.println("Hello");
            }
        }).start();
    }
}

Code analysis: For the usage of Runnable's anonymous inner class, several points can be analyzed:

①The Thread class requires the Runnable interface as a parameter, and the abstract run method is the core used to specify the content of the thread task. In order to specify the method body of the run, the implementation class of the Runnable interface has to be required.

② In order to save the trouble of defining a Runnable implementation class, an anonymous inner class has to be used.

③ The abstract run method must be overwritten and rewritten, so the method name, method parameters, and method return value have to be written again, and cannot be written wrong; in fact, it seems that only the method body is the key.

(2) First experience with Lambda expressions

Lambda is an anonymous function , which can be understood as a piece of code that can be passed; with the new syntax of Java8, the anonymous inner class writing method of the above Runnable interface can achieve the same effect through simpler Lambda expressions!

public class Test {
    public static void main(String[] args) {
        // Lambda表达式
        new Thread(() -> 
            System.out.println("Hello")).start();
    }
}

Code analysis: This code is exactly the same as the execution effect just now, and can be passed at the compilation level of JDK8 or higher.

① It can be seen from the semantics of the code: we start a thread, and the content of the thread task is specified in a more concise form; we only need to put the code to be executed in a Lambda expression, no need to define a class , no object creation is required .

②Advantages of Lambda: Simplify the use of anonymous inner classes, and the syntax is simpler; in fact, Lambda is shorthand for anonymous inner classes.

(3) Standard format of Lambda

A Lambda expression is an anonymous function, and a function is equivalent to a method in Java ; Lambda omits object-oriented rules and regulations, and the standard format of Lambda consists of 3 parts:

(参数类型 参数名称) -> {
 代码体;
}

Format specification:

①(parameter type parameter name): parameter list;

②{code body;}: method body;

③-> : Arrow, which separates the parameter list and method body, and plays the role of connection;

Implementation comparison of Lambda and method

For example: public static void main(String[] args), Lambda is in this form (String[] args); modifier list, return value, method name can be omitted:

anonymous inner class

public void run() {
  System.out.println("aa");
}

Lambda expression

() -> System.out.println("bb")

(4) Lambda with no parameters and no return value

Define an interface with a parameterless method returning void:

package com.zl;

public interface Swimmable {
    // 抽象方法,省略了public static
    void swimming();
}

Test class:

package com.zl;

public class SwimmableTest {
    public static void main(String[] args) {
        // 使用匿名内部类
        play(new Swimmable() {
            @Override
            public void swimming() {
                System.out.println("匿名内部类游泳!");
            }
        });

        // 使用Lambda表达式:对匿名内部类的简写
        play(()-> System.out.println("Lambda的游泳!"));
    }
    // 调用方法
    public static void play(Swimmable s){
        s.swimming();
    }
}

(5) Lambda with parameters and return value

Define an interface with a parameter method returning int type:

package com.zl;

public interface Love {
    int myLove(String name);
}

test class

package com.zl;

public class LoveTest {
    public static void main(String[] args) {
        // 匿名内部类
        lovers(new Love() {
            @Override
            public int myLove(String name) {
                System.out.println("匿名内部类的方式");
                return 1;
            }
        });
        // Lambda表达式,多行代码需要一个大括号{}
        lovers((String name) ->{
            System.out.println("Lambda表达式的方式");
            return 1;
        });
    }

    // 调用方法
    public static void lovers(Love love){
        int count = love.myLove("小红");
        System.out.println("返回值是"+count);
    }
}

Small summary:

① When we call the method in the future, we can consider using Lambda expression when we see that the parameter is an interface. Lambda expression is equivalent to rewriting the abstract method in the interface.

② Comparison of anonymous inner class and Lambda expression results:

Anonymous inner class: A new class will be formed after compilation, called: class name $.class, which can be opened directly.

Lambda expression: No new class will be generated after compilation (the class will be formed when it is running), it is the original class, but at this time the original class cannot be opened, and the decompilation tool cannot be decompiled; we use JDK's own A tool: javap, disassemble the bytecode, view the bytecode instruction: javap -c -p filename.class, and find that a private static method will be generated in this class; in fact, the lambda expression The code will be placed in this newly added static method.

Summary: Anonymous inner class will be a class file when compiling; Lambda will form a class when the program is running: add a new method in the class, the method body of this method is the code in the Lambda expression, and will also form an anonymous Inner class implements the interface, rewrites the abstract method, and calls the newly generated method in the rewritten method of the interface.

(6) Lambda omitted format

Based on the Lambda standard format, the rules for using ellipsis are:

① The type of parameters in parentheses can be omitted;

② If there is only one parameter in the parentheses, the parentheses can be omitted;

③If there is one and only one statement inside the braces, you can omit the braces, the return keyword and the statement semicolon at the same time;

For a Lambda expression:

(int a) -> { 
    return new Person(); 
} 

The first step is omitted: omit the type

(a) -> { 
    return new Person(); 
}

 The second step is omitted: when there is only one parameter, the parentheses can also be omitted

a -> { 
    return new Person(); 
}

The third step is omitted: if there is only one statement in the brackets, omit the braces, the return keyword, and the closing semicolon (must be omitted at the same time)

a -> new Person()

Example: When calling the Collectons.sort() method to sort, the parameter inside can only be a List collection; for sorting a custom type, a comparator needs to be passed (comparison logic is written in the comparator)

Person class:

package com.zl.mapper;

public class Person {
    private String name;
    private int age;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

PersonSort class: put the above Person into the List collection, and then sort

package com.zl.mapper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class PersonSort {
    public static void main(String[] args) {
        // 创建一个ArrayList集合
        List<Person> pList = new ArrayList<>();
        // 准备数据
        Person p1 = new Person("张三", 20);
        Person p2 = new Person("李四", 19);
        Person p3 = new Person("王五", 22);
        // 把数据添加到集合当中
        pList.add(p1);
        pList.add(p2);
        pList.add(p3);
        // 调用Collections工具类的sort方法进行排序
        // 第一种方法:采用匿名内部类的方式
        Collections.sort(pList, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });
        // 第二种方式:采用Lambda表达式
        Collections.sort(pList,(Person o1,Person o2)->{
            return o1.getAge()-o2.getAge();
        });
        // 第三种方式:使用Lambda表达式的省略模式
        Collections.sort(pList,((o1, o2) -> o1.getAge()-o2.getAge()));

        // 打印
        for (Person person : pList) {
            System.out.println(person);
        }
        // 打印的第二种方式
        pList.forEach(person -> System.out.println(person));

    }
}

(7) Prerequisites for Lambda

The syntax of Lambda is very concise, but there are several conditions to pay special attention to when using Lambda expressions:

①The parameter or local variable type of the method must be an interface to use the Lambda expression;

②There is one and only one abstract method in the interface;

How to determine that there is only one abstract method in the interface? Use a functional interface

Functional interface in Java refers to: an interface with one and only one abstract method.

②Functional interface, that is, an interface suitable for functional programming scenarios. The embodiment of functional programming in Java is Lambda , so the functional interface is an interface that can be applied to Lambda . Only by ensuring that there is one and only one abstract method in the interface can Lambda in Java be deduced smoothly.

③ Use the FunctionalInterface annotation, which is similar to the @Override annotation. Java 8 introduces a new annotation for functional interfaces: @FunctionalInterface. This annotation can be used on the definition of an interface

@FunctionalInterface 
public interface Operator { 
  void myMethod(); 
} 

④Once this annotation is used to define an interface, the compiler will forcibly check whether the interface does have one and only one abstract method, otherwise an error will be reported. However, even if this annotation is not used, as long as the definition of a functional interface is met, this is still a functional interface, and it works the same!

(8) Comparison between Lambda and anonymous inner class

Know the difference between Lambda and anonymous inner class in use?

①The required types are different

Anonymous inner class: The required type can be: class, abstract class, interface.

Lambda expression: The required type must be an interface .

②The number of abstract methods is different

Anonymous inner classes : As many abstract methods as you want in the interface .

Lambda expressions: Only one abstract method can be present in the required interface.

③ Different implementation principles:

Anonymous inner class: It will form a class after compilation.

Lambda expression: It is to dynamically generate classes when the program is running.

Summary: On the one hand, the Lambda expression is an object of the implementation class of the interface, on the other hand, the Lambda expression is an anonymous function; when there is only one abstract method in the interface, it is recommended to use the Lambda expression; in other cases, it is still necessary to use the anonymous inner class !

3.2 Enhancement of interface

(1) Addition of interfaces in JDK8

In JDK8, the interface has been enhanced. Before JDK8, there can only be constants and abstract methods in the interface.

interface 接口名{
    静态常量;
    抽象方法;
}

After JDK8, the interface has been added, and the interface can have default methods and static methods

interface 接口名{
    静态常量;
    抽象方法;
    默认方法;
    静态方法;
}

(2) Default method

We first create an interface, and then create a class to implement this interface, and we must override the methods in the interface!

Anmail interface

package com.zl.anmails;

public interface Anmails {
    // 抽象方法
    void fly();
}

The Bird class implements the interface and overrides the methods inside

package com.zl.anmails;

public class Bird implements Anmails{
    @Override
    public void fly() {
        System.out.println("小鸟起飞!");
    }
}

What if we add other methods to the interface at this time? Then the implementation class must rewrite this abstract method, which is not conducive to the extension of the interface; so we can define it as a default method, and the syntax format is as follows:

interface 接口名{
    修饰符 default 返回值类型 方法名{
        方法体;
   }
}

Define a default method in the interface:

①This default method, the implementation class will inherit the past by default, and the inherited method is exactly the same as the parent class!

Of course, you can also rewrite and write your own business logic in the method body.

package com.zl.anmails;

public interface Anmails {
    // 抽象方法
    void fly();
    // 默认方法
    public default void eat(){
        System.out.println("小鸟爱吃虫子");
    }
}

(3) Static method

In JDK8, a static method has been added to the interface, which is also used for the extension of the interface. The syntax rules are as follows:

interface 接口名{
    修饰符 static 返回值类型 方法名{
        方法体;
   }
}

Add a static method to the interface:

①The static method in the interface cannot be overridden in the implementation class, in other words, it is not inherited by default.

②Calling can only be called through the interface type : interface name. static method name () ; use polymorphism to create objects, and use object. to access , because there is no such static method in the implementation class.

package com.zl.anmails;

public interface Anmails {
    // 抽象方法
    void fly();

    // 默认方法
    public default void eat() {
        System.out.println("小鸟爱吃虫子");
    }

    // 静态方法
    public static void tryCatch(){
        System.out.println("小鸟尝试抓虫子");
    }
}

The difference between default method and static method:

①The default method is called through the instance, and the static method is called through the interface name.

②The default method can be inherited, and the implementation class can directly call the default method of the interface, or override the default method of the interface; static methods cannot be inherited, and the implementation class cannot override the static method of the interface, and can only be called using the interface name.

3.3 Functional Interface

① If the interface declares only one abstract method, the interface is called a functional interface. Simply put, in Java8, a Lambda expression is an instance of a functional interface ; this is the relationship between a Lambda expression and a functional interface.

② We know that the prerequisite for using Lambda expressions is the need for a functional interface, and Lambda expressions do not care about the interface name and abstract method name when using Lambda expressions; they only care about the parameter list and return value type of the abstract method. Therefore, in order to make it more convenient for us to use Lambda expressions, a large number of commonly used functional interfaces are provided in the JDK.

custom functional interface

package com.zl.anmails;

public class Computer {
    public static void main(String[] args) {
        // 调用fun方法
        fun((arr) -> {
            int sum = 0;
            for (int i : arr) {
                sum += i;
            }
            return sum;
        });
    }
    // 调用求和的方法
    public static void fun(Operator operator){
        int[] arr = {1,2,3,4};
        int sum = operator.getSum(arr);
        System.out.println("sum = "+sum);
    }
}

/**
 * 函数式接口
 */
// 用来求和的接口
@FunctionalInterface 
interface Operator{
    int getSum(int[] arr);
}

Functional interfaces are provided for us in the JDK, mainly in the java.util.function package!

Four core functional interfaces

Note: The following interfaces are already provided, and they are similar to the function interface Opera we customized above!

functional interface title Parameter Type use
Consumer<T> consumer interface T Applies an operation to an object of type T, containing the methods:void accept(T t)
Supplier<T> supply interface none Returns an object of type T, containing the methods:T get()
Function<T, R> functional interface T Applies an operation to an object of type T, and returns the result. The result is an object of type R. Include method:R apply(T t)
Predicate<T> Judgmental interface T Determines whether an object of type T satisfies a constraint, and returns a boolean value. Include method:boolean test(T t)

(1)Supplier

Supplier is an interface with no parameters and a return value . For the Lambda expression, it is necessary to provide a return data type.

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

Example: To find the maximum value in a set of data, instead of defining interfaces and abstract methods by yourself, use the Supplier functional interface as a parameter

package com.zl.fun;
import java.util.Arrays;
import java.util.function.Supplier;

public class SupplierFun {

    public static void main(String[] args) {
        // 使用Lambda表达式,相当于重写get()方法的逻辑
        fun(()->{
            int arr[] = {1,2,7,8,4,5,6};
            // 排序
            Arrays.sort(arr);
            // 找到最后一个元素就是最大值
            return arr[arr.length-1];

        });
    }

    // 调用接口中的方法
    public static void fun(Supplier<Integer> supplier){
        Integer max = supplier.get();
        System.out.println("max = "+max);
    }
}

(2)Consumer

Consumer is an interface with parameters and no return value . The Supplier interface introduced earlier is used to produce data, while the Consumer interface is used to consume data. When using it, you need to specify a generic type to define the parameter type

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

Example: Uniformly convert the input data to lowercase output

package com.zl.fun;

import java.util.function.Consumer;

public class ConsumerFun {
    public static void main(String[] args) {
        fun((msg)->{
            // 小写转大写
            String s = msg.toLowerCase();
            System.out.println(msg+"对应的小写"+s); // Hello AAA对应的小写hello aaa
        });
    }

    // 调用接口中的方法
    public static void fun(Consumer<String> consumer){
        consumer.accept("Hello AAA");
    }
}

Default method: andThen

If the parameters and return value of a method are all of Consumer type, then the effect can be achieved. When consuming a piece of data, first do an operation, and then do another operation to realize the combination, and this method is the default method andThen in the Consumer interface method

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}

Specific operation:

package com.zl.fun;

import java.util.Locale;
import java.util.function.Consumer;

public class ConsumerAndFun {
    public static void main(String[] args) {
        fun(msg1->{
            System.out.println(msg1 + "-> 转换为小写:" + msg1.toLowerCase());
        },msg2->{
            System.out.println(msg2 + "-> 转换为大写:" + msg2.toUpperCase(Locale.ROOT));
        });
    }

    // 调用接口中的方法
    public static void fun(Consumer<String> c1,Consumer<String> c2){
        String str = "HELLO world";
        c1.accept(str);
        c2.accept(str);
        // 上面就等价于
        c1.andThen(c2).accept(str);
    }
}

(3)Function

Function is an interface with parameters and a return value . The Function interface obtains data of another type based on data of one type. The former is called a precondition, and the latter is called a postcondition.

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

Example: passing in a string returns a number

package com.zl.fun;

import javax.print.DocFlavor;
import java.util.function.Function;

public class FuncationFun {
    public static void main(String[] args) {
        fun((num)->{
            Integer number = Integer.parseInt(num);
            return number;
        });
    }

    // 调用接口中的方法
    public static void fun(Function<String, Integer> function){
        Integer apply = function.apply("666");
        System.out.println("applay = "+apply);
    }
}

Default method: andThen, also used for combined operations

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
   }

Specific operation:

package com.zl.fun;

import java.util.function.Function;

public class FuncationAndthenFun {

    public static void main(String[] args) {
        fun(num1->{
            return Integer.parseInt(num1);
        },num2->{
            return num2*10+6;
        });
    }
    // 调用接口中的方法
    public static void fun(Function<String, Integer> f1,Function<Integer,Integer> f2){
        String str = "666";
        Integer i1 = f1.apply(str);
        Integer i2 = f2.apply(i1);
        System.out.println("i2 = "+i2);
    }
}

Note: The action order of the default compose method is just opposite to that of the andThen method! The static method identity is to return whatever parameters are input!

(4)Predicate

Predicate is an interface with parameters and a return value of Boolean.

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

Example: Find whether the length of a string is greater than 5

package com.zl.fun;

import java.util.function.Predicate;

public class PredicateFun {
    public static void main(String[] args) {
        fun(msg->{
            return msg.length() > 5;
        },"Hello World");
    }

    // 调用接口中的方法
    public static void fun(Predicate<String> p,String str){
        boolean b = p.test(str);
        System.out.println("b = "+b);
    }
}

The default method in Predicate provides logical relationship operations and, or, negate, isEquals methods

package com.zl.fun;

import java.util.function.Predicate;

public class PredicateTest {
    public static void main(String[] args) {
        fun(msg1->{
            return msg1.contains("H");
        },msg2->{
            return msg2.contains("W");
        });
    }

    // 调用接口中的方法
    public static void fun(Predicate<String> p1, Predicate<String> p2){
        // 1. 判断p1包含H,同时P2包含W
        boolean b1 = p1.and(p2).test("Hello World");
        System.out.println(b1);
        // 2. 判断p1包含H,或者P2包含W
        boolean b2 = p1.or(p2).test("Hello World");
        System.out.println(b2);
        // 3.结果取反,表示p1不包含H
        boolean b3 = p1.negate().test("HelloWorld");
        System.out.println(b3);
        // 4. 判断两个结果是否相等
        boolean bb1 = p1.test("Hello");
        boolean bb2 = p2.test("World");

    }

}

3.4 Method References

①Lambda expression is a grammar that can simplify the assignment of variables or formal parameters of functional interfaces; while method references and constructor references are used to simplify Lambda expressions!

②When the operation to be passed to the Lambda body has an implementation method, you can use the method reference! Method reference can be regarded as a deep expression of Lambda expression. In other words, a method reference is a Lambda expression, that is, an instance of a functional interface, pointing to a method through the method name, which can be considered as a syntactic sugar of a Lambda expression.

③Essence of method reference: method reference is an instance of functional interface!

(1) Why use method references?

When using Lambda expressions, there will also be code redundancy, such as: using Lambda expressions to find the sum of an array

package com.zl.method;

import java.util.function.Consumer;

public class Test {
    public static void main(String[] args) {
        printSum(arr->{
            int sum = 0;
            for (int i : arr) {
                sum += i;
            }
            System.out.println("数组之和:" + sum);
        });

    }

    /**
     * 求数组的和,和上面功能代码相同
     * @param arr
     */
    public static void getSum(int arr[]){
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        System.out.println("数组之和:" + sum);
    }

    public static void printSum(Consumer<int[]> consumer){
        int[] arr = {30,10,26,18,45,9};
        consumer.accept(arr);
    }
}

The logical business of the above two methods is obviously the same, and the code is redundant, so that the reference of the method can be used

package com.zl.method;

import java.util.function.Consumer;

public class Test {
    public static void main(String[] args) {
        // 引用下面相同功能的代码
        printSum(Test::getSum);
    }

    /**
     * 求数组的和,和上面功能代码相同
     * @param arr
     */
    public static void getSum(int arr[]){
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        System.out.println("数组之和:" + sum);
    }

    public static void printSum(Consumer<int[]> consumer){
        int[] arr = {30,10,26,18,45,9};
        consumer.accept(arr);
    }
}

(2) Format of method reference

Symbolic representation: ::

Symbol description: The double colon is a method reference operator, and the expression it is in is called a method reference

Application scenario: If the solution to be implemented by the Lambda expression already has the same solution in other methods, then method references can be used!

The use of method references in JDK8 is quite flexible. Common reference methods have the following forms:

①instanceName::methodName 对象::方法名
②ClassName::staticMethodName 类名::静态方法
③ClassName::methodName 类名::普通方法

(3) Object name:: method name

The most common usage; if a member method already exists in a class, you can refer to the member method by the object name

package com.zl.method;

import java.util.Date;
import java.util.function.Supplier;

public class MethodTest01 {
    public static void main(String[] args) {
        // 练习1:
        // 第一种方式:匿名内部类
        Consumer<String> c1 = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        c1.accept("张三");

        // 方法二:Lambda表达式
        Consumer<String> c2 = (msg)->{
            System.out.println(msg);
        };
        c2.accept("李四");

        // 方法三:方法引用
        Consumer<String> c3 = System.out::println;
        c3.accept("王五");

        // 练习2:
        Date date = new Date();
        Supplier<Long> supplier = ()->{ return date.getTime();};
        System.out.println(supplier.get());
        // 通过 方法引用 的方式处理
        Supplier<Long> supplier = date::getTime;
        System.out.println(supplier.get());
    }
}

Summary: The abstract method a in the functional interface has the same formal parameter list and return value type of a method b of the object called when it is internally implemented (or the same: such as polymorphism, automatic unboxing, etc.), then we Method b can be used to rewrite and replace method a. This method b is a non-static method that requires an object to call!

(4) Class name:: static method name

It is also a more common way to call static methods through class names!

The original way:

package com.zl.method;

import java.util.Date;
import java.util.function.Supplier;

public class MethodTest01 {
    public static void main(String[] args) {
        fun(()->{
            return System.currentTimeMillis();
        });
        
        // 采用方法引用的方式
       Supplier<Long> s = System::currentTimeMillis;
        System.out.println(s.get());
    }

    public static void fun(Supplier<Long> supplier){
        Long time = supplier.get();
        System.out.println(time);
        // 上面就等价于
        System.out.println(supplier.get());
    }
}

The streamlined way:

public class FunctionRefTest04 {
    public static void main(String[] args) {
        Supplier<Long> supplier1 = ()->{
            return System.currentTimeMillis();
       };
        System.out.println(supplier1.get());

        // 通过 方法引用 来实现
        Supplier<Long> supplier2 = System::currentTimeMillis;
        System.out.println(supplier2.get());
   }
}

Summary: The abstract method a in the functional interface has the same formal parameter list and return value type of a method b of the class called when it is internally implemented (or the same: such as polymorphism, automatic unboxing, etc.), then we Method b can be used to rewrite and replace method a. This method b is a static method that requires a class to call!

(5) Class name:: reference instance method (difficult)

①In Java object-oriented, the class name can only call the static method, and the class name refers to the instance method is premised, in fact, the first parameter is used as the caller of the method!

②The abstract method has two parameters (n), and the internally implemented method has one parameter (n-1). The n parameters can be divided into 1 and n-1, and the first parameter is the caller of the method!

Example 1:

package com.zl.methods;

import java.util.Comparator;

public class Test03 {
    public static void main(String[] args) {

        // 第一种:匿名内部类
        Comparator<String> com1 = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) { // 两个参数
                return o1.compareTo(o2); // 一个参数
            }
        };
        System.out.println(com1.compare("abc", "abd"));
        // 第二种:Lambda
        Comparator<String> com2 = (s1,s2)->{
            return s1.compareTo(s2);
        };
        System.out.println(com2.compare("abc", "abd"));
        // 第三种:类::实例方法
        Comparator<String> com3 = String::compareTo;
        System.out.println(com3.compare("abc","abb"));

    }
}

 Example 2:

package com.zl.method;

import java.util.function.Function;

public class MethodTest02 {
    public static void main(String[] args) {
        Function<String,Integer> function = (s)->{
            return s.length();
        };
        System.out.println(function.apply("hello"));

        // 使用方法引用
        Function<String,Integer> function2 = String::length;
        System.out.println(function2.apply("hello"));
    }


}

Summary: The return value type of the abstract method a in the functional interface and a method b of the object called by its internal implementation are the same. At the same time, there are n parameters in abstract method a, and n-1 parameters in method b, and the first parameter of abstract method a is the caller of method b, and the last n-1 parameters of abstract method a are the same as the method The types of n-1 parameters of b are the same (or consistent: such as polymorphism, automatic unboxing, etc.) . Then we can use method b to rewrite and replace method a. This method b is a non-static method that requires an object to call, but it is formally written as the class to which object a belongs!

(6) Class name::constructor

Since the name of the constructor is exactly the same as the name of the class, the constructor reference uses the format of ::new!

ClassName::new 类名::new

 Person class: provide parameterized and parameterless construction methods

package com.zl.method;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

test:

Note: If you use a constructor with one parameter, you can define it as Function; if you define it as a constructor with two parameters, use BiFunction; the final return is a Person type!

package com.zl.method;

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

public class MythodTest03 {
    public static void main(String[] args) {
        Supplier<Person> supplier = ()->{
            return new Person();
        };
        System.out.println(supplier.get());

        // 通过 方法引用来实现
        Supplier<Person> supplier1 = Person::new;
        System.out.println(supplier1.get());
        // 也可以给属性赋值
        BiFunction<String ,Integer,Person> function = Person::new;
        System.out.println(function.apply("张三",22)); // 底层调用有参数构造方法

    }
}

Summary: A certain constructor in the class corresponding to the class name is called, which constructor in the class is called specifically? Depends on the parameter list of the abstract method of the functional interface!

(7) Array::Constructor

When the Lambda expression creates an array object and satisfies the formal parameter of the Lambda expression, which happens to be the length of the array object created, the array construction reference can be used.

Format:数组类型名::new

TypeName[]::new 数组名[]::new 

 Example 1: Pass a number and return an array of the corresponding length

public static void main(String[] args) {
        Function<Integer,String[]> fun1 = (len)->{
            return new String[len];
       };
        String[] a1 = fun1.apply(3);
        System.out.println("数组的长度是:" + a1.length);

        // 方法引用 的方式来调用数组的构造器
        Function<Integer,String[]> fun2 = String[]::new;
        String[] a2 = fun2.apply(5);
        System.out.println("数组的长度是:" + a2.length);
   }

Summary: Method reference is an abbreviation for Lambda expressions that meet specific circumstances. It makes our Lambda expressions more concise, and can also be understood as an abbreviation of lambda expressions. However, it should be noted that method references can only Reference to an existing method.  

3.5 Collection Stream operation (emphasis)

① There are two most important changes in Java8. The first is a Lambda expression; the other is a Stream API.

Stream API (java.util.stream) introduces the real functional programming style into Java . This is by far the best supplement to the Java class library, because the Stream API can greatly improve the productivity of Java programmers, allowing programmers to write efficient, clean, and concise code.

③Stream is the key abstraction concept for processing collections in Java8. It can specify the operations you want to perform on collections, and can perform very complex operations such as searching, filtering and mapping data. Using the Stream API to operate on collection data is similar to performing database queries using SQL. You can also use the Stream API to perform operations in parallel. In short, the Stream API provides an efficient and easy-to-use way of processing data.

(1) What is Stream?

①In actual development, most data sources in the project come from MySQL, Oracle, etc. But now there are more data sources, such as MongDB, Radis, etc., and these NoSQL data need to be processed at the Java level.

②Stream is a data channel for manipulating the sequence of elements generated by data sources (collections, arrays, etc.).

③The difference between Stream and Collection collection: Collection is a static memory data structure, talking about data , while Stream is about calculation, talking about data calculation (sorting, searching, filtering, mapping, traversal, etc.) . The former is mainly oriented to the memory and stored in the memory, while the latter is mainly oriented to the CPU , and the calculation is realized through the CPU.

Notice:

①Stream itself does not store elements.

②Stream will not change the source object. Instead, they return a new Stream holding the result.

③ Stream operations are delayed. This means that they wait until the result is needed; i.e. once the terminating operation is executed, the chain of intermediate operations is executed and the result is produced.

④ Once the Stream has executed the termination operation, it cannot call other intermediate operations or termination operations.

(2) Three steps in the operation of Stream?

1- Create a Stream

Stream instantiation: a data source (such as: collection, array), get a stream!

2- Intermediate operations

Each processing will return a new Stream holding the result, that is, the return value of the method of the intermediate operation is still an object of the Stream type. Therefore, the intermediate operation can be one 操作链, and the data of the data source can be processed n times, but it will not be actually executed before the final operation.

3- Terminate operation (terminal operation)

The return value type of the method of terminating the operation is no longer Stream, so once the terminating operation is executed, the entire Stream operation is ended. Once the terminating operation is executed, the chain of intermediate operations is executed, finally producing the result and ending the Stream.

(3) Create a Stream instance

Method 1: through collection

 The Collection interface in Java8 is extended to provide two methods for obtaining streams:

default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流

example:

package com.zl.methods;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class Test04 {
    public static void main(String[] args) {
        // 创建一个数组
        int[] arr = {1,2,3,4,5,6};
        // 把一个数组转换成一个集合
        List<int[]> list = Arrays.asList(arr);
        // 得到Stream实例(返回一个顺序流)
        Stream<int[]> stream = list.stream();
        // 得到Stream实例(返回一个并行流)
        Stream<int[]> stream1 = list.parallelStream();
    }
}

Method 2: through the array

The static method stream() of Arrays in Java8 can get the array stream:

static <T> Stream<T> stream(T[] array): 返回一个流
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)

example:

package com.zl.methods;

import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;


public class Test04 {
    public static void main(String[] args) {
        // 创建一个数组
        int[] arr = {1,2,3,4,5,6};
        // 得到Stream实例(返回一个顺序流)
        IntStream stream = Arrays.stream(arr);
        // 再例如
        Integer[] integers = {1,2,3,4,5};
        Stream<Integer> stream1 = Arrays.stream(integers);

    }
}

Method 3: Through Stream's of()

The static method of() of the Stream class can be called to create a stream by displaying values, which can receive any number of parameters.

public static<T> Stream<T> of(T... values) : 返回一个流

It is mainly used to store multiple data without containers (arrays, collections)

package com.zl.methods;

import java.util.stream.Stream;

public class Test04 {
    public static void main(String[] args) {
        // 通过静态方法进行调用
        Stream<Integer> stream = Stream.of(1, 2, 3, 4);
    }
}

(4) A series of intermediate operations

Multiple intermediate operations can be connected to form a pipeline, and unless a termination operation is triggered on the pipeline, the intermediate operations will not perform any processing! And when the operation is terminated, it is processed all at once, which is called "lazy evaluation".

Prepare the Employee class

package com.zl.data;

import java.util.Objects;

public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Employee() {
        System.out.println("Employee().....");
    }

    public Employee(int id) {
        this.id = id;
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return id == employee.id && age == employee.age && Double.compare(employee.salary, salary) == 0 && Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, salary);
    }
}

prepare data

package com.zl.data;

import java.util.ArrayList;
import java.util.List;


public class EmployeeData {
    public static List<Employee> getEmployees(){
        List<Employee> list = new ArrayList<>();

        list.add(new Employee(1001, "马化腾", 34, 6000.38));
        list.add(new Employee(1002, "马云", 2, 19876.12));
        list.add(new Employee(1003, "刘强东", 33, 3000.82));
        list.add(new Employee(1004, "雷军", 26, 7657.37));
        list.add(new Employee(1005, "李彦宏", 65, 5555.32));
        list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
        list.add(new Employee(1007, "任正非", 26, 4333.32));
        list.add(new Employee(1008, "扎克伯格", 35, 2500.32));

        return list;
    }
}

1- Screening and Slicing

method describe
filter(Predicate) Receive a Lambda that excludes certain elements from the stream
distinct() Filter, remove duplicate elements through the hashCode() and equals() of the elements generated by the stream
limit(long maxSize) truncates the stream so that it has no more than the given number of elements
skip(long n) Skip elements, returning a stream with the first n elements discarded. If there are less than n elements in the stream, an empty stream is returned. Complementary to limit(n)

Requirement: Query the employees whose salary is greater than 7000 in the employee table

package com.zl.data;

import java.util.List;
import java.util.stream.Stream;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 实例Stream
        Stream<Employee> stream = list.stream();
        // 进行筛选(参数是Predicate函数式接口)
        // stream.filter(emp -> emp.getSalary() > 7000);
        // 进行打印,实际上也就是终止条件
        // stream.filter(emp -> emp.getSalary() > 7000).forEach(emp-> System.out.println(emp));
        // 后面也可以使用方法引用
        stream.filter(emp -> emp.getSalary() > 7000).forEach(System.out::println);

    }
}

Requirements: truncate the stream so that there are no more than a given number of elements

package com.zl.data;

import java.util.List;
import java.util.stream.Stream;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 实例Stream
        Stream<Employee> stream = list.stream();
        // 截断流
        stream.limit(2).forEach(System.out::println);

    }
}

Requirement: Skip the first 2 and only print the latter; in fact, it is a complementary relationship with limit (only the first few)

package com.zl.data;

import java.util.List;
import java.util.stream.Stream;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 实例Stream
        Stream<Employee> stream = list.stream();
        // 跳过前2个
        stream.skip(2).forEach(System.out::println);
    }
}

Requirements: deduplication, deduplication according to the equals method and the hashCode method

package com.zl.data;

import java.util.List;
import java.util.stream.Stream;


public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 添加几个相同的元素
        list.add(new Employee(1008, "特朗布", 35, 2500.32));
        list.add(new Employee(1008, "特朗布", 35, 2500.32));
        list.add(new Employee(1008, "特朗布", 35, 2500.32));

        // 实例Stream
        Stream<Employee> stream = list.stream();
        // 进行去重
        stream.distinct().forEach(System.out::println);
    }
}

2- Mapping

method describe
map(Function f) Takes a function as an argument, which is applied to each element and maps it to a new element.
mapToDouble(ToDoubleFunction f) Receives a function as an argument, the function will be applied to each element, resulting in a new DoubleStream.
mapToInt(ToIntFunction f) Receives a function as an argument, the function will be applied to each element, resulting in a new IntStream.
mapToLong(ToLongFunction f) Receives a function as a parameter, the function will be applied to each element, resulting in a new LongStream.
flatMap(Function f) Takes a function as argument, swaps each value in the stream with another stream, and concatenates all streams into one stream

Requirements: uppercase to lowercase

package com.zl.data;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class Test01 {
    public static void main(String[] args) {
      // 把一个数组转化成集合
        List<String> list = Arrays.asList("aa", "bb", "cc");
        // 实例Stream
        Stream<String> stream = list.stream();
        // 大写转小写
        // stream.map(str->str.toUpperCase()).forEach(System.out::println);
        // 使用方法引用也可以
        stream.map(String::toUpperCase).forEach(System.out::println);
    }
}

Requirement: Get the names of employees whose name length is greater than 3

package com.zl.data;

import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 获取员工姓名长度大于3的员工姓名
        // 方法一:先过滤到名字长度低于3的,在映射名字进行打印
        list.stream().filter(emp->emp.getName().length()>3).map(emp->emp.getName()).forEach(System.out::println);
        // 方式二:先映射名字
        list.stream().map(emp->emp.getName()).filter(name->name.length()>3).forEach(System.out::println);
        // 方式三:使用方法引用
        list.stream().map(Employee::getName).filter(name->name.length()>3).forEach(System.out::println);

    }
}

3- Sort

method describe
sorted() produces a new stream in which the natural order of the
sorted(Comparator com) yields a new stream sorted in comparator order

Requirements: perform natural sorting

package com.zl.data;

import java.util.Arrays;

public class Test01 {
    public static void main(String[] args) {
        // 定义一个数组
        int[] arr = new int[]{1,2,9,76,3,4,5};
        Arrays.stream(arr).sorted().forEach(System.out::println);
    }
}

Requirement: Borrow comparator for sorting

package com.zl.data;

import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 借用比较器进行排序,按照年级进行比较
        list.stream().sorted((o1,o2)-> o1.getAge() - o2.getAge()).forEach(System.out::println);
    }
}

(5) Terminate operation

① Terminal operations generate results from the stream's pipeline. The result can be any value that is not a stream, for example: List, Integer, or even void.

② After the stream has been terminated, it cannot be used again.

1- Match and Find

method describe
allMatch(Predicate p) Checks if all elements match
anyMatch(Predicate p) Checks if at least one element matches
noneMatch(Predicate p) Checks if all elements are not matched
findFirst() returns the first element
findAny() returns any element in the current stream
count() Returns the total number of elements in the stream
max(Comparator c) Returns the maximum value in the stream
min(Comparator c) Returns the minimum value in the stream
forEach(Consumer c) Internal iteration (using the Collection interface requires the user to iterate, which is called external iteration. In contrast, the Stream API uses internal iteration-it does the iteration for you)

Requirement: Whether all employees are older than 18

package com.zl.data;

import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        boolean b = list.stream().allMatch(emp -> emp.getAge() > 18);
        System.out.println(b); // false
    }
}

Requirement: Whether there is an employee whose salary is greater than 10000 (at least one)

package com.zl.data;

import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        boolean b = list.stream().anyMatch(emp -> emp.getSalary() > 10000);
        System.out.println(b); // true
    }
}

 Requirement: return the first element

package com.zl.data;

import java.util.List;
import java.util.Optional;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 返回的是一个Option
        Optional<Employee> optionalEmployee = list.stream().findFirst();
        System.out.println(optionalEmployee); // Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
        // 在调用get方法就能拿到第一个值
        System.out.println(optionalEmployee.get()); // Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    }
}

Requirement: Return the total number of elements whose salary is greater than 7000 in the stream

package com.zl.data;

import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        long count = list.stream().filter(emp -> emp.getSalary() > 7000).count();
        System.out.println(count); // 3
    }
}

Requirement: return the highest salary

package com.zl.data;

import java.util.List;
import java.util.Optional;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 先获取返回最高工资的员工
        // 得到的是一个Option
        Optional<Employee> optionalEmployee = list.stream().max((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()));
        System.out.println(optionalEmployee); // Optional[Employee{id=1002, name='马云', age=2, salary=19876.12}]

        // 方法1(根据上面在调用get方法,获取员工,在调用getSalary获取到薪资)
        System.out.println(list.stream().max((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary())).get().getSalary());
        // 方法2(映射)
        System.out.println(list.stream().map(emp -> emp.getSalary()).max((salgrade1, salgrade2) -> Double.compare(salgrade1, salgrade2)).get());
        // 使用方法引用
        System.out.println(list.stream().map(emp -> emp.getSalary()).max(Double::compare).get());

    }
}

Note: A method for traversing collections has been added in JDK8

package com.zl.data;

import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 进行遍历---使用lambda表达式
        list.forEach((emp)-> System.out.println(emp));
        // 使用方法的引用
        list.forEach(System.out::println);
    }
}

2 - reduction

method describe
reduce(T identity, BinaryOperator b) The elements in the stream can be combined repeatedly to obtain a value. return T
reduce(BinaryOperator b) The elements in the stream can be combined repeatedly to obtain a value. return Optional<T>

Requirements: Calculate the sum of natural numbers from 1-10

The first parameter is the seed of a random number: it is equivalent to specifying an initial value, and this value needs to be added to the final result of subsequent accumulation.

The second parameter BinaryOperator: actually inherits the BiFunction class, and two parameters return a value.

package com.zl.data;

import java.util.Arrays;
import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        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)); // 55
        // 第二种方法(调用Integer的sum方法进行累加)
        System.out.println(list.stream().reduce(0, (x1, x2) -> Integer.sum(x1,x2))); // 55
        // 第三种方法:在二的基础上就可以使用方法的引用
        System.out.println(list.stream().reduce(0, Integer::sum)); // 55
    }
}

Requirement: Calculate the sum of the company's employees (first map in the statute)

package com.zl.data;

import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 先映射在规约(得到的是Option)
        // 在调用get方法得到值
        System.out.println(list.stream().map(emp -> emp.getSalary()).reduce((s1,s2)->Double.sum(s1,s2))); // Optional[58424.08]
        System.out.println(list.stream().map(emp -> emp.getSalary()).reduce(Double::sum).get()); // 58424.08

    }
}

Note: The concatenation of map and reduce is often referred to as the map-reduce pattern, made famous by Google for web searches.

3- Collect

The sort method called sorts, but it does not affect the original data, so we can store the sorted data in a collection; the implementation of the method in the Collector interface determines how to perform collection operations on the stream (such as collecting to List , Set, Map).

method describe
collect(Collector c) Transform streams into other forms. Receive an implementation of the Collector interface, which is used to summarize the elements in the Stream

In addition, the Collectors utility class provides many static methods, which can easily create common collector instances. The specific methods and instances are as follows:

method return type effect
toList Collector<T, ?, List<T>> Collect the elements in the stream into a List
List<Employee> emps= list.stream().collect(Collectors.toList());
method return type effect
toSet Collector<T, ?, Set<T>> Collect the elements in the stream into a Set
Set<Employee> emps= list.stream().collect(Collectors.toSet());
method return type effect
toCollection Collector<T, ?, C> 把流中元素收集到创建的集合
Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new));

需求:查找工资大于6000的员工,结果返回到一个List

package com.zl.data;

import java.util.List;
import java.util.stream.Collectors;


public class Test01 {
    public static void main(String[] args) {
        List<Employee> list = EmployeeData.getEmployees();
        // 进行过滤(并把数据存储到List集合)
        List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList());
        // 进行打印
        list1.forEach(System.out::println);
        
    }
}

3.6 新的时间和日期 API

JDK8之前:日期时间API

(1)java.lang.System类的currentTimeMills方法

①System类提供的public static long currentTimeMillis():用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。

②此方法适于计算时间差。

package com.zl.data;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Test01 {
    public static void main(String[] args) {
        // 获取当前时间的毫秒数
        long timeMillis = System.currentTimeMillis();
        System.out.println(timeMillis);
        // 获取昨天此时的时间
        Date date = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
    }
}

(2)java.util.Date

①构造器

Date():使用无参构造器创建的对象可以获取本地当前时间
Date(long 毫秒数):把该毫秒值换算成日期时间对象

②常用方法

getTime(): 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数
toString(): 把此 Date 对象转换为以下形式的 String

例:

package com.zl.data;

import java.util.Date;

public class Test01 {
    public static void main(String[] args) {
        // 无参构造器
        Date date = new Date();
        System.out.println(date.toString()); // Mon Apr 10 19:46:50 CST 2023
        // 有参构造
        Date date1 = new Date(100L);
        System.out.println(date1.toString()); // Thu Jan 01 08:00:00 CST 1970
        // 调用getTime方法获取当前的毫秒数(和System.currentTimeMillis相同的效果)
        System.out.println(date.getTime());
        System.out.println(System.currentTimeMillis());

    }
}

③其子类java.sql.Date,对应着数据库中的Date类型,只有有参构造器

package com.zl.data;

import java.sql.Date;
import java.text.ParseException;

public class Test01 {
    public static void main(String[] args) throws ParseException {
        // java.sql.Date
        // 获取时间戳
        long timeMillis = System.currentTimeMillis(); // 1681181172832
        System.out.println(timeMillis);
        // 只有一个有参构造器
        java.sql.Date date = new Date(timeMillis);
        // 只打印年月日,但是其实也包含时分秒,只是没有显示,从下面调用getTime方法的结果与前面的时间戳相等也能体现
        System.out.println(date); // 2023-04-11
        System.out.println(date.getTime()); // 1681181172832


    }
}

(3)java.text.SimpleDateFormat

java.text.SimpleDateFormat类是一个不与语言环境有关的方式来格式化和解析日期的具体类

①可以进行格式化:日期 --> 文本

②可以进行解析:文本 --> 日期

构造器:

SimpleDateFormat() :默认的模式和语言环境创建对象
public SimpleDateFormat(String pattern):该构造方法可以用参数pattern指定的格式创建一个对象

格式化:

public String format(Date date):方法格式化时间对象date

解析:

public Date parse(String source):从给定字符串的开始解析文本,以生成一个日期

例:

package com.zl.data;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Test01 {
    public static void main(String[] args) throws ParseException {
        // 无参构造器
        Date date = new Date();
        System.out.println(date); // Mon Apr 10 20:02:33 CST 2023
        // 指定格式化的格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 把日期格式化为字符串
        System.out.println(sdf.format(date)); // 2023-04-10 20:02:33
        // 把时间的字符串解析为日期
        System.out.println(sdf.parse("2022-4-10 6:10:10")); // Sun Apr 10 06:10:10 CST 2022

    }
}

(4)java.util.Calendar(日历)

①Date类的API大部分被废弃了,替换为Calendar(抽象类)。

②Calendar 类是一个抽象类,主用用于完成日期字段之间相互操作的功能。

获取Calendar实例

第一种方法:创建子类GregorianCalendar

第二种方法:调用Calendar的静态方法getInstance

package com.zl.data;


import java.util.Calendar;
import java.util.GregorianCalendar;

public class Test01 {
    public static void main(String[] args)  {
        // 第一种方法
        GregorianCalendar gregorianCalendar = new GregorianCalendar();
        System.out.println(gregorianCalendar);
        // 第二种方法(常用)实际上也是子类GregorianCalendar
        Calendar calendar = Calendar.getInstance();
        System.out.println(calendar.getClass()); // class java.util.GregorianCalendar
    }
}

常用的方法

public int get(int field):返回给定日历字段的值
public void set(int field,int value) :将给定的日历字段设置为指定的值
public void add(int field,int amount):根据日历的规则,为给定的日历字段添加或者减去指定的时间量
public final Date getTime():将Calendar转成Date对象
public final void setTime(Date date):使用指定的Date对象重置Calendar的时间

常用的field字段

package com.zl.data;


import java.util.Calendar;
import java.util.Date;

public class Test01 {
    public static void main(String[] args)  {
        Calendar calendar = Calendar.getInstance();
        // get方法
        System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 3,今天是这个周的第几天
        System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 101,今天是这个年的第几天
        // set方法
        calendar.set(Calendar.DAY_OF_WEEK,5); // 把今天是这个周的第几天设置为另一个值
        System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 5,把今天设置为这个周的第5天
        // add方法
        calendar.add(Calendar.DAY_OF_WEEK,1);
        System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 6,在原来的基础上增加一天
        calendar.add(Calendar.DAY_OF_WEEK,-2);
        System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 4,在原来的基础上减少两天
        // getTime方法,从Calendar转换为Date
        System.out.println(calendar.getTime()); // Wed Apr 12 11:20:29 CST 2023
        // setTime方法,使用指定的Date重置Calendar
        Date date = new Date();
        calendar.setTime(date);
        // 其实再次获取今天是这周的第几天
        System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 3,重置过后还是原来的第三天

    }
}

例题:

例:将一个java.util.Date的实例转换为java.sql.Date的实例

package com.zl.data;


import java.util.Date;

public class Test01 {
    public static void main(String[] args)  {
       // 很容易想到的思路,进行强转,会出现ClassCastException异常
        // 原因我们创建的就是父类java.util.Date,不能往下转为java.sql.Date(可以王当前的父类转,但是不能往当前的子类进行转)
        /*Date date = new Date();
        java.sql.Date date2 = (java.sql.Date) date; // err*/
        // 正确的做法---根据时间戳进行转换
        Date date = new Date();
        java.sql.Date date1 = new java.sql.Date(date.getTime());
    }
}

JDK8:新的日期时间API

如果我们可以跟别人说:“我们在1502643933071见面,别晚了!”那么就再简单不过了。但是我们希望时间与昼夜和四季有关,于是事情就变复杂了。JDK 1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了。而Calendar并不比Date好多少。它们面临的问题是: 

①可变性:像日期和时间这样的类应该是不可变的。

②偏移性:Date中的年份是从1900开始的,而月份都从0开始。

③格式化:格式化只对Date有用,Calendar则不行。

④此外,它们也不是线程安全的;不能处理闰秒等。

Java 8 以一个新的开始为 Java 创建优秀的 API。新的日期时间API包含:

java.time – 包含值对象的基础包
java.time.chrono – 提供对不同的日历系统的访问。
java.time.format – 格式化和解析时间和日期
java.time.temporal – 包括底层框架和扩展特性
java.time.zone – 包含时区支持的类

(1)本地日期时间:LocalDate、LocalTime、LocalDateTime---类似于Calendar

获取实例对象

方法 描述
now()/ now(ZoneId zone) 静态方法,根据当前时间创建对象/指定时区的对象
of(xx,xx,xx,xx,xx,xxx) 静态方法,根据指定日期/时间创建对象
package com.zl.data;


import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;


public class Test01 {
    public static void main(String[] args)  {
        // now():获取当前日期和时间对应的实例
        LocalDate localDate = LocalDate.now(); // 年月日
        LocalTime localTime = LocalTime.now(); // 时分秒
        LocalDateTime localDateTime = LocalDateTime.now(); // 年月日和时分秒
        System.out.println(localDate); // 2023-04-11
        System.out.println(localTime); // 14:41:45.596
        System.out.println(localDateTime); // 2023-04-11T14:41:45.596

        // of():获取指定的日期,时间对应的实例
        LocalDate localDate1 = LocalDate.of(2023, 4, 11);
        LocalDateTime localDateTime1 = LocalDateTime.of(2023, 4, 11, 14, 45, 30);
        System.out.println(localDate1); // 2023-04-11
        System.out.println(localDateTime1); // 2023-04-11T14:45:30
    }
}

常用方法:获取(get)、修改(with)、增加(plus)、减少(min)

方法 描述
getDayOfMonth()/getDayOfYear() 获得月份天数(1-31) /获得年份天数(1-366)
getDayOfWeek() 获得星期几(返回一个 DayOfWeek 枚举值)
getMonth() 获得月份, 返回一个 Month 枚举值
getMonthValue() / getYear() 获得月份(1-12) /获得年份
getHours()/getMinute()/getSecond() 获得当前对象对应的小时、分钟、秒
withDayOfMonth()/withDayOfYear()/withMonth()/withYear() 将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象
with(TemporalAdjuster t) 将当前日期时间设置为校对器指定的日期时间
plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours() 向当前对象添加几天、几周、几个月、几年、几小时
minusMonths() / minusWeeks()/minusDays()/minusYears()/minusHours() 从当前对象减去几月、几周、几天、几年、几小时
plus(TemporalAmount t)/minus(TemporalAmount t) 添加或减少一个 Duration 或 Period
isBefore()/isAfter() 比较两个 LocalDate
isLeapYear() 判断是否是闰年(在LocalDate类中声明)
format(DateTimeFormatter t) 格式化本地日期、时间,返回一个字符串
parse(Charsequence text) 将指定格式的字符串解析为日期、时间

package com.zl.data;


import java.time.LocalDateTime;

public class Test01 {
    public static void main(String[] args)  {
        LocalDateTime localDateTime = LocalDateTime.now();
        // getXxx()获取
        int dayOfMonth = localDateTime.getDayOfMonth();
        System.out.println(dayOfMonth); // 11,获取今天是这个月的第几天
        // withXxx()修改
        LocalDateTime localDateTime1 = localDateTime.withDayOfMonth(15); // 把今天修改为这个月的第15天
        System.out.println(localDateTime); // 2023-04-11T14:57:05.485,原来的并没有更改
        System.out.println(localDateTime1); // 2023-04-15T14:57:05.485,返回新的LocalDateTime对象才被更改,体现了不可变性
        // plusXxx()增加
        LocalDateTime localDateTime2 = localDateTime1.plusDays(5); // 又会生成一个新的LocalDateTime对象
        System.out.println(localDateTime2); // 2023-04-20T15:00:25.650,在原来的基础上增加了5天
        // minXxx()减少
        LocalDateTime localDateTime3 = localDateTime2.minusDays(4);
        System.out.println(localDateTime3); // 2023-04-16T15:02:58.050,在原来的基础上增加了4天
    }
}

(2)瞬时:Instant---类似于Date

①Instant:时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。 时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。

②java.time.Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。

方法 描述
now() 静态方法,返回默认UTC时区的Instant类的对象
ofEpochMilli(long epochMilli) 静态方法,返回在1970-01-01 00:00:00基础上加上指定毫秒数之后的Instant类的对象
atOffset(ZoneOffset offset) 结合即时的偏移来创建一个 OffsetDateTime
toEpochMilli() 返回1970-01-01 00:00:00到当前时间的毫秒数,即为时间戳
package com.zl.data;


import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class Test01 {
    public static void main(String[] args)  {
        // 类似于Date的无参构造器
        Instant instant = Instant.now();
        System.out.println(instant); //2023-04-11T07:20:51.614Z与现在的时间差了8个小时
        OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); // 设置偏移的8个小时
        System.out.println(offsetDateTime); // 2023-04-11T15:20:51.614+08:00
        // 类似于Date的有参构造器
        Instant instant1 = Instant.ofEpochMilli(System.currentTimeMillis());
        System.out.println(instant1); // 2023-04-11T07:20:51.695Z
        // 获取当前的毫秒数
        long toEpochMilli = instant.toEpochMilli();
        System.out.println(toEpochMilli); // 1681197782827
    }
}

(3)日期时间格式化:DateTimeFormatter---类似于SimpleDateFormat

DateTimeFormatter用户格式化和解析:LocalDate、LocalTime、LocalDateTime!

自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)

ofPattern(String pattern) 静态方法,返回一个指定字符串格式的DateTimeFormatter
format(TemporalAccessor t) Format a date, time, and return a string
parse(CharSequence text) Parses a sequence of characters in the specified format into a date, time
package com.zl.data;


import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;

public class Test01 {
    public static void main(String[] args)  {
        LocalDateTime localDateTime = LocalDateTime.now();
        // 自定义格式
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 格式化
        String strDateTime = dtf.format(localDateTime);
        System.out.println(strDateTime); // 2023-04-11 15:44:14
        // 解析(得到的是一个TemporalAccessor接口,LocalDateTime实现了这个接口)
        TemporalAccessor temporalAccessor = dtf.parse("2023-10-10 15:30:30");
        // 在调用LocalDateTime.from方法转换为LocalDateTime
        LocalDateTime localDateTime1 = LocalDateTime.from(temporalAccessor);
        System.out.println(localDateTime1); // 2023-10-10T15:30:30

    }
}

Guess you like

Origin blog.csdn.net/m0_61933976/article/details/129040679