24.JDK1.8特性

本章目标

  • Lambda表达式
  • 函数式接口
  • 方法引用
  • Stream流
  • Date Time API

本章内容

本章内容了解即可

JDK1.8新增了非常多的特性,我们主要讨论以下几个:

  • Lambda表达式:Lambda允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
  • 函数式接口functional Interface),有且仅有一个抽象方法的接口,但可以有多个非抽象的方法
  • 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法:默认方法就是一个在接口里面有了一个实现的方法。
  • Stream API:新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Date Time API:加强对日期与时间的处理。
  • Optional类:Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

一、Lambda表达式

Lambda表达式是一种匿名函数,基于数学中的λ演算得名,简单的理解,它是没有声明的方法,即:没有名称,但它有参数列表、方法主体、返回类型,可能还有可以抛出的异常

1、 Lambda表达式语法

(parameters) -> expression 或 (parameters) -> { statements; }

参数说明:

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

简单实例如下:

// 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)

2、 排序示例

按照Java传统的语法规则编写拥有People对象的List集合peopleList,需要对age进行排序,JDK1.8之前:

// 创建People比较器
Comparator<People> comparator = new Comparator<People>() {
  @Override
    public int compare(People o1, People o2) {
      return o1.getAge() > o2.getAge();
    }
};

// 排序
Collections.sort(peopleList, comparator);

JDK1.8后,使用Lambda表达式:

// Lambda表达式方式
Comparator<People> comparator = (o1, o2) -> {
      return o1.getAge() > o2.getAge();
};

// 排序
Collections.sort(peopleList, comparator);

更简洁的写法如下:

peopleList.sort(Comparator.comparingInt(People::getAge));

由上例可知Lambda表达式代替了new Comparator()内部类 ,通过Lambda表达式来创建该接口的对象。

总结:在Java中,Lambda表达式是一种简化的代码写法,可以用来创建匿名函数。它们可以替代传统的内部类或者定义接口的方式

3、 使用场景:

使用场景 Lambda表达式示例
布尔表达式 (List list) -> list.isEmpty()
创建对象 () -> new People(10)
消费一个对象 (People p) ->
从一个对象中选择/抽取 (int i) -> i.getAge()
组合两个值 (int a, int b) -> a * b
比较两个对象 (People p1, People p2) -> p1.getAge().compareTo(p2.getAge())

Lambda表达式必须接合函数式接口一起使用

二、函数式接口

函数式接口(functional Interface),有且仅有一个抽象方法的接口,但可以有多个非抽象的方法。

1、 格式

修饰符 interface 接口名 {
     public abstract 返回值类型 方法名 (可选参数列表);
}

public abstract可以省略(因为默认修饰为public abstract)

示例:

public interface MyFunctionalInterface {
    public abstract void method();
}

2、 注解@FunctionalInterface

@FunctionalInterface,是JDK1.8中新引入的一个注解,专门指代函数式接口,用于一个接口的定义上。

和@Override注解的作用类似,@FunctionalInterface注解可以用来检测接口是否是函数式接口。如果是函数式接口,则编译成功,否则编译失败(接口中没有抽象方法或者抽象方法的个数多余1个)。

3、 无参无返回类型

接口:

package com.it;

public interface FunctionInterface {

    public void show();

}

传统方式实现

 FunctionInterface fi = new FunctionInterface() {
            @Override
            public void show() {
                System.out.println("aa");
           }
  };
fi.show();

光标放在FunctionInterface()后,按alt+enter之后会有提示转换为lamdba

lamdba方式实现:

package com.it;

public class Test {

    public static void main(String[] args) {
        //传统方式创建接口实现类或内部类实现接口方法
        //使用Lambda表达式代替匿名内部类
        FunctionInterface fi = () -> System.out.println("hello lamdba");
        fi.show();
    }
}

4、 有参无返回类型

接口:

package com.it;

public interface FunctionInterface {

    public void show(String name);

}

实现类:

package com.it;

public class Test {

    public static void main(String[] args) {

        FunctionInterface fi = (name) -> System.out.println("hello lamdba"+name);
        fi.show("tom");
    }
}

5、 有参有返回类型

接口:

package com.it;

public interface FunctionInterface {

    public int sum(int a, int b);

}

实现类:

package com.it;

public class Test {

    public static void main(String[] args) {

        FunctionInterface fi = (a,b) -> a+b;
        int sum = fi.sum(1, 1);
        System.out.println(sum);
    }
}

6 、常用的函数式接口

上面示例中我们自己写了些函数式接口,实际上1.8中也提供了一些函数式接口,来完成我们常见的操作,Supplier,Consumer,Function,Predicate这些就是用来完成基本应用的

JDK 1.8 之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

在JDK1.8新增了java.util.function包下的很多函数式接口,用来支持Java的函数式编程,从而丰富了Lambda表达式的使用场景。

接口名 功能描述 使用示例
Supplier<T> 无参数,返回一个结果 Supplier supplier = () -> 42;
Consumer<T> 接受一个参数并且不返回结果 Consumer consumer = (s) -> System.out.println(s);
BiConsumer<T, U> 接受两个参数并且不返回结果 BiConsumer<String, Integer> biConsumer = (s, i) -> System.out.println(s + i);
Function<T, R> 接受一个参数并返回结果 Function<Integer, String> function = (i) -> “Result:” + i;
BiFunction<T, U, R> 接受两个参数并返回结果 BiFunction<String, Integer, String> biFunction = (s, i) -> s + i;
Predicate<T> 接受一个参数并返回布尔值 Predicate predicate = (i) -> i > 0;
BiPredicate<T, U> 接受两个参数并返回布尔值 BiPredicate<String, Integer> biPredicate = (s, i) -> s.length() > i;
UnaryOperator 接受一个参数并返回相同类型的结果 UnaryOperator unaryOperator = (i) -> i * 2;
BinaryOperator 接受两个参数并返回相同类型的结果 BinaryOperator binaryOperator = (a, b) -> a + b;

三、方法引用

适合于调用无参的方法及构造方法,不适合调用有参的。但不要为了Lamdba而Lamdba

方法引用可以看作是Lambda表达式深层次的表达, Lambda表达中可以直接引用已有Java类或对象的方法或构造器。方法引用与lambda表达式结合使用,可以进一步简化代码。

1、 方法引用的通用特性:

方法引用所使用方法的入参和返回值与lambda表达式实现的函数式接口的入参和返回值一致

2、 格式:

使用操作符 “::”将类(或对象)与方法名分割开来

具体分为如下的四种情况:

类型 语法 对应的Lambda表达式
静态方法引用 类名::staticMethod () -> 类名.staticMethod()
实例方法引用 类名::instMethod (inst) -> inst.instMethod()
带参方法引用 类名::instMethod (inst,args) -> inst.instMethod(args)
构建方法引用 类名::new () -> new 类名()

方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖

示例Person类

package com.woniuxy.lambda;

/**
 * @author :fengsir
 * @date :Created in 2023/02/7 10:09
 * @description:TODO
 * @modified By:
 * @version: 1.0
 */
public class Person {
    private int id;
    private String name;
    private int age;
    public Person() {

    }

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

    public static void testStatic() {
        System.out.println("static");
    }

    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;
    }

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

下面的示例围绕着调用Person中的静态方法和非静态方法等展开

3、 类名::静态方法名

函数接口:

package com.it;
public interface FunctionInterface {
    public void testLamdba();
}

实现类:

public class Test {
    public static void main(String[] args) {
        //Lamdba方式调用: 相当于在匿名内部实现中调用了Person的testStatic()
        FunctionInterface fi = () -> Person.testStatic();
        //方法引用的方式调用
        FunctionInterface fi = Person::testStatic;
        fi.testLamdba();
    }
}

调用带参的方法参考示例3.7,需要在接口上定义参数,比较麻烦,不要为了Lamdba而Lamdba

4、 类名::无参实例方法名

函数接口:

调用Person类中的getName方法,返回String,注意参数里需要传入Person对象

public interface FunctionInterface {
    public String testLamdba(Person person);
}

实现类:

public class Test {
    public static void main(String[] args) {
        //Lamdba方式调用
        FunctionInterface fi = (person) -> person.getName();
        //方法引用方式
        FunctionInterface fi = Person::getName;
        String result = fi.testLamdba(new Person(1, "tom", 20));
        System.out.println(result);
    }
}

5、 类名::带参实例方法名

函数接口:

要调用带参的方法,在函数接口中即要定义传入的对象,又要传参数

public interface FunctionInterface {
    public void testLamdba(Person person,String name);
}

实现类:

public class Test {
    public static void main(String[] args) {
        //Lamdba方式调用
        FunctionInterface fi = (person,name) -> person.setName(name);
        //方法引用方式
        FunctionInterface fi = Person::setName;
        Person person = new Person();
        fi.testLamdba(person,"tom");
        System.out.println(person.getName());
    }
}

表达式的第一个入参为实例方法的调用者,后面的入参与实例方法的入参一致

6、 构造器引用类名::new

函数接口:

期望调用方法返回一个对象

public interface FunctionInterface {
    public Person testLamdba();
}

实现类:

public class Test {
    public static void main(String[] args) {
        //Lamdba方式调用
        //FunctionInterface fi = () -> new Person();
        //方法引用方式
        FunctionInterface fi = Person::new;
        Person person = fi.testLamdba();
        System.out.println(person);
    }
}

方法引用并不适合创建带参的构造方法,不要为了方法引用而方法引用

四、Stream

在实际Java程序中,集合的使用往往随着业务需求、复杂度而变得更加复杂,在这其中将可能会涉及到更多的运算,如:求和、平均值、分组、过滤、排序等等。如何这些操作混合出现,又该如何实现?难道遍历、再遍历、再运算么?

Stream是自JDK1.8 版本后Java API中的新成员,它允许你以声明的方式处理数据集合,你可以把它看成是遍历数据集的高级迭代器,通过声明方式,对集合中的每个元素进行一系列并行或串行的流水线操作。此外,Stream还可以透明地并行处理,而无需写任何多线程代码了。

整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理

1、 Stream操作

Stream操作分为三步:

  • 创建操作符
  • 中间操作符
  • 终止操作符

4.1、创建操作符

  • 通过Collection对象的stream()或parallelStream()方法。
  • 通过Arrays类的stream()方法。
  • 通过Stream接口的of()、iterate()、generate()方法。
  • 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法

4.2、 中间操作符

中间操作符在执行指定处理逻辑后,数据流依然可以传递给下一级的操作符。中间操作符包含8种:

  • map(mapToInt,mapToLong,mapToDouble) 转换操作:把比如A->B,这里默认提供了转int,long,double的操作符。

  • flatmap(flatmapToInt,flatmapToLong,flatmapToDouble)拍平操作:比如把int[]{2,3,4}拍平变成 2,3,4,即从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作。

  • limit限流操作:比如数据流中有10个,我只要前3个就可以使用。

  • distinct去重操作:重复元素去重。

  • filter过滤操作:对集合数据进行过滤。

  • peek消费操作:如果想对数据进行某些操作,如:读取、编辑修改等。

  • skip跳过操作:跳过某些元素。

  • sorted排序操作:对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。

    (具体可参照源码java.util.stream.Stream)

4.3、 终止操作符

终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。

  • collect收集操作:将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream的核心在于Collectors。
  • count统计操作:统计最终的数据个数。
  • findFirst、findAny查找操作:查找第一个、查找任何一个,返回的类型为Optional。
  • noneMatch、allMatch、anyMatch匹配操作:数据流中是否存在符合条件的元素,返回值为bool 值。
  • min、max最值操作:需要自定义比较器,返回数据流中最大、最小的值。
  • reduce规约操作:将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
  • forEach、forEachOrdered遍历操作:这里就是对最终的数据进行消费了。
  • toArray数组操作:将数据流的元素转换成数组。

2、 示例

2.1、 传统方式实现查找和排序

实现筛选出名字中包含“xc”字符串的人,并按照其年龄进行排序。

List<People> peoples = new ArrayList<>();
// 遍历 + 判断
for (People people : allPeoples) {
    if (people.getName().contains("xc")) {
        peoples.add(people);
    }
}

// 对年龄排序

Collections.sort(peoples, new Comparator<People>() {
    @Override
    public int compare(People p1, People p2) {
        return Integer.compare(p1.getAge(), p2.getAge());
    }
});

2.2、 Stream方式

List<People> peoples2 = allPeoples.stream() //创建Stream流
                .filter(people -> people.getName().contains("xc")) //中间操作
                .sorted(Comparator.comparing(People::getAge)) // 中间操作
                .collect(Collectors.toList()); //终止流

2.3、 通过forEach来终止流

list.stream() //创建流
                .filter(person -> person.getName().contains("e")) //中间操作
                .sorted(Comparator.comparing(Person::getAge)) //中间操作
                .forEach(person -> System.out.println(person)); //结束流

2.4、Stream方式有以下显而易见的好处:

  • 代码以声明方式写的:说明想要完成什么(筛选出满足条件的数据)而不是说明如何实现一个操作(利用循环和if条件等控制流语句)。
  • 多个基本操作链接起来:将多个基础操作链接起来,来表达复杂的数据处理流水线(如下图),同时体现了代码的清晰、可读性。

2.5、集合操作

参考1.8API中关于Iterable的forEach方法

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

public class LambdaExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Orange");

        // 使用Lambda表达式迭代集合
        list.forEach(item -> System.out.println(item));

        // 如果只是简单的操作,还可以进一步简化Lambda表达式
        list.forEach(System.out::println);
    }
}

————————————————-以下内容自行查看————————————————–—

4.3 实战演练(了解)

Stream的一系列操作,必须要使用终止操作符,否则整个数据流是不会执行起来的。

4.3.1. map

转换、映射操作,将元素转换成其他形式或提取一些信息。

比如,从People集合中获取所有人的年龄:

allPeoples.stream()
    .map(People::getAge)
    .forEach(System.out::println);

4.3.2. flatmap

将元素拍平拍扁 ,将拍扁的元素重新组成Stream,并将这些Stream 串行合并成一条Stream。

比如,带有-字符的字符串进行拆分,并输出:

Stream.of("x-c-b-e-y-o-n-d","a-b-c-d")
    .flatMap(m -> Stream.of(m.split("-")))
    .forEach(System.out::println);

输出:

x
c
b
e
y
o
n
d
a
b
c
d

4.3.3. limit

限流操作,限制集合元素的个数。

比如,集合中有10个元素,我只要前4个就可以使用:

Stream.of(1,2,3,4,5,6,7,8,9,10)
    .limit(4)
    .forEach(System.out::println);

输出:

1
2
3
4

4.3.4. distint

去重操作,重复元素去重,类似数据库中的关键字distinct

比如,集合中可能存在一些重复的数据,需要进行去重操作:

Stream.of("xcbeyond","Niki","Liky","xcbeyond")
    .distinct()
    .forEach(System.out::println);

输出:

xcbeyond
Niki
Liky

4.3.5. filter

过滤、筛选,对某些元素进行过滤,不符合筛选条件的将无法进入流的下游。

比如,筛选出一个数字集合中的所有偶数:

Stream.of(1,2,3,4,5,6,7,8,9,10)
    .filter(n -> 0 == n%2)
    .forEach(System.out::println);

输出:

2
4
6
8
10

4.3.6. peek

消费操作,如果想对数据进行某些操作,如:读取、编辑修改等。

比如,将People集合中name统一修改为name+age的形式:

allPeoples.stream()
    .peek(people -> people.setName(people.getName() + people.getAge()))
    .forEach(people -> System.out.println(people.getName()));

4.3.7. skip

跳过操作,跳过某些元素。

比如,一个数字集合,跳过前4个元素:

Stream.of(1,2,3,4,5,6,7,8,9,10)
    .skip(4)
    .forEach(System.out::println);

输出:

5
6
7
8
9
10

4.3.8. sorted

排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。

比如,将People集合按照年龄排序:

allPeoples.stream()
    .sorted(Comparator.comparing(People::getAge))
    .forEach(System.out::println);

4.3.9. collect

收集操作,终止操作符,用于将最终的数据收集到新的集合中,如,List、Set、Map等集合。

比如,将People集合按照年龄排序,并存放在一个新的集合中,供后续使用:

List<People> sortedPeople = allPeoples.stream()
    .sorted(Comparator.comparing(People::getAge))
    .collect(Collectors.toList());

4.3.10. count

统计操作,用于对集合元素个数的统计,返回类型是long。

比如,计算People集合中年龄大于30的人数:

long count = allPeoples.stream()
    .filter(people -> people.getAge() > 30)
    .count();

4.3.11. findFirst、findAny

查找操作,查找第一个、任何一个,返回的类型为Optional。常用于查询集中符合条件的元素,并结合Optional.isPresent()进行判断,防止出现未找到而强制获取数据元素的异常情况。

比如,查找People集合中名字为xcbeyond的人:

People xcbeyondPeople = null;
Optional<People> optional = allPeoples.stream()
    .filter(people -> "xcbeyond".equals(people.getName()))
    .findFirst();
if (optional.isPresent()) {
    xcbeyondPeople = optional.get();
}

4.3.12. noneMatch、allMatch、anyMatch

匹配操作,判断数据流中是否存在符合条件的元素,返回值为boolean值。

  • noneMatch:没有匹配条件的元素
  • allMatch、anyMatch:全匹配

比如,判断People集合中是否有名字为xcbeyond的人:

boolean bool = allPeoples.stream()
    .allMatch(people -> "xcbeyond".equals(people.getName()));

4.3.13. min、max

最值操作,根据自定义比较器,返回数据流中最大、最小的元素。

比如,找到People集合中最大年龄的人:

People maxAgePeople = null;
Optional<People> maxAgeOptional = allPeoples.stream()
    .max(Comparator.comparing(People::getAge));
if (maxAgeOptional.isPresent()) {   // 可能没有,则需要进行判断
    maxAgePeople = maxAgeOptional.get();
}

4.3.14. reduce

规约操作:将整个数据流的值规约为一个值,其中count、min、max底层就是使用reduce。

比如,对一个整数集合进行求和:

int sum = Stream.of(1,9,8,4,5,6,-1)
    .reduce(0,(e1,e2)->e1+e2);
System.out.println(sum);

输出:

32

五、 Date Time API

1、 为什么我们需要新的Java日期/时间API?

1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。

2、Java8日期/时间API的特点

不变性:新的日期/时间API中,所有的类都是不可变的,这对多线程环境有好处。

关注点分离:新的API将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。

清晰:在所有的类中,方法都被明确定义用以完成相同的行为。举个例子,要拿到当前实例我们可以使用now()方法,在所有的类中都定义了format()和parse()方法,而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,一旦你使用了其中某个类的方法,与其他类协同工作并不困难。

实用操作:所有新的日期/时间API类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分等。

3、 Java日期/时间API包

Java日期/时间API包含以下相应的包:

java.time包:这是新的Java日期/时间API的基础包,所有的主要基础类都是这个包的一部分,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration等等。所有这些类都是不可变的和线程安全的,在绝大多数情况下,这些类能够有效地处理一些公共的需求。

java.time.chrono包:这个包为非ISO的日历系统定义了一些泛化的API,我们可以扩展AbstractChronology类来创建自己的日历系统。

java.time.format包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为java.time包中相应的类已经提供了格式化和解析的方法。

java.time.temporal包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。

java.time.zone包:这个包包含支持不同时区以及相关规则的类

4、 示例:

4.1、 LocalDate

是一个不可变的类,它表示默认格式(yyyy-MM-dd)的日期

import java.time.*;

public class LocalDateDemo {
    /**
     * LocalDate是一个不可变的类,它表示默认格式(yyyy-MM-dd)的日期,
     * @param args
     */
    public static void main(String[] args) {
        // 当前日期yyyy-MM-dd
        LocalDate localDate1 = LocalDate.now();
        System.out.println(localDate1);
        // 创建一个日期
        LocalDate localDate2 = LocalDate.of(2017, 10, 17);
        System.out.println(localDate2);
        // 获取指定时区的当前时间
        LocalDate localDate3 = LocalDate.now(ZoneId.of("Asia/Kolkata"));
        System.out.println(localDate3);
        // 格林威治时间+天数
        //默认获取的是以UTC时区,世界协调时间,为基础
        LocalDate localDate4 = LocalDate.ofEpochDay(365);
        System.out.println(localDate4);
        // 某年的第几天的日期
        LocalDate localDate5 = LocalDate.ofYearDay(2017, 200);
        System.out.println(localDate5);
    }
}

4.2、 LocalTime

是一个不可变的类,它的实例代表一个符合人类可读格式的时间,默认格式是hh:mm:ss.zzz。

import java.time.LocalTime;
import java.time.ZoneId;

public class LocalTimes {
    /**
     * LocalTime是一个不可变的类,它的实例代表一个符合人类可读格式的时间,默认格式是hh:mm:ss.zzz。
     * @param args
     */
    public static void main(String[] args) {
        //当前时间
        LocalTime time = LocalTime.now();
        System.out.println("Current Time="+time);
        // 创建当前时间
        LocalTime specificTime = LocalTime.of(12,20,25,40);
        System.out.println("Specific Time of Day="+specificTime);

        // 获取指定时区当前时间
        LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
        System.out.println("Current Time in IST="+timeKolkata);

        // 当天多少秒的时间
        LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
        System.out.println("10000th second time= "+specificSecondTime);
    }
}

4.3、 LocalDateTime

是一个不可变的日期-时间对象,它表示一组日期-时间,默认格式是yyyy-MM-dd-HH-mm-ss.zzz。它提供了一个工厂方法,接收LocalDate和LocalTime输入参数,创建LocalDateTime实例。

import java.time.*;

public class LocalDateTimes {
    /**
     * LocalDateTime是一个不可变的日期-时间对象,它表示一组日期-时间,默认格式是yyyy-MM-dd-HH-mm-ss.zzz。
     * 它提供了一个工厂方法,接收LocalDate和LocalTime输入参数,创建LocalDateTime实例。
     * @param args
     */
    public static void main(String[] args) {
        // 当前日期时间
        LocalDateTime today = LocalDateTime.now();
        System.out.println("Current DateTime="+today);

        // 当前日期时间
        today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
        System.out.println("Current DateTime="+today);

        // 指定时间日期时间
        LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
        System.out.println("Specific Date="+specificDate);

        // 当前指定时区日期时间
        LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
        System.out.println("Current Date in IST="+todayKolkata);

        // 格林威治后多少分钟的日期时间
        LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
        System.out.println("10000th second time from 01/01/1970= "+dateFromBase);
    }

}

4.4、 Instant类

是用在机器可读的时间格式上的,它以Unix时间戳的形式存储日期时间。

import java.time.Duration;
import java.time.Instant;

public class instants {
    /**
     * Instant类是用在机器可读的时间格式上的,它以Unix时间戳的形式存储日期时间。
     * @param args
     */
    public static void main(String[] args) {
        // 当前时间戳
        Instant timestamp = Instant.now();
        System.out.println("Current Timestamp = "+timestamp); // Current Timestamp = 2018-06-26T15:54:21.310Z

        Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli()); // Specific Time = 2018-06-26T15:54:21.310Z
        System.out.println("Specific Time = "+specificTime);

        Duration thirtyDay = Duration.ofDays(30);
        System.out.println(thirtyDay); // PT720H
    }
}

4.5、 日期运算

大多数日期/时间API类都实现了一系列工具方法,如:加/减天数、周数、月份数等。还有其他的工具方法能够使用TemporalAdjuster调整日期,并计算两个日期间的周期。

import java.time.*;

public class DateUtils {
    /**
     * 大多数日期/时间API类都实现了一系列工具方法,如:加/减天数、周数、月份数,等等。
     * 还有其他的工具方法能够使用TemporalAdjuster调整日期,并计算两个日期间的周期。
     * @param args
     */
    public static void main(String[] args) {
        // 当前日期时间,UTC(世界协调时间格式)
        LocalDateTime today = LocalDateTime.now();
        System.out.println("Current DateTime="+today);

        // 当前日期时间
        today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
        System.out.println("Current DateTime="+today);

        // 指定时间日期时间
        LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
        System.out.println("Specific Date="+specificDate);

        // 当前指定时区日期时间
        LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
        System.out.println("Current Date in IST="+todayKolkata);

        // 格林威治后多少分钟的日期时间
        LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
        System.out.println("10000th second time from 01/01/1970= "+dateFromBase);
    }
}

4.6、 旧的日期时间支持

旧的日期/时间类已经在几乎所有的应用程序中使用,因此做到向下兼容是必须的。这也是为什么会有若干工具方法帮助我们将旧的类转换为新的类,反之亦然。

  public static void main(String[] args) {

        //Date to Instant
        Instant timestamp = new Date().toInstant();
        //Now we can convert Instant to LocalDateTime or other similar classes
        LocalDateTime date = LocalDateTime.ofInstant(timestamp,
                        ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
        System.out.println("Date = "+date);

        //Calendar to Instant
        Instant time = Calendar.getInstance().toInstant();
        System.out.println(time);
        //TimeZone to ZoneId
        ZoneId defaultZone = TimeZone.getDefault().toZoneId();
        System.out.println(defaultZone);

        //ZonedDateTime from specific Calendar
        ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
        System.out.println(gregorianCalendarDateTime);

        //Date API to Legacy classes
        Date dt = Date.from(Instant.now());
        System.out.println(dt);

        TimeZone tz = TimeZone.getTimeZone(defaultZone);
        System.out.println(tz);

        GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
        System.out.println(gc);

    }

4.7、 Date与LocalDateTime、LocalDate、LocalTime互转

// 01. java.util.Date --> java.time.LocalDateTime
public void dateToLocalDateTime() {
    java.util.Date date = new java.util.Date();
    Instant instant = date.toInstant();
    ZoneId zone = ZoneId.systemDefault();
    LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
}

// 02. java.util.Date --> java.time.LocalDate
public void dateToLocalDate() {
    java.util.Date date = new java.util.Date();
    Instant instant = date.toInstant();
    ZoneId zone = ZoneId.systemDefault();
    LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
    LocalDate localDate = localDateTime.toLocalDate();
}

// 03. java.util.Date --> java.time.LocalTime
public void dateToLocalTime() {
    java.util.Date date = new java.util.Date();
    Instant instant = date.toInstant();
    ZoneId zone = ZoneId.systemDefault();
    LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
    LocalTime localTime = localDateTime.toLocalTime();
}

// 04. java.time.LocalDateTime --> java.util.Date
public void localDateTimeToUdate() {
    LocalDateTime localDateTime = LocalDateTime.now();
    ZoneId zone = ZoneId.systemDefault();
    Instant instant = localDateTime.atZone(zone).toInstant();
    java.util.Date date = Date.from(instant);
}

// 05. java.time.LocalDate --> java.util.Date
public void localDateToUdate() {
    LocalDate localDate = LocalDate.now();
    ZoneId zone = ZoneId.systemDefault();
    Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
    java.util.Date date = Date.from(instant);
}

// 06. java.time.LocalTime --> java.util.Date
public void localTimeToUdate() {
    LocalTime localTime = LocalTime.now();
    LocalDate localDate = LocalDate.now();
    LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
    ZoneId zone = ZoneId.systemDefault();
    Instant instant = localDateTime.atZone(zone).toInstant();
    java.util.Date date = Date.from(instant);
}

posted @ 2025-04-30 10:03  icui4cu  阅读(20)  评论(0)    收藏  举报