JDK8新特性(lambda表达式、方法引用、函数式接口、Stream、计时器、日历)

本文详细介绍了Java8的主要特性,包括Lambda表达式的概念与语法,方法引用的静态、成员、this引用和super引用,函数式接口的定义与应用,Stream API的概述及操作,计时器的使用,以及新日期API的日历功能。通过实例解析了这些新特性的具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

Java8 (又称 JKD1.8) 是 Java 语言开发的一个主要版本。
Oracle公司于2014年3月18日发布Java8 。

  • 支持Lambda表达式
  • 函数式接口
  • 新的Stream API
  • 新的日期 API
  • 其他特性

一、Lambda表达式

1.1概述

Lambda是一种特殊的匿名内部类,语法更简洁

允许吧函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递

1.2语法

<函数式接口> <变量名> = (参数1,参数2…) -> {

​ // 方法体

};

Lambda引入了新的操作符:->(箭头操作符),->将表达式分成两部分:

  • 左侧:(参数1,参数2…)表示参数列表
  • 右侧:{ }内部是方法体

注意事项:

  • 形参列表的数据类型会自动推断。
  • 如果形参列表为空,只需保留() 。
  • 如果形参只有1个,()可以省略,只需要参数的名称即可。
  • 如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有一句。
  • [Lambda不会生成一个单独的内部类文件。

二、方法的引用

2.1概念

方法引用来自官方的说明

You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it's often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.
你使用lambda表达式创建匿名方法。 但是,有时lambda表达式除了调用现有方法外什么也不做。 在这种情况下,通常更容易按名称引用现有方法。 方法引用使你可以执行此操作; 它们是紧凑的,对于已经具有名称的方法lambda表达式更易于阅读。
  • 方法引用是Lambda表达式的一种简写形式。
  • 如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。

2.2语法

方法引用符 -----> “::”

双冒号::为方法引用符,而它所在的表达式被称为方法引用。如果Lambda表达式赋值的方法已经在某个类中有具体的实现,那么则可以通过双冒号来引用该方法作为Lambda表达式的替代者。

案例:

public interface Actor {
    /**
     * 演员表演节目
     */
    void perform(String item);  
}
public class ActorTest {
    public static void main(String[] args) {
        // 使用匿名内部类实现
        Actor actor1 = new Actor() {
            @Override
            public void perform(String item) {
                System.out.println(item);
            }
        };
        actor1.perform("跳舞");
        System.out.println("---------------------");
        // 使用lambda的形式
        Actor actor2 = item -> System.out.println(item);
        actor2.perform("唱歌");
        System.out.println("--------------------");
        // 方法引用
        /*
          这个接口方法的实现,本质来说就是调用了System类中out对象的println方法
           JDK8新特性 - 方法引用符 :: 双冒号
           语法规则:
                成员方法引用: 对象名::方法名
                静态方法引用: 类名::方法名
                this        this::方法名
                supper      supper::方法名
         */
        Actor actor = System.out::println; // 成员方法引用
        actor1.perform("弹钢琴");
    }
}

分析:

上面的示例中,Lambda表达式的作用就是调用System.out中的println(String msg)方法,这个方法已经有具体的实现。

使用方法引用进一步简化代码

总结:

方法引用与Lambda表达式一样,只能应用于函数式接口。方法有静态方法、成员方法和构造方法之分,方法引用因此也分为静态方法引用、成员方法引用和构造方法引用

2.3静态方法引用

语法:

类名::方法名

案例:

// 计算器接口
public interface Calculator {
    int calculate(int a,int b);
}

public class MathUtil {
    public static int add(int a, int b){
        return a + b;
    }
    public static int minus(int a, int b){
        return a - b;
    }
    public static int multiply(int a, int b){
        return a * b;
    }
    public static int divided(int a, int b){
        return a / b;
    }
}

public class CalculatorTest {

    public static void main(String[] args) {
//        Calculator c = new Calculator() {
//            @Override
//            public int calculate(int a, int b) {
//                return MathUtil.minus(a, b);
//            }
//        };
//        Calculator c = (int a, int b) -> {
//            return MathUtil.minus(a, b);
//        };
//        Calculator c = (a, b) -> MathUtil.minus(a, b);
        Calculator c = MathUtil::minus;
        int result = c.calculate(1, 10);
        System.out.println(result);

        Calculator c1 = MathUtil::multiply;
        int result1 = c1.calculate(1, 10);
        System.out.println(result1);
    }
}

// 
public class CalculatorTest {

    public static void main(String[] args) {
        // 使用匿名内部类实现
        Calculator c1 = new Calculator() {
            @Override
            public int calculate(int a, int b) {
                return Integer.sum(a,b);// 求和
            }
        };
        System.out.println("c1 : " + c1.calculate(1,2));
        System.out.println("---------------------");
        // 这里接口的实现只是调用了Integer类提供的静态方法sum,可以实现代码简化
        // 使用lambda实现
        Calculator c2 = (a, b) -> Integer.sum(a, b);
        System.out.println("c2 : " + c2.calculate(3,4));
        System.out.println("---------------------");
        // 使用方法引用
        Calculator c3 = Integer::sum;
        System.out.println("c3 : " + c3.calculate(5,6));
        System.out.println("---------------------");
        // 比较器接口 Comparator
        // 匿名内部类实现
        Comparator<Double> comparator1 = new Comparator<Double>() {
            @Override
            public int compare(Double o1, Double o2) {
                return Double.compare(o1,o2); // o1大于o2返回1 ,o1小于o2返回-1,等于比较复杂
                /*
                // Cannot use doubleToRawLongBits because of possibility of NaNs.
                 long thisBits    = Double.doubleToLongBits(d1);
                 long anotherBits = Double.doubleToLongBits(d2);

                 return (thisBits == anotherBits ?  0 :    // Values are equal
                (thisBits < anotherBits ? -1 :             // (-0.0, 0.0) or (!NaN, NaN)
                 1));                                      // (0.0, -0.0) or (NaN, !NaN)
                 */
            }
        };
        System.out.println("小于的情况: " + comparator1.compare(1.0,10.0));
        System.out.println("大于的情况: " + comparator1.compare(6.0,5.0));
        System.out.println("等于的情况: " + comparator1.compare(1.0,1.0));
        System.out.println("---------------------");

        // 使用方法引用
        Comparator<Double> comparator2 = Double::compare;
        System.out.println("小于的情况: " + comparator2.compare(1.0,10.0));
        System.out.println("大于的情况: " + comparator2.compare(6.0,5.0));
        System.out.println("等于的情况: " + comparator2.compare(0.0,0.0));
    }
}

2.4成员方法引用

语法:

对象名:: 方法名

案例:

public interface Printable {
    // 打印信息
    void print(String msg);
}

public class StandardConsole {
    public void print(String msg){
        System.out.println("控制台打印" + msg);
    }
}

public class PrintableTest {
    public static void main(String[] args) {
        // 匿名内部类实现
        Printable p1 = new Printable() {
            @Override
            public void print(String msg) {
                new StandardConsole().print(msg);
            }
        };
        p1.print("方法实现1");
        // 方法引用
        Printable p2 = new StandardConsole()::print;
        p2.print("成员方法引用实现2");
    }
}

2.5this引用成员方法

语法:

this::方法名

案例:

// 照相机解耦
public interface Camera {
    // 拍照方法
    void takePhoto(String name);
}

// 人类
public class Person {
    public void dance(){
        System.out.println("跳舞");
    }
    public void sing(String s){
        System.out.println(s);
    }
    public void run(){
        System.out.println("跑步");
    }
    public void takePhoto(String name){
        System.out.println("给" + name + "拍照");
    }
    public void travel(String name) {
        Camera c1 = new Camera() {
            @Override
            public void takePhoto(String name) {
                Person.this.takePhoto(name);
            }
        };
        // 成员方法引用
        Camera c2 = Person.this::takePhoto;
        // this方法引用
        Camera c3 = this::takePhoto;
        c3.takePhoto(name);
    }
}

public class PersonTest {

    public static void main(String[] args) {
        Person p = new Person();
        p.travel("中国");
    }
}

2.6super引用父类成员方法

语法:

super::方法名

案例:

public interface Customer {
    // 交流业务
    void communicateBusiness();
}

public class SoftEngineer {
    public void analysBusyness(){
        System.out.println("软件工程师分析业务");
    }
}

public class JavaProgammer extends SoftEngineer{
    // 重写方法
    public void analysBusyness(){
        System.out.println("java程序员与客户交流业务");
    }
    // Java程序员与客户交流业务
    // 匿名内部类形式
    public void communicateWithCustomer() {
        Customer c1 = new Customer() {
            @Override
            public void communicateBusiness() {
                JavaProgammer.super.analysBusyness();
            }
        };
        // super方法引用
        Customer c2 = super::analysBusyness;
        c2.communicateBusiness();
    }
}

public class JavaProgammerTest {
    public static void main(String[] args) {
        JavaProgammer javaProgammer = new JavaProgammer();
        javaProgammer.communicateWithCustomer();
        javaProgammer.analysBusyness();
    }
}

2.7构造方法引用

语法:

类名::new

案例:

public class Student {
    private String name;
    private String sex;
    public Student() {
    }
    public Student(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

public interface StudentBuilder {
    Student build(String name,String sex);
}

public class StudentBuilderTest {
    public static void main(String[] args) {
        StudentBuilder builder1 = new StudentBuilder() {
            @Override
            public Student build(String name, String sex) {
                return new Student(name,sex);
            }
        };
        Student s1 =  builder1.build("张三","男");
        System.out.println("匿名方法1" + s1);

        StudentBuilder builder2 = Student::new; // 构造方法引用
        Student s2 = builder2.build("李四","女");
        System.out.println("成员方法: " + s2);
    }
}

三、函数式接口

3.1. 什么是函数式接口

函数式接口

A functional interface is any interface that contains only one abstract method. (A functional interface may contain one or more default methods or static methods.) Because a functional interface contains only one abstract method, you can omit the name of that method when you implement it.
函数式接口是仅包含一种抽象方法的任何接口。 (一个函数式接口可能包含一个或多个默认方法或静态方法。)由于一个函数式接口仅包含一个抽象方法,因此在实现该方法时可以省略该方法的名称。

示例:

package com.qf.functional;

@FunctionalInterface
public interface Hello {

    void sayHello(String name);

    static void show(){} //JDK8新特性

    default void print(){} //JDK8新特性

    private void test(){} // JDK9特性
}

JDK8专门为函数式接口提供了一个注解标识@FunctionalInterface,该注解只能使用在接口类型的定义上,表明这是一个函数式接口,编译器在编译时就是会对该接口进行检测:接口中是否只有一个抽象接口方法。如果有多个抽象接口方法或者一个抽象接口方法也没有,则将报编译错误

3.2Consumer接口

Consumer顾名思义就是消费者的意思。可以消费一个被接收到的数据,至于如何消费,就需要看这个接口被如何实现。

// 源码
@FunctionalInterface
public interface Consumer<T> {
    
    void accept(T t); // 接受一个被消费的数据
    
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

案例:

/**
 * 函数接口 - 消费接口,通常会被应用在集合的遍历上
 * 如何消费,看具体的时实现
 */
public class ConsumerTest {

    public static void main(String[] args) {
        // 第一种,匿名内部类的形式
        Consumer<String> c1 = new Consumer<String>(){
            @Override
            public void accept(String s) {
                System.out.println(s + "这个字符串被消费了");
            }
        };
        c1.accept("abcd");
        System.out.println("-------------------");

        // 第二种使用lambda
        Consumer<String> c2 = (String s) -> {
            System.out.println(s + "这个字符串被消费了");
        };
        c2.accept("efgh");
        System.out.println("-------------------");

        // 第三种 lambda的省略版
        Consumer<String> c3 = s -> System.out.println(s + "这个字符串被消费了");
        c3.accept("ijkl");
        System.out.println("-------------------");

        // 集合List  Consumer接口
        List<String> names = Arrays.asList("张三", "莉莉丝", "王五");
        // 第一lambda遍历
        names.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        System.out.println("-------------------");

        // 第二种lambda遍历
        names.forEach((String s) -> {
            System.out.println(s);
        });
        System.out.println("-------------------");

        // 第三种lambda遍历
        names.forEach(s -> System.out.println(s));
        System.out.println("-------------------");

        // 第四种lambda遍历   方法引用符的使用 ::
        names.forEach(System.out::println);
    }
}

3.3BiConsumer接口

BiConsumer也是一个消费者,只是这个消费者可以一次性消费两个数据(一般是键值对)。至于如何消费,就需要看这个接口被如何实现。

// 源码
@FunctionalInterface
public interface BiConsumer<T, U> {
    
    void accept(T t, U u); // 接受两个被消费的数据

    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);

        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }
}
// 函数接口 - BiConsumer 一次消费两个数据的消费接口 一般用于map 实现键值对的,具体消费看如何实现
public class BiConsumerTest {

    public static void main(String[] args) {

        // 第一种使用匿名内部类的形式
        BiConsumer<String,Double> consumer01 = new BiConsumer<String, Double>() {
            @Override
            public void accept(String s, Double d) {
                System.out.println(s + "==>" + d);
            }
        };
        consumer01.accept("张三",10.0);
        System.out.println("----------------------------");

        // 第二种使用lambda的第一种形式
        BiConsumer<String,Double> consumer02 = (String s,Double d) -> {
            System.out.println(s + "==>" + d);
        };
        consumer02.accept("李四",20.0);
        System.out.println("----------------------------");

        // 第三种使用lambda的第二种形式
        BiConsumer<String, Double> consumer03 = (s,d) -> System.out.println(s + "==>" + d);
        consumer03.accept("王五",40.0);
        System.out.println("----------------------------");

        // BiConsume中的 HashMap
        System.out.println("BiConsumer中的HashMap");
        HashMap<String, Double> map = new HashMap<>();
        map.put("张三",10.0);
        map.put("李四",20.0);
        map.put("王五",30.0);
        // 第一种,匿名内部类的形式
        // HashMap的遍历
        map.forEach(new BiConsumer<String, Double>() {
            @Override
            public void accept(String s, Double d) {
                System.out.println(s + "==>" + d);
            }
        });
        System.out.println("----------------------------");

        //第二种,使用lambda的第一种形式
        map.forEach((String s,Double d) -> {
            System.out.println(s + "==>" + d);
        });
        System.out.println("----------------------------");

        // 第三种,使用lambda的第二种形式
        map.forEach((s,d) -> System.out.println(s + "==>" + d));
    }
}

3.4Predicate接口

Predicate是条件的意思,可以检测给定数据是否满足条件,也可以与其他条件进行衔接。至于如何检测,就需要看这个接口被如何实现。

// 源码
@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);
	// and
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
	// 取反
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
	// 或
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
	// 相等
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

案例:

/*
 Predicate接口测试:Preadicate表示条件,所需的条件
 条件之间的衔接  (|| or)与 (&& and)非 (!negate)
boolean test(T t);//检测是否满足条件
default Predicate<T> and(Predicate<? super T> other);//条件之间的逻辑与衔接
default Predicate<T> negate();//条件取反
default Predicate<T> or(Predicate<? super T> other);//条件之间的逻辑或衔接
 */
public class PredicateTest {
    public static void main(String[] args) {
        // 第一种方式 匿名内部类
        Predicate<Integer> p1 = new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer > 10; // 自定义条件
            }
        };
        System.out.println("p1: " + p1.test(20));
        System.out.println("-----------------------");
        // 第二种方式 lambda的形式1
        Predicate<Integer> p2 = (Integer integer) -> {
            return integer > 10;
        };
        System.out.println("p2: " + p2.test(20));
        System.out.println("-----------------------");
        // 第二种方式 lambda的形式2
        Predicate<Integer> p3 = num -> num > 10;
        Predicate<Integer> p4 = num -> num < 20;
        Predicate<Integer> p5 = num -> num < 50;
        System.out.println("p3: " + p3.test(20));
        System.out.println("p4: " + p4.test(20));
        System.out.println("p5: " + p5.test(20));
        System.out.println("-----------------------");
        // Prediate 条件衔接 && || ! 与或非
        // &&
        Predicate<Integer> p6 = p3.and(p4); // num > 10 && num < 20
        System.out.println("p6: " + p6.test(20));
        System.out.println("-----------------------");
        // ||
        Predicate<Integer> p7 = p3.or(p4); // num > 10 || num < 20
        System.out.println("p7: " + p7.test(20));
        System.out.println("-----------------------");
        // 取反1
        Predicate<Integer> p8 = p3.negate(); // num <= 10
        System.out.println("p8: " + p8.test(20));
        System.out.println("-----------------------");
        // 取反2
        Predicate<String> p9 = s -> s.contains("abc"); // num <= 10
        System.out.println("p9: " + p9.test("abc"));
        Predicate<String> p10 = p9.negate();
        System.out.println("p10: " + p10.test("abc"));
        System.out.println("-----------------------");
        // 调用show方法
        show();
    }
    // 按要求打印集合中的元素
    public static void show(){
        // 创建集合
        List<String> names = new ArrayList<String>();
        names.add("青鸢");
        names.add("赤鸢");
        names.add("青鸾");
        names.add("赤鸾");
        // 匿名内部类实现要求
        Predicate<String> p1 = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("青"); // 第一字符为青
            }
        };
        // lambda 实现
        Predicate<String> p2 = s -> s.startsWith("赤");// 第一字符为赤
        names.forEach(s -> {
            if (p1.test(s)){ // 按p1 的条件执行
                System.out.println(s);
            }
            if (p2.test(s)){ // 按 p2 的条件执行
                System.out.println(s);
            }
        });
    }
}

3.5Function接口

Function是功能的意思,可以将一种数据类型的对象转换为另一种数据类型的对象,至于如何转换,就需要看这个接口被如何实现。

// 源码
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t); // 将此函数应用于给定的参数
    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    // 接着
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
    /**
     * Returns a function that always returns its input argument.
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

案例:

/*
 函数式接口 - Function   测试
 数据类型的转换
 */
public class FunctionTest {
    public static void main(String[] args) {
               // 原类型  目标类型
        Function<String, Integer> f1 = new Function<String, Integer>() {
            @Override      // 匿名内部类实现
            public Integer apply(String s) {
                return Integer.parseInt(s); // 字符串转数字
            }
        };
        System.out.println("f1: " + f1.apply("1234"));
        System.out.println("---------------------------");
        // lambda 实现
        Function<String, Integer> f2 = s -> Integer.parseInt(s);
        System.out.println("f2: " + f2.apply("4567"));
        System.out.println("---------------------------");
        // Function 的andThen方法
        // 完成转换后接着执行  - 案例:字符串转数字后进行平方运算
        Function<Integer,Long> f3 = number -> Long.valueOf(number * number);
        Function<String,Long> f4 = f2.andThen(f3);
        System.out.println("f3: " + f3.apply(8));
        System.out.println("f4: " + f4.apply("9"));
        System.out.println("---------------------------");
    }
}

练习:

现有文本存储学生信息如下: - student.txt
谢霆锋,男,37
刘德华,男,52
郭富城,男,46
张学友,男,40
要求将学生信息从文本中读取出来并转换为学生对象,然后存储在集合中。
public class FunctionExercise {
    public static void main(String[] args) {
        // 路径
        String path = "d:/imgs/student.txt";

        // 按字符串读取
        // 匿名内部类实现 Function
        System.out.println("按字符串读取:");
        Function<String,Student> f1 = new Function<String, Student>() {
            @Override
            public Student apply(String s) {
                return new Student(s.split(","));
            }
        };
        List<Student> students1 = readStudent1(path,f1); // 读取文本
        students1.forEach(System.out::println); // 遍历打印
        System.out.println("------------------------------");

        // 使用lambda
        Function<String,Student> f2 = s -> new Student(s.split(","));
        List<Student> students2 = readStudent1(path,f1);
        students2.forEach(System.out::println);
        System.out.println("------------------------------");

        // 按字符串数组读取
        // 匿名内部类实现
        System.out.println("按字符串数组读取:");
        Function<String[],Student> f3 = new Function<String[], Student>() {
            @Override
            public Student apply(String[] strings) {
                return new Student(strings);
            }
        };
        List<Student> students3 = readStudent2(path,f3);
        students3.forEach(System.out::println);
        System.out.println("------------------------------");

        //  方法引用实现
        Function<String[],Student> f4 = Student::new;
        List<Student> students4 = readStudent2(path,f4);
        students4.forEach(System.out::println);
    }

    // 读取文本方法 按字符串读
    public static List<Student> readStudent1(String path,Function<String,Student> function){
        ArrayList<Student> students = new ArrayList<>();
        // JDK7新特性 try()
        try (FileReader reader = new FileReader(path);
             BufferedReader br = new BufferedReader(reader)){
            String line;
            while ((line = br.readLine()) != null){
                Student stu = function.apply(line);
                students.add(stu);
            }
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
        return students;
    }

    // 按字符串数组读取
    public static List<Student> readStudent2(String path,Function<String[],Student> function){
        ArrayList<Student> students = new ArrayList<Student>();
        try (FileReader reader = new FileReader(path);
             BufferedReader br = new BufferedReader(reader)){
            String line;
            while ((line = br.readLine()) != null){
                String[] arr = line.split(",");
                Student stu = function.apply(arr);
                students.add(stu);
            }
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
        return students;
    }
}

// 学生类
public class Student {
    private String name;
    private char sex;
    private int age;
    // 无参构造
    public Student() {
    }
    // 有参构造
    public Student(String name, char sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public char getSex() {
        return sex;
    }
    public void setSex(char sex) {
        this.sex = sex;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                '}';
    }
}

3.6Supplier接口

供给型接口

案例:

public class SupplierTest {

    public static void main(String[] args) {
        // 生成随机数组
        int[] arr1 = getNums(() -> new Random().nextInt(100),5);
        System.out.println(Arrays.toString(arr1));

    }
    // 供给型接口
    public static int[] getNums(Supplier<Integer> supplier,int count){
        int[] arr = new int[count];
        for (int i = 0; i < count; i++){
            arr[i] = supplier.get();
        }
        return arr;
    }

}

四、Stream

4.1概述

管道

A pipeline is a sequence of aggregate operations.
管道就是一系列的聚合操作。
A pipeline contains the following components:
管道包含以下组件:
A source: This could be a collection, an array, a generator function, or an I/O channel.
源:可以是集合,数组,生成器函数或I / O通道。
Zero or more intermediate operations. An intermediate operation, such as filter, produces a new stream.
零个或多个中间操作。诸如过滤器之类的中间操作产生新的流。
A terminal operation. A terminal operation, such as forEach, produces a non-stream result, such as a primitive value (like a double value), a collection, or in the case of forEach, no value at all. 
终结操作。终端操作(例如forEach)会产生非流结果,例如原始值(如双精度值),集合,或者在forEach的情况下根本没有任何值。

A stream is a sequence of elements. Unlike a collection, it is not a data structure that stores elements. Instead, a stream carries values from a source through a pipeline.
流是一系列元素。 与集合不同,它不是存储元素的数据结构。 取而代之的是,流通过管道携带来自源的值。
The filter operation returns a new stream that contains elements that match its predicate (this operation's parameter).
筛选器操作返回一个新流,该流包含与筛选条件(此操作的参数)匹配的元素。

4.2获取流的方式

Collection接口

default Stream<E> stream();

Stream接口

//获取流
public static<T> Stream<T> of(T... values);
//将两个流拼接形成一个新的流
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b);

4.3聚合操作和终结操作

Stream接口

聚合操作

Stream<T> filter(Predicate<? super T> predicate);//根据给定的条件过滤流中的元素
<R> Stream<R> map(Function<? super T, ? extends R> mapper);//将流中元素进行类型转换
Stream<T> distinct();//去重
Stream<T> sorted();//排序,如果存储元素没有实现Comparable或者相关集合没有提供Comparator将抛出异常
Stream<T> limit(long maxSize);//根据给定的上限,获取流中的元素
Stream<T> skip(long n);//跳过给定数量的元素

IntStream mapToInt(ToIntFunction<? super T> mapper);//将流中元素全部转为整数
LongStream mapToLong(ToLongFunction<? super T> mapper);//将流中元素全部转为长整数
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);//将流中元素全部转为双精度浮点数

终结操作

void forEach(Consumer<? super T> action);//遍历操作流中元素
<A> A[] toArray(IntFunction<A[]> generator);//将流中元素按照给定的转换方式转换为数组
<R, A> R collect(Collector<? super T, A, R> collector);//将流中的元素按照给定的方式搜集起来

Optional<T> min(Comparator<? super T> comparator);//根据给定的排序方式获取流中最小元素
Optional<T> max(Comparator<? super T> comparator);//根据给定的排序方式获取流中最大元素
Optional<T> findFirst(); //获取流中第一个元素

long count();//获取流中元素数量
boolean anyMatch(Predicate<? super T> predicate);//检测流中是否存在给定条件的元素
boolean allMatch(Predicate<? super T> predicate);//检测流中元素是否全部满足给定条件
boolean noneMatch(Predicate<? super T> predicate);//检测流中元素是否全部不满足给定条件

案例:

public class StreamTest {
    /**
     * 流的基本操作
     * @param args
     */
    public static void main(String[] args) {

        // 定义 数组的方式
        Integer[] numbers = {1,2,3,4,5,6,7,8,9};

        // 两种定义方法
        Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9);
        Stream<Integer> s2 = Stream.of(numbers);

        // 过滤操作
        Stream<Integer> numberStream1 = s2.filter(num -> num > 5); // 保留大于5的元素
        // 进行一次终结操作  终结操作不会返回流,因此流不够再使用   遍历
        numberStream1.forEach(System.out::println);
        System.out.println("-------------------------");

        // 集合的方式
        List<Integer> list = Arrays.asList(numbers);
        Stream<Integer> s3 = list.stream();
        // Integre转Double
        Stream<Double> doubleStream = s3.map(num -> num * 1.0);
        doubleStream.forEach(System.out::println);
        System.out.println("-------------------------");

        // 流的去重
        // 上面的流终结了,不能在使用,新定义一个流
        Stream<Integer> s4 = Stream.of(1,2,1,5,5,2,3,5,1,6);
        // 实现去重
        Stream<Integer> distinctStream = s4.distinct(); // 此时s4就消亡了
        distinctStream.forEach(System.out::println);
        System.out.println("-------------------------");

        // 流的排序操作
        Stream<Integer> s5 = Stream.of(1,2,1,5,5,2,3,5,1,6);

//        Stream<Integer> sorted = s5.sorted((o1, o2) -> o1 - o2);
//        sorted.forEach(System.out::println);

//        s4.sorted((o1, o2) -> o1 - o2).forEach(System.out::println); // 报错 - 状态异常
        s5.sorted((o1, o2) -> o1 - o2).forEach(System.out::println); // 升序
//        s5.sorted((o1, o2) -> o2 - o1).forEach(System.out::println); // 降序

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

        // Map 的方式  Map没有直接转换成Stream的方法
        HashMap<String,Double> map = new HashMap<>();
        map.put("张三",20.0);
        map.put("李四",30.0);
        map.put("王五",10.0);
        // 拿键
        Set<String> set = map.keySet();
        Stream<String> s6 = set.stream();
        s6.forEach(System.out::println);
        System.out.println("-------------------------");

        // 拿到键值
        Stream<Map.Entry<String,Double>> s7 = map.entrySet().stream();
        s7.forEach(System.out::println);
        System.out.println("-------------------------");

        // 获取给定的上限
        Stream<Integer> s8 = Stream.of(numbers);
        s8.limit(4).forEach(System.out::println); // 获取前4个元素
        System.out.println("-------------------------");

        // 跳过给定数量的元素
        Stream<Integer> s9 = Stream.of(1,3,2,4,5,7,6,8);
        s9.skip(2).forEach(System.out::println); // 从头跳过2个元素
        System.out.println("-------------------------");

        // 将流中的元素全部转换为String型
        Stream<Integer> s10 = Stream.of(1,3,2,4,5,7,6,8);
//        s10.map(new Function<Integer, String>() {
//            @Override
//            public String apply(Integer integer) {
//                return "str: " + integer;
//            }
//        }).forEach(System.out::println);
        s10.map((Integer integer) -> "str: " + integer).forEach(System.out::println);
        System.out.println("-------------------------");
        
        // 根据给定的排序方式获取流中最小元素
        Optional<Integer> opt1 = Stream.of(1,2,3,4,5,6).min((o1, o2) -> o1 - o2);
        if (opt1.isPresent()){
            System.out.println("流中的最小值为: " + opt1.get());
        }
        System.out.println("-------------------------");

        // Optional 容器 根据给定的排序方式获取流中最大元素
        Optional<Integer> opt2 = Stream.of(1,2,3,4,5,6).max((o1, o2) -> o1 - o2);
        if (opt2.isPresent()){
            System.out.println("流中的最大值为: " + opt2.get());
        }
        System.out.println("-------------------------");

        // 获取流中第一个元素,通常和filter配合使用
        // 找到流中第一个奇数
        // 实现思路 :首先将全部奇数找出,用filter过滤,然后去第一个数据元素
        Optional<Integer> opt3 = Stream.of(2,2,1,3,5).filter(num -> num %2 != 0).findFirst();
        Integer integer = opt3.orElse(-1);
        System.out.println("流中第一个奇数为: " + integer);
        System.out.println("-------------------------");

        // 获取流中元素数量count
        long count = Stream.of(1, 2, 3, 4, 5, 6, 7, 8).count();
        System.out.println("流中的元素个数为: " + count);

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

        //anyMatch表示任何一个元素匹配即可
        boolean anyMatch = Stream.of("user", "password").anyMatch(role -> "user".equals(role));
        System.out.println("anyMatch的结果为: " + anyMatch);
        System.out.println("-------------------------");

        //noneMatch表示任何一个元素匹配即可
        boolean noneMatch = Stream.of("user", "password").noneMatch(role -> "user".equals(role));
        System.out.println("noneMatch的结果为: " + noneMatch);
        System.out.println("-------------------------");

        //allMatch表示任何一个元素匹配即可
        boolean allMatch = Stream.of("user", "password").allMatch(role -> "user".equals(role));
        System.out.println("noneMatch的结果为: " + allMatch);
        System.out.println("-------------------------");

        // 使用Stream流将一个基本数据类型的数组转换为包装类型的数组,
        // 再将包装类型的数组转换基本数据类型数组。
        Integer[] integers = {1,2,3,4,5,6};
        // Integer转int
        int[] ints = Arrays.stream(integers).mapToInt(Integer::intValue).toArray();
        System.out.println(Arrays.toString(ints));

        // int转Interger
        // toArray将流中元素按照给定的转换方式转换为数组
        Integer[] intes = Arrays.stream(ints).boxed().toArray(Integer[]::new);
        System.out.println(Arrays.toString(intes));
        System.out.println("-------------------------");
        
    }
}

五、计时器

timer、timerTask

Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。

它可以安排任务“执行一次”或者定期“执行多次

案例:

public class TimerTest {
    // 计时器 计时器任务
    public static void main(String[] args) {
        // 使用原子类来记数
        AtomicInteger counter = new AtomicInteger(0);
        // 定时做什么事
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() { // 计时器任务要执行的
                System.out.println("定时任务");
                // 原子类实现自增1
                int currentCount = counter.incrementAndGet();
                if (currentCount == 10){ // 满足条件就取消定时任务
                    timer.cancel();// 取消定时任务
                }
            }
        };

        // 日历类Calendar
        // Duration 一个时间方面的工具类,进行时间单位的计量转换

        // 解决问题 - 从当前时间延迟多少毫秒是明天的9点
        // delay 表示第一次执行任务的延迟时间,0表示不延迟,period表示任务循环执行的周期,单位是毫秒
        // timer.schedule(task,0, Duration.ofDays(1).toMillis());

        // 获取现在的时间
        Long now = System.currentTimeMillis();
        // 获取一个日历对象 , 默认时间就是现在
        Calendar c = Calendar.getInstance();

        // 获取小时数
        int hours = c.get(Calendar.HOUR_OF_DAY);
        if (hours > 9){ // 判断是否跨天 - 当天是否已过目标时间
            c.roll(Calendar.DAY_OF_MONTH,1); // 天数延长一天
        }
        // 设置定时任务的启动时间
        c.set(Calendar.HOUR_OF_DAY,9);
        c.set(Calendar.MINUTE,0);
        c.set(Calendar.SECOND,0);
        c.set(Calendar.MILLISECOND,0);
        Long targetTime = c.getTimeInMillis();
        Long delay = targetTime - now;

        timer.schedule(task,0, 2000);
    }
}

六、日期类

案例:日历的制作

public class MyDay {

    // 天数
    private int days;
    // 天数是否是当前月份的
    private boolean crrentDayOfMonth;

    public MyDay(int days, boolean crrentDayOfMonth) {
        this.days = days;
        this.crrentDayOfMonth = crrentDayOfMonth;
    }

    // 打印天 - 是当月的为绿色字体 ,不是当月的为红色字体
    public void showDays(){ // 又问题的 System out和err 之间线程安全
        if (crrentDayOfMonth){
//            System.out.print(days + "\t");
            System.out.print("\33[36;1m"+days+"\t\33[0m");
        }else{
//  \33[%d;%d;%dm可以更改输出样式,改变的内容包括字体颜色、背景颜色、样式(粗体、斜体、下划线)
//  \33[%d;%d;%dm的第一个数字是前景色(字体颜色),范围31-36;第二个数字是背景色,范围41-46;第三个数字是样式,取值1,3,4
            //                 红黄橙蓝紫绿
            System.out.print("\33[31;1m"+days+"\t\33[0m");
        }
    }

    @Override
    public String toString() {
        return "MyDay{" +
                "days=" + days +
                ", crrentDayOfMonth=" + crrentDayOfMonth +
                '}';
    }
}

public class MyCalendar {

    // 42个格子,打印42个  6 * 7 = 42
    private static final int CAPACITY = 42;

    // 星期字符串数组
    String[] weeks = {"一","二","三","四","五","六","日"};
    // 日期格式1
    String format1 = "yyyy年MM月dd日 hh:mm:ss";
    SimpleDateFormat sdf1 = new SimpleDateFormat(format1);

    // 打印当前时间的方法
    public void showNowDay(){
        Date date = new Date();
        System.out.println("当前日期为:");
        System.out.println(sdf1.format(date));
    }

    // 得到需要打印天数的42个天数 (包括上一个月、当前月和下一个月)
    public List<MyDay> getListOfDays(int year,int month){

        ArrayList<MyDay> days = new ArrayList<>(CAPACITY); // 固定42个大小的集合

        // 根据传入的年份和月份获取目标日期对象
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR,year);
        calendar.set(Calendar.MONTH,month-1);

        // 计算当前月的天数
        int daysOfCurrentMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

        // 上一个月天数   -  当前月星期数-1
        int daysOfLastMonth = calendar.get(Calendar.DAY_OF_WEEK) - 1;
        // 计算上一个月的最大天数
        int daysOfLastMonthMax;
        // 当前月是1月
        if (month == 1){ // 表示上一个月最大天数是31天
            daysOfLastMonthMax = 31; // 上一年的12月
        } else {
            calendar.set(Calendar.MONTH,month-2);
            daysOfLastMonthMax = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        }

        // 计算下一个月的天数
        int daysOfNextMonth = CAPACITY - daysOfCurrentMonth - daysOfLastMonth;

        // 先保存上一个月的天数到集合中
        for (int i = (daysOfLastMonthMax - daysOfLastMonth + 1); i <= daysOfLastMonthMax; i++){
            days.add(new MyDay(i,false));
        }
        // 接着保存当前月的天数到集合中
        for (int i = 1; i <= daysOfCurrentMonth; i++){
            days.add(new MyDay(i,true));
        }
        // 最后保存下一个月的天数到集合中
        for (int i = 1; i <= daysOfNextMonth; i++){
            days.add(new MyDay(i,false));
        }
        return days;
    }

    // 打印日历的方法 - 全
    public void showCalendar(int year,int month){
        // 打印当前日期
        showNowDay();
        System.out.println("---------------------");
        // 打印目标日期
        System.out.println("查询日历为: " + year + "年" + month + "月" );
        for (int i = 0; i < weeks.length; i++){
            System.out.print(weeks[i] + "\t"); // 打印星期
        }
        System.out.println();// 换行

        // 调用打印日期的方法
        printCalendar(year,month);
    }

    // 打印目标日期的方法
    public void printCalendar(int year,int month){
        // 调用方法,判断年份,月份是否符合
        isYearMonth(year,month);
        // 获取需要打印的日期
        List<MyDay> days = getListOfDays(year, month);
        // 遍历打印
        for (int i = 1; i <= days.size(); i++){
            MyDay myDay = days.get(i-1); // 获取MyDay对象
            myDay.showDays(); // 调用MyDay的打印方法
            if (i % 7 == 0){
                System.out.println();
            }
        }
    }

    // 判断year和month是否符合规范
    public boolean isYearMonth(int year,int month){
        // 月份1-12月
        if (1 <= month && month <= 12){
            return true;
        }else {
            throw new RuntimeException("月份不符合!");
//            return false;
        }
    }

}

public class MyCalendarTest {
    /**
     * MyCalendar测试
     */
    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);
        System.out.println("请输入要查询日期的年份:");
        int year = input.nextInt();
        System.out.println("请输入要查询日期的月份(1-12):");
        int month = input.nextInt();
        MyCalendar myCalendar = new MyCalendar();
        myCalendar.showCalendar(year,month);
//        myCalendar.showCalendar(2022,7);
        input.close();

    }
}

运行结果:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值