精华内容
参与话题
问答
  • 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
    
    展开全文
  • 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"))));
        }
    展开全文
  • Java 13 来袭,最新最全新特性解读

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

    2017年8月,JCP执行委员会提出将Java的发布频率改为每六个月一次,新的发布周期严格遵循时间点,将在每年的3月份和9月份发布。

    目前,JDK官网上已经可以看到JDK 13的进展,最新版的JDK 13将于2019年9月17日发布。

    目前,JDK13处于Release-Candidate Phase(发布候选阶段),将于9月17日正式发布。目前该版本包含的特性已经全部固定,主要包含以下五个:

    JEP 350,Dynamic CDS Archives

    JEP 351,ZGC: Uncommit Unused Memory

    JEP 353,Reimplement the Legacy Socket API

    JEP 354: Switch Expressions (Preview)

    JEP 355,Text Blocks (Preview)

    下面来逐一介绍下这五个重要的特性。

    Dynamic CDS Archives

    这一特性是在JEP310:Application Class-Data Sharing基础上扩展而来的,Dynamic CDS Archives中的CDS指的就是Class-Data Sharing。

    那么,这个JEP310是个啥东西呢?

    我们知道在同一个物理机/虚拟机上启动多个JVM时,如果每个虚拟机都单独装载自己需要的所有类,启动成本和内存占用是比较高的。所以Java团队引入了CDS的概念,通过把一些核心类在每个JVM间共享,每个JVM只需要装载自己的应用类,启动时间减少了,另外核心类是共享的,所以JVM的内存占用也减少了。

    CDS 只能作用于 Boot Class Loader 加载的类,不能作用于 App Class Loader 或者自定义的 Class Loader 加载的类。

    在 Java 10 中,则将 CDS 扩展为 AppCDS,顾名思义,AppCDS 不止能够作用于 Boot Class Loader了,App Class Loader 和自定义的 Class Loader 也都能够起作用,大大加大了 CDS 的适用范围。也就说开发自定义的类也可以装载给多个JVM共享了。

    Java 10中包含的JEP310的通过跨不同Java进程共享公共类元数据来减少了内存占用和改进了启动时间。

    但是,JEP310中,使用AppCDS的过程还是比较复杂的,需要有三个步骤:

    1、决定要 Dump 哪些 Class
    2、将类的内存 Dump 到归档文件中
    3、使用 Dump 出来的归档文件加快应用启动速度
    

    这一次的JDK 13中的JEP 350 ,在JEP310的基础上,又做了一些扩展。允许在Java应用程序执行结束时动态归档类,归档类将包括默认的基础层 CDS(class data-sharing)存档中不存在的所有已加载的应用程序类和库类。

    也就是说,在Java 13中再使用AppCDS的时候,就不在需要这么复杂了。

    ZGC: Uncommit Unused Memory

    在讨论这个问题之前,想先问一个问题,JVM的GC释放的内存会还给操作系统吗?

    GC后的内存如何处置,其实是取决于不同的垃圾回收器的。因为把内存还给OS,意味着要调整JVM的堆大小,这个过程是比较耗费资源的。

    在JDK 11中,Java引入了ZGC,这是一款可伸缩的低延迟垃圾收集器,但是当时只是实验性的。并且,ZGC释放的内存是不会还给操作系统的。

    而在Java 13中,JEP 351再次对ZGC做了增强,本次 ZGC 可以将未使用的堆内存返回给操作系统。之所以引入这个特性,是因为如今有很多场景中内存是比较昂贵的资源,在以下情况中,将内存还给操作系统还是很有必要的:

    • 1、那些需要根据使用量付费的容器
    • 2、应用程序可能长时间处于空闲状态并与许多其他应用程序共享或竞争资源的环境。
    • 3、应用程序在执行期间可能有非常不同的堆空间需求。例如,启动期间所需的堆可能大于稍后在稳定状态执行期间所需的堆。

    Reimplement the Legacy Socket API

    使用易于维护和调试的更简单、更现代的实现替换 java.net.Socket 和 java.net.ServerSocket API。

    java.net.Socket和java.net.ServerSocket的实现非常古老,这个JEP为它们引入了一个现代的实现。现代实现是Java 13中的默认实现,但是旧的实现还没有删除,可以通过设置系统属性jdk.net.usePlainSocketImpl来使用它们。

    运行一个实例化Socket和ServerSocket的类将显示这个调试输出。这是默认的(新的):

    java -XX: TraceClassLoading JEP353  | grep Socket
    [0.033s][info   ][class,load] java.net.Socket source: jrt:/java.base
    [0.035s][info   ][class,load] java.net.SocketOptions source: jrt:/java.base
    [0.035s][info   ][class,load] java.net.SocketImpl source: jrt:/java.base
    [0.039s][info   ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
    [0.042s][info   ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
    [0.042s][info   ][class,load] sun.nio.ch.NioSocketImpl source: jrt:/java.base
    [0.043s][info   ][class,load] sun.nio.ch.SocketDispatcher source: jrt:/java.base
    [0.044s][info   ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
    [0.044s][info   ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
    [0.044s][info   ][class,load] java.net.ServerSocket source: jrt:/java.base
    [0.045s][info   ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
    [0.045s][info   ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
    

    上面输出的sun.nio.ch.NioSocketImpl就是新提供的实现。

    如果使用旧的实现也是可以的(指定参数jdk.net.usePlainSocketImpl):

    $ java -Djdk.net.usePlainSocketImpl -XX: TraceClassLoading JEP353  | grep Socket
    [0.037s][info   ][class,load] java.net.Socket source: jrt:/java.base
    [0.039s][info   ][class,load] java.net.SocketOptions source: jrt:/java.base
    [0.039s][info   ][class,load] java.net.SocketImpl source: jrt:/java.base
    [0.043s][info   ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
    [0.046s][info   ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
    [0.047s][info   ][class,load] java.net.AbstractPlainSocketImpl source: jrt:/java.base
    [0.047s][info   ][class,load] java.net.PlainSocketImpl source: jrt:/java.base
    [0.047s][info   ][class,load] java.net.AbstractPlainSocketImpl$1 source: jrt:/java.base
    [0.047s][info   ][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net
    [0.047s][info   ][class,load] java.net.SocketOption source: jrt:/java.base
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net
    [0.047s][info   ][class,load] jdk.net.SocketFlow source: jrt:/jdk.net
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net
    [0.047s][info   ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net
    [0.048s][info   ][class,load] jdk.net.LinuxSocketOptions source: jrt:/jdk.net
    [0.048s][info   ][class,load] jdk.net.LinuxSocketOptions$$Lambda$2/0x0000000800b51040 source: jdk.net.LinuxSocketOptions
    [0.049s][info   ][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net
    [0.049s][info   ][class,load] java.net.StandardSocketOptions source: jrt:/java.base
    [0.049s][info   ][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base
    [0.051s][info   ][class,load] sun.net.ext.ExtendedSocketOptions$$Lambda$3/0x0000000800b51440 source: sun.net.ext.ExtendedSocketOptions
    [0.057s][info   ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
    [0.057s][info   ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
    [0.058s][info   ][class,load] java.net.ServerSocket source: jrt:/java.base
    [0.058s][info   ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
    [0.058s][info   ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
    

    上面的结果中,旧的实现java.net.PlainSocketImpl被用到了。

    Switch Expressions (Preview)

    在JDK 12中引入了Switch表达式作为预览特性。JEP 354修改了这个特性,它引入了yield语句,用于返回值。这意味着,switch表达式(返回值)应该使用yield, switch语句(不返回值)应该使用break。

    在以前,我们想要在switch中返回内容,还是比较麻烦的,一般语法如下:

    int i;
    switch (x) {
        case "1":
            i=1;
            break;
        case "2":
            i=2;
            break;
        default:
            i = x.length();
            break;
    }
    

    在JDK13中使用以下语法:

    int i = switch (x) {
        case "1" -> 1;
        case "2" -> 2;
        default -> {
            int len = args[1].length();
            yield len;
        }
    };
    

    或者

    int i = switch (x) {
        case "1": yield 1;
        case "2": yield 2;
        default: {
            int len = args[1].length();
            yield len;
        }
    };
    

    在这之后,switch中就多了一个关键字用于跳出switch块了,那就是yield,他用于返回一个值。和return的区别在于:return会直接跳出当前循环或者方法,而yield只会跳出当前switch块。

    Text Blocks (Preview)

    在JDK 12中引入了Raw String Literals特性,但在发布之前就放弃了。这个JEP在引入多行字符串文字(text block)在意义上是类似的。

    text block,文本块,是一个多行字符串文字,它避免了对大多数转义序列的需要,以可预测的方式自动格式化字符串,并在需要时让开发人员控制格式。

    我们以前从外部copy一段文本串到Java中,会被自动转义,如有一段以下字符串:

     <html>
      <body>
          <p>Hello, world</p>
      </body>
    </html>
    

    将其复制到Java的字符串中,会展示成以下内容:

    "<html>\n"  
    "    <body>\n"  
    "        <p>Hello, world</p>\n"  
    "    </body>\n"  
    "</html>\n";
    

    即被自动进行了转义,这样的字符串看起来不是很直观,在JDK 13中,就可以使用以下语法了:

    """
    <html>
      <body>
          <p>Hello, world</p>
      </body>
    </html>
    """;
    

    使用“”“作为文本块的开始符合结束符,在其中就可以放置多行的字符串,不需要进行任何转义。看起来就十分清爽了。

    如常见的SQL语句:

    String query = """
        SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
        WHERE `CITY` = 'INDIANAPOLIS'
        ORDER BY `EMP_ID`, `LAST_NAME`;
    """;
    

    看起来就比较直观,清爽了。

    总结

    以上,就是JDK13中包含的5个特性,能够改变开发者的编码风格的主要有Text Blocks和Switch Expressions两个新特性,但是这两个特性还处于预览阶段。

    而且,JDK13并不是LTS(长期支持)版本,如果你正在使用Java 8(LTS)或者Java 11(LTS),暂时可以不必升级到Java 13.

    参考资料:
    https://openjdk.java.net/projects/jdk/13/
    https://metebalci.com/blog/what-is-new-in-java-13/
    https://www.jianshu.com/p/890196bf529a

    在这里插入图片描述

    展开全文
  • Android 10正式版发布,看看都有哪些新特性

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

    谷歌在今年3月推出了Android 10.0的首个测试版,昨天,Android 10.0的正式版正式向外发布,而最先尝到新版本的自然是亲儿子Pixel手机。

    新特性解读

    根据Android官网的介绍,Android 10.0将聚焦于隐私可控、手机自定义与使用效率,此版本主要带来了十大新特性。
    在这里插入图片描述

    创新与新体验

    可折叠

    基于强大的多窗口支持,Android 10扩展了跨应用程序窗口的多任务处理,并在设备折叠或展开时提供屏幕连续性来维护应用程序状态。有关如何优化可折叠应用程序的详细信息,请参阅开发人员指南

    5G 网络

    Android 10承诺提供持续更快的速度和更低的延迟,并增加了对5G的平台支持,并扩展了现有api,以帮助您利用这些增强。您可以使用连接性api来检测设备是否具有高带宽连接,并检查连接是否已计量。有了这些,你的应用程序和游戏可以为5G以上的用户定制丰富的身临其境的体验。

    Live Caption

    此功能将自动向视频、播客和音频消息添加说明文字。这些说明是实时性和系统性,因此它们不限于特定的应用程序。Live Caption 文本框可以调整大小并在屏幕周围移动。Live Caption 不仅对那些发现自己处于音频无法选择的情况下的用户很有帮助,而且对听力障碍者来说也非常有益。

    具体参考 https://youtu.be/YL-8Xfx6S5o

    智能回复通知

    使用机器学习来预测你在回复信息时可能会说些什么,这项功能在 Android P 中已经有提供,但仅限于谷歌专用的应用程序。Android 10 中,它已经内置到整个通知系统中,并且不仅提供对信息的回复建议,还可以获得建议的操作。比如,如果朋友要你出去吃饭,你的手机会建议你发送回应短信,并且它还会在 Google 地图中直接显示位置信息。此功能也适用于 Signal 等消息应用。
    在这里插入图片描述

    暗黑主题

    Android Q 引入的另一项新功能是新系统暗黑主题,它适用于 Android 系统 UI 和 Android 设备上运行的应用。暗黑主题为开发人员带来许多好处,比如能够降低功耗、对于低视力和对强光敏感的用户来说可以提高屏幕内容可见度。
    在这里插入图片描述

    手势导航

    引入手势导航后,应用程序不仅可以实现全屏幕的内容显示,还能最大限度地减少系统导航键的可见程度,这对于当下主流的全面屏手机尤为重要。
    在这里插入图片描述

    用户可以从左下角或右下角斜向滑动以启动 Google Assistant 助手,在触发的角落会有相应的 “小手柄” 指示器来作为视觉提示。

    另外,团队还为包含导航抽屉(Navigation Drawer)的应用增加了一个名为 peek 的动作:用户轻压屏幕边缘然后再向内划动,便可拉出应用抽屉页面。所有版本的 DrawerLayout 均提供 peek 支持,其中以 DrawerLayout 1.1.0-alpha02 的体验最优。

    在这里插入图片描述

    隐私与安全

    隐私是Android 10的核心关注点,从平台中更强的保护到考虑隐私的新功能。在之前版本的基础上,Android 10在保护隐私和给用户控制权方面做了广泛的改变,改进了系统UI,更严格的权限,并限制了数据应用程序的使用。有关如何在应用程序中支持这些功能的详细信息,请参阅Android 10开发人员站点

    位置数据控制

    用户可以通过一个新的权限选项更好地控制他们的位置数据——他们现在可以允许一个应用程序只在应用程序实际使用时(在前台运行)访问位置。对于大多数应用程序来说,这提供了足够的访问权限,而对于用户来说,这在透明度和控制方面是一个很大的改进。要了解更多关于位置更改的信息,请参阅开发人员指南我们的博客文章

    保护网络位置数据

    大多数用于扫描网络的api已经需要粗定位权限。Android 10增加了对这些api的保护,转而要求良好的位置权限,具体参考Android 10 保护网络位置数据

    防止设备跟踪

    应用程序不能再访问可用于跟踪的不可重置设备标识符,包括设备IMEI、序列号和类似标识符。默认情况下,当连接到Wi-Fi网络时,设备的MAC地址也是随机的。阅读最佳实践,以帮助您为用例选择正确的标识符,并在这里查看详细信息。

    保护外部存储数据

    Android 10引入了一些变化,让用户可以更好地控制外部存储中的文件和应用程序数据。应用程序可以将自己的文件存储在私有沙箱中,但必须使用MediaStore访问共享的媒体文件,并使用系统文件选择器访问新下载集合中的共享文件。点击链接了解更多

    阻止不必要的干扰

    Android 10防止应用程序从后台启动,而后台的应用程序会出人意料地跳到前台,并从另一个应用程序转移注意力。

    安全

    在Android上,我们一直在评估系统的安全性。通过和第三方的手机安全平台合作,我们分析师研究如Gartner的2019年5月手机操作系统和设备的安全,一个比较的Android平台报告(需要订阅),得分最高的评级在26个30类别,提前在多个点从网络安全身份验证和恶意软件保护。
    在Android 10中,我们引入了更多的特性,通过在加密、平台强化和身份验证方面的改进来确保用户的安全。

    存储加密

    所有兼容Android 10的设备都需要加密用户数据,为了提高安全效率,Android 10使用了我们的新加密模式Adiantum

    TLS 1.3

    Android 10默认情况下支持TLS 1.3,这是对TLS标准的一个重大修订,具有性能优势和增强的安全性。

    平台硬化

    Android 10还包括对该平台几个安全关键领域的增强,以及对BiometricPrompt框架的更新,该框架在隐式和显式身份验证中都提供了对人脸和指纹的健壮支持。点击这里阅读更多关于Android 10安全更新的信息。

    相机与多媒体

    照片动态深度

    应用程序现在可以请求一个动态深度图像,它由一个JPEG、XMP元数据(与深度相关的元素相关)和一个嵌入在同一文件中的深度和置信度图组成。动态深度是生态系统的一种开放格式,我们正在与合作伙伴合作,将其引入运行Android 10或更高版本的设备中。
    在这里插入图片描述

    音频播放捕获

    现在,任何播放音频的应用程序都可以让其他应用程序使用新的音频回放捕获API捕获其音频流。除了启用标题和副标题之外,该API还允许您支持流行的用例,比如实时流媒体游戏。我们在构建这个新功能时考虑到了隐私和版权保护,因此一个应用程序捕捉另一个应用程序的音频的能力受到了限制。请阅读我们的博客文章

    音频和视频编解码器

    Android 10增加了对开源视频编解码器AV1的支持,它允许媒体供应商使用更少的带宽向Android设备传输高质量的视频内容。此外,Android 10支持使用Opus进行音频编码,Opus是一种开放的、免版税的编解码器,针对语音和音乐流媒体进行了优化,HDR10+用于支持Opus的设备上的高动态范围视频。

    本地MIDI 接口

    对于用c++执行音频处理的应用程序,Android 10引入了一个本地MIDI API,通过NDK与MIDI设备通信。该API允许使用非阻塞读取在音频回调中检索MIDI数据,从而支持对MIDI消息进行低延迟处理。在这里用示例应用程序和源代码试一试。

    Vulkan

    Vulkan 1.1现在是所有运行Android 10或更高版本的64位设备的必备版本,也是所有32位设备的推荐版本。我们已经在生态系统中看到了支持Vulkan的强大势头——在运行Android N或更高版本的设备中,超过半数支持Vulkan 1.0.3或更高版本。随着Android 10的新要求,我们预计在未来的一年里,Android的使用率会进一步上升。

    连接优化

    改进的点对点和互联网连接

    我们对Wi-Fi堆栈进行了重构,以提高隐私和性能,还改进了一些常见的用例,比如管理物联网设备和建议互联网连接——而不需要位置许可。网络连接api使得通过本地Wi-Fi管理物联网设备变得更加容易,可以实现配置、下载或打印等对等功能。网络建议api允许应用程序在互联网连接方面显示用户更喜欢的Wi-Fi网络。

    无线模式

    应用程序现在可以通过启用高性能和低延迟模式来请求自适应Wi-Fi。如果低延迟对用户体验非常重要,比如实时游戏、活动语音呼叫和类似的用例,那么这将是一个巨大的优势。该平台配合设备固件工作,以满足最低功耗的要求。

    系统优化

    ART优化

    ART运行时的改进可以帮助您的应用程序启动得更快、消耗更少的内存、运行得更流畅——而不需要您做任何工作。由谷歌Play提供的艺术简介,让艺术在运行之前就预先编译应用程序的部分。在运行时,分代垃圾收集使垃圾收集在时间和CPU方面更高效,减少jank,并帮助应用程序在低端设备上更好地运行。

    在这里插入图片描述

    神经网络1.2

    我们添加了60个新操作,包括ARGMAX、ARGMIN、量化LSTM,以及一系列性能优化。这为加速更大范围的模型奠定了基础,比如用于目标检测和图像分割的模型。我们正在与硬件供应商和流行的机器学习框架(如TensorFlow)合作,优化和推出对NNAPI 1.2的支持。

    更快更流行

    通过Android 10,我们将继续专注于更快地将新平台引入设备,与我们的设备制造商和高通(Qualcomm)等硅合作伙伴密切合作。Treble项目发挥了关键作用,帮助我们将18个合作伙伴设备以及8个像素设备纳入今年的Beta测试项目,比去年增加了一倍多。更棒的是,我们预计这些设备将在今年年底前获得官方的Android 10更新,我们正在与几家合作伙伴合作推出其他新的旗舰产品和更新。我们已经看到Android 10的强大势头,在未来的几个月里,将有比以往任何Android版本更多的设备获得这个新版本。

    Android 10也是第一个支持Project Mainline(官方名称为谷歌Play system updates)的版本,这是我们的新技术,用于保护Android用户,并通过重要的代码更改保持他们的设备的新鲜——直接来自谷歌Play。通过谷歌播放系统更新,我们可以在所有运行Android 10或更高版本的设备上更新特定的内部组件,而不需要设备制造商进行完整的系统更新。我们希望在接下来的几个月里为消费者设备带来第一次更新。

    对于开发人员来说,我们希望Android 10中的这些更新能够促进平台实现在不同设备上的一致性,并随着时间的推移带来更大的一致性,从而降低开发和测试成本。

    应用开发跟进Android 10

    现在,随着今天Android 10的公开发布和设备更新即将到来,我们要求所有Android开发人员尽快更新您当前的应用程序,以保证兼容性,让您的用户顺利过渡到Android 10。

    下面,我们给出开发的几点建议:

    Android 10上安装应用

    从谷歌Play将当前应用程序安装到运行Android 10或模拟器的像素或其他设备上,然后进行测试。你的应用程序应该看起来很棒,运行良好,功能齐全,并能正确处理Android 10的所有行为变化。观察隐私更改、手势导航、对仿生库的动态链接器路径的更改等方面的影响。

    测试Android 10的隐私功能

    测试的内容包括新的位置权限、范围存储、对后台活动启动的限制、对数据和标识符的更改等等。要开始查看顶级隐私更改清单,并查看隐私更改文档以了解更多测试领域。

    测试受限制的非sdk接口

    测试应用程序中的库和sdk

    如果发现问题,请尝试更新到最新版本的SDK,或者向SDK开发人员寻求帮助。

    更新和发布兼容的应用程序

    当您完成测试并进行任何更新时,我们建议您立即发布兼容的应用程序。当用户更新到Android 10时,这将帮助您向他们提供一个平稳的过渡。

    让应用程序经过测试并为新版本的Android做好准备,对于整个生态系统中更快的平台更新是至关重要的,所以如果可能的话,请优先考虑Android 10的适配工作。

    体验链接

    • 模拟器
      https://developer.android.google.cn/studio/run/managing-avds.html

    • Android 10 的各项行为变更
      https://developer.android.google.cn/about/versions/10/behavior-changes-all

    • 隐私变更
      https://developer.android.google.cn/about/versions/10/privacy/changes

    • 手势导航
      https://developer.android.google.cn/guide/navigation/gesturenav

    • 生物验证库的动态链接路径变化
      https://developer.android.google.cn/about/versions/10/behavior-changes-all#bionic

    • 新的位置权限
      https://developer.android.google.cn/about/versions/10/privacy/changes#app-access-device-location

    • 分区储存
      https://developer.android.google.cn/about/versions/10/privacy/changes#scoped-storage

    • 从后台启动 activity
      https://developer.android.google.cn/about/versions/10/privacy/changes#background-activity-starts

    • 关于数据和设备识别符方面的变更
      https://developer.android.google.cn/about/versions/10/privacy/changes#data-ids

    • 隐私特性清单
      https://developer.android.google.cn/about/versions/10/privacy#top-privacy-changes

    • 行为变更文档
      https://developer.android.google.cn/about/versions/10/privacy/changes

    • 《非 SDK 接口在 Android 10 中的受限情况出现变化》
      https://developer.android.google.cn/about/versions/10/non-sdk-q

    Android 10 新功能和APIs

    我们推荐每个应用程序都可以尝试下如下的一些功能:

    • 黑暗主题:通过添加一个dark主题或启用Force dark,为启用系统范围的dark主题的用户提供一致的体验。
    • 手势导航:在你的应用程序中支持手势导航,从边缘到边缘,并确保你的自定义手势是系统导航手势的补充。
    • 折叠优化:通过优化可折叠设备,为当今的创新设备提供无缝体验。

    链接

    • 深色主题
      https://developer.android.google.cn/guide/topics/ui/look-and-feel/darktheme
    • Force Dark 功能
      https://developer.android.google.cn/guide/topics/ui/look-and-feel/darktheme#force_dark
    • 手势导航
      https://developer.android.google.cn/guide/navigation/gesturenav
    • 针对折叠屏为应用进行优化
      https://developer.android.google.cn/guide/topics/ui/foldables

    除此之外,我们也推荐以下内容:

    • 互动通知:如果您的通知包含消息,请在通知中启用智能回复,以吸引用户并让他们立即采取行动。
    • 生物识别技术:如果您使用生物特征验证,请移动到BiometricPrompt,这是在现代设备上支持指纹验证的首选方法。
    • 音频播放捕获:要支持字幕或游戏录制,请在应用程序中启用音频回放捕捉功能——这是接触更多用户并使应用程序更易访问的好方法。
    • 编解码器:对于媒体应用程序,可以尝试AV1用于视频流,HDR10+用于高动态范围的视频。对于语音和音乐流,可以使用Opus编码,对于音乐家,可以使用本地MIDI API。
    • 网络api优化:如果您的应用程序通过Wi-Fi管理物联网设备,请尝试使用新的网络连接api来实现配置、下载或打印等功能。

    链接

    • 通知内的智能回复及建议操作
      https://developer.android.google.cn/about/versions/10/features#smart-suggestions
    • BiometricPrompt
      https://developer.android.google.cn/training/sign-in/biometric-auth
    • 音频回放捕捉功能
      https://developer.android.google.cn/preview/features/playback-capture
    • AV1
      https://en.wikipedia.org/wiki/AV1
    • Opus
      http://opus-codec.org/
    • HDR 10+
      https://en.wikipedia.org/wiki/High-dynamic-range_video#HDR10+
    • 原生 MIDI API
      https://developer.android.google.cn/ndk/guides/audio/midi
    • 网络连接 API
      https://developer.android.google.cn/guide/topics/connectivity/wifi-bootstrap

    参考链接:

    Welcoming Android 10!

    android官网

    Android 10 开发者官网
    Android Studio 3.5 稳定版

    按照操作步骤

    展开全文
  • spring的4种事务特性,5种隔离级别,7种传播行为

    万次阅读 多人点赞 2017-10-04 11:11:35
    事务特性(4种): 原子性 (atomicity):强调事务的不可分割. 一致性 (consistency):事务的执行的前后数据的完整性保持一致. 隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰 持久性...
  • JDK9新特性

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

    千人学习 2018-12-25 17:18:41
    postgresql功能特性原理及操作演示; 数据库优化相关内容;

空空如也

1 2 3 4 5 ... 20
收藏数 583,335
精华内容 233,334
关键字:

特性