【JDK8语法新特性】:超全总结{lamda,stream,optional,新日期类API},JDK8对策略模式支持,可以直接贴代码运行测试。

Java8新特性

速度快

  1. 优化了HashMap与HashSet集合类的数据结构

    • JDK1.7版本采用的是哈希表(数组)+链表而JDK1.8采用的是哈希表+链表+红黑树

    • 哈希表+数组+红黑树的概述

    • 基本原理:先得到对象的hashcode值,对数组长度取余得到数组下标,若是该数组下标的位置上无元素,直接放入,若是有使用equals方法进行比较,若是相同则覆盖,若是不同则将该对象放入链表的头部其余元素后移(1.8放入链尾)

    • 如何避免Hash冲突(碰撞)

      • 将重写toHashCode与equals方法保持一致:equals为true时HashCode值相同,对象相同时equals值为true
      • 加载因子(0.75): 当使用的哈希表的容量达到了限定容量的百分之75就对哈希表进行扩容,扩容成原来的两倍
    • 如何优化

      • 当链表的长度大于8并且HashMap的总容量大于64时就将链表转为红黑树
    • 红黑树除了在增加元素的效率比不上链表,查改删的效率都高于链表。而且使用了红黑树,在扩容以后,就不用再去重新计算hashcode的值进行重组,只需要将原来红黑树的某一部分取出来,将原来红黑树所在的哈希表下标加上原来哈希表的容量的新下标,放入新的哈希表的新下标即可。

  2. 优化了ConCurrentHashMap与ConcurrentHashSet

    • 取消了concurrentLevel而使用CAS算法
  3. 优化了JVM虚拟机的内存结构了

    • 将方法区彻底的从堆内存中的永久区(PremGen)分离开,以前堆内存的永久区就是方法区,分离出来的方法区使用的是物理内存,并且物理内存有多,方法区就能用多大,极大的减少了方法区被垃圾回收机制回收的可能性,新分离出来的方法区叫做(MetaSpace(元空间))

代码更少(增加了新的语法,lamda表达式)(主要)

强大的Stream API(主要)

  • 使用StreamAPI操作集合的数据,将会得到极大的效率提升

便于并行

最大减少空指针异常(Optional API)

提供了线程安全的时间类

Lomda表达式(非常重要)

策略模式

定义

  • 策略模式是行为模式之一,它对一系列的算法加以封装,为所有算法定义一个抽象的算法接口,并通过继承该抽象算法接口对所有的算法加以封装和实现,具体的算法选择交由客户端决定(策略)。Strategy模式主要用来平滑地处理算法的切换

策略模式应用的背景

  • 需求:将学生按照不同的条件查询出来

    • 学生类如下:
     @Date
     public class Student implements Comparable{
      private String name;
      private Integer age;
      private double consumption;
      private Status status;
    
      public enum Status{
          study,
          rest,
          sleep;
      }
    
    • 学生数据如下:
    public class StudentDao {
      public List<Student> selectAll() {
          ArrayList<Student> students = new ArrayList<>();
          students.add(new Student("张三", 18, 1000, Student.Status.study));
          students.add(new Student("王五", 21, 2000, Student.Status.rest));
          students.add(new Student("赵六", 29, 3000, Student.Status.sleep));
          students.add(new Student("阿七", 34, 4000, Student.Status.study));
          students.add(new Student("六四", 40, 5000, Student.Status.rest));
          return students;
        }
     }
    
  • 在Java8中的解决方案:

    • 定义一个函数式接口
     @FunctionalInterface
     public interface StudentDaoFilter<T> {
        public boolean filter(T t);
     }
    
    • 实现函数式接口完成策略的实现
    //有了Java8的函数式接口后,就可以不用先实现个匿名内部类后,只需要关注于具体的策略实现
    public class MainSoluton {
        public static void main(String[] args) {
           StudentDao studentDao = new StudentDao();
           List<Student> students = studentDao.selectAll();
           //根据消费水平查
           List<Student> s1 = filterStudentDao(students, (student -> student.getConsumption() > 3000));
           System.out.println("----------");
           System.out.println(s1);
           //根据消费水平与学习状态查
           List<Student> s2 = filterStudentDao(students, (student -> student.getConsumption() > 3000 && student.getStatus().equals(Student.Status.study)));
           System.out.println("----------");
           s2.forEach(System.out::println);
           System.out.println("----------");
           //按照状态查
           List<Student> s3 = filterStudentDao(students, student -> student.getStatus().equals(Student.Status.rest));
           s3.forEach(System.out::println);
        }
    
        public static List<Student> filterStudentDao(List<Student> oldStudents, StudentDaoFilter<Student> studentDaoFilter) {
           ArrayList<Student> newStudents = new ArrayList<>();
           for (Student s : oldStudents) {
                 if (studentDaoFilter.filter(s)) {
                    newStudents.add(s);
                 }
           }
           return newStudents;
        }
     }
    

具体语法

  • 基本语法:

    /*
    * 一、Lambda 表达式的基础语法:Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符
    * 						    箭头操作符将 Lambda 表达式拆分成两部分:
    *
    * 左侧:Lambda 表达式的参数列表
    * 右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
    *
    * 语法格式一:无参数,无返回值
    * 		() -> System.out.println("Hello Lambda!");
    *
    * 语法格式二:有一个参数,并且无返回值
    * 		(x) -> System.out.println(x)
    *
    * 语法格式三:若只有一个参数,小括号可以省略不写
    * 		x -> System.out.println(x)
    *
    * 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
    *		Comparator<Integer> com = (x, y) -> {
    *			System.out.println("函数式接口");
    *			return Integer.compare(x, y);
    *		};
    *
    * 语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
    * 		Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    *
    * 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
    * 		(Integer x, Integer y) -> Integer.compare(x, y);
    *
    * 上联:左右遇一括号省
    * 下联:左侧推断类型省
    * 横批:能省则省
    *
      */
    public class MainTest1 {
       //()-> System.out.println("线程运行");
       @Test
       public void test1() {
          Runnable runnable = () -> System.out.println("线程运行");
          runnable.run();
       }
    
       //(x)->System.out.println("消费者已消费");
       @Test
       public void test2() {
          Consumer consumer = (x) -> System.out.println("消费者已消费" + x + "元");
          consumer.accept(500);
       }
       //(x,y)->x.comparetor(y);
       @Test
       public void test3(){
          Comparator<Integer> comparator=(x,y)-> x.compareTo(y);
          int compare = comparator.compare(3, 2);
          System.out.println(compare);
       }
       //需要写多条语句时
       @Test
       public void test4(){
          Comparator<Integer> comparator=(x,y)->{
                System.out.println(x+y);
                return x.compareTo(y);
          };
          System.out.println(comparator.compare(1,2));
       }
    } 
    
  • Lamda表达式对于函数式接口的支持以及内置的四大核心函数式接口(*)

    /**
     * Lamda表达式对于“函数式接口”的支持
       * 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 
       * 可以使用注解 @FunctionalInterface 修饰,该注解用于检查是否是函数式接口。
     * Java8 内置的四大核心函数式接口
       *
       * Consumer<T> : 消费型接口
       * 		void accept(T t);
       *
       * Supplier<T> : 供给型接口
       * 		T get();
       *
       * Function<T, R> : 函数型接口
       * 		R apply(T t);
       *
       * Predicate<T> : 断言型接口
       * 		boolean test(T t);
       *
       */
    
    public class MainTest2 {
       //Consumer<T>:消费型接口
       @Test
       public void test01(){
          Consumer consumer=(x)->System.out.println(x);
          consumer.accept("I am consumer!");
       }
       //Supplier<T>:生产者接口
       @Test
       public void test02(){
          Supplier supplier=()-> "I am Supplier";
          System.out.println(supplier.get());
       }
       //Function<T,R>:函数式型接口
       @Test
       public void test03(){
          Function<Integer,Integer> function=(x)->x;
          System.out.println(function.apply(1));
       }
       //Predicate<T>:断言接口
       @Test
       public void test4(){
          Predicate<Integer> predicate=(x)->x.equals(2);
          System.out.println(predicate.test(1));
       }
    }
    
  • Lamda表达式表现形式:方法,对象,构造器引用(可以理解为:对于lamda表达式的简写)

    /*
    * 一、方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用
    * 			  (可以将方法引用理解为 Lambda 表达式的另外一种表现形式)
    *
    * 1. 对象的引用 :: 实例方法名
    *
    * 2. 类名 :: 静态方法名
    *
    * 3. 类名 :: 实例方法名
    *
    * 注意:
    * 	 ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
    * 	 ②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,可以使用"类名::实例方法名"的语法格式: ClassName::MethodName
    *
    * 二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!
    * 1. 类名 :: new
    * 三、数组引用
    * 	类型[] :: new;
    *
    *
    */
    public class MainTest3 {
          //实例引用::实例方法名
          @Test
          public void test01(){
             //这句话相当于实现了一个Consumer的函数式接口
             Consumer consumer=System.out::println;
             //这里会打印"nihao"
             consumer.accept("nihao");
             Student student = new Student();
             //这里相当于实现了一个Supplier的函数式接口
             Supplier supplier=student::getAge;
             //这里打印学生的名字
             System.out.println(supplier.get());
          }
          //类名::静态方法名
          @Test
          public void test02(){
             //前两个泛型是形参,最后一个是返回值
             BiFunction<Double,Double,Double> biFunction= Math::max;
             //输出30.00
             System.out.println(biFunction.apply(1.12,30.00));
          }
          //类名::实例方法名
          @Test
          public void test03(){
             BiPredicate<String,String> biPredicate=String::equals;
             //这里输出true
             System.out.println(biPredicate.test("nihao","nihao"));
          }
          //构造器引用
          @Test
          public void test04(){
             //前一个是形参后一个是返回值
             Function<String,Student> function=Student::new;
             //这里会输出一个学生对象
             System.out.println(function.apply("zhangsan"));
             BiFunction<String,Integer,Student> f2=Student::new;
             System.out.println(f2.apply("wangwu",25));
          }
          //数组引用
          @Test
          public void test05(){
             Function<Integer,Student[]> function=Student[]::new;
             //这里创建了一个长度为10的学生对象数组。
             System.out.println(function.apply(10).length);
          }
       }
    

Stream流(非常重要!!)

下文使用的学生对象以及数据是上文所提到的

Stream API 的必须做的操作流程

  1. 创建Stream流对象
  2. 中间步骤
  3. 终止步骤
  • 这里需要特别注意:
    • 惰性求值现象:只有当做终止操作时,所有的中间操作才会一次性执行

创建Stream流对象与中间步骤

 public class MainTest01 {
    private List<Student> students = new StudentDao().selectAll();

    //创建流
    @Test
    public void test1() {
       /**
          * 一:
          * Collection接口提供了两种抽象方法:stream():创建顺序流,parallelStream():创建并行流
          * 之间的区别,这里可以简单的理解为,并行流使用了fork/join框架,并行流会在数据量大的时候处理的效率会更高
          */
       Stream<Student> studentStream = students.stream();//创建顺序流
       Stream<Student> parallelStream = students.parallelStream();//创建并行流
       //2:通过Arrays工具类的stream()流获得一个数组的顺序流
       Function<Integer, Integer[]> f1 = Integer[]::new;
       Stream<Integer> integerStream = Arrays.stream(f1.apply(10));
       //3:通过Stream类中的静态方法of()来获取流
       Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
       //4:创建无限流
       //迭代
       Stream<Integer> integerStream1 = Stream.iterate(0, x -> x + 2).limit(10);
       //limit()流的终止操作,返回一个该截取长度的流
       integerStream1.forEach(System.out::println);
       //生成
       Stream<Double> tStream = Stream.generate(Math::random).limit(2);
       tStream.forEach(System.out::println);
    }

    //中间步骤
    @Test
    public void test2() {
       /*
       筛选与切片
             filter——接收 lambda , 从流中排除某些元素。
             limit——截断流,使其元素不超过给定数量。
             skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
             distinct——筛选,通过流所生映射
       映射
             map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
             flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一元素的 hashcode() 和 equals() 去除重复元素
       排序
             sorted()——自然排序
          sorted(Comparator com)——定制排序
          */
       //filter():按指定规则过滤
       students.stream().filter(student -> student.getAge()>38).forEach(System.out::println);
       //limit(n):截取前n个元素
       System.out.println("---------------------------");
       students.stream().filter(student -> student.getAge()>38).limit(2).forEach(System.out::println);
       //skip(n):跳过前n个元素
       System.out.println("---------------------------");
       students.stream().filter(student -> student.getAge()>38).skip(2).forEach(System.out::println);
       //distinct():去除重复元素根据hashcode()与equals()方法
       System.out.println("---------------------------");
       students.stream().distinct().forEach(System.out::println);
       //映射map():为所有元素进行相同的操作
       System.out.println("---------------------------");
       students.stream().map(student -> student.getName()).forEach(System.out::println);
       //flatMap():若map的映射是将其中元素转换成流,那么flatmap可以将映射后形成的一个个小流,合并成一个大流
       //需求将每一个字符串集合的每一个字符串分开成单个字符,并且依次打印每一个字符
       List<String> strings = Arrays.asList("aaa", "bbb", "ccc", "eee");
       Stream<Stream<Character>> listStream = strings.stream().map(MainTest01::toChar);
       listStream.forEach(clist->clist.forEach(System.out::println));
       System.out.println("---------------------------");
       Stream<Character> characterStream = strings.stream().flatMap(MainTest01::toChar);
       characterStream.forEach(System.out::println);
       //自然排序sorted():对象所属的实体类实现了Comparable接口重写了compareTo()方法
       System.out.println("------------------------------");
       students.stream().sorted().forEach(System.out::println);
       //定制排序sorted():自己定制规则进行排序
       System.out.println("------------------------------");
       students.stream().sorted((s1,s2)->{
             return -s1.compareTo(s2);
       }).forEach(System.out::println);

    }
    //将字符串转换为字符数组的方法
    public static Stream<Character> toChar(String s){
       ArrayList<Character> characters = new ArrayList<>();
       for (Character c : s.toCharArray()) {
          characters.add(c);
       }
       return characters.stream();
    }
    //惰性求值现象:只有当做终止操作时,所有的中间操作才会一次性执行
    @Test
    public void test3(){
       Stream<Student> studentStream = students.stream().filter(student -> {
             System.out.println("过滤开始");
             return student.getAge() > 28;
       });
       System.out.println("结束流操作开始");
       studentStream.forEach(System.out::println);
    }
 }

终止步骤

//3. 终止操作
public class MainTest02 {
     private List<Student> students=new StudentDao().selectAll();
     /*
        allMatch——检查是否匹配所有元素
        anyMatch——检查是否至少匹配一个元素
        noneMatch——检查是否没有匹配的元素
        findFirst——返回第一个元素
        findAny——返回当前流中的任意元素
        count——返回流中元素的总个数
        max——返回流中最大值
        min——返回流中最小值
     */
     @Test
     public void test01(){
        //allMatch()
        boolean allMatch = students.stream().allMatch(student -> Student.Status.rest.equals(student.getStatus()));
        System.out.println(allMatch);
        //anyMatch()
        boolean anyMatch = students.stream().anyMatch(student -> student.getStatus().equals(Student.Status.study));
        System.out.println(anyMatch);
        //noneMatch()
        boolean noneMatch = students.stream().noneMatch(student -> student.getStatus().equals(Student.Status.study));
        System.out.println(noneMatch);
        //findFirst()返回第一个元素
        Optional<Student> first = students.stream().findFirst();
        System.out.println(first);
        //findAny()返回当前流中任意元素
        Optional<Student> any = students.stream().findAny();
        System.out.println(any);
        //max()返回流中最大值
        Optional<Double> max = students.stream().map(Student::getConsumption).max(Double::compareTo);
        System.out.println(max);
        //min()返回流中最小值
        Optional<Double> min = students.stream().map(Student::getConsumption).min(Double::compareTo);
        System.out.println(min);
        //count()返回流中元素的个数
        long count = students.stream().count();
        System.out.println(count);
     }
     /*
        归约
        reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
     */
     @Test
     public void test02(){
        //方式一:从指定的初始值开始归约
        Double sum = students.stream().map(Student::getConsumption).reduce(0.0, (x, y) -> x + y);
        System.out.println(sum);
        //方式二:不指定初始值的归约
        Optional<Double> sum2 = students.stream().map(Student::getConsumption).reduce(Double::sum);
        System.out.println(sum2);
        //map与reduce的结合往往可以用于大数据的处理上
        //需求:搜索学生中名字出现“六”的个数
        Optional<Integer> countLiu = students.stream().map(Student::getName).map(name -> {
              if (name.contains("六")) {
                 return 1;
              } else {
                 return 0;
              }
        }).reduce(Integer::sum);
        System.out.println(countLiu);
     }
     /*
        collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法,将流转换为各种集合
     */
     @Test
     public void test03(){
        //list集合
        List<String> nameList = students.stream().map(Student::getName).collect(Collectors.toList());
        System.out.println("list集合:"+nameList);
        //set集合
        Set<String> nameSet = students.stream().map(Student::getName).collect(Collectors.toSet());
        System.out.println("Set集合:"+nameSet);
        //转Set集合的第二种方式
        HashSet<String> nameHashSet = students.stream().map(Student::getName).collect(Collectors.toCollection(HashSet::new));
        System.out.println("HashSet集合:"+nameHashSet);
        //转Map集合:map的value为整个对象,注意当流中有重复元素的时候转map会抛出异常
        Map<String, Student> studentMap = students.stream().collect(Collectors.toMap(Student::getName, student -> student));
        studentMap.forEach((key,value)->{
              System.out.println("key="+key+":"+"map"+value);
        });
        //转Map集合:map的value为对象的单一属性
        Map<String, Double> studentMap1 = students.stream().collect(Collectors.toMap(Student::getName, Student::getConsumption));
        studentMap1.forEach((key,value)->{
              System.out.println("key="+key+":"+"map"+value);
        });
     }
     /*
        将流中的元素实现数据库操作中组函数功能
     */
     @Test
     public void test04(){
        //最大值
        Optional<Double> max = students.stream().map(Student::getConsumption).collect(Collectors.maxBy(Double::compareTo));
        System.out.println(max);
        //最小值
        Optional<Double> min = students.stream().map(Student::getConsumption).collect(Collectors.minBy(Double::compareTo));
        System.out.println(min);
        //求和
        Double sum = students.stream().collect(Collectors.summingDouble(Student::getConsumption));
        System.out.println(sum);
        //平均值
        Double averaging = students.stream().collect(Collectors.averagingDouble(Student::getConsumption));
        System.out.println(averaging);
        //计数
        Long count = students.stream().collect(Collectors.counting());
        System.out.println(count);
        //获得一个对该组数值进行操作的包装类,该包装类包含了组函数的操作
        DoubleSummaryStatistics doubleSummaryStatistics = students.stream().collect(Collectors.summarizingDouble(Student::getConsumption));
        System.out.println("-----------------");
        System.out.println(doubleSummaryStatistics.getMax());
        System.out.println(doubleSummaryStatistics.getAverage());
     }
     /*
        将流进行分组操作
     */
     @Test
     public void test5(){
        Map<Student.Status, List<Student>> statusListMap = students.stream().collect(Collectors.groupingBy(Student::getStatus));
        System.out.println(statusListMap);
     }
     /*
        将流进行多级分组
     */
     @Test
     public void test6(){
        Map<Student.Status, Map<String, List<Student>>> statusMapMap = students.stream().collect(Collectors.groupingBy(Student::getStatus
                 , Collectors.groupingBy((student -> {
                    if (student.getAge() < 20) {
                          return "青年";
                    } else if (student.getAge() < 30) {
                          return "中年";
                    } else {
                          return "老年";
                    }
                 }))));
        System.out.println(statusMapMap);
     }
     /*
     分区
     */
     @Test
     public void test7(){
        Map<Boolean, List<Student>> booleanMap = students.stream().collect(Collectors.partitioningBy(student -> student.getConsumption() > 2000));
        System.out.println(booleanMap);
     }
     /*
     Collections的拼接以及归约
     */
     @Test
     public void test8(){
        //拼接:Collections.joining()
        String joinName = students.stream().map(Student::getName).collect(Collectors.joining(",", "{", "}"));
        System.out.println(joinName);
        //归约:Collections.reducing()
        Optional<Double> reduce = students.stream().map(Student::getConsumption).collect(Collectors.reducing(Double::sum));
        System.out.println(reduce);
     }
  }

Optional API

/*
 * 一、Optional 容器类:用于尽量避免空指针异常
 * 	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
 */
public class TestOptional {
    @Test
    public void test01(){
        //Optional.of(T t)
        Optional<Student> op1 = Optional.of(new Student("xiaoming"));
        System.out.println(op1.get());
        //Optional.empty(),空Optional不能使用get方法NoSuchElementException异常,因为里面没有内置对象
        Optional<Object> empty = Optional.empty();
        System.out.println(empty);
        //Optional.ofNullable(T t)
        Optional<Object> op2 = Optional.ofNullable(null);
        System.out.println(op2);
        //isPresent()
        System.out.println(op2.isPresent()+"   and   "+empty.isPresent());
        //orElse(T t)
        System.out.println(op2.orElse(new Student("nihaoh")));
        //orElseGet(Supplier s)
        System.out.println(empty.orElseGet(()->{
            return new Student("woshi");
        }));
        //map(Function f)
        System.out.println(op1.map(Student::getName));
        //flatMap(Function mapper)
        System.out.println(op1.flatMap(student -> {
            return Optional.ofNullable(student.getName());
        }));
    }
}

提供了线程安全的时间类

关于JDK7时间类线程不安全的测试与解决方法

/**
 * 测试JDK1.7的时间类的线程安全,并在JDK1.7中解决
 */
public class TestSimpleDateFormat {
    //JDk1.7时间类的线程安全
    @Test
    public void test1() throws Exception {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy--MM--dd");
        Callable<Date> callable = new Callable() {
            @Override
            public Date call() throws Exception {
                return dateFormat.parse("2001--09--16");
            }
        };
        //System.out.println(callable.call());
        //创建一个线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        List<Future<Date>> result=new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            result.add(pool.submit(callable));
        }
        for (Future<Date> future :result) {
            System.out.println(future.get());
        }
        pool.shutdown();
        //报错:java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
    }
    //解决方案一
    @Test
    public void test02() throws ExecutionException, InterruptedException {
        //使用TreadLocal类包装要共享的变量SimpleDateFormat
        Callable<Date> callable = new Callable() {
            @Override
            public Date call() throws Exception {
                return SimpleDateFormatThreadLocal.convert("2001--09--16");
            }
        };
        //System.out.println(callable.call());
        //创建一个线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        List<Future<Date>> result=new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            result.add(pool.submit(callable));
        }
        for (Future<Date> future :result) {
            System.out.println(future.get());
        }
        pool.shutdown();
    }
    //解决方案二:使用Java8中的线程安全的时间类
    @Test
    public void test03() throws Exception{
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy--MM--dd");
        Callable<LocalDate> callable = new Callable() {
            @Override
            public LocalDate call() throws Exception {
                return LocalDate.parse("2001--09--16",dateTimeFormatter);
            }
        };
        //System.out.println(callable.call());
        //创建一个线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        List<Future<LocalDate>> result=new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            result.add(pool.submit(callable));
        }
        for (Future<LocalDate> future :result) {
            System.out.println(future.get());
        }
        pool.shutdown();
    }
}

/**
 * TreadLocal类包装共享变量SimpleDateFormat
 */
public class SimpleDateFormatThreadLocal{
    private static final ThreadLocal<DateFormat> local=new ThreadLocal<DateFormat>(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy--MM--dd");
        }
    };
    public static final Date convert(String srouce) throws ParseException {
        return local.get().parse(srouce);
    }
}

常用方法

//常用类与方法测试
public class mainTest1 {
    //LocalDate,LocalTime,LocalDateTime
    @Test
    public void test01(){
        //上述时间类都有一样的方法,分别代表的是:日期、时间、日期时间
        LocalDateTime now = LocalDateTime.now();//获取当前时间
        System.out.println(now);
        //获取指定日期的日期时间包装类
        LocalDateTime dateTime01 = LocalDateTime.of(2001, 9, 16, 0, 6);
        System.out.println(dateTime01);
        //对日期与时间的加减操作
        LocalDateTime dateTime02 = dateTime01.plusYears(3).minusYears(2);
        System.out.println(dateTime02);
        //获得日期时间包装类的具体某一项时间的值
        System.out.println(dateTime01.getDayOfMonth());
        System.out.println(dateTime01.getHour());
        System.out.println(dateTime01.getDayOfWeek());
    }
    //Instant : 时间戳。 (使用 Unix 元年  1970年1月1日 00:00:00 所经历的毫秒值)
    //默认使用的是UTC时区
    @Test
    public void test02(){
         Instant instant = Instant.now();
        System.out.println(instant);//运行的本机时间为16:45,打印时间为2021-11-16T08:44:10.596Z
        //ofHours(int hours)根据偏移时间,得到一个实例ZoneOffset。
        //中国的时区与UTC时区差了八个小时
        OffsetDateTime odt = instant.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);
        //得到一个纳秒数
        System.out.println(instant.getNano());//914000000
        //ofEpochSecond(long epochSecond)获得由1970-01-01t00:00:00z时代偏移Long秒的Instant实例。
        Instant instant1 = Instant.ofEpochSecond(5);
        System.out.println(instant1);//1970-01-01T00:00:05Z
    }
    //Duration : 用于计算两个“时间”间隔
	//Period : 用于计算两个“日期”间隔(注意)
	@Test
	public void test03() throws InterruptedException {
        Instant ins1 = Instant.now();
        Thread.sleep(1000);
        Instant ins2 = Instant.now();
        System.out.println(Duration.between(ins1,ins2));
        System.out.println("-------------------");
        //Period.between只能算月份之内的数据,比如计算2020-04-21距离2021-02-22间隔的天数
        // 那么用Period.between来计算就是只有一天。
        // 计算间隔天数需要改成
        // ChronoUnit.DAYS.between(timeLocal, LocalDate.now());
        LocalDate now = LocalDate.now();
        LocalDate data = LocalDate.of(2021, 9, 16);
        Period period = Period.between(data,now);
        System.out.println(period);
        System.out.println(period.getYears());
        System.out.println(period.getDays());
    }
    //时间校正器:TemporalAdjuster
    @Test
    public void test04(){
        LocalDate date1 = LocalDate.now();
        System.out.println(date1);
        //将date1中的日期改为当月10号
        LocalDate date2 = date1.withDayOfMonth(10);
        System.out.println(date2);
        //使用校正器将date1的日期改为下一周的星期日
        LocalDate date3 = date1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(date3);
        //自定义时间校正器,改为下个工作日
        LocalDate date5 = date1.with((date) -> {
            LocalDate date4 = (LocalDate) date;
            DayOfWeek dayOfWeek = date1.getDayOfWeek();
            if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
                return date4.plusDays(3);
            } else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
                return date4.plusDays(2);
            } else {
                return date4.plusDays(1);
            }
        });
        System.out.println(date5);
    }
    //DateTimeFormatter:解析和格式化日期或时间
    @Test
    public void test5(){
        //默认的解析格式是:DateTimeFormatter.ISO_LOCAL_DATE
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
		LocalDateTime ldt = LocalDateTime.now();
		//按照dtf的格式格式成字符串
		String strDate = ldt.format(dtf);
		System.out.println(strDate);
		//将字符串日期按照dtf指定的格式解析成系统日期
		LocalDateTime newLdt = ldt.parse(strDate, dtf);
		System.out.println(newLdt);
    }
    //时区:ZoneId
    @Test
    public void test06(){
        //查看所有的时区
        //Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        //availableZoneIds.forEach(System.out::println);
        //将当前时间设置为指定时区的时间
        LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(now);
        //带时区的时间
        ZonedDateTime now1 = ZonedDateTime.now(ZoneId.of("US/Pacific"));
        System.out.println(now1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值