jdk1.8新特性-最新最全

1 篇文章 0 订阅
1 篇文章 1 订阅

一、引言

jdk1.8出来已经一段时间了,现在12也已经出来了,越来越多的公司已经在使用了jdk1.8的新特性了,1.8和1.6相比有很大层次的特性的提升。从代码运行的性能、方法的简单化都有了很大层次的提升。

二、新特性

在jdk1.8中对hashMap等map集合的数据结构优化。hashMap数据结构的优化
原来的hashMap采用的数据结构是哈希表(数组+链表),hashMap默认大小是16,一个0-15索引的数组,如何往里面存储元素,首先调用元素的hashcode
方法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么比较它们的equals方法比较内容
如果内容一样,后一个value会将前一个value的值覆盖,如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,在某些情况下如果链表
无限下去,那么效率极低,碰撞是避免不了的
加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生
在1.8之后,在数组+链表+红黑树来实现hashmap,当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入
除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾
ConcurrentHashMap (锁分段机制),concurrentLevel,jdk1.8采用CAS算法(无锁算法,不再使用锁分段),数组+链表中也引入了红黑树的使用

1、default关键字
在java里面,我们通常都是认为接口里面是只能有抽象方法,不能有任何方法的实现的,那么在jdk1.8里面打破了这个规定,引入了新的关键字default,通过使用default修饰方法,可以让我们在接口里面定义具体的方法实现,如下:

   public interface NewCharacter {
      public void test1();
        public default void test2(){
       	 System.out.println("我是新特性1");
    	}
   	}

其实在接口中中加了default一个默认的方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,如下。
其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,如下。

public class NewCharacterImpl implements NewCharacter{
    @Override
    public void test1() {
       
    }
    
    public static void main(String[] args) {
        NewCharacter nca = new NewCharacterImpl();
        nca.test2();
    }
}

所以说这个default方法是所有的实现类都不需要去实现的就可以直接调用,那么比如说jdk的集合List里面增加了一个sort方法,那么如果定义为一个抽象方法,其所有的实现类如arrayList,LinkedList等都需要对其添加实现,那么现在用default定义一个默认的方法之后,其实现类可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。

2、Lambda 表达式

Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
Lambda表达式

2. 基础语法

java8中引入了一个新的操作符 ->,该操作符称为箭头操作符或Lambda操作符,该箭头符号将整个Lambda表达式拆分成两部分:
左侧:Lambda表达式的参数列表,即接口中对应抽象方法的参数列表。
右侧:Lambda表达式中所需要执行的功能,即Lambda表达式体。即需要实现的抽象方法的功能体。

2.1. 语法格式一 无参数,无返回值

对应格式为: () -> 方法体… 括号内无参数

例如:() -> Sysout…

@Test
public void test1 (){
    //无参数 , 无返回值 对应格式为: () -> 方法体...   括号内无参数
    Runnable run = new Runnable() {
        @Override
        public void run() {
            System.out.println("old run");
        }
    };
    run.run();
    System.out.println("-----------");
    Runnable run1 = () -> System.out.println("lambda run");
    run1.run();
}
/*result:
    old run
    -----------
    lambda run
*/

2.2. 语法格式二 有一个参数,无返回值

对应语法格式为 (x) -> 无返回值的方法体

例如: (x) -> System.out.println(x)

若有且只有一个参数,左侧的小括号可以省略不写

例如: x -> System.out.println(x)

// 有一个参数 , 无返回值
@Test
public void test2(){
    List<String> list = new ArrayList<>();
    Consumer<String> consumer = (s) -> list.add(s);//将consumer接收到的对象进行具体的功能的消费
    consumer.accept("ddd");
    consumer.accept("aaa");
    consumer.accept("ccc");
    list.forEach(System.out::println);
    /*
        Result:
            ddd
            aaa
            ccc
     */
}

2.3. 语法格式三 有两个或两个以上参数,有返回值

有两个或两个以上参数,有返回值,并且 lambda 体中有多条语句
语法为: 
(x,y) -> {
    方法体
    return 返回值
}
	多条语句必须使用大括号包括在内,
有返回值,需要使用return 返回返回值.
 Comparator<Integer> com = (x, y) -> {
    System.out.println("x为"+x);
    System.out.println("y为"+y);
    return Integer.compare(x,y);
    };

在这里插入图片描述

3、函数式接口

定义:“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。
常见的四大函数式接口

  • Consumer 《T》:消费型接口,有参无返回值

       @Test
      public void test(){
          changeStr("hello",(str) -> System.out.println(str));
      }
    
      /**
       *  Consumer<T> 消费型接口
       * @param str
       * @param con
       */
      public void changeStr(String str, Consumer<String> con){
          con.accept(str);
      }
    
  • Supplier 《T》:供给型接口,无参有返回值

      @Test
      public void test2(){
          String value = getValue(() -> "hello");
          System.out.println(value);
      }
    
      /**
       *  Supplier<T> 供给型接口
       * @param sup
       * @return
       */
      public String getValue(Supplier<String> sup){
          return sup.get();
      }
    
  • Function 《T,R》::函数式接口,有参有返回值

      @Test
      public void test3(){
          Long result = changeNum(100L, (x) -> x + 200L);
          System.out.println(result);
      }
    
      /**
       *  Function<T,R> 函数式接口
       * @param num
       * @param fun
       * @return
       */
      public Long changeNum(Long num, Function<Long, Long> fun){
          return fun.apply(num);
      }
    
  • Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型

      public void test4(){
          boolean result = changeBoolean("hello", (str) -> str.length() > 5);
          System.out.println(result);
      }
    
      /**
       *  Predicate<T> 断言型接口
       * @param str
       * @param pre
       * @return
       */
      public boolean changeBoolean(String str, Predicate<String> pre){
          return pre.test(str);
      }
    

四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。

总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了.

方法引用

若lambda体中的内容有方法已经实现了,那么可以使用“方法引用” 
也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单

(a) 方法引用
三种表现形式:

  1. 对象::实例方法名

  2. 类::静态方法名

  3. 类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用 者,第二个参数是实例方法的参数时可用)

     public void test() {
          /**
          *注意:
          *   1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
          *   2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
          *
          */
          Consumer<Integer> con = (x) -> System.out.println(x);
          con.accept(100);
     
          // 方法引用-对象::实例方法
          Consumer<Integer> con2 = System.out::println;
          con2.accept(200);
     
          // 方法引用-类名::静态方法名
          BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
          BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
          Integer result = biFun2.apply(100, 200);
     
          // 方法引用-类名::实例方法名
          BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
          BiFunction<String, String, Boolean> fun2 = String::equals;
          Boolean result2 = fun2.apply("hello", "world");
          System.out.println(result2);
     	}
    

(b)构造器引用
格式:ClassName::new

public void test() {
  /**
  *注意:
  *   1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
  *   2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
  *
  */
  Consumer<Integer> con = (x) -> System.out.println(x);
  con.accept(100);

  // 方法引用-对象::实例方法
  Consumer<Integer> con2 = System.out::println;
  con2.accept(200);

  // 方法引用-类名::静态方法名
  BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
  BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
  Integer result = biFun2.apply(100, 200);

  // 方法引用-类名::实例方法名
  BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
  BiFunction<String, String, Boolean> fun2 = String::equals;
  Boolean result2 = fun2.apply("hello", "world");
  System.out.println(result2);
}

©数组引用

格式:Type[]::new

public void test(){
       // 数组引用
       Function<Integer, String[]> fun = (x) -> new String[x];
       Function<Integer, String[]> fun2 = String[]::new;
       String[] strArray = fun2.apply(10);
       Arrays.stream(strArray).forEach(System.out::println);
}

Stream API

Stream操作的三个步骤

  • 创建stream
  • 中间操作(过滤、map)
  • 终止操作

stream的创建:

// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
List<String> list = new ArrayList<>();
Strean<String> stream1 = list.stream();

// 2.通过Arrays的静态方法stream()获取数组流
String[] str = new String[10];
Stream<String> stream2 = Arrays.stream(str);

// 3.通过Stream类中的静态方法of
Stream<String> stream3 = Stream.of("aa","bb","cc");

// 4.创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);

//生成
Stream.generate(() ->Math.random());

Stream的中间操作:

/**
 * 筛选 过滤  去重
 */
emps.stream()
        .filter(e -> e.getAge() > 10)
        .limit(4)
        .skip(4)
        // 需要流中的元素重写hashCode和equals方法
        .distinct()
        .forEach(System.out::println);


/**
 *  生成新的流 通过map映射
 */
emps.stream()
        .map((e) -> e.getAge())
        .forEach(System.out::println);


/**
 *  自然排序  定制排序
 */
emps.stream()
        .sorted((e1 ,e2) -> {
            if (e1.getAge().equals(e2.getAge())){
                return e1.getName().compareTo(e2.getName());
            } else{
                return e1.getAge().compareTo(e2.getAge());
            }
        })
        .forEach(System.out::println);

Stream的终止操作:

/**
   *      查找和匹配
   *          allMatch-检查是否匹配所有元素
   *          anyMatch-检查是否至少匹配一个元素
   *          noneMatch-检查是否没有匹配所有元素
   *          findFirst-返回第一个元素
   *          findAny-返回当前流中的任意元素
   *          count-返回流中元素的总个数
   *          max-返回流中最大值
   *          min-返回流中最小值
   */

  /**
   *  检查是否匹配元素
   */
  boolean b1 = emps.stream()
          .allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
  System.out.println(b1);

  boolean b2 = emps.stream()
          .anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
  System.out.println(b2);

  boolean b3 = emps.stream()
          .noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
  System.out.println(b3);

  Optional<Employee> opt = emps.stream()
          .findFirst();
  System.out.println(opt.get());

  // 并行流
  Optional<Employee> opt2 = emps.parallelStream()
          .findAny();
  System.out.println(opt2.get());

  long count = emps.stream()
          .count();
  System.out.println(count);

  Optional<Employee> max = emps.stream()
          .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
  System.out.println(max.get());

  Optional<Employee> min = emps.stream()
          .min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
  System.out.println(min.get());

还有功能比较强大的两个终止操作 reduce和collect
reduce操作: reduce:(T identity,BinaryOperator)/reduce(BinaryOperator)-可以将流中元素反复结合起来,得到一个值

/**
* reduce :规约操作
*/
List list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer count2 = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(count2);

Optional<Double> sum = emps.stream()
        .map(Employee::getSalary)
        .reduce(Double::sum);
System.out.println(sum);

collect操作:Collect-将流转换为其他形式,接收一个Collection接口的实现,用于给Stream中元素做汇总的方法

   /**
    *  collect:收集操作
    */

   List<Integer> ageList = emps.stream()
           .map(Employee::getAge)
           .collect(Collectors.toList());
   ageList.stream().forEach(System.out::println);

并行流和串行流

在jdk1.8新的stream包中针对集合的操作也提供了并行操作流和串行操作流。并行流就是把内容切割成多个数据块,并且使用多个线程分别处理每个数据块的内容。Stream api中声明可以通过parallel()与sequential()方法在并行流和串行流之间进行切换。 jdk1.8并行流使用的是fork/join框架进行并行操作

ForkJoin框架
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。
关键字:递归分合、分而治之。

采用 “工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线
程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的
处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因
无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果
某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子
问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程
的等待时间,提高了性能.。

/**
* 要想使用Fark—Join,类必须继承
* RecursiveAction(无返回值)
* Or
* RecursiveTask(有返回值)
*
*/
public class ForkJoin extends RecursiveTask<Long> {

  /**
   * 要想使用Fark—Join,类必须继承RecursiveAction(无返回值) 或者
   * RecursiveTask(有返回值)
   *
   * @author Wuyouxin
   */
  private static final long serialVersionUID = 23423422L;

  private long start;
  private long end;

  public ForkJoin() {
  }

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

  // 定义阙值
  private static final long THRESHOLD = 10000L;

  @Override
  protected Long compute() {
      if (end - start <= THRESHOLD) {
          long sum = 0;
          for (long i = start; i < end; i++) {
              sum += i;
          }
          return sum;
      } else {
          long middle = (end - start) / 2;
          ForkJoin left = new ForkJoin(start, middle);
          //拆分子任务,压入线程队列
          left.fork();
          ForkJoin right = new ForkJoin(middle + 1, end);
          right.fork();

          //合并并返回
          return left.join() + right.join();
      }
  }

  /**
   * 实现数的累加
   */
  @Test
  public void test1() {
      //开始时间
      Instant start = Instant.now();

      //这里需要一个线程池的支持
      ForkJoinPool pool = new ForkJoinPool();

      ForkJoinTask<Long> task = new ForkJoin(0L, 10000000000L);
      // 没有返回值     pool.execute();
      // 有返回值
      long sum = pool.invoke(task);

      //结束时间
      Instant end = Instant.now();
      System.out.println(Duration.between(start, end).getSeconds());
  }

  /**
   * java8 并行流 parallel()
   */
  @Test
  public void test2() {
      //开始时间
      Instant start = Instant.now();

      // 并行流计算    累加求和
      LongStream.rangeClosed(0, 10000000000L).parallel()
              .reduce(0, Long :: sum);

      //结束时间
      Instant end = Instant.now();
      System.out.println(Duration.between(start, end).getSeconds());
  }

  @Test
  public void test3(){
      List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
      list.stream().forEach(System.out::print);

      list.parallelStream()
          .forEach(System.out::print);
  }

展示多线程的效果:

@Test
public void test(){
    // 并行流 多个线程执行
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    numbers.parallelStream()
            .forEach(System.out::print);

    //
    System.out.println("=========================");
    numbers.stream()
                 .sequential()
                 .forEach(System.out::print);
}

Optional容器

使用Optional容器可以快速的定位NPE,并且在一定程度上可以减少对参数非空检验的代码量。

/**
    *      Optional.of(T t); // 创建一个Optional实例
    *      Optional.empty(); // 创建一个空的Optional实例
    *      Optional.ofNullable(T t); // 若T不为null,创建一个Optional实例,否则创建一个空实例
    *      isPresent();    // 判断是够包含值
    *      orElse(T t);   //如果调用对象包含值,返回该值,否则返回T
    *      orElseGet(Supplier s);  // 如果调用对象包含值,返回该值,否则返回s中获取的值
    *      map(Function f): // 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty();
    *      flatMap(Function mapper);// 与map类似。返回值是Optional
    *
    *      总结:Optional.of(null)  会直接报NPE
    */

Optional<Employee> op = Optional.of(new Employee("zhansan", 11, 12.32, Employee.Status.BUSY));
       System.out.println(op.get());

       // NPE
       Optional<Employee> op2 = Optional.of(null);
       System.out.println(op2);
@Test
   public void test2(){
       Optional<Object> op = Optional.empty();
       System.out.println(op);

       // No value present
       System.out.println(op.get());
   }
@Test
   public void test3(){
       Optional<Employee> op = Optional.ofNullable(new Employee("lisi", 33, 131.42, Employee.Status.FREE));
       System.out.println(op.get());

       Optional<Object> op2 = Optional.ofNullable(null);
       System.out.println(op2);
      // System.out.println(op2.get());
   }
   @Test
   public void test5(){
       Optional<Employee> op1 = Optional.ofNullable(new Employee("张三", 11, 11.33, Employee.Status.VOCATION));
       System.out.println(op1.orElse(new Employee()));
       System.out.println(op1.orElse(null));
   }

   @Test
   public void test6(){
       Optional<Employee> op1 = Optional.of(new Employee("田七", 11, 12.31, Employee.Status.BUSY));
       op1 = Optional.empty();
       Employee employee = op1.orElseGet(() -> new Employee());
       System.out.println(employee);
   }

   @Test
   public void test7(){
       Optional<Employee> op1 = Optional.of(new Employee("田七", 11, 12.31, Employee.Status.BUSY));
       System.out.println(op1.map( (e) -> e.getSalary()).get());
  }

接口中可以定义默认实现方法和静态方法

在接口中可以使用default和static关键字来修饰接口中定义的普通方法

public interface Interface {
   default  String getName(){
       return "zhangsan";
   }

   static String getName2(){
       return "zhangsan";
   }
}

在JDK1.8中很多接口会新增方法,为了保证1.8向下兼容,1.7版本中的接口实现类不用每个都重新实现新添加的接口方法,引入了default默认实现,static的用法是直接用接口名去调方法即可。当一个类继承父类又实现接口时,若后两者方法名相同,则优先继承父类中的同名方法,即“类优先”,如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法。

jdk8新特性之双冒号 :: 用法及详解

类名::方法名,相当于对这个方法闭包的引用,类似js中的一个function。比如:

Function<String,String> func =  String::toUpperCase;

新建一个类,里面声明四个代表各种情况的方法:

public class DoubleColon {

  public static void printStr(String str) {
      System.out.println("printStr : " + str);
  }

  public void toUpper(){
      System.out.println("toUpper : " + this.toString());
  }

  public void toLower(String str){
      System.out.println("toLower : " + str);
  }

  public int toInt(String str){
      System.out.println("toInt : " + str);
      return 1;
  }
}

把它们用::提取为函数,再使用:

Consumer<String> printStrConsumer = DoubleColon::printStr;
Consumer<String>
printStrConsumer.accept("printStrConsumer");

Consumer<DoubleColon> toUpperConsumer = DoubleColon::toUpper;
toUpperConsumer.accept(new DoubleColon());

BiConsumer<DoubleColon,String> toLowerConsumer = DoubleColon::toLower;
toLowerConsumer.accept(new DoubleColon(),"toLowerConsumer");

BiFunction<DoubleColon,String,Integer> toIntFunction = DoubleColon::toInt;
int i = toIntFunction.apply(new DoubleColon(),"toInt");
  • :: 和方法互转

    BiFunction<DoubleColon,String,Integer> biFunction = new BiFunction<DoubleColon, String, Integer>() {
              @Override
              public Integer apply(DoubleColon doubleColon, String s) {
                  return doubleColon.toInt(s);
              }
          };
    

BiFunction<DoubleColon,String,Integer>中第一个和第一个表示入参的类型,第三个表示返回值类型

	BiFunction<DoubleColon,String,Integer> biFunction =DoubleColon ::toInt;
  • :: 和-> 互转

    BiFunction<DoubleColon,String,Integer> biFunction =DoubleColon ::toInt;
    BiFunction<DoubleColon,String,Integer> biFunction1 =(d,s)-> d.toInt(s)
    
  • -> 和 方法互转

    BiFunction<DoubleColon,String,Integer> biFunction1 =(d,s)-> d.toInt(s);
    
    BiFunction<DoubleColon,String,Integer> biFunction = new BiFunction<DoubleColon, String, Integer>() {
    	          @Override
    	          public Integer apply(DoubleColon d, String s) {
    	              return d.toInt(s);
    	          }
    	      };
    

新的日期API LocalDate | LocalTime | LocalDateTime

新的日期API都是不可变的,更使用于多线程的使用环境中

表示日期的LocalDate
表示时间的LocalTime
表示日期时间的LocalDateTime

@Test
public void test(){
    // 从默认时区的系统时钟获取当前的日期时间。不用考虑时区差
    LocalDateTime date = LocalDateTime.now();
    //2018-07-15T14:22:39.759
    System.out.println(date);

    System.out.println(date.getYear());
    System.out.println(date.getMonthValue());
    System.out.println(date.getDayOfMonth());
    System.out.println(date.getHour());
    System.out.println(date.getMinute());
    System.out.println(date.getSecond());
    System.out.println(date.getNano());

    // 手动创建一个LocalDateTime实例
    LocalDateTime date2 = LocalDateTime.of(2017, 12, 17, 9, 31, 31, 31);
    System.out.println(date2);
    // 进行加操作,得到新的日期实例
    LocalDateTime date3 = date2.plusDays(12);
    System.out.println(date3);
    // 进行减操作,得到新的日期实例
    LocalDateTime date4 = date3.minusYears(2);
    System.out.println(date4);
}


    @Test
public void test2(){
    // 时间戳  1970年1月1日00:00:00 到某一个时间点的毫秒值
    // 默认获取UTC时区
    Instant ins = Instant.now();
    System.out.println(ins);

    System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
    System.out.println(System.currentTimeMillis());

    System.out.println(Instant.now().toEpochMilli());
    System.out.println(Instant.now().atOffset(ZoneOffset.ofHours(8)).toInstant().toEpochMilli());
}

@Test
public void test3(){
    // Duration:计算两个时间之间的间隔
    // Period:计算两个日期之间的间隔

    Instant ins1 = Instant.now();

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();
    Duration dura = Duration.between(ins1, ins2);
    System.out.println(dura);
    System.out.println(dura.toMillis());

    System.out.println("======================");
    LocalTime localTime = LocalTime.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LocalTime localTime2 = LocalTime.now();
    Duration du2 = Duration.between(localTime, localTime2);
    System.out.println(du2);
    System.out.println(du2.toMillis());
}

@Test
public void test4(){
   LocalDate localDate =LocalDate.now();

   try {
       Thread.sleep(1000);
   } catch (InterruptedException e) {
       e.printStackTrace();
   }

   LocalDate localDate2 = LocalDate.of(2016,12,12);
   Period pe = Period.between(localDate, localDate2);
   System.out.println(pe);
}
@Test
public void test5(){
    // temperalAdjust 时间校验器
    // 例如获取下周日  下一个工作日
    LocalDateTime ldt1 = LocalDateTime.now();
    System.out.println(ldt1);

    // 获取一年中的第一天
    LocalDateTime ldt2 = ldt1.withDayOfYear(1);
    System.out.println(ldt2);
    // 获取一个月中的第一天
    LocalDateTime ldt3 = ldt1.withDayOfMonth(1);
    System.out.println(ldt3);

    LocalDateTime ldt4 = ldt1.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
    System.out.println(ldt4);

    // 获取下一个工作日
    LocalDateTime ldt5 = ldt1.with((t) -> {
        LocalDateTime ldt6 = (LocalDateTime)t;
        DayOfWeek dayOfWeek = ldt6.getDayOfWeek();
        if (DayOfWeek.FRIDAY.equals(dayOfWeek)){
            return ldt6.plusDays(3);
        }
        else if (DayOfWeek.SATURDAY.equals(dayOfWeek)){
            return ldt6.plusDays(2);
        }
        else {
            return ldt6.plusDays(1);
        }
    });
    System.out.println(ldt5);
}
@Test
public void test6(){
    // DateTimeFormatter: 格式化时间/日期
    // 自定义格式
    LocalDateTime ldt = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    String strDate1 = ldt.format(formatter);
    String strDate = formatter.format(ldt);
    System.out.println(strDate);
    System.out.println(strDate1);

    // 使用api提供的格式
    DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
    LocalDateTime ldt2 = LocalDateTime.now();
    String strDate3 = dtf.format(ldt2);
    System.out.println(strDate3);

    // 解析字符串to时间
    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime time = LocalDateTime.now();
    String localTime = df.format(time);
    LocalDateTime ldt4 = LocalDateTime.parse("2017-09-28 17:07:05",df);
    System.out.println("LocalDateTime转成String类型的时间:"+localTime);
    System.out.println("String类型的时间转成LocalDateTime:"+ldt4);
}
    // ZoneTime  ZoneDate       ZoneDateTime
@Test
public void test7(){
    LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(now);

    LocalDateTime now2 = LocalDateTime.now();
    ZonedDateTime zdt = now2.atZone(ZoneId.of("Asia/Shanghai"));
    System.out.println(zdt);

    Set<String> set = ZoneId.getAvailableZoneIds();
    set.stream().forEach(System.out::println);
}
  • 之前使用的java.util.Date月份从0开始,我们一般会+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum
  • java.util.Date和SimpleDateFormat都不是线程安全的,而LocalDate和LocalTime和最基本的String一样,是不变类型,不但线程安全,而且不能修改。
  • java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数,更加明确需求取舍
  • 新接口更好用的原因是考虑到了日期时间的操作,经常发生往前推或往后推几天的情况。用java.util.Date配合Calendar要写好多代码,而且一般的开发人员还不一定能写对。
附件JDK1.8 中文档

链接: https://pan.baidu.com/s/1z2M30TK3w6iP7quQA6D3kA 提取码: 8h43

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java1111115555

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值