第二节:java8新特性

第一节:java8新特性

一、主要内容

  • Lambda表达式和函数式接口
  • 四大函数式接口
  • 方法/构造器/数组引用
  • StreamAPI
  • 并行流与串行流
  • 接口的默认方法/静态方法
  • Optional容器类
  • 新时间日期API
  • 其他新特性

二、具体实现

1. 什么是函数式接口

  • 函数式接口:接口中只有一个抽象方法的接口
  • 特性:可以使用注解@FunctionalInterface修饰检查是否为函数式接口。
  • 为什么需要函数式接口:有接口就要有东西去实现它,Java中的lambda无法单独出现,它需要一个函数式接口来盛放,lambda表达式方法体其实就是函数接口的实现.
  • 函数式接口的创建方式:
    //添加此注解后,接口中只能有一个抽象方法。
    @FunctionalInterface
    public interface MyPredicate<T> {
          
          //这里的接口接受泛型且可以接受多个泛型。
        public boolean test(T t);
    }
    

2. Lambda表达式

  • 理解:Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

  • 注意:Lambda表达式需要函数式接口的支持。没有函数式接口则无法实现Lambda

  • 如何体现更优:

    如下所示,当我们需要重写一个 Comparator接口的compare方法时时我们有两种方式:
    	第一种:创建一个类去实现 Comparator,然后去重写compare方法。
    	第二种:直接采用匿名内部类的形式compare方法。
    

    思考:第一种和第二种代码过于冗余:故采用Lambda表达式的方式。

       //原来的匿名内部类
     @Test
    public void test1() {
     TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>() {
         @Override
         public int compare(Integer o1, Integer o2) {
             return Integer.compare(o1, o2);
         }
     });
    }
     //Lambda表达式
     public void test2() {
    	 TreeSet<Integer> ts = new TreeSet<>((x, y) -> Integer.compare(x, y));
    }
    
  • Lambda表达式的语法结构:

    Lambda表达式基础语法:java8中引入一个新的操作符“->”该操作符称为箭头操作符
                          或Lambda操作符
       箭头操作符将Lamba表达式拆分成两部分
            左侧:Lambda表达式参数列表
            右侧:Lambda表达式中所需要执行的功能,即Lambda体
    
  • Lambda表达式的四种语法结构

    语法格式一:无参数,无返回值。
      	 () -> System.out.println("Hello World")
    语法格式二:有一个参数,无返回值。
         (x) -> System.out.println(x); 注意:当只有一个参数小括号可以不写:
          x -> System.out.println(x);
    语法格式三:有多个参数,有返回值,并且Lambda体重有多条语句(多条语句用{})。
          (x,y) -> {
                  System.out.println("hello");          
                  return Integer.compare(x,y);
                  };
    语法格式四:有多个参数,且Lambda体中只有一条语句,则{}和return都可以省略
    语法格式五:Lambda 表达式中的参数列表的数据类型可以省略不写,因为JVM编译
               器通过上下文推断出,数据类型,即“类型推短”。
    	(Integer x,Integer y) -> Integer.compare(x,y);      
    	 (x,y) -> Integer.compare(x,y);
    

语法格式一:无参数,无返回值。

public void Test1(){
    
    
        Runnable r = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("Hello World");
            }
        };
        r.run();
        //语法格式一:无参数,无返回值。
        //用() -> System.out.println("Hello Lambda")代表run方法的方法体
        System.out.println("----------------------");
        Runnable r1 = () -> System.out.println("Hello Lambda");
        r1.run();
    }

语法格式二:有一个参数,无返回值。

 public void Test2(){
    
    
 		//匿名内部类的形式
        Consumer<String> con1 = new Consumer<String>() {
    
    
            @Override
            public void accept(String s) {
    
    
                System.out.println(s);;
            }
        };
        con1.accept("aaa");
       	//Lambda表达式的形式
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("aaa");
    }

语法格式三:有多个参数,有返回值,并且Lambda体重有多条语句。

public void Test3(){
    
    
		//匿名内部类的形式
        Comparator<Integer> com = new Comparator<Integer>() {
    
    
            @Override
            public int compare(Integer o1, Integer o2) {
    
    
                System.out.println("hello");
                return Integer.compare(o1,o2);
            }
        };
        com.compare(1,2);
		//Lambda表达式的形式
        Comparator<Integer> com1 = (x,y) -> {
    
    
            System.out.println("hello");
            return Integer.compare(x,y);
        };
        com1.compare(1,2);
    }
  • 总结:Lambda表达式是对匿名内部类的一种改进,由以上内容可以看出,Lambda表达式可以是看做匿名内部类所重写方法的方法体重内容,其中左侧为:重写方法的参数列表,右侧为:方法体重内容。然后以实例对象的当时去调用被重写的方法。即Lambda表达式是对原函数式接口中方法体中类容的传入,而并不需要采用匿名内部类形式的冗余代码。

3. 四大函数式接口

  • 作用:要使用Lambda表达式往往离不开使用函数式接口,但是如果每次使用Lambda表达式都要自定义一个函数式接口的话,那么Lambda表达式就没有完全起到简化代码的作用.
  • java8中核心的函数式接口有以下四个:
名称 作用 参数 返回值
Consumer<T,> 消费型接口,给定一个参数,对这个参数进行一系列的操作。内置函数为:void accept(T t) T
Supplier<T,> 供给型接口,返回类型为T的参数,内置函数为: T get() T
Function<T,R> 函数型接口:对类型为T的对象操作,返回类型为R的对象,内置函数为:R apply(T t) T R
Predicate<T,> 断言型接口:确定类型为T的对象是否满足某约束,并返回boolean 值。内置函数为: boolean test(T t) T
  • 消费型接口
  // Consumer<T> 消费型接口
    @Test
    public void test1(){
    
    
        happy(1000,(m) -> System.out.println(m));
    }
    public void happy(double money,Consumer<Double> con){
    
    
        con.accept(money);
    }
  • 供给型接口
   //Supplier<T>供给型接口
    @Test
    public void test2(){
    
    
        List<Integer> list = getNumList(10,() -> (int)(Math.random() *100));
        list.forEach(System.out::println);
    }
    //需求:指定个数的整数,并放入集合
    public List<Integer> getNumList(int num, Supplier<Integer> sup){
    
    
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
    
    
            Integer n = sup.get();
            list.add(n);
        }
        return list;
    }
  • 函数型接口
	//函数型接口
    @Test
    public void Test3(){
    
    
        String ss = strHandler("\t\t asdad \t",(str) -> str.trim());
        System.out.println(ss);
    }
    //用于处理字符串
    public String strHandler(String str,Function<String,String> fun){
    
    
        return fun.apply(str);
    }
 //断言型接口
    @Test
    public void Test4(){
    
    
        List<String> list = Arrays.asList("hello","std","Lambda");
        List<String> strList = filterStr(list,(s) -> s.length() >3);
        for (String str: strList) {
    
    
            System.out.println(str);
        }
    }
    //需求 将满足条件的字符串,放入到集合中
    public List<String> filterStr(List<String> list,Predicate<String> pre){
    
    
        List<String> strList = new ArrayList<>();
        for (String str:list) {
    
    
            if (pre.test(str)){
    
    
                strList.add(str);
            }
        }
        return strList;
    }
}

4. 引用

4.1 方法引用
  • 什么是方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用是Lambda表达式的另外一种表现形式)

  • 三种语法格式:

    主要有三种语法格式:
       第一种:对于实例方法有
             对象::实例方法名
             类::实例方法名
       第二种:对弈静态方法
             类::静态方法
    注意:(1)Lambda体重调用方法的参数与返回值类型,要与函数式接口中抽象方法的
             函数列表和返回值类型相同。
         (2)若Lambda参数列表中第一个参数是实例方法的调用者,第二个参数是实例方法的
             参数时,才可以使用  类名::method
    

第一种 :对象::实例方法名

public void test1(){
    
    
        //无方法引用
        PrintStream ps1 = System.out;
        Consumer<String> con1 = (x) ->ps1.println(x);

        //有方法引用
        PrintStream ps2 = System.out;
        Consumer<String> con2 = ps2::println;
    }

第二种 类::静态方法名

public void test3(){
    
    
        //无方法引用
        Comparator<Integer> com = (x,y) ->Integer.compare(x,y);
        //有方法引用
        Comparator<Integer> com1 = Integer::compareTo;
    }
4.1 构造器引用

格式:

  • ClassName::new
  • 注意:需要调用的构造器的参数要与函数式接口中的抽象方法的参数列表保持一致。
 public void test4(){
    
    
        //无引用
        Supplier<Employee> sup = () ->new Employee();
        //有引用
        Supplier<Employee> sup2 = Employee::new;
    }
4.1 数组引用
  • 格式:Type::new
   @Test
    public void test5(){
    
    
        Function<Integer,String[]> fun = (x) ->new String[x];
        String[] strs = fun.apply(10);
        System.out.println(strs.length);

        Function<Integer,String[]> fun2 = String[]::new;
        String[] str2 = fun2.apply(10);
        System.out.println(str2.length);
    }

5. StreamAPI

5.1 Stream是什么???

在这里插入图片描述

5.2 Stream三大操作步骤

在这里插入图片描述

5.3 Stream的创建
public void test1(){
    
    
        //第一种创建方式:通过Collection系列集合提供的stream()或parallelStream
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        //第二种创建方式:通过Arrays中的静态方法stream()获取数据流
        Employee [] emps = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(emps);

        //第三种创建方式:通过Stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("aa","bb","cc");

        //第四种创建方式:创建无限流
        //迭代
        Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);
        stream4.limit(10).forEach(System.out::println);

        //生成
        Stream.generate(() ->Math.random()).limit(5).forEach(System.out::println);
    }
5.3 Stream的中间操作

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

  • 筛选与切片
    filter:接收Lambda,从流中排出某些元素
    limit:截断流,是元素不超过给定的数量
    skip:跳过元素,返回一个扔掉了前n个元素的流,若流种不足n个,则返回一个空流。与limit(n)互补
    distinct:筛选,通过流所乘车元素的hashCode()和equals去重重复元素

  • 映射
    map:接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
    flatMap:接收一个函数作为参数,将流中每个值都转换成另一个流,然后把所有的流连接处一个流

  • 排序
    sorted():自然排序(以字典顺序排序)
    sorted(Comparator com)----定制排序(自己设定排序规则)

List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 58, 6666.99),
            new Employee("赵六", 16, 3333.99),
            new Employee("田七", 8, 7777.99)
    );
    @Test
    public void test1(){
    
    
        employees.stream()
                .filter((e)->e.getAge() >35)
                .forEach(System.out::println);
    }
    @Test
    public void test2(){
    
    
        //map
        List<String> list = Arrays.asList("ccc","aaa","bbb");
        list.stream()
                .map((str) -> str.toUpperCase())
                .forEach(System.out::println);
        System.out.println("--------------------");
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
        System.out.println("---------------------");

        //flatMap(嵌套流)
        Stream<Stream<Character>> stream = list.stream().map(TestStream02::filterCharacter);
        stream.forEach((sm)->{
    
    
            sm.forEach(System.out::println);
        });
        System.out.println("------------------------");
        list.stream().flatMap(TestStream02::filterCharacter).forEach(System.out::println);

    }

    @Test
    public void test3(){
    
    
        //sorted
        List<String> list = Arrays.asList("ccc","aaa","bbb");
        list.stream()
                .sorted().forEach(System.out::println);
        System.out.println("------------------");

        employees.stream()
                .sorted((e1,e2) ->{
    
    
                    if (e1.getAge() == e2.getAge()){
    
    
                        return e1.getName().compareTo(e2.getName());
                    }else {
    
    
                        return e1.getAge() - e2.getAge();
                    }
                }).forEach(System.out::println);
    }

    public static Stream<Character> filterCharacter(String str){
    
    
        List<Character> list = new ArrayList<>();
        for (Character ch:str.toCharArray()) {
    
    
            list.add(ch);
        }
        return list.stream();
    }
5.4 Stream的终止操作
  • 查找与匹配
    allMatch : 检查是否匹配所有元素
    anyMatch : 检查是否至少匹配一个元素
    noneMatch :检查是否没有匹配所有。
    findFirst :返回第一个元素
    findAny :返回当前流中的任意元素
    count :返回流中元素的总和
    max : 返回流中最大值
    min : 返回流中最小值
  • 归约
    reduce(T identity,BinaryOperator) : 可以将流中元素反复结合起来,得到一个值
  • 收集
    collect 将流装换为其他形式。接受一个Collection接口的实现,用于给Stream中元素做汇总的方法
 List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99, Status.BUSY),
            new Employee("李四", 38, 5555.99, Status.FORE),
            new Employee("王五", 58, 6666.99, Status.VOCATION),
            new Employee("赵六", 16, 3333.99, Status.FORE),
            new Employee("田七", 8, 7777.99, Status.BUSY)
    );

    @Test
    public void test1() {
    
    
        //allMatch : 检查是否匹配所有元素
        boolean b1 = employees.stream()
                .allMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b1);

        // anyMatch : 检查是否至少匹配一个元素
        boolean b2 = employees.stream()
                .anyMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b2);

        //noneMatch :检查是否没有匹配所有。
        boolean b3 = employees.stream()
                .noneMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b3);

        //findFirst :返回第一个元素
        Optional<Employee> op = employees.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .findFirst();
        System.out.println(op.get());

        //findAny :返回当前流中的任意元素
        Optional<Employee> op2 = employees.stream().findAny();
        System.out.println(op2.get());

        //count :返回流中元素的总和
        System.out.println(employees.stream().count());
        //max : 返回流中最大值
        Optional<Employee> op3 = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(op3.get());
        //min : 返回流中最小值
        Optional<Employee> op4 = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(op4.get());
    }

    @Test
    public void test2() {
    
    
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);
        System.out.println("-----------------------");

        Optional<Double> op = employees.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(op.get());
    }

    @Test
    public void test3() {
    
    
        List<String> list = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("--------------------");
        HashSet<String> hs = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hs.forEach(System.out::println);
    }

6. 并行流与串行流

  • 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流

  • Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换

6. 1 Fork / Join

在这里插入图片描述
在这里插入图片描述

public class ForkJoinCalculate01 extends RecursiveTask<Long> {
    
    
    private static final long serialVersionUID = -5948143340027475694L;
    private long start;
    private long end;

    private static final long THRESHOLD = 10000;

    public ForkJoinCalculate01(long start, long end) {
    
    
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
    
    
        //做一个累加和的案例
        //当我们所做的累加和的总数没有超过THRESHOLD时,直接采用单个任务计算,
        // 当我们超过THRESHOLD时,采用二分法进行拆分计算
        // (拆成子任务,如:THRESHOLD = 10,当我们计算1~11 的累加时,拆分成两部分,一部分计算1~5,一部分计算6~11,然和合并。)
        long length = end - start;
        if(length <= THRESHOLD){
    
    
            long sum = 0;
            for (long i = start; i <= end ; i++) {
    
    
                sum += i;
            }
            return sum;
        }else {
    
    
            long middle = (end + start) / 2;
            ForkJoinCalculate01 left = new ForkJoinCalculate01(start,middle);
            left.fork();

            ForkJoinCalculate01 right = new ForkJoinCalculate01(middle +1,end);
            right.fork();

            return left.join() + right.join();
        }
    }

 /*
        ForkJoin框架
     */
    @Test
    public void test01(){
    
    
        long start = System.currentTimeMillis();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task= new ForkJoinCalculate01(0,100000000L);
        Long sum = pool.invoke(task);
        System.out.println(sum);

        long end = System.currentTimeMillis();
        System.out.println("耗费时间:" + (end-start));

    }
    /*
        for循环
     */
    @Test
    public void test02(){
    
    
        long start = System.currentTimeMillis();

        long sum = 0L;
        for (long i = 0; i <= 100000000L ; i++) {
    
    
            sum += i;
        }
        System.out.println(sum);

        long end = System.currentTimeMillis();
        System.out.println("耗费时间:" + (end-start));
    }
    @Test
    public void Test03(){
    
    
        //串行流(单线程):切换为并姓刘parallel()
        //并行流:切换为串行流sequential
        LongStream.rangeClosed(0,100000L)
                .parallel() //底层为ForkJoin
                .reduce(0,Long::sum);
    }

7. 接口的默认方法/静态方法

7.1 默认方法
public interface MyFun {
    
    

    default String getName(){
    
    
        return "libo";
    }

    default Integer getAge(){
    
    
        return 22;
    }
}

类优先原则:
在这里插入图片描述

7.2 静态方法
public interface MyFun {
    
    

    static void getAddr(){
    
    
        System.out.println("addr");
    }

    static String Hello(){
    
    
        return "Hello World";
    }
}

8. Optional容器类

9. 新时间日期API

9.1 旧版本日期类所存在的问题
  • 计算困难,且不准确(毫秒值与日期直接转换比较繁琐,其次通过毫秒值来计算时间的差额步骤较多.)
//从java.util包的下的日期类相关类过渡到java.time包下的日期类
/*
    需求:获取1995年12月16日,到至今过了多少天

    步骤思路:
        1. 初始化Date对象,无参构造,获取当前时间戳
        2. 初始化当前距离1970年1月1日过了多少毫秒
        3. 初始化Calendar兑现并设置时间为1995年12月16日,并将Calendar对象装换为Date对象,
            再转换为1995年12月16日距离1970年1月1日过了多少毫秒
        4. 将两个毫秒数做相减操作,然后将毫秒数转换为天数
 */
public class DateTest01 {
    
    
    public static void main(String[] args) {
    
    
        // 获取当前距离1970年1月1日过了多少毫秒
        Date d = new Date();
        long s1 = d.getTime();

        // 初始化Calendar兑现并设置时间为1995年12月16日
        Calendar c = Calendar.getInstance();//通过getInstance()方法获取Calendar实例
        c.set(1995,11,16);

        //并将Calendar对象装换为Date对象
        Date d2 = c.getTime();
        long s2 = d2.getTime();// 再转换为1995年12月16日距离1970年1月1日过了多少毫秒

        long intervalDay = (s1 - s2) / 1000 / 60 / 60 / 24;
        System.out.println(intervalDay);

        //新版本日期类完成上述需求
        long day = ChronoUnit.DAYS.between(LocalDate.of(1995,12,16),LocalDate.now());
        System.out.println(day);      
    }
}
  • 多线程安全问题:SimpleDateFormat类是线程不安全的,在多线程的情况下,全局共享一个SimpleDateFormat类中的Calendar对象有可能会出现异常.
public class DateTest02 {
    
    
    final static SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
    
    
        //创建10个线程同时去操作SIMPLE_DATE_FORMAT对象、

        for (int i = 0; i < 10; i++) {
    
    
            /*
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Date data = null;
                    try {
                        data = SIMPLE_DATE_FORMAT.parse("2020-12-7 12:12:12");
                        System.out.println(data);
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
             */
            new Thread(() ->{
    
    
                Date data = null;
                try {
    
    
                    synchronized (SIMPLE_DATE_FORMAT){
    
    
                        data = SIMPLE_DATE_FORMAT.parse("2020-12-7 12:12:12");
                        System.out.println(data);
                    }
                } catch (ParseException e) {
    
    
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

9.2 Date-Time API中的基本类
  • 时间戳:Instant类:Instant类对时间轴上的单一瞬时点建模,可以用于记录应用程序中的事件时间戳,在之后学习的类型转换中,均可以使用Instant类作为中间类完成转换

  • 时间 / 日期 差
    Duration类:Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性
    Period类:Period类表示一段时间的年、月、日.

  • 本地时间 / 日期
    LocalDate类:LocalDate是一个不可变的日期时间对象,表示日期,通常被视为年月日.
    LocalTime类:LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表示为纳秒精度.
    LocalDateTime类

  • 时区
    ZonedDateTime类:ZonedDateTime是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。
    ZonedDate类
    ZonedTime类

9.3 本地时间 / 日期

在这里插入图片描述

  • 常用方法之:now()方法
    Date-Time API中的所有类均生成不可变实例,它们是线程安全的,并且这些类不提供公共构造函数,也就是说没办法通过new的方式直接创建,需要采用工厂方法加以实例化.
public class Test01 {
    
    
    public static void main(String[] args) {
    
    
        //使用now方法创建Instant的实例对象
        Instant instantNow = Instant.now();

        //使用now方法创建LocalDate的实例对象.
        LocalDate localDateNow = LocalDate.now();

        //使用now方法创建LocalTime的实例对象.
        LocalTime localTimeNow = LocalTime.now();

        //使用now方法创建LocalDateTime的实例对象.
        LocalDateTime localDateTimeNow = LocalDateTime.now();

        //使用now方法创建ZonedDateTime的实例对象.
        ZonedDateTime zonedDateTimeNow = ZonedDateTime.now();

        //将实例对象打印到控制台.
        System.out.println("Instant:"+instantNow);
        System.out.println("LocalDate:"+localDateNow);
        System.out.println("LocalTime:"+localTimeNow);
        System.out.println("LocalDateTime:"+localDateTimeNow);
        System.out.println("ZonedDateTime:"+zonedDateTimeNow);
    }
}

/*
    不仅仅是刚才提供的几个类可以使用now方法,Java8的Time包中还提供了其他的几个类可以更精
        准的获取某些信息.
    Year类(表示年)
    YearMonth类(表示年月)
    MonthDay类(表示月日)
 */
public class Test02 {
    
    
    public static void main(String[] args) {
    
    
        //初始化Year的实例化对象.
        Year year = Year.now();
        //初始化YearMonth的实例化对象
        YearMonth month = YearMonth.now();
        //初始化MonthDay的实例化对象.
        MonthDay monthDay = MonthDay.now();

        System.out.println("Year" + year);
        System.out.println("Month" + month);
        System.out.println("MonthDay" + monthDay);

    }
}
  • 常用方法之:of ()方法
    of方法可以根据给定的参数生成对应的日期/时间对象,基本上每个基本类都有of方法用于生成的对应的对象,而且重载形式多变,可以根据不同的参数生成对应的数据
public class Test03 {
    
    
    public static void main(String[] args) {
    
    
        //初始化2018年8月8日的  对象.
        LocalDate localDate = LocalDate.of(2018, 8, 8);
        System.out.println("localDate:" + localDate);

        /*
            初始化晚上7点0分0秒的LocalTime对象.
            LocalTime.of方法的重载形式有以下几种,可以根据实际情况自行使用.
            LocalTime of(int hour, int minute) -> 根据小时/分钟生成对象.
            LocalTime of(int hour, int minute, int second) -> 根据小时/分钟/秒生成 对象.
            LocalTime of(int hour, int minute, int second, int nanoOfSecond) -> 根据小时/分钟/毫秒/纳秒生成对象.
        注意:如果秒和纳秒为0的话,那么默认不会封装这些数据,只显示小时和分钟.
          */
        LocalTime localTime = LocalTime.of(19, 0, 0, 0);
        System.out.println("localTime:" + localTime);

        /*
        初始化2018年8月8日下午7点0分的LocalDateTime对象.
         LocalDateTime.of方法的重载形式有以下几种,可以根据事情自行使用.
         LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond)
            -> 根据年/月/日/时/分/秒生成对象.
         LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute)
            -> 根据年/月/日/时/分生成对象.
         注意:LocalDateTime of(LocalDate date, LocalTime time)方法可以将一个 LocalDate对象和一个LocalTime对象合并封装为一个LocalDateTime对象.
         */
        LocalDateTime localDateTime = LocalDateTime.of(2018, 8, 8, 8, 20, 0);
        System.out.println("LocalDateTime:" + localDateTime);

        LocalDateTime time = LocalDateTime.of(localDate, localTime);
        System.out.println("LocalDateTime2:" + time);

    }
}
  • 常用方法之:plus和minus方法.
/*
    3.根据现有实例创建日期与时间对象
        想要修改某个日期/时间对象的现有实例时,我们可以使用plus和minus方法来完成操作.
        Java8中日期时间相关的API中的所有实例都是不可改变的,一旦创建
        LocalDate,LocalTime,LocalDateTime就无法修改他们(类似于String),这对于
        线程安全非常有利

 plus方法在LocalDate与LocalTime中的使用
   LocalDate中定义了多种对日期进行增减操作的方法
        LocalDate plusDays(long days) 增加天数
        LocalDate plusWeeks(long weeks) 增加周数
        LocalDate plusMonths(long months) 增加月数
        LocalDate plusYears(long years) 增加年数
   LocalTime中定义了多种对时间进行增减操作的方法
        LocalTime plusNanos(long nanos) 增加纳秒
        LocalTime plusSeconds(long seconds) 增加秒
        LocalTime plusMinutes(long minutes) 增加分钟
        LocalTime plusHours(long hours) 增加小时
   plus(TemporaAmount amountToAdd) 增加一段时间

 */
public class Test08 {
    
    
    public static void main(String[] args) {
    
    
        //封装LocalDate对象参数为2016年2月13日.
        LocalDate localDate = LocalDate.of(2016, 2, 13);
        System.out.println("localDate = " + localDate);
        //计算当前时间的4天后的时间.
        LocalDate date1 = localDate.plusDays(4);
        System.out.println("date1 = " + date1);
        //计算当前时间的3周后的时间.
        LocalDate date2= localDate.plusWeeks(3);
        System.out.println("date2 = " + date2);
        //计算当前时间的5个月后的时间.
        LocalDate date3 = localDate.plusMonths(5);
        System.out.println("date3 = " + date3);
        //计算当前时间的2年后的时间.
        LocalDate date4 = localDate.plusYears(2);
        System.out.println("date4 = " + date4);


        //封装LocalTime对象参数为8时14分39秒218纳秒.
        LocalTime time = LocalTime.of(8, 14, 39, 218);
        //计算当前时间500纳秒后的时间.
        LocalTime plusNanosTime = time.plusNanos(500);
        //计算当前时间45秒后的时间.
        LocalTime plusSecondsTime = time.plusSeconds(45);
        //计算当前时间19分钟后的时间.
        LocalTime plusMinutesTime = time.plusMinutes(19);
        //计算当前时间3小时后的时间.
        LocalTime plusHoursTime = time.plusHours(3);

        System.out.println("当前的时间是:" + time);
        System.out.println("45秒后的时间是:" + plusSecondsTime);
        System.out.println("19分钟后的时间是:" + plusMinutesTime);
        System.out.println("500纳秒后的时间是:" + plusNanosTime);
        System.out.println("3小时后的时间是:" + plusHoursTime);
    }
}
/*
    plus(TemporaAmount amountToAdd)
    需求:
        计算当前时间的2年3个月零8天后的时间是多少
 */
public class Test09 {
    
    
    public static void main(String[] args) {
    
    
        //封装当前时间 ->now方法
        LocalDateTime now = LocalDateTime.now();
       //固然可以使用对于年月日依次+2,+3,+8的方式来操作,但是有些繁琐,
        LocalDateTime endTime = now.plusYears(2).plusMonths(3).plusDays(8);
        System.out.println(endTime);
        //首先我 们先将2年3月8天封装为一段时间,也就是封装为一个Period对象
        Period time = Period.of(2, 3, 8);
        LocalDateTime endTime2 = now.plus(time);
        System.out.println(endTime2);
    }
}
/*
    plus(long l,TemporaUnit unit)
   注意第一个参数为单位,第二个参数为时间长度. 例:plus(1, ChronoUnit.DECADES)加1个10年.
    plus(1, ChronoUnit.CENTURIES)加1个100年.
 */
/*
    结婚10年称为锡婚,2020年2月2日11点11分11秒称为对称日,
    很多情侣准备在那天结婚,如果在那 天结婚了,那么锡婚会发生在什么时候.
 */
public class Test10 {
    
    
    public static void main(String[] args) {
    
    
        LocalDateTime localDateTime = LocalDateTime.of(2020, Month.FEBRUARY, 2, 11, 11, 11);
        System.out.println("localDateTime = " + localDateTime);
        //使用plus方法进行计算,加一个十年
        LocalDateTime time = localDateTime.plus(1, ChronoUnit.DECADES);
        System.out.println("time = " + time);
        //如果锡婚后的半天准备要请所有亲戚朋友吃饭,那么吃饭的时间是.
        LocalDateTime eatTime = time.plus(1, ChronoUnit.HALF_DAYS);
        System.out.println("半天后吃饭,吃饭的时候是:" + eatTime);
    }
}
  • 常用方法之:with()方法.
/*
    with方法修改时间
        第一种方式:
            LocalDateTime withNano(int i) 修改纳秒
            LocalDateTime withSecond(int i) 修改秒
            LocalDateTime withMinute(int i) 修改分钟
            LocalDateTime withHour(int i) 修改小时
            LocalDateTime withDayOfMonth(int i) 修改日
            LocalDateTime withMonth(int i) 修改月
            LocalDateTime withYear(int i) 修改年
        第二种方式:
            with(TemporalField field, long newValue)

            例:with(ChronoField.DAY_OF_MONTH,1); 将日期中的月份中的天数改为1.
            例:with(ChronoField.YEAR,2021); 将日期中的年份改为2021.
 */
public class Test11 {
    
    
    public static void main(String[] args) {
    
    
        LocalDateTime time = LocalDateTime.now();
        //经过使用发现time中的时间有错误,应该是1日,在不知道原有时间的基础上,无法进 行增减操作,所以可以直接使用with方法进行修改.
        LocalDateTime endTime = time.withDayOfMonth(1);
        System.out.println("修改前错误的时间是:" + time);
        System.out.println("修改完成之后的时间是:" + endTime);

        LocalDateTime time2 = LocalDateTime.now();
        //经过使用发现time中的时间有错误,应该是1日,在不知道原有时间的基础上, 无法进行增减操作,所以可以直接使用with方法进行修改.
        LocalDateTime endTime2 = time.with(ChronoField.DAY_OF_MONTH,1);
        System.out.println("修改前错误的时间是:" + time2);
        System.out.println("修改完成之后的时间是:" + endTime2);

    }
}
9.3 时间戳 Instant
@Test
public void test02(){
    
    
    // 默认获取 UTC 时区 (UTC:世界协调时间)
    Instant ins1 = Instant.now();
    System.out.println(ins1);

    //带偏移量的时间日期 (如:UTC + 8)
    OffsetDateTime odt1 = ins1.atOffset(ZoneOffset.ofHours(8));
    System.out.println(odt1);

    //转换成对应的毫秒值
    long milli1 = ins1.toEpochMilli();
    System.out.println(milli1);

    //构建时间戳
    Instant ins2 = Instant.ofEpochSecond(60);
    System.out.println(ins2);
}

9.4 时间 / 日期 差

Duration:计算两个时间之间的间隔
Period:计算两个日期之间的间隔

@Test
public void test03(){
    
    
    //计算两个时间之间的间隔 between
    Instant ins1 = Instant.now();
    try {
    
    
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    
    
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();
    Duration dura1 = Duration.between(ins1, ins2);
    System.out.println(dura1.getSeconds());
    System.out.println(dura1.toMillis());
}

@Test
public void test04(){
    
    
    LocalDate ld1 = LocalDate.of(2016, 9, 1);
    LocalDate ld2 = LocalDate.now();
    Period period = Period.between(ld1, ld2);  // ISO 标准
    System.out.println(period.getYears());
    System.out.println(period.toTotalMonths());
}

9.5 时区

ZonedDate
ZonedTime
ZonedDateTime

@Test
public void test02(){
    
    
    //查看支持的时区
    Set<String> set = ZoneId.getAvailableZoneIds();
    set.forEach(System.out::println);

    //指定时区
    LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
    System.out.println(ldt1);

    //在已构建好的日期时间上指定时区
    LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
    ZonedDateTime zdt1 = ldt2.atZone(ZoneId.of("Europe/Tallinn"));
    System.out.println(zdt1);
}
@Test
public void test03(){
    
    
	// Date 转 LocalDateTime 
	Date date = new Date();
    Instant instant = date.toInstant();
    ZoneId zoneId = ZoneId.systemDefault();
    LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();

	// LocalDateTime 转 Date
	LocalDateTime localDateTime = LocalDateTime.now();
	ZoneId zoneId = ZoneId.systemDefault();
    ZonedDateTime zdt = localDateTime.atZone(zoneId);
    Date date = Date.from(zdt.toInstant());
	
	// 原则:利用 时间戳Instant
}
9.6 时间校正器

@Test
public void test01(){
    
    
    //TemporalAdjusters:时间校正器
    LocalDateTime ldt1 = LocalDateTime.now();
    System.out.println(ldt1);

    //指定日期时间中的 年 月 日 ...
    LocalDateTime ldt2 = ldt1.withDayOfMonth(10);
    System.out.println(ldt2);

    //指定时间校正器
    LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    System.out.println(ldt3);

    //自定义时间校正器
    LocalDateTime ldt5 = ldt1.with((ta) -> {
    
    
        LocalDateTime ldt4 = (LocalDateTime) ta;
        DayOfWeek dow1 = ldt4.getDayOfWeek();
        if (dow1.equals(DayOfWeek.FRIDAY)) {
    
    
            return ldt4.plusDays(3);
        } else if (dow1.equals(DayOfWeek.SATURDAY)) {
    
    
            return ldt4.plusDays(2);
        } else {
    
    
            return ldt4.plusDays(1);
        }
    });
    System.out.println(ldt5);
}

/*
    调节器TemporalAdjuster与查询TemporalQuery
    TemporalAdjusters类中常用静态方法的使用
        static TemporalAdjuster firsyDayOfNextMonth()
        下个月的第一天
        static TemporalAdjuster firstDayOfNextYear()
        下一年的第一天
        static TemporalAdjuster firstDayOfYear()
        当年的第一天
        static TemporaAdjuster firstInMonth(DayOfWeek dayOfWeek)
        当月的第一个周x(通过参数确定)
        static TemporaAdjuster lastDayOfMonth()
        当月的最后一天
        static TemporaAdjuster lastDayOfYear()
        当年的最后一天
        static TemporaAdjuste lastInMonth(DayOfWeek dayOfWeek)
        当月的最后一个周x(通过参数确定)
        static TemporaAdjuster next(DayOfWeek dayOfWeek)
        下一个周x(通过参数确定)
        static TemporaAdjuster previous(DayOfWeek dayOfWeek)
上一个周x(通过参数确定)
 */
public class Test12 {
    
    
    public static void main(String[] args) {
    
    
        //封装日期时间对象为当前时间,
        LocalDate time = LocalDate.now();
        /*with方法可以修改time对象中封装的数据,需要传入一个TemporalAdjuster对象, 通过查看发现TemporalAdjuster是一个接口,方法的参数是一个接口,那么实际上传入的是 这个接口的实现类对象. TemporalAdjusters的类可以给我们提供一些常用的方法. */
        //with方法传入了TemporalAdjuster类的实现对象,是由TemporalAdjusters类的方 法实现了adjustInto方法,当前的逻辑是:将时间修改为当月的第一天.
        LocalDate firstDayOfMonth = time.with(TemporalAdjusters.firstDayOfMonth());
        //将时间修改为下个月的第一天.
        LocalDate firstDayOfNextMonth = time.with(TemporalAdjusters.firstDayOfNextMonth());
        //将时间修改为下一年的第一天.
        LocalDate firstDayOfNextYear = time.with(TemporalAdjusters.firstDayOfNextYear());
        //将时间修改为本年的第一天.
        LocalDate firstDayOfYear = time.with(TemporalAdjusters.firstDayOfYear());
        //将时间修改为本月的最后一天.
        LocalDate lastDayOfMonth = time.with(TemporalAdjusters.lastDayOfMonth());
        //将时间修改为本年的最后一天.
        LocalDate lastDayOfYear = time.with(TemporalAdjusters.lastDayOfYear());
    }
}
9.7 格式化

SimpleDateFormat类在刚开始的讲过了是线程不安全的,所以Java8提供了新的格
式化类 DateTimeFormatter.

public class Test13 {
    
    
    public static void main(String[] args) {
    
    
        LocalDateTime now = LocalDateTime.now();
        //now对象可以直接调用format方法进行格式化
        String s1 = now.format(DateTimeFormatter.ISO_DATE_TIME);
        String s2 = now.format(DateTimeFormatter.ISO_DATE);
        System.out.println("now = " + now);
        System.out.println("s1 = " + s1);
        System.out.println("s2 = " + s2);
        //解析字符串的方式通过LocalDateTime类的静态方法parse方法传入需要解析的字符串即可
        LocalDateTime time = LocalDateTime.parse(s2);
        System.out.println(time);

    }
}
public class Test14 {
    
    
    public static void main(String[] args) {
    
    
        //默认格式化
        DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME;
        LocalDateTime ldt1 = LocalDateTime.now();
        String str1 = ldt1.format(dtf1);
        System.out.println(str1);

        //自定义格式化 ofPattern
        DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt2 = LocalDateTime.now();
        String str2 = ldt2.format(dtf2);
        System.out.println(str2);

        //解析
        LocalDateTime newDate = ldt1.parse(str1, dtf1);
        System.out.println(newDate);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_28384023/article/details/110726387