JDK1.8新特性

JDK1.8新特性

Lambda表达式

  • 原java开发
 // 筛选颜色为红色
  public  List<Product> filterProductByColor(List<Product> list){
      List<Product> prods = new ArrayList<>();
      for (Product product : list){
          if ("红色".equals(product.getColor())){
              prods.add(product);
          }
      }
      return prods;
   }
  • 我们发现实际上这些过滤方法的核心就只有if语句中的条件判断,其他均为模版代码,每次变更一下需求,都需要新增一个方法,然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?
  • 使用lambda表达式
   定义过滤方法:
   public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){
           List<Product> prods = new ArrayList<>();
           for (Product prod : list){
               if (mp.test(prod)){
                   prods.add(prod);
               }
           }
           return prods;
       }
   使用lambda表达式进行过滤:
   public void test(){
         List<Product> products = filterProductByPredicate(proList, (p) -> p.getPrice() < 8000);
         for (Product pro : products){
             System.out.println(pro);
         }
     }
   Lmabda表达式的语法总结: () -> ();

lambda总结

前置语法
无参数无返回值() -> System.out.println(“Hello World!”)
有一个参数无返回值(x) -> System.out.println(x)
有切只有一个参数无返回值x -> System.out.println(x)
有多个参数,有返回值,有多条lambda体语句(x ,y) -> {System.out.println(“xxx”);return xxx;};
有多个参数,有返回值,只有一条lambda体语句(x, y) -> xxxx
  • 口诀:左右遇一省括号,左侧推断类型省
  • 当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能只能匹配对应的抽象方法,因此引入了函数式接口的概念

方法引用

  • System.out :: println
    " :: "即为方法引用

Stream API

Stream 操作的三个步骤

  • 创建stream
  • 中间操作(过滤、map)
  • 终止操作
    在这里插入图片描述
Stream创建
   // 1.校验通过Collection 系列集合提供的stream()或者paralleStream()
   List<String> list = new ArrayList<>();
   Strean<String> stream = list.stream();//串行流
   Stream<String> parallelStream = list.parallelStream(); //并行流

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

   // 3.通过Stream类中的静态方法of()、iterate()、generate()
   Stream<String> stream3 = Stream.of("aa","bb","cc");
   Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2).limit(6); // 迭代
   Stream<Double> stream3 = Stream.generate(Math::random).limit(2);

   // 4.BufferedReader.lines() 方法,将每行内容转成流
   BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
   Stream<String> lineStream = reader.lines();
   lineStream.forEach(System.out::println);

   // 5.Pattern.splitAsStream() 方法,将字符串分隔成流 
   Pattern pattern = Pattern.compile(",");
   Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
   stringStream.forEach(System.out::println);
Stream中间操作
筛选、过滤、去重
方法说明
filter过滤流中的某些元素(只保留返回值为true的项)
limit(n)获取前n个元素
skip(n)跳过前n个元素,配合limit(n)可实现分页
distinct通过流中元素的 hashCode() 和 equals() 去除重复元素
/**
 *  筛选 过滤  去重
 */
emps.stream()
        .filter(e -> e.getAge() > 10)--过滤
        .limit(4)--限制(输出前4个)
        .skip(4)--跳过(前4个不输出)
        .count()--统计
        // 需要流中的元素重写hashCode和equals方法
        .distinct()--去重
        .forEach(System.out::println)--遍历输出;

/**
 * 根据对象属性去重 
 */
 List<User> list = new ArrayList<User>() {{
 	add(new User("Tony", 20, "12"));
	add(new User("Pepper", 20, "123"));
	add(new User("Tony", 22, "1234"));
	add(new User("Tony", 22, "12345"));
}};
//只通过名字去重
List<User> streamByNameList = list.stream().collect(Collectors.collectingAndThen(
	Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new
));
System.out.println(streamByNameList);//[User{name='Pepper', age=20, Phone='123'}, User{name='Tony', age=20, Phone='12'}]
//通过名字和年龄去重
List<User> streamByNameAndAgeList = list.stream().collect(Collectors.collectingAndThen(
	Collectors.toCollection(
			() -> new TreeSet<>(Comparator.comparing(o -> o.getName() + o.getAge()))), ArrayList::new
));
System.out.println(streamByNameAndAgeList);//[User{name='Pepper', age=20, Phone='123'},User{name='Tony', age=20, Phone='12'},User{name='Tony', age=22, Phone='1234'}]
映射
方法说明
map函数作为参数,该函数被应用到每个元素,并将其映射成一个新的元素。新值类型可以和原来的元素的类型不同。
flatMap函数作为参数,将流中每个值换成另一个流,再把所有流连成一个流。 新值类型可以和原来的元素的类型不同。
mapToInt/Long/Double跟map差不多。只是将其转为基本类型。
flatMap ToInt/Long/Double跟flatMap差不多。只是将其转为基本类型。
/**
 * 新值类型和原来的元素类型相同
 */
List<String> list = Arrays.asList("a,b,c", "1,2,3");
//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
s1.forEach(System.out::println);
// abc  123
Stream<String> s2 = list.stream().flatMap(s -> {
    //将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream<String> s3 = Arrays.stream(split);
    return s3;
});
s2.forEach(System.out::println);
// a b c 1 2 3


/**
 * 新值类型和原来的元素类型不同
 */
User u1 = new User("aa", 10);
User u2 = new User("bb", 20);
User u3 = new User("cc", 10);
List<User> list = Arrays.asList(u1, u2, u3);
Set<Integer> ageSet = list.stream().map(User::getAge).collect(Collectors.toSet());
ageSet.forEach(System.out::println);
//20 10
int[] ageInt = list.stream().map(User::getAge).mapToInt(Integer::intValue).toArray();
//下边这样也可以
//Integer[] ages = list.stream.map(User::getAge).toArray(Integer[]::new);
for (int i : ageInt) {
	System.out.println(i);
}
//10 20 10

//.concat多个流进行合并
Stream<String> a=Stream.of("1","2","3");
Stream<String> b=Stream.of("4","5","6");
Stream.concat(a,b).forEach(System.out::println);
/**
 * 合并多个List
 */
Stream.of(List1<A>,...ListN<A>).flatMap(Collection::stream).distinct().collect(Collectors.toList());
排序
方法说明
sorted()自然排序,流中元素需实现Comparable接口。 例:list.stream().sorted()
sorted(Comparator com)定制排序。常用以下几种:list.stream().sorted(Comparator.reverseOrder());list.stream().sorted(Comparator.comparing(Student::getAge));list.stream().sorted(Comparator.comparing(Student::getAge).reversed())
/**
 * 原始类型排序
 */
List<String> list = Arrays.asList("aa", "ff", "dd");
//String 类自身已实现Comparable接口
list.stream().sorted().forEach(System.out::println);

/**
 * 对象单字段排序
 */
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> userList = Arrays.asList(u1, u2, u3, u4);
//按年龄升序
userList.stream().sorted(Comparator.comparing(User::getAge))
        .forEach(System.out::println);

/**
 * 对象多字段、全部升序排序
 */
List<User> userList = Arrays.asList(u1, u2, u3, u4);
// 写法1(推荐)
userList.stream().sorted(Comparator
                .comparing(User::getAge)
                .thenComparing(User::getName)
        // 可以写多个.thenComparing
).forEach(System.out::println);
System.out.println("------------------------------------");
// 写法2
userList.stream().sorted(
        (o1, o2) -> {
            String tmp1 = o1.getAge() + o1.getName();
            String tmp2 = o2.getAge() + o2.getName();
            return tmp1.compareTo(tmp2);
        }
).forEach(System.out::println);
System.out.println("------------------------------------");
// 写法3
userList.stream().sorted(
        (o1, o2) -> {
            if (!o1.getAge().equals(o2.getAge())) {
                return o1.getAge().compareTo(o2.getAge());
            } else {
                return o1.getName().compareTo(o2.getName());
            }
        }
).forEach(System.out::println);

/**
 * 对象多字段、升序+降序
 */
List<User> userList = Arrays.asList(u1, u2, u3, u4);
userList.stream().sorted(
        (o1, o2) -> {
            if (!o1.getAge().equals(o2.getAge())) {
                return o1.getAge().compareTo(o2.getAge());
            } else {
                return o2.getName().compareTo(o1.getName());
            }
        }
).forEach(System.out::println);
消费
方法说明
peek类似于map,能得到流中的每一个元素。 但map接收的是一个Function表达式,有返回值; 而peek接收的是Consumer表达式,没有返回值。
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> list = Arrays.asList(u1, u2, u3, u4);
List<User> list1 = list.stream()
		.peek(o -> o.setAge(100))
		.collect(Collectors.toList());
System.out.println(list1);
Stream终止操作
匹配、最值、个数
方法说明
allMatch接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
angMatch接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst返回流中第一个元素
findAny返回流中的任意元素
count返回流中元素的总个数
max返回流中元素最大值
min返回流中元素最小值
/**
 * 单个类型
 */
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 匹配
boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4);  //true
// 获取第一个/第任意个
Integer findFirst = list.stream().findFirst().get(); //1
Integer findAny = list.stream().findAny().get(); //1
// 计数、最大值、最小值
long count = list.stream().count(); //5
Integer max = list.stream().max(Integer::compareTo).get(); //5
Integer min = list.stream().min(Integer::compareTo).get(); //1

/**
 * 获取对象中的字段的最值
 */
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> list = Arrays.asList(u1, u2, u3, u4);
//获取最小年龄的用户。
User user1 = list.stream()
		.min(Comparator.comparing(User::getAge))
		.orElse(null);
System.out.println(user1);
System.out.println("------------------------------------");
//获取先按姓名升序,姓名相同则按年龄升序。然后获取最小的那个(第一个)
User user = list.stream().min((o1, o2) -> {
	if (o1.getAge().equals(o2.getAge())) {
		return o1.getName().compareTo(o2.getName());
	} else {
		return o1.getAge().compareTo(o2.getAge());
	}
}).orElse(null);
System.out.println(user);

收集
方法说明
collect接收一个Collector实例,将流中元素收集成另外一个数据结构。
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> list = Arrays.asList(u1, u2, u3, u4);

//字符串分隔符连接
String joinName = list.stream().map(User::getName).collect(Collectors.joining(",", "(", ")"));
System.out.println(joinName);//(dd,bb,aa,aa)

//转为list
List<Integer> ageList = list.stream().map(User::getAge).collect(Collectors.toList());
System.out.println(ageList);//[40, 20, 20, 30]

//转为set
Set<Integer> ageSet = list.stream().map(User::getAge).collect(Collectors.toSet());
System.out.println(ageSet);//[20, 40, 30]

//转为map
List<User> list = Arrays.asList(s1, s2, s3);

// value为对象中的属性
Map<String, Integer> ageMap = list.stream().collect(Collectors.toMap(User::getName, User::getAge));
System.out.println(ageMap);
//{aa=20, bb=20, dd=40}

// value为对象(注:key不能相同,否则报错)
Map<String, User> userMap = list.stream().collect(Collectors.toMap(User::getName, user -> user));
// 或者这么写:
// Map<String, User> userMap = list.stream().collect(Collectors.toMap(User::getName, Function.identity()));
System.out.println(userMap);
//{aa=User{name='aa', age=20}, bb=User{name='bb', age=20}, dd=User{name='dd', age=40}}
聚合
//1.用户总数
Long count = list.stream().collect(Collectors.counting());
System.out.println(count);//4

//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(User::getAge).collect(Collectors.maxBy(Integer::compare)).get();
System.out.println(maxAge);//40

//3.所有人的年龄
Integer sumAge = list.stream().collect(Collectors.summingInt(User::getAge));
System.out.println(sumAge);//110

//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(User::getAge));
System.out.println(averageAge);// 27.5

// 统计上边所有数据
DoubleSummaryStatistics stat = list.stream().collect(Collectors.summarizingDouble(User::getAge));
System.out.println("count:" + stat.getCount() + " max:" + stat.getMax() + " sum:" + stat.getSum()
        + " average:" + stat.getAverage());
//count:4 max:40.0 sum:110.0 average:27.5
分组
/**
 * 单字段分组
 */
//根据年龄分组
Map<Integer, List<User>> listMap = list.stream().collect(Collectors.groupingBy(User::getAge));
for (Map.Entry<Integer, List<User>> entry : listMap.entrySet()) {
    System.out.println(entry.getKey() + "-->" + entry.getValue());
}
//20-->[User{name='bb', age=20}, User{name='aa', age=20}]
//40-->[User{name='dd', age=40}]
//30-->[User{name='aa', age=30}]

/**
 * 多字段分组
 */
Map<String, List<User>> listMap = list.stream()
        .collect(Collectors.groupingBy(
                user -> {
                    return user.getAge() + ":" +
                            user.getName();
                }));
for (Map.Entry<String, List<User>> entry : listMap.entrySet()) {
    System.out.println(entry.getKey() + "-->" + entry.getValue());
}
//30:aa-->[User(name=aa, age=30)]
//20:aa-->[User(name=aa, age=20)]
//20:bb-->[User(name=bb, age=20)]
//40:dd-->[User(name=dd, age=40)]

/**
 * 多重分组
 */
// 先根据年龄分组再根据名字分组
Map<Integer, Map<String, List<User>>> ageNameMap = list.stream().collect(
        Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getName)));
for (Map.Entry<Integer, Map<String, List<User>>> entry : ageNameMap.entrySet()) {
    System.out.println(entry.getKey() + "-->" + entry.getValue());
}
//20-->{aa=[User{name='aa', age=20}], bb=[User{name='bb', age=20}]}
//40-->{dd=[User{name='dd', age=40}]}
//30-->{aa=[User{name='aa', age=30}]}
分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map<Boolean, List<User>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 20));
for (Map.Entry<Boolean, List<User>> entry : partMap.entrySet()) {
    System.out.println(entry.getKey() + "-->" + entry.getValue());
}
//false-->[User{name='bb', age=20}, User{name='aa', age=20}]
//true-->[User{name='dd', age=40}, User{name='aa', age=30}]
方法说明
Optional reduce(BinaryOperator accumulator)第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素; 第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素; 依次类推。
T reduce(T identity, BinaryOperator accumulator)流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner)在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。在并行流(parallelStream)中,我们知道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。

Reduce和Collect

reduce操作

  • reduce:(T identity,BinaryOperator)/reduce(BinaryOperator)-可以将流中元素反复结合起来,得到一个值
  /**
    *  reduce :规约操作
    */
    List<Integer> 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);

并行流和串行流

ForkJoin框架

  • 就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总

  • 关键字:递归分合、分而治之

  • 采用“工作窃取”模式

    • 当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线
      程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中
      相对于一般的线程池实现,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();    // 判断是否包含值(return :ture ? flase)
        *  ifPresent(T);   //判断是否存在值,存在可对其进行修改
        *  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());
          }

新的日期API LocalDate | LocalTime | LocalDateTime

  • 新的日期API都是不可变的,更使用于多线程的使用环境中
    	@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(){
            // 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 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);
        }

新的日期API的几个有点

1、之前使用的java.util.Date月份从0开始,我们一般会+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum

2、java.util.Date和SimpleDateFormat都不是线程安全的,而LocalDate和LocalTime和最基本的String一样,是不变类型,不但线程安全,而且不能修改。

3、java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数,更加明确需求取舍

4、新接口更好用的原因是考虑到了日期时间的操作,经常发生往前推或往后推几天的情况。用java.util.Date配合Calendar要写好多代码,而且一般的开发人员还不一定能写对。
LocalDate 只包含日月年
public static void localDateTest() {
        //获取当前日期,只含年月日 固定格式 yyyy-MM-dd    2018-05-04
        LocalDate today = LocalDate.now();
        // 根据年月日取日期,5月就是5,
        LocalDate oldDate = LocalDate.of(2018, 5, 1);
        // 根据字符串取:默认格式yyyy-MM-dd,02不能写成2
        LocalDate yesteday = LocalDate.parse("2018-05-03");
        // 如果不是闰年 传入29号也会报错
        LocalDate.parse("2018-02-29");
    }
LocalDate常用转化
/**
  * 日期转换常用,第一天或者最后一天...
  */
  public static void localDateTransferTest(){
      //2018-05-04
      LocalDate today = LocalDate.now();
      // 取本月第1天: 2018-05-01
      LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
      // 取本月第2天:2018-05-02
      LocalDate secondDayOfThisMonth = today.withDayOfMonth(2);
      // 取本月最后一天,再也不用计算是28,29,30还是31: 2018-05-31
      LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
      // 取下一天:2018-06-01
      LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1);
      // 取2018年10月第一个周三 so easy?:  2018-10-03
      LocalDate thirdMondayOf2018 = LocalDate.parse("2018-10-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY));
    }
LocalTime 只包含时分秒
public static void localTimeTest(){
        //16:25:46.448(纳秒值)
        LocalTime todayTimeWithMillisTime = LocalTime.now();
        //16:28:48 不带纳秒值
        LocalTime todayTimeWithNoMillisTime = LocalTime.now().withNano(0);
        LocalTime time1 = LocalTime.parse("23:59:59");
    }
LocalDateTime
public static void localDateTimeTest(){
        //转化为时间戳  毫秒值
        long time1 = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        long time2 = System.currentTimeMillis();
        //时间戳转化为localdatetime
        DateTimeFormatter df= DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss.SSS");
	System.out.println(df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time1),ZoneId.of("Asia/Shanghai"))));
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值