• java1.8新特性入门级讲解


    Java1.8新特性

    java1.8引入了许多的特性,在此我们主要讨论一下几点:

    • 接口静态方法

    • 接口默认方法 : 默认方法就是一个在接口里面有了一个或多个实现的方法。

    • 函数式接口: 一个在接口只有一个待实现的方法。 (重要)

    • Lambda 表达式: Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。 (重要)

    • Stream API: 新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。 (重要)

    • Date Time API: 加强对日期与时间的处理。

    • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

    接口静态方法

    为什么接口要支持静态方法

    接口中的静态方法背后的思想是提供一种简单的机制,允许通过将相关的方法内聚在接口中,而不必创建新的对象。

    抽象类也可以做同样的事情。主要的区别在于抽象类可以有构造函数、成员变量和方法。

    推荐把和只和接口相关的静态utility方法放在接口中(提高内聚性),而不需要额外创建一些utility类专门去放置这些方法。

    interface NewInterface { 
    
        // 静态方法
        static void hello() 
        { 
            System.out.println("Hello, New Static Method Here"); 
        } 
    
        // 抽象方法 
        void overrideMethod(String str); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    接口默认方法

    Java 8 新增了接口的默认方法。

    简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

    我们只需在方法名前面加个 default 关键字即可实现默认方法。

    为什么要有这个特性?

    首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

    JDK8 中接口默认方法有什么用

    以前创建了一个接口,并且已经被大量的类实现。
    如果需要再扩充这个接口的功能加新的方法,就会导致所有已经实现的子类需要重写这个方法。
    如果在接口中使用默认方法就不会有这个问题。
    所以从 JDK8 开始新加了接口默认方法,便于接口的扩展。

    例子:
    public interface Vehicle {
       default void print(){
          System.out.println("我是一辆车!");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    函数式接口与Lambda表达式

    为什么我要把这两个放一起讲呢?是因为Lambda表达式的原理就是调用的函数式接口

    函数式接口与Lambda的关联

    我以Runnable举例,这是一个最常见的函数式接口:

    // 函数式接口的注解 @FunctionalInterface 其实也可以不加,而加上的理由:1、为了美观、易读 2、加上该注解能够更好地让编译器进行检查。
    @FunctionalInterface
    // 这个接口里面只有一个实现方法,被我们成为函数式接口
    public interface Runnable {
        public abstract void run();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    作用:可以使用Lambda来简化函数式接口

        public static void main(String[] args) {
            // 就用Runable实现run的方法,启动线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("这个代码看着就很麻烦");
                }
            }).start();
            
            // Lambda 表达式 是用来简化函数式接口的
            new Thread(()->{
                System.out.println("这个看着就很清爽");
            }).start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    好,经过以上的两个案例,想必大家多少理解了函数式接口与Lambda是个啥玩意,那么下面我来说一下四大函数式接口,这里很重要,与Stream流息息相关

    四大函数式接口

    1、Function :函数式接口,有参有返回值

    我们先来看源码:安装1、2、3、的顺序读

    @FunctionalInterface
    // 1、相信大家看了也摸不着头脑 这样
    public interface Function<T, R> {
        // 3、我们就看这里,这里不就是说,给一个T的类,返回一个R的类吗?T与R是泛型,代指类
        R apply(T t);
    
        // 2、下面的这些有default关键字,是接口默认方法,所以我们就不看了,最后一个是接口静态方法,不是我们要找的函数式接口方法
        default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
    
        default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }
    
        static <T> Function<T, T> identity() {
            return t -> t;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    好,上一步不理解没关系,这里我们看案例:
        public static void main(String[] args) {
            // 这里就是通过function方法把String的值转换为Integer类型,看着就很多
            Function<String, Integer> function = new Function<String, Integer>() {
                @Override
                public Integer apply(String o) {
                    return Integer.parseInt(o);
                }
            };
            System.out.println(function.apply("21"));
    
            // 因为这是一个函数式接口,我们用Lambda简化一下
            Function<String, Integer> function2 = (str)->{return Integer.parseInt(str);};
            System.out.println(function2.apply("35"));
    
            // 再简化,这里主要讲的函数式接口的使用,函数式接口与Lambda的关系,之后会将Lambda表达式的
            Function<String, Integer> function3 = Integer::parseInt;
            System.out.println(function3.apply("67"));
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    看一下输出:

    在这里插入图片描述

    2、Predicate < T >: 断言型接口,有参有返回值,返回值是boolean类型

    我们先来看源码:

    @FunctionalInterface
    public interface Predicate<T> {
        // 还是只看这个,意思就很明确了,传入一个泛型然后返回一个boolean类型
        boolean test(T t);
    
        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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    好,看案例:
            Predicate<Integer> predicate = new Predicate<Integer>() {
                @Override
                public boolean test(Integer o) {
                    if (o>10){
                        return true;
                    }else {
                        return false;
                    }
                }
            };
            System.out.println("predicate--1==>"+predicate.test(1));
            System.out.println("predicate--11==>"+predicate.test(11));
    
            //因为这是一个函数式接口,我们用Lambda简化一下
            Predicate<Integer> predicate2 = (o)->{
                if (o>10){
                    return true;
                }else {
                    return false;
                }
            };
            System.out.println("predicate2--15==>"+predicate2.test(15));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    看结果:

    在这里插入图片描述

    3、Consumer < T >:消费型接口,有参无返回值

    看源码:

    @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); };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    看案例:
            Consumer<String> consumer = new Consumer<String>() {
                @Override
                public void accept(String o) {
                    System.out.println("胖虎打"+o);
                }
            };
            consumer.accept("大熊");
    
    		// Lambda简化
            Consumer<String> consumer2 = str ->{
                System.out.println("大熊打"+str);
            };
            consumer2.accept("胖虎");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    结果:

    在这里插入图片描述

    4、Supplier < T >:供给型接口,无参有返回值

    看源码:

    @FunctionalInterface
    public interface Supplier<T> {
        // 没有传参返回一个 T 类型的值
        T get();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    看案例:
            Supplier<String> supplier = new Supplier<String>() {
                @Override
                public String get() {
                    return "就没有返回值";
                }
            };
            System.out.println(supplier.get());
            
            //简化
            Supplier<Integer> supplier2 = ()->{ return 1024; };
            System.out.println(supplier2.get());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果:

    在这里插入图片描述

    Lambda表达式:

    Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

    Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

    使用 Lambda 表达式可以使代码变的更加简洁紧凑。

    语法

    lambda 表达式的语法格式如下:

    (parameters) -> expression

    或

    (parameters) ->{ statements; }

    lambda表达式的重要特征:
    • **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
    • **可选的参数圆括号:**一个参数无需定义圆括号,但多个参数需要定义圆括号。
    • **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。
    • **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

    Lambda 表达式简单实例

    // 1. 不需要参数,返回值为 5  
    () -> 5  
      
    // 2. 接收一个参数(数字类型),返回其2倍的值  
    x -> 2 * x  
      
    // 3. 接受2个参数(数字),并返回他们的差值  
    (x, y) -> x – y  
      
    // 4. 接收2个int型整数,返回他们的和  
    (int x, int y) -> x + y  
      
    // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
    (String s) -> System.out.print(s)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    案例:

    public class Java8Tester {
       public static void main(String args[]){
          Java8Tester tester = new Java8Tester();
            
          // 类型声明
          MathOperation addition = (int a, int b) -> a + b;
            
          // 不用类型声明
          MathOperation subtraction = (a, b) -> a - b;
            
          // 大括号中的返回语句
          MathOperation multiplication = (int a, int b) -> { return a * b; };
            
          // 没有大括号及返回语句
          MathOperation division = (int a, int b) -> a / b;
            
          System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
          System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
          System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
          System.out.println("10 / 5 = " + tester.operate(10, 5, division));
            
          // 不用括号
          GreetingService greetService1 = message ->
          System.out.println("Hello " + message);
            
          // 用括号
          GreetingService greetService2 = (message) ->
          System.out.println("Hello " + message);
            
          greetService1.sayMessage("Runoob");
          greetService2.sayMessage("Google");
       }
        
        // 这也是函数式接口 只不过是类里面的接口
       interface MathOperation {
          int operation(int a, int b);
       }
        
       interface GreetingService {
          void sayMessage(String message);
       }
        
       private int operate(int a, int b, MathOperation mathOperation){
          return mathOperation.operation(a, b);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    输出结果:

    10 + 5 = 15
    10 - 5 = 5
    10 x 5 = 50
    10 / 5 = 2
    Hello Runoob
    Hello Google

    这里我就带大家过一遍,Lambda知识点很多,入门或者正常使用的话,看我这个就足够了,如果还行深究就可以看一下这篇文章:Java-lambda表达式入门看这一篇就够了 - 腾讯云开发者社区-腾讯云 (tencent.com)

    Stream流:

    Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

    Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

    Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

    这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

    元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

    什么是 Stream?

    Stream(流)是一个来自数据源的元素队列并支持聚合操作

    • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
    • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
    • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

    和以前的Collection操作不同, Stream操作还有两个基础的特征:

    • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
    • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

    按照装我的理解,这玩意就是用来处理集合的,而且非常的高效

    生成流

    在 Java 8 中, 集合接口有两个方法来生成流:

    • stream() − 为集合创建串行流。
    • parallelStream() − 为集合创建并行流。
    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
    
    • 1
    • 2

    各种API:

    forEach

    Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

    Random random = new Random();
    random.ints().limit(10).forEach(System.out::println);
    
    • 1
    • 2

    map

    map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

    List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
    // 获取对应的平方数
    List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
    
    • 1
    • 2
    • 3

    filter

    filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

    List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    // 获取空字符串的数量
    long count = strings.stream().filter(string -> string.isEmpty()).count();
    
    • 1
    • 2
    • 3

    limit

    limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

    Random random = new Random();
    random.ints().limit(10).forEach(System.out::println);
    
    • 1
    • 2

    sorted

    sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

    Random random = new Random();
    random.ints().limit(10).sorted().forEach(System.out::println);
    
    • 1
    • 2

    并行(parallel)程序

    parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:

    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    // 获取空字符串的数量
    long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
    
    • 1
    • 2
    • 3

    我们可以很容易的在顺序运行和并行直接切换。

    Collectors

    Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

    List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
     
    System.out.println("筛选列表: " + filtered);
    String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
    System.out.println("合并字符串: " + mergedString);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    统计

    另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

    List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
     
    IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
     
    System.out.println("列表中最大的数 : " + stats.getMax());
    System.out.println("列表中最小的数 : " + stats.getMin());
    System.out.println("所有数之和 : " + stats.getSum());
    System.out.println("平均数 : " + stats.getAverage());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我的案例:

    public class Demo {
        public static void main(String[] args) {
            List<Student> list = new ArrayList<>();
            Student student = new Student(18,"张三",1);
            list.add(student);
            student = new Student(13,"张三",1);
            list.add(student);
            student = new Student(21,"李四",1);
            list.add(student);
            student = new Student(10,"小红",0);
            list.add(student);
            student = new Student(25,"大红",0);
            list.add(student);
            // 我在里面list存放了 四个对象
            // 1、18岁男性张三
            // 2、21岁男性李四
            // 3、10岁女性小红
            // 4、25岁女性大红
    
            // 案例一、现在我要找所有的女性
            System.out.println("案例一、现在我要找所有的女性");
            List<Student> collect = list.stream().filter(x -> {
                return x.getSex() == 0;
            }).collect(Collectors.toList());
            for (Student student1 : collect) {
                System.out.println(student1);
            }
            System.out.println("简化案例一");
            // 案例一、简化
            list.stream().filter(x->{return x.getSex()==0;}).collect(Collectors.toList()).forEach(System.out::println);
    
            // 案例二、所有人名字
            System.out.println("案例二、所有人名字");
            list.stream().map(Student::getName).collect(Collectors.toList()).forEach(x->{
                System.out.print(x+"、");
            });
            System.out.println();
            System.out.println("案例二、所有人名字且不重复");
            list.stream().map(Student::getName).collect(Collectors.toSet()).forEach(x->{
                System.out.print(x+"、");
            });
            System.out.println();
            System.out.println("案例三、所有人名字且只要三条条数据");
            list.stream().map(Student::getName).limit(3).collect(Collectors.toList()).forEach(x->{
                System.out.print(x+"、");
            });
        }
    }
    class Student{
        private int age;
        private String name;
        private int sex; // 0为女 1为男
    
        public Student(int age, String name, int sex) {
            this.age = age;
            this.name = name;
            this.sex = sex;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getSex() {
            return sex;
        }
    
        public void setSex(int sex) {
            this.sex = sex;
        }
    
    
        @Override
        public String toString() {
            return "Student{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", sex=" + sex +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    这里我就带大家过一遍,Stream知识点很多,入门或者正常使用的话,看我这个就足够了,如果还行深究就可以看一下这篇文章:Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合_云深i不知处的博客-CSDN博客_java stream 分组聚合

    日期时间 API

    Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。

    在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

    • 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
    • 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
    • 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

    Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

    • Local(本地) − 简化了日期时间的处理,没有时区的问题。
    • Zoned(时区) − 通过制定的时区处理日期时间。

    新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

    本地化日期时间 API

    LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。代码如下:

    import java.time.LocalDate;
    import java.time.LocalTime;
    import java.time.LocalDateTime;
    import java.time.Month;
     
    public class Java8Tester {
       public static void main(String args[]){
          Java8Tester java8tester = new Java8Tester();
          java8tester.testLocalDateTime();
       }
        
       public void testLocalDateTime(){
        
          // 获取当前的日期时间
          LocalDateTime currentTime = LocalDateTime.now();
          System.out.println("当前时间: " + currentTime);
            
          LocalDate date1 = currentTime.toLocalDate();
          System.out.println("date1: " + date1);
            
          Month month = currentTime.getMonth();
          int day = currentTime.getDayOfMonth();
          int seconds = currentTime.getSecond();
            
          System.out.println("月: " + month +", 日: " + day +", 秒: " + seconds);
            
          LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
          System.out.println("date2: " + date2);
            
          // 12 december 2014
          LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
          System.out.println("date3: " + date3);
            
          // 22 小时 15 分钟
          LocalTime date4 = LocalTime.of(22, 15);
          System.out.println("date4: " + date4);
            
          // 解析字符串
          LocalTime date5 = LocalTime.parse("20:15:30");
          System.out.println("date5: " + date5);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    输出结果:

    当前时间: 2016-04-15T16:55:48.668
    date1: 2016-04-15
    月: APRIL, 日: 15, 秒: 48
    date2: 2012-04-10T16:55:48.668
    date3: 2014-12-12
    date4: 22:15
    date5: 20:15:30

    使用时区的日期时间API

    如果我们需要考虑到时区,就可以使用时区的日期时间API:

    import java.time.ZonedDateTime;
    import java.time.ZoneId;
     
    public class Java8Tester {
       public static void main(String args[]){
          Java8Tester java8tester = new Java8Tester();
          java8tester.testZonedDateTime();
       }
        
       public void testZonedDateTime(){
        
          // 获取当前时间日期
          ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
          System.out.println("date1: " + date1);
            
          ZoneId id = ZoneId.of("Europe/Paris");
          System.out.println("ZoneId: " + id);
            
          ZoneId currentZone = ZoneId.systemDefault();
          System.out.println("当期时区: " + currentZone);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    输出结果为:

    date1: 2015-12-03T10:15:30+08:00[Asia/Shanghai]
    ZoneId: Europe/Paris
    当期时区: Asia/Shanghai

    Optional 类

    Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

    Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

    Optional 类的引入很好的解决空指针异常。

    类声明

    以下是一个 java.util.Optional 类的声明:

    public final class Optional<T>
    extends Object
    
    • 1
    • 2

    类方法

    序号方法 & 描述
    1**static Optional empty()**返回空的 Optional 实例。
    2**boolean equals(Object obj)**判断其他对象是否等于 Optional。
    3**Optional filter(Predicate
    4** Optional flatMap(Function> mapper)**如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
    5**T get()**如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
    6**int hashCode()**返回存在值的哈希码,如果值不存在 返回 0。
    7**void ifPresent(Consumer consumer)**如果值存在则使用该值调用 consumer , 否则不做任何事情。
    8**boolean isPresent()**如果值存在则方法会返回true,否则返回 false。
    9**Optional map(Function mapper)**如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。
    10**static Optional of(T value)**返回一个指定非null值的Optional。
    11**static Optional ofNullable(T value)**如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
    12**T orElse(T other)**如果存在该值,返回值, 否则返回 other。
    13**T orElseGet(Supplier other)**如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。
    14** T orElseThrow(Supplier exceptionSupplier)**如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
    15**String toString()**返回一个Optional的非空字符串,用来调试

    注意: 这些方法是从 java.lang.Object 类继承来的。

    Optional 实例

    我们可以通过以下实例来更好的了解 Optional 类的使用:

    import java.util.Optional;
     
    public class Java8Tester {
       public static void main(String args[]){
       
          Java8Tester java8Tester = new Java8Tester();
          Integer value1 = null;
          Integer value2 = new Integer(10);
            
          // Optional.ofNullable - 允许传递为 null 参数
          Optional<Integer> a = Optional.ofNullable(value1);
            
          // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
          Optional<Integer> b = Optional.of(value2);
          System.out.println(java8Tester.sum(a,b));
       }
        
       public Integer sum(Optional<Integer> a, Optional<Integer> b){
        
          // Optional.isPresent - 判断值是否存在
            
          System.out.println("第一个参数值存在: " + a.isPresent());
          System.out.println("第二个参数值存在: " + b.isPresent());
            
          // Optional.orElse - 如果值存在,返回它,否则返回默认值
          Integer value1 = a.orElse(new Integer(0));
            
          //Optional.get - 获取值,值需要存在
          Integer value2 = b.get();
          return value1 + value2;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    输出结果为:

    第一个参数值存在: false
    第二个参数值存在: true
    10

    以上部分内容参照:

    Java 8 日期时间 API | 菜鸟教程 (runoob.com)

    Nullable(value1);

      // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
      Optional b = Optional.of(value2);
      System.out.println(java8Tester.sum(a,b));
    
    • 1
    • 2
    • 3

    }

    public Integer sum(Optional a, Optional b){

      // Optional.isPresent - 判断值是否存在
        
      System.out.println("第一个参数值存在: " + a.isPresent());
      System.out.println("第二个参数值存在: " + b.isPresent());
        
      // Optional.orElse - 如果值存在,返回它,否则返回默认值
      Integer value1 = a.orElse(new Integer(0));
        
      //Optional.get - 获取值,值需要存在
      Integer value2 = b.get();
      return value1 + value2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    }
    }

    
    输出结果为:
    
    > 第一个参数值存在: false
    > 第二个参数值存在: true
    > 10
    
    
    
    
    
    ## 以上部分内容参照:
    
    [Java 8 日期时间 API | 菜鸟教程 (runoob.com)](https://www.runoob.com/java/java8-datetime-api.html)
    
    [Java 8 Optional 类 | 菜鸟教程 (runoob.com)](https://www.runoob.com/java/java8-optional-class.html)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    孙卫琴的《精通Vue.js》读书笔记-插槽slot的基本用法
    【Java面试】面试自閟了!工作5年的小伙伴今天面试被吊打问我,并行和并发有什么区别?
    基于WT588F02A-16S语音芯片在启蒙玩具用品的方案设计应用解析
    Python之第八章 模块和包 --- 包
    Vue 动态设置css伪元素content的值,解决不渲染和渲染为数字的问题
    【licheePi-dock】驱动一个OLED-SSD1306
    React学习笔记
    gslx680触摸屏驱动源码码分析(gslX680.c)
    MyBatis学习笔记(二)
    Vite项目构建chrome extension,实现多入口
  • 原文地址:https://blog.csdn.net/qq_57581439/article/details/127425140