精华内容
下载资源
问答
  • JDK1.8 新特性(全)

    万次阅读 多人点赞 2018-06-27 22:01:54
    JDK1.8 新特性 本文主要介绍了JDK1.8版本中的一些新特性,乃作者视频观后笔记,仅供参考。 jdk1.8新特性知识点: Lambda表达式 函数式接口 *方法引用和构造器调用 Stream API 接口中的默认方法和静态方法 新时间...

    JDK1.8 新特性

    本文主要介绍了JDK1.8版本中的一些新特性,乃作者视频观后笔记,仅供参考。

    jdk1.8新特性知识点:

    • Lambda表达式
    • 函数式接口
    • *方法引用和构造器调用
    • Stream API
    • 接口中的默认方法和静态方法
    • 新时间日期API

    在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算法(无锁算法,不再使用锁分段),数组+链表中也引入了红黑树的使用

    Lambda表达式

    lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码

    先来体验一下lambda最直观的优点:简洁代码

      //匿名内部类
      Comparator<Integer> cpt = new Comparator<Integer>() {
          @Override
          public int compare(Integer o1, Integer o2) {
              return Integer.compare(o1,o2);
          }
      };
    
      TreeSet<Integer> set = new TreeSet<>(cpt);
    
      System.out.println("=========================");
    
      //使用lambda表达式
      Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y);
      TreeSet<Integer> set2 = new TreeSet<>(cpt2);

    只需要一行代码,极大减少代码量!!

    这样一个场景,在商城浏览商品信息时,经常会有条件的进行筛选浏览,例如要选颜色为红色的、价格小于8000千的….

    // 筛选颜色为红色
    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;
     }
    
    // 筛选价格小于8千的
    public  List<Product> filterProductByPrice(List<Product> list){
        List<Product> prods = new ArrayList<>();
        for (Product product : list){
            if (product.getPrice() < 8000){
                prods.add(product);
            }
        }
        return prods;
     }

    我们发现实际上这些过滤方法的核心就只有if语句中的条件判断,其他均为模版代码,每次变更一下需求,都需要新增一个方法,然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?

    优化一:使用设计模式

    定义一个MyPredicate接口

    public interface MyPredicate <T> {
        boolean test(T t);
    }

    如果想要筛选颜色为红色的商品,定义一个颜色过滤类

    public class ColorPredicate implements MyPredicate <Product> {
    
         private static final String RED = "红色";
    
         @Override
         public boolean test(Product product) {
             return RED.equals(product.getColor());
         }

    定义过滤方法,将过滤接口当做参数传入,这样这个过滤方法就不用修改,在实际调用的时候将具体的实现类传入即可。

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

    例如,如果想要筛选价格小于8000的商品,那么新建一个价格过滤类既可

    public class PricePredicate implements MyPredicate<Product> {
        @Override
        public boolean test(Product product) {
            return product.getPrice() < 8000;
        }
    }

    这样实现的话可能有人会说,每次变更需求都需要新建一个实现类,感觉还是有点繁琐呀,那么再来优化一下

    优化二:使用匿名内部类

    定义过滤方法:

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

    调用过滤方法的时候:

    // 按价格过滤
    public void test2(){
        filterProductByPredicate(proList, new MyPredicate<Product>() {
            @Override
            public boolean test(Product product) {
                return product.getPrice() < 8000;
            }
        });
    }
    
     // 按颜色过滤
     public void test3(){
         filterProductByPredicate(proList, new MyPredicate<Product>() {
             @Override
             public boolean test(Product product) {
                 return "红色".equals(product.getColor());
             }
         });
     }

    使用匿名内部类,就不需要每次都新建一个实现类,直接在方法内部实现。看到匿名内部类,不禁想起了Lambda表达式。

    优化三:使用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表达式进行过滤

    @Test
    public void test4(){
          List<Product> products = filterProductByPredicate(proList, (p) -> p.getPrice() < 8000);
          for (Product pro : products){
              System.out.println(pro);
          }
      }

    在jdk1.8中还有更加简便的操作 Stream API

    优化四:使用Stream API

    甚至不用定义过滤方法,直接在集合上进行操作

    // 使用jdk1.8中的Stream API进行集合的操作
    @Test
    public void test(){
        // 根据价格过滤
        proList.stream()
               .fliter((p) -> p.getPrice() <8000)
               .limit(2)
               .forEach(System.out::println);
    
        // 根据颜色过滤
        proList.stream()
               .fliter((p) -> "红色".equals(p.getColor()))
               .forEach(System.out::println);
    
        // 遍历输出商品名称
        proList.stream()
               .map(Product::getName)
               .forEach(System.out::println);
    }

    Lmabda表达式的语法总结: () -> ();

    前置语法
    无参数无返回值() -> System.out.println(“Hello WOrld”)
    有一个参数无返回值(x) -> System.out.println(x)
    有且只有一个参数无返回值x -> System.out.println(x)
    有多个参数,有返回值,有多条lambda体语句(x,y) -> {System.out.println(“xxx”);return xxxx;};
    有多个参数,有返回值,只有一条lambda体语句(x,y) -> xxxx

    口诀:左右遇一省括号,左侧推断类型省

    注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念

    函数式接口

    函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。

    什么是函数式接口?
    简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@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 test2() {
    
            // 构造方法引用  类名::new
            Supplier<Employee> sup = () -> new Employee();
            System.out.println(sup.get());
            Supplier<Employee> sup2 = Employee::new;
            System.out.println(sup2.get());
    
            // 构造方法引用 类名::new (带一个参数)
            Function<Integer, Employee> fun = (x) -> new Employee(x);
            Function<Integer, Employee> fun2 = Employee::new;
            System.out.println(fun2.apply(100));
     }

    (c)数组引用

    格式: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<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);

    并行流和串行流

    在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的用法是直接用接口名去调方法即可。当一个类继承父类又实现接口时,若后两者方法名相同,则优先继承父类中的同名方法,即“类优先”,如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法。

    新的日期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(){
            // 时间戳  197011000000 到某一个时间点的毫秒值
            // 默认获取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);
        }

    补充:

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

    新的日期API的几个优点:

     * 之前使用的java.util.Date月份从0开始,我们一般会+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum
     * java.util.Date和SimpleDateFormat都不是线程安全的,而LocalDate和LocalTime和最基本的String一样,是不变类型,不但线程安全,而且不能修改。
     * java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数,更加明确需求取舍
     * 新接口更好用的原因是考虑到了日期时间的操作,经常发生往前推或往后推几天的情况。用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"))));
        }
    展开全文
  • java14新特性

    万次阅读 2020-09-12 02:58:34
    新特性一、简化instanceof //以前的写法 public class Feature01 { @Test public void test1(){ Object obj = new String("hello,Java14"); obj = null;//在使用null 匹配instanceof 时,返回都是false. if...

    新特性一、简化instanceof

    //以前的写法
    public class Feature01 {
        @Test
        public void test1(){
    
            Object obj = new String("hello,Java14");
            obj = null;//在使用null 匹配instanceof 时,返回都是false.
            if(obj instanceof String){
                String str = (String) obj;
                System.out.println(str.contains("Java"));
            }else{
                System.out.println("非String类型");
            }
    
            //举例1:新特性的写法一
            if(obj instanceof String str){ //新特性:省去了强制类型转换的过程
                System.out.println(str.contains("Java"));
            }else{
                System.out.println("非String类型");
            }
        }
    }
    
    
    class InstanceOf{
    
        String str = "abc";
    
        public void test(Object obj){
    		// 新特性的写法二
            if(obj instanceof String str){//此时的str的作用域仅限于if结构内。
                System.out.println(str.toUpperCase());
            }else{
                System.out.println(str.toLowerCase());
            }
    
        }
    
    }
    
    //举例3:
    class Monitor{
        private String model;
        private double price;
    
    //    public boolean equals(Object o){
    //        if(o instanceof Monitor other){
    //            if(model.equals(other.model) && price == other.price){
    //                return true;
    //            }
    //        }
    //        return false;
    //    }
    
    
        public boolean equals(Object o){
            return o instanceof Monitor other && model.equals(other.model) && price == other.price;
        }
    
    }
    

    新特性二、改进NullPointerException

    jdk14中这个新特性能够告诉你到底是那个方法导致空指针异常,而再jdk14以前则只会告诉你哪一行代码报空指针异常。如果遇到链式调用的代码则不能确定到底是那个环节导致了异常

    再程序运行的时候需要添加一行参数:-XX:+ShowCodeDetailsInExceptionMessages
    可能后期版本中就会将其作为默认参数,但再java14中需要添加这个参数才能使用这个新特性

    新特性三、Rocord (预览特性、后期版本一定会被确定下来)

    record的出现是为了解决java啰嗦的用来作为数据的承载类(entity层),例如get、set、toString等方法
    如下定义代码

    public record User(String name,Integer age) {
    }
    

    编译后就是

    public final class User extends java.lang.Record {
        private final java.lang.String name;
        private final java.lang.Integer age;
    
        public User(java.lang.String name, java.lang.Integer age) { /* compiled code */ }
    
        public java.lang.String toString() { /* compiled code */ }
    
        public final int hashCode() { /* compiled code */ }
    
        public final boolean equals(java.lang.Object o) { /* compiled code */ }
    
        public java.lang.String name() { /* compiled code */ }
    
        public java.lang.Integer age() { /* compiled code */ }
    }
    

    成员属性是final的只有get方法,与以前的不同采用变量名作为get方法的方法名
    如下示例代码

    public static void main(String[] args) throws IOException {
        User user = new User("张三", 14);
        System.out.println(user.name()); //相当于getName获取到了name字段
        System.out.println(user.age());//相当于getAge获取到了age字段
        System.out.println(user);//打印输出: User[name=张三, age=14]
    }
    

    在这里插入图片描述

    record注意点

    record可以定义静态的属性、静态的方法、构造方法、实例方法

    1. 如果声明非静态的属性则会报语法错误
    public record Person(String name,Person partner) {
        //不可以声明非静态的属性
        private int id;//报错
    }
    
    1. 不可以将record定义的类声明为abstract的
      下面的定义不符合语法
    abstract record Order(){ //报错
    
    }
    
    1. 不可以给record定义的类声明显式的父类(非Record类)下面的定义不符合语法
    record Order() extends Thread{ //报错
    
    }
    

    新特性四、弃用ParallelScavenge (Young区的GC)与

    SerialOld GC (Old区的GC)组合作为GC

    这个组合的使用场景是拥有一个很大的Young区和一个很小的Old区

    废弃了parallel young generation GC与SerialOld GC的组合( -XX:+UseParallelGC与XX:-UseParallelOldGC配合开启),现在使用-XX:+UseParallelGC -XX:UseParallelOldGC或者-XX:-UseParallelOldGC都会出现告警如下

    Java HotSpot(TM) 64-Bit Server VM warning: Option UseParallelOldGC was deprecated in version 14.0 and will likely be removed in a future release.

    新特性五、直接删除CMS垃圾回收器,在Jdk9时就已经标记为废除

    CMS的弊端 :

    1. 会产生内存碎片,导致并发清除后,用户线程可用的空间不足。
    2. 既然强调了并发(Concurrent),CMS收集器对CPU资源非常敏感
    3. CMS 收集器无法处理浮动垃圾(一边回收、另一边产生垃圾称为浮动垃圾)

    新特性六、ZGC(未来的垃圾回收器,STW在10ms以内)

    使用方式

    -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
    
    展开全文
  • MYSQL新特性secure_file_priv 读写文件

    万次阅读 多人点赞 2019-09-03 11:38:55
    1290 – The MySQL server is running with the –secure-file-priv option so it cannot ...secure-file-priv特性 secure-file-priv参数是用来限制LOAD DATA, SELECT … OUTFILE, and LOAD_FILE()传到哪个指定...

    1290 – The MySQL server is running with the –secure-file-priv option so it cannot execute this statement

    secure-file-priv特性
    secure-file-priv参数是用来限制LOAD DATA, SELECT … OUTFILE, and LOAD_FILE()传到哪个指定目录的。

    • ure_file_priv的值为null ,表示限制mysqld 不允许导入|导出
    • 当secure_file_priv的值为/tmp/ ,表示限制mysqld 的导入|导出只能发生在/tmp/目录下
    • 当secure_file_priv的值没有具体值时,表示不对mysqld 的导入|导出做限制

    如何查看secure-file-priv参数的值:

    show global variables like '%secure%';

    没有到处权限。

    mysql> show global variables like '%secure%';
    +------------------+-------+
    | Variable_name    | Value |
    +------------------+-------+
    | secure_auth      | OFF   |
    | secure_file_priv |       |
    +------------------+-------+
    2 rows in set (0.00 sec)
     

    有导出权限,导出路径需在/var/lib/mysql-files/下

    mysql> show global variables like '%secure%';
    +--------------------------+-----------------------+
    | Variable_name            | Value                 |
    +--------------------------+-----------------------+
    | require_secure_transport | OFF                   |
    | secure_auth              | ON                    |
    | secure_file_priv         | /var/lib/mysql-files/ |
    +--------------------------+-----------------------+
    3 rows in set (0.09 sec)

    MYSQL新特性secure_file_priv对读写文件的影响
    此开关默认为NULL,即不允许导入导出。

    解决问题:
    windows下:

    修改my.ini 在[mysqld]内加入secure_file_priv=

    linux下:

    修改my.cnf 在[mysqld]内加入secure_file_priv=
    MYSQL新特性secure_file_priv对读写文件的影响
    然后重启mysql,再查询secure_file_priv

    展开全文
  • 使用ES6新特性async await进行异步处理

    万次阅读 多人点赞 2018-07-12 15:56:30
    我们往往在项目中会遇到这样的业务需求,就是首先...那是相当恶心的,下面我就来讲一下如何使用ES6的新特性async await进行异步处理,使上述情况就好像写同步代码一样,首先我们先举个例子: 先写上json文件: cod...

    我们往往在项目中会遇到这样的业务需求,就是首先先进行一个ajax请求,然后再进行下一个ajax请求,而下一个请求需要使用上一个请求得到的数据,请求少了还好说,如果多了,就要一层一层的嵌套,就好像有点callback的写法了,那是相当恶心的,下面我就来讲一下如何使用ES6的新特性async await进行异步处理,使上述情况就好像写同步代码一样,首先我们先举个例子:
    先写上json文件:
    code.json:

    {
        "code":0,
        "msg":"成功"
    }
    

    person.json:

    {
        "code":0,
        "list":[
            {
                "id":1,
                "name":"唐僧"
            },
            {
                "id":2,
                "name":"孙悟空"
            },
            {
                "id":3,
                "name":"猪八戒"
            },
            {
                "id":4,
                "name":"沙僧"
            }
        ]
    }
    

    比如我们有两个请求,如下,这里用的axios:

     function getCode(){
          return axios.get('json/code.json');
      }
     function getlist(params){
          return axios.get('json/person.json',{params})
      }
    

    我们第二个请求获取列表的时候需要使用第一个请求得到的code值,只有code值为0时,才能请求,而且当做参数传进去,那么我们看一下常规的做法吧

    function getFinal(){
    	  console.log("我是getFinal函数")
          getCode().then(function(res){
             if(res.data.code == 0){
                   console.log(res.data.code);
                     var params = {
                          code:res.data.code
                      }
                   getlist(params).then(function(res){
                        if(res.data.code == 0){
                             console.log(res.data.list);
                           }
                       })
                    }
              })
          }
      getFinal();
    

    看结果
    这里写图片描述
    虽然结果出来了,可是这种写法真的挺难受的,下面来一个async await的写法

    async function getResult(){
                console.log("我是getResult函数")
                let code = await getCode();
                console.log(code.data.code);
                if(code.data.code == 0){
                    var params = {
                        code:code.data.code
                    }
                    let list = await getlist(params);
                    console.log(list.data.list);
                }
            }
    getResult();
    

    下面看结果
    这里写图片描述
    当然还剩最后一点,处理异常,可以加上try catch

    async function getResult(){
                console.log("我是getResult函数")
                try{
    				let code = await getCode();
    	            console.log(code.data.code);
    	            if(code.data.code == 0){
    	                var params = {
    	                    code:code.data.code
    	                }
    	                let list = await getlist(params);
    	                console.log(list.data.list);
    	            }
    			}catch(err){
    				console.log(err);
    			}
            }
    getResult();
    

    个人认为做vue项目的时候,如果对于异常没有特殊处理,可以不加try catch,因为打印出来的错误跟vue自己报的错误是一样的,而且还是黑的字体,不如醒目的红色来的痛快啊!当然如果要对错误进行特殊处理,那么就加上吧

    代码风格是不是简便了许多,而且异步代码变成了同步代码,下面我稍微讲一下后者写法的代码执行顺序

    首先在 function 前面加 async 用来说明这个函数是一个异步函数,当然,async是要和await配合使用的,第一个请求

    let code = await getCode();
    

    await 意思是等一下,等着getCode()这个函数执行完毕,得到值后再赋值给code,然后再用code的值进行下一步操作,就是这么简单!!!赶紧去改代码!!

    展开全文
  • Redis 6.0 新特性

    万次阅读 2020-05-09 15:22:44
    Redis 6.0.0 稳定版(GA)终于发布,这个版本提供了诸多令人心动的新特性及功能改进,比如新网络协议RESP3,新的集群代理,ACL等,其中关注度最高的应该是“多线程”了,带着众多疑问,我们来一起开始“Redis 6.0 ...
  • Java 13 来袭,最新最全新特性解读

    万次阅读 多人点赞 2019-09-16 09:40:51
    2017年8月,JCP执行委员会提出将Java的发布频率改为每六个月一次,的发布周期严格遵循时间点,将在每年的3月份和9月份发布。 目前,JDK官网上已经可以看到JDK 13的进展,最新版的JDK 13将于2019年9月17日发布。 ...
  • Hadoop新特性

    千次阅读 2020-08-27 17:24:56
    1、2.x新特性 1.1 集群间数据拷贝 1.2 小文件存档 1.3 回收站 2、 3.x新特性 2.1 多NN的HA架构 2.2 纠删码 1、2.x新特性 1.1 集群间数据拷贝 1)scp实现两个远程主机之间的文件复制 scp -r hello.txt ...
  • JDK9新特性

    万人学习 2017-09-24 18:25:31
    跳票一年,jdk9终于在2017年9月21日发布,那么他有哪些新的特性呢? 1)jdk9的介绍 2)jdk9的新特性的整体罗列 3)jdk的新特性一一展示 ...... 带你一起领略jdk9的划时代之美,我们的目标是发现更好的自己。
  • Android 10正式版发布,看看都有哪些新特性

    万次阅读 多人点赞 2019-09-04 11:04:23
    根据Android官网的介绍,Android 10.0将聚焦于隐私可控、手机自定义与使用效率,此版本主要带来了十大新特性。 创新与新体验 可折叠 基于强大的多窗口支持,Android 10扩展了跨应用程序窗口的多任务处理,并在设...
  • jdk新特性

    千次阅读 2019-07-02 18:22:10
    jdk8新特性: java8新特性(1)— lambda表达式 java8新特性(2)— 方法引用 java8新特性(3)— 函数式接口 java8新特性(4)— Stream流 java8新特性(5)— Optional 类 java8新特性(6)— 日期与时间 java8...
  • 360新版特性界面

    千次下载 热门讨论 2012-05-22 23:01:21
    360新版特性界面 通过Qt,模仿360新版特性界面的实现,具备移动,缓慢效果,等等简单的功能。
  • Laravel5.5新特性

    千人学习 2017-08-03 16:49:54
    Laravel5.5新特性系列,带你了解Laravel5.5的新功能以及功能改进,Laravel越来越强大,同时也越来越发杂。希望掌握使用Laravel的童鞋尽快学习。
  • H5新特性和C3新特性

    千次阅读 2019-04-20 23:51:32
    1.H5新特性 拖拽释放(Drap and drop) API ondrop 拖放是一种常见的特性,即抓取对象以后拖到另一个位置。 在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放。 自定义属性data-id 语义化更好的内容标签...
  • 序言 关于JDK11,自从去年也就是2018年9月份 Oracle 对外公布——JDK11正式发布以来,博主就跃跃欲试,但是由于项目周期时间紧,... 新特性概览 -> 详细文档说明 -> 官方 API -> 实践与应用(除了体验新...
  • PostgreSQL 11 新特性

    万次阅读 2018-09-19 15:43:25
    PostgreSQL 全球开发组预计 2018 年底发布最新的 PosgtreSQL Release 11。在官方的发行说明中,介绍了该版本带来的新特性和增强功能。
  • JDK1.8新特性(一):JDK1.8究竟有哪些新特性

    万次阅读 多人点赞 2019-12-22 13:43:34
    如果能熟练掌握使用这些差异、新特性,你会发现另一片天地。今天抽时间整理下,JDK1.8新特性究竟有哪些?并以【JDK1.8新特性】专题逐一展开讨论、学习。 (JDK1.8新特性常常在面试中被问及的频率很高哦) JDK1.8概述...
  • Vue3.0新特性全面解读

    千人学习 2020-06-08 20:52:41
    本课程将会带领大家熟悉vue3.0新特性,并学会使用组合式api进行项目代码编写,以及了解这里面的一些注意点与思想方法。 可能有的同学会疑惑:是不是在vue3.0版本下,2.x的代码没法运行了 其实在vue3.0下,2.x的代码...
  • JAVA的新特性

    万次阅读 多人点赞 2018-11-15 10:14:17
    JDK1.8的新特性 匿名类 接口新特性 Lambda(λ)表达式; 数据流:深入到JDK源码中写代码;Spark增强版的数据流 时间的新特性 1.匿名类 多线程:Runnable接口 1 先写一个类,实现runnable接口,重写run方法 2 在main方法中...
  • 【小家java】java5新特性(简述十大新特性) 重要一跃 【小家java】java6新特性(简述十大新特性) 鸡肋升级 【小家java】java7新特性(简述八大新特性) 不温不火 【小家java】java8新特性(简述十大新特性) 饱受...
  • 360新版特性界面源代码

    千次下载 热门讨论 2012-06-02 21:55:35
    360新版特性界面源代码 实现了360新版特性界面的效果,主要涉及到Qt的一些事件处理与自定义控件。
  • /** * 枚举 */ enum Fruit { PEAR, APPLE, GRAPE, MANGO, ORANGE, PAPAYA; } public class SwitchTest { public static void main(String[] args) { int numberOfLetters;... Fruit fruit = Fruit.APPLE;...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,628,851
精华内容 651,540
关键字:

新特性