美文网首页
JDK1.8新特性总结 Day37 2018-12-27

JDK1.8新特性总结 Day37 2018-12-27

作者: Ernest_Chou | 来源:发表于2018-12-27 21:47 被阅读0次

    JDK1.8新特性总结

    一、十大特性

    • 1.default关键字
    • 2.Lambda表达式
    • 3.接口新增:默认方法与静态方法
    • 4.方法引用,与Lambda表达式联合使用
    • 5.引入重复注解
    • 6.类型注解
    • 7.Stream函数式操作流元素集合
    • 8.最新的Date/Time API (JSR 310)
    • 9.新增base64加解密API
    • 10.数组并行(parallel)操作

    • 1.JVMPermGen空间被移除:取代它的是Metaspace(JEP 122)元空间

    二、新特性

    1、default关键字

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

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

    那这么定义一个方法的作用是什么呢?为什么不在接口的实现类里面再去实现方法呢?

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

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

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

    2、Lambda 表达式

    2.1、 Lambda表达式的写法

    Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。

    • Lambda 表达式junit测试代码
     public List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
        
        /**
         * 1.Lambda表达式
         */
        @Test
        public void testLambda(){
            list.forEach(System.out::println);
            list.forEach(e -> System.out.println("方式二:"+e));
        }
    

    首先,什么是函数式编程,引用廖雪峰先生的教程里面的解释就是说:

    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

    简单的来说就是,函数式编程语言里面函数也可以跟变量,对象一样使用了,也就是说函数既可以作为参数,也可以作为返回值了,看一下下面这个例子。

    //这是常规的Collections的排序的写法,需要对接口方法重写
            public void test1(){
            List<String> list =Arrays.asList("aaa","fsa","ser","eere");
            Collections.sort(list, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o2.compareTo(o1);
                }
            });
            for (String string : list) {
                System.out.println(string);
            }
        }
    //这是带参数类型的Lambda的写法
            public void testLamda1(){
            List<String> list =Arrays.asList("aaa","fsa","ser","eere");
            Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
                return b.compareTo(a);
            }
            );
            for (String string : list) {
                System.out.println(string);
            }
        }
    //这是不带参数的lambda的写法
            public void testLamda2(){
            List<String> list =Arrays.asList("aaa","fsa","ser","eere");
            Collections.sort(list, (a,b)->b.compareTo(a)
            );
            for (String string : list) {
                System.out.println(string);
            }
    

    可以看到不带参数的写法一句话就搞定了排序的问题,所以引入lambda表达式的一个最直观的作用就是大大的简化了代码的开发,像其他一些编程语言Scala,Python等都是支持函数式的写法的。当然,不是所有的接口都可以通过这种方法来调用,只有函数式接口才行,jdk1.8里面定义了好多个函数式接口,我们也可以自己定义一个来调用,下面说一下什么是函数式接口。

    2.2、函数式接口

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

    @FunctionalInterface
    public interface MyLamda {
       
       public void test1(String y);
    
    //这里如果继续加一个抽象方法便会报错
    //    public void test1();
       
    //default方法可以任意定义
       default String test2(){
           return "123";
       }
       
       default String test3(){
           return "123";
       }
    
    //static方法也可以定义
       static void test4(){
           System.out.println("234");
       }
    
    }
    

    看一下这个接口的调用,符合lambda表达式的调用方法。

    MyLamda m = y -> System.out.println("ss"+y);
    

    2.3、方法与构造函数引用

    jdk1.8提供了另外一种调用方式::,当 你 需 要使用 方 法 引用时 , 目 标引用 放 在 分隔符::前 ,方法 的 名 称放在 后 面 ,即ClassName :: methodName 。例如 ,Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()的快捷写法,如下示例。

    //先定义一个函数式接口
    @FunctionalInterface
    public interface TestConverT<T, F> {
        F convert(T t);
    }
    

    测试如下,可以以::形式调用。

    public void test(){
        TestConverT<String, Integer> t = Integer::valueOf;
        Integer i = t.convert("111");
        System.out.println(i);
    }
    

    此外,对于构造方法也可以这么调用。

    //实体类User和它的构造方法
    public class User {    
      private String name;
      
      private String sex;
    
      public User(String name, String sex) {
          super();
          this.name = name;
          this.sex = sex;
      }
    }
    //User工厂
    public interface UserFactory {
      User get(String name, String sex);
    }
    //测试类
      UserFactory uf = User::new;
      User u = uf.get("ww", "man");
    

    这里的User::new就是调用了User的构造方法,Java编译器会自动根据UserFactory.get方法的签名来选择合适的构造函数。

    • 方法引用,与Lambda表达式联合使用junit测试代码
     /**
         * 4.方法引用,与Lambda表达式联合使用
         */
        @Test
        public void testMethodReference(){
            //构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;
            final Car car = Car.create( Car::new );
            final List< Car > cars = Arrays.asList( car );
            //静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;
            cars.forEach( Car::collide );
            //任意对象的方法引用。它的语法是Class::method。无参,所有元素调用;
            cars.forEach( Car::repair );
            //特定对象的方法引用,它的语法是instance::method。有参,在某个对象上调用方法,将列表元素作为参数传入;
            final Car police = Car.create( Car::new );
            cars.forEach( police::follow );
        }
        
        public static class Car {
            public static Car create( final Supplier< Car > supplier ) {
                return supplier.get();
            }              
                 
            public static void collide( final Car car ) {
                System.out.println( "静态方法引用 " + car.toString() );
            }
                 
            public void repair() {   
                System.out.println( "任意对象的方法引用 " + this.toString() );
            }
            
            public void follow( final Car car ) {
                System.out.println( "特定对象的方法引用 " + car.toString() );
            }
        }
    

    2.4、局部变量限制

    Lambda表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获LambdaLambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为final,或事实上是final
      为什么局部变量有这些限制?
      (1)实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此, Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
      (2)这一限制不鼓励你使用改变外部变量的典型命令式编程模式。

    final int num = 1;
    Converter<Integer, String> stringConverter =
           (from) -> String.valueOf(from + num);
    stringConverter.convert(2); 
    

    3.接口新增:默认方法与静态方法

     /**
         * 3.接口新增:默认方法与静态方法
         *  default 接口默认实现方法是为了让集合类默认实现这些函数式处理,而不用修改现有代码
         *  (List继承于Iterable<T>,接口默认方法不必须实现default forEach方法)
         */
        @Test
        public void testDefaultFunctionInterface(){
            //可以直接使用接口名.静态方法来访问接口中的静态方法
            JDK8Interface1.staticMethod();
            //接口中的默认方法必须通过它的实现类来调用
            new JDK8InterfaceImpl1().defaultMethod();
            //多实现类,默认方法重名时必须复写
            new JDK8InterfaceImpl2().defaultMethod();
        }
        
        public class JDK8InterfaceImpl1 implements JDK8Interface1 {
            //实现接口后,因为默认方法不是抽象方法,重写/不重写都成!
    //        @Override
    //        public void defaultMethod(){
    //            System.out.println("接口中的默认方法");
    //        }
        }
        
        public class JDK8InterfaceImpl2 implements JDK8Interface1,JDK8Interface2 {
            //实现接口后,默认方法名相同,必须复写默认方法
            @Override
            public void defaultMethod() {
                //接口的
                JDK8Interface1.super.defaultMethod();
                System.out.println("实现类复写重名默认方法!!!!");
            }
        }
        public interface JDK8Interface1 {
            //1.接口中可以定义静态方法了
            public static void staticMethod(){
                System.out.println("接口中的静态方法");
            }
            
            //2.使用default之后就可以定义普通方法的方法体了
            public default void defaultMethod(){
                System.out.println("接口中的默认方法");
            }
        }
        public interface  JDK8Interface2 {
            //1.接口中可以定义静态方法了
            public static void staticMethod(){
                System.out.println("接口中的静态方法");
            }
            
            //2.使用default之后就可以定义普通方法的方法体了
            public default void defaultMethod(){
                System.out.println("接口中的默认方法");
            }
        }
    

    4.方法引用,与Lambda表达式联合使用

    /**
         * 4.方法引用,与Lambda表达式联合使用
         */
        @Test
        public void testMethodReference(){
            //构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;
            final Car car = Car.create( Car::new );
            final List< Car > cars = Arrays.asList( car );
            //静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;
            cars.forEach( Car::collide );
            //任意对象的方法引用。它的语法是Class::method。无参,所有元素调用;
            cars.forEach( Car::repair );
            //特定对象的方法引用,它的语法是instance::method。有参,在某个对象上调用方法,将列表元素作为参数传入;
            final Car police = Car.create( Car::new );
            cars.forEach( police::follow );
        }
        
        public static class Car {
            public static Car create( final Supplier< Car > supplier ) {
                return supplier.get();
            }              
                 
            public static void collide( final Car car ) {
                System.out.println( "静态方法引用 " + car.toString() );
            }
                 
            public void repair() {   
                System.out.println( "任意对象的方法引用 " + this.toString() );
            }
            
            public void follow( final Car car ) {
                System.out.println( "特定对象的方法引用 " + car.toString() );
            }
        }
    

    5.引入重复注解

     /**
         * 5.引入重复注解
         * 1.@Repeatable 
         * 2.可以不用以前的“注解容器”写法,直接写2次相同注解即可
         * 
         * Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
         */
        @Test
        public void RepeatingAnnotations(){
            RepeatingAnnotations.main(null);
        }
    
    package com.zgldoing.base.jdk8Features;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filter;
    import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filterable;
    import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filters;
    /**
     * @ClassName:RepeatingAnnotations
     * @Description:重复注解@Repeatable
     * @author zgldo
     *
     */
    public class RepeatingAnnotations {
        @Target( ElementType.TYPE )
        @Retention( RetentionPolicy.RUNTIME )
        public @interface Filters {
            Filter[] value();
        }
         
        @Target( ElementType.TYPE )
        @Retention( RetentionPolicy.RUNTIME )
        @Repeatable( Filters.class )
        public @interface Filter {
            String value();
            String value2();
        };
         
        @Filter( value="filter1",value2="111" )
        @Filter( value="filter2", value2="222")
        //@Filters({@Filter(  value="filter1",value2="111" ),@Filter(  value="filter2", value2="222")}).注意:JDK8之前:1.没有@Repeatable2.采用本行“注解容器”写法
        public interface Filterable {        
        }
             
        public static void main(String[] args) {
            //获取注解后遍历打印值
            for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
                System.out.println( filter.value() +filter.value2());
            }
        }
    }
    
    

    6.类型注解

    /**
         * 6.类型注解
         * 新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
         * 
         */
        @Test
        public void ElementType(){
            Annotations.main(null);
        }
    
    package com.zgldoing.base.jdk8Features;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.util.ArrayList;
    import java.util.Collection;
    
    import com.zgldoing.base.jdk8Features.Annotations.Holder;
    import com.zgldoing.base.jdk8Features.Annotations.NonEmpty;
    /**
     * @ClassName:Annotations
     * @Description:新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
     * @author zgldo
     *
     */
    public class Annotations {
          @Retention( RetentionPolicy.RUNTIME )
            @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
            public @interface NonEmpty {        
            }
                 
            public static class Holder< @NonEmpty T > extends @NonEmpty Object {
                public void method() throws @NonEmpty Exception {           
                }
            }
                 
            public static void main(String[] args) {
                final Holder< String > holder = new @NonEmpty Holder< String >();       
                @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();       
            }
    }
    
    

    7、Stream函数式操作流元素

    定义:流是Java API的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,也就是说我们不用写多线程代码了。

    Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

    Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

    流的操作类型分为两种:

    • Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
    • Terminal:一个流只能有一个 terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect

    在对于一个Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。

    构造流的几种方式

    // 1. Individual values
    Stream stream = Stream.of("a", "b", "c");
    // 2. Arrays
    String [] strArray = new String[] {"a", "b", "c"};
    stream = Stream.of(strArray);
    stream = Arrays.stream(strArray);
    // 3. Collections
    List<String> list = Arrays.asList(strArray);
    stream = list.stream();
    
    • Stream函数式操作流元素集合
    /**
         * 2.Stream函数式操作流元素集合
         */
        @Test
        public void testStream(){
            List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
            System.out.println("求和:"+nums
                    .stream()//转成Stream
                    .filter(team -> team!=null)//过滤
                    .distinct()//去重
                    .mapToInt(num->num*2)//map操作
                    .skip(2)//跳过前2个元素
                    .limit(4)//限制取前4个元素
                    .peek(System.out::println)//流式处理对象函数
                    .sum());//
        }
        
    

    8、Date API更新

    1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。

    8.1 LocalDate/LocalTime/LocalDateTime

    LocalDate为日期处理类、LocalTime为时间处理类、LocalDateTime为日期时间处理类,方法都类似,具体可以看API文档或源码,选取几个代表性的方法做下介绍。

    now相关的方法可以获取当前日期或时间,of方法可以创建对应的日期或时间,parse方法可以解析日期或时间,get方法可以获取日期或时间信息,with方法可以设置日期或时间信息,plusminus方法可以增减日期或时间信息;

    8.2 TemporalAdjusters

    这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。

    8.3 DateTimeFormatter

    以前日期格式化一般用SimpleDateFormat类,但是不怎么好用,现在1.8引入了DateTimeFormatter类,默认定义了很多常量格式(ISO打头的),在使用的时候一般配合LocalDate/LocalTime/LocalDateTime使用,比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss的形式:

    LocalDateTime dt = LocalDateTime.now();  
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");         
    System.out.println(dtf.format(dt));
    
    • Date/Time API junit测试代码
    /**
         * 7.最新的Date/Time API (JSR 310)
         * UTC即为世界标准时间,世界统一时间,国际协调时间,字母T是时间和日期的分隔符,T后面表示的即为时间,末尾的z表示UTC统一时间。
         */
        @Test
        public void DateTime(){
            //1.Clock
            final Clock clock = Clock.systemUTC();
            System.out.println( clock.instant() );//2018-12-27T13:25:18.636Z
            System.out.println( clock.millis() );//1545917118662
            
            //2. ISO-8601格式且无时区信息的日期部分
            final LocalDate date = LocalDate.now();
            final LocalDate dateFromClock = LocalDate.now( clock );
                     
            System.out.println( date );//2018-12-27
            System.out.println( dateFromClock );//2018-12-27
                     
            // ISO-8601格式且无时区信息的时间部分
            final LocalTime time = LocalTime.now();
            final LocalTime timeFromClock = LocalTime.now( clock );
                     
            System.out.println( time );         //21:25:18.692
            System.out.println( timeFromClock );//13:25:18.692
            
            // 3.ISO-8601格式无时区信息的日期与时间
            final LocalDateTime datetime = LocalDateTime.now();
            final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
                     
            System.out.println( datetime );         //2018-12-27T21:25:18.692
            System.out.println( datetimeFromClock );//2018-12-27T13:25:18.692
            
            // 4.特定时区的日期/时间,
            final ZonedDateTime zonedDatetime = ZonedDateTime.now();
            final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
            final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
                     
            System.out.println( zonedDatetime );         //2018-12-27T21:25:18.694+08:00[Asia/Shanghai]
            System.out.println( zonedDatetimeFromClock );//2018-12-27T13:25:18.694Z
            System.out.println( zonedDatetimeFromZone ); //2018-12-27T05:25:18.706-08:00[America/Los_Angeles]
            
            //5.在秒与纳秒级别上的一段时间
            final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
            final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
             
            final Duration duration = Duration.between( from, to );
            System.out.println( "Duration in days: " + duration.toDays() );  //Duration in days: 365
            System.out.println( "Duration in hours: " + duration.toHours() );//Duration in hours: 8783
        }
    

    9.新增base64加解密API

    /**
         * 8.新增base64加解密API
         */
        @Test
        public void testBase64(){
            final String text = "就是要测试加解密!!abjdkhdkuasu!!@@@@";
            String encoded = Base64.getEncoder()
                .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
            System.out.println("加密后="+ encoded );
             
            final String decoded = new String( 
                Base64.getDecoder().decode( encoded ),
                StandardCharsets.UTF_8 );
            System.out.println( "解密后="+decoded );
        }
    

    10.数组并行(parallel)操作

      /**
         * 9.数组并行(parallel)操作
         */
        @Test
        public void testParallel(){
            long[] arrayOfLong = new long [ 20000 ];        
            //1.给数组随机赋值
            Arrays.parallelSetAll( arrayOfLong, 
                index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
            //2.打印出前10个元素
            Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
                i -> System.out.print( i + " " ) );
            System.out.println();
            //3.数组排序
            Arrays.parallelSort( arrayOfLong );     
            //4.打印排序后的前10个元素
            Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
                i -> System.out.print( i + " " ) );
            System.out.println();
        }
    

    11.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间

     /**
         * 10.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
         */
        @Test
        public void testMetaspace(){
            //-XX:MetaspaceSize初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整
            //-XX:MaxMetaspaceSize最大空间,默认是没有限制
            //-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
            //-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
        }
    

    三、总结

    总的来说,jdk1.8的一些新特性主要还是简化了代码的写法,减少了部分开发量,但是需要一些时间来熟悉。很惭愧,1.8的新特性还不是很熟悉,所以还是要继续努力,还需要多看些源码。

    四、附录 junit测试代码

    1、 JDK8_features.java

    package com.zgldoing.base.jdk8Features;
    
    import static org.junit.jupiter.api.Assertions.*;
    
    import java.nio.charset.StandardCharsets;
    import java.time.Clock;
    import java.time.Duration;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.time.Month;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Base64;
    import java.util.List;
    import java.util.concurrent.ThreadLocalRandom;
    import java.util.function.Supplier;
    
    import org.junit.jupiter.api.Test;
    
    import com.google.common.collect.Lists;
    
    /**
     * 
     * @ClassName:JDK8_features
     * @Description:JDK8新特性
     * 
     */
    public class JDK8_features {
        
        public List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
        
        /**
         * 1.Lambda表达式
         */
        @Test
        public void testLambda(){
            list.forEach(System.out::println);
            list.forEach(e -> System.out.println("方式二:"+e));
        }
        
        /**
         * 2.Stream函数式操作流元素集合
         */
        @Test
        public void testStream(){
            List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
            System.out.println("求和:"+nums
                    .stream()//转成Stream
                    .filter(team -> team!=null)//过滤
                    .distinct()//去重
                    .mapToInt(num->num*2)//map操作
                    .skip(2)//跳过前2个元素
                    .limit(4)//限制取前4个元素
                    .peek(System.out::println)//流式处理对象函数
                    .sum());//
        }
        
        /**
         * 3.接口新增:默认方法与静态方法
         *  default 接口默认实现方法是为了让集合类默认实现这些函数式处理,而不用修改现有代码
         *  (List继承于Iterable<T>,接口默认方法不必须实现default forEach方法)
         */
        @Test
        public void testDefaultFunctionInterface(){
            //可以直接使用接口名.静态方法来访问接口中的静态方法
            JDK8Interface1.staticMethod();
            //接口中的默认方法必须通过它的实现类来调用
            new JDK8InterfaceImpl1().defaultMethod();
            //多实现类,默认方法重名时必须复写
            new JDK8InterfaceImpl2().defaultMethod();
        }
        
        public class JDK8InterfaceImpl1 implements JDK8Interface1 {
            //实现接口后,因为默认方法不是抽象方法,重写/不重写都成!
    //        @Override
    //        public void defaultMethod(){
    //            System.out.println("接口中的默认方法");
    //        }
        }
        
        public class JDK8InterfaceImpl2 implements JDK8Interface1,JDK8Interface2 {
            //实现接口后,默认方法名相同,必须复写默认方法
            @Override
            public void defaultMethod() {
                //接口的
                JDK8Interface1.super.defaultMethod();
                System.out.println("实现类复写重名默认方法!!!!");
            }
        }
        public interface JDK8Interface1 {
            //1.接口中可以定义静态方法了
            public static void staticMethod(){
                System.out.println("接口中的静态方法");
            }
            
            //2.使用default之后就可以定义普通方法的方法体了
            public default void defaultMethod(){
                System.out.println("接口中的默认方法");
            }
        }
        public interface  JDK8Interface2 {
            //1.接口中可以定义静态方法了
            public static void staticMethod(){
                System.out.println("接口中的静态方法");
            }
            
            //2.使用default之后就可以定义普通方法的方法体了
            public default void defaultMethod(){
                System.out.println("接口中的默认方法");
            }
        }
        /**
         * 4.方法引用,与Lambda表达式联合使用
         */
        @Test
        public void testMethodReference(){
            //构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;
            final Car car = Car.create( Car::new );
            final List< Car > cars = Arrays.asList( car );
            //静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;
            cars.forEach( Car::collide );
            //任意对象的方法引用。它的语法是Class::method。无参,所有元素调用;
            cars.forEach( Car::repair );
            //特定对象的方法引用,它的语法是instance::method。有参,在某个对象上调用方法,将列表元素作为参数传入;
            final Car police = Car.create( Car::new );
            cars.forEach( police::follow );
        }
        
        public static class Car {
            public static Car create( final Supplier< Car > supplier ) {
                return supplier.get();
            }              
                 
            public static void collide( final Car car ) {
                System.out.println( "静态方法引用 " + car.toString() );
            }
                 
            public void repair() {   
                System.out.println( "任意对象的方法引用 " + this.toString() );
            }
            
            public void follow( final Car car ) {
                System.out.println( "特定对象的方法引用 " + car.toString() );
            }
        }
        
        /**
         * 5.引入重复注解
         * 1.@Repeatable 
         * 2.可以不用以前的“注解容器”写法,直接写2次相同注解即可
         * 
         * Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
         */
        @Test
        public void RepeatingAnnotations(){
            RepeatingAnnotations.main(null);
        }
        
        /**
         * 6.类型注解
         * 新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
         * 
         */
        @Test
        public void ElementType(){
            Annotations.main(null);
        }
        
        /**
         * 7.最新的Date/Time API (JSR 310)
         * UTC即为世界标准时间,世界统一时间,国际协调时间,字母T是时间和日期的分隔符,T后面表示的即为时间,末尾的z表示UTC统一时间。
         */
        @Test
        public void DateTime(){
            //1.Clock
            final Clock clock = Clock.systemUTC();
            System.out.println( clock.instant() );//2018-12-27T13:25:18.636Z
            System.out.println( clock.millis() );//1545917118662
            
            //2. ISO-8601格式且无时区信息的日期部分
            final LocalDate date = LocalDate.now();
            final LocalDate dateFromClock = LocalDate.now( clock );
                     
            System.out.println( date );//2018-12-27
            System.out.println( dateFromClock );//2018-12-27
                     
            // ISO-8601格式且无时区信息的时间部分
            final LocalTime time = LocalTime.now();
            final LocalTime timeFromClock = LocalTime.now( clock );
                     
            System.out.println( time );         //21:25:18.692
            System.out.println( timeFromClock );//13:25:18.692
            
            // 3.ISO-8601格式无时区信息的日期与时间
            final LocalDateTime datetime = LocalDateTime.now();
            final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
                     
            System.out.println( datetime );         //2018-12-27T21:25:18.692
            System.out.println( datetimeFromClock );//2018-12-27T13:25:18.692
            
            // 4.特定时区的日期/时间,
            final ZonedDateTime zonedDatetime = ZonedDateTime.now();
            final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
            final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
                     
            System.out.println( zonedDatetime );         //2018-12-27T21:25:18.694+08:00[Asia/Shanghai]
            System.out.println( zonedDatetimeFromClock );//2018-12-27T13:25:18.694Z
            System.out.println( zonedDatetimeFromZone ); //2018-12-27T05:25:18.706-08:00[America/Los_Angeles]
            
            //5.在秒与纳秒级别上的一段时间
            final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
            final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
             
            final Duration duration = Duration.between( from, to );
            System.out.println( "Duration in days: " + duration.toDays() );  //Duration in days: 365
            System.out.println( "Duration in hours: " + duration.toHours() );//Duration in hours: 8783
        }
        
        /**
         * 8.新增base64加解密API
         */
        @Test
        public void testBase64(){
            final String text = "就是要测试加解密!!abjdkhdkuasu!!@@@@";
            String encoded = Base64.getEncoder()
                .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
            System.out.println("加密后="+ encoded );
             
            final String decoded = new String( 
                Base64.getDecoder().decode( encoded ),
                StandardCharsets.UTF_8 );
            System.out.println( "解密后="+decoded );
        }
        
        /**
         * 9.数组并行(parallel)操作
         */
        @Test
        public void testParallel(){
            long[] arrayOfLong = new long [ 20000 ];        
            //1.给数组随机赋值
            Arrays.parallelSetAll( arrayOfLong, 
                index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
            //2.打印出前10个元素
            Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
                i -> System.out.print( i + " " ) );
            System.out.println();
            //3.数组排序
            Arrays.parallelSort( arrayOfLong );     
            //4.打印排序后的前10个元素
            Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
                i -> System.out.print( i + " " ) );
            System.out.println();
        }
        
        /**
         * 10.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
         */
        @Test
        public void testMetaspace(){
            //-XX:MetaspaceSize初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整
            //-XX:MaxMetaspaceSize最大空间,默认是没有限制
            //-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
            //-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
        }
        
    }
    
    

    2、重复注解@Repeatable

    package com.zgldoing.base.jdk8Features;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filter;
    import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filterable;
    import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filters;
    /**
     * @ClassName:RepeatingAnnotations
     * @Description:重复注解@Repeatable
     * @author zgldo
     *
     */
    public class RepeatingAnnotations {
        @Target( ElementType.TYPE )
        @Retention( RetentionPolicy.RUNTIME )
        public @interface Filters {
            Filter[] value();
        }
         
        @Target( ElementType.TYPE )
        @Retention( RetentionPolicy.RUNTIME )
        @Repeatable( Filters.class )
        public @interface Filter {
            String value();
            String value2();
        };
         
        @Filter( value="filter1",value2="111" )
        @Filter( value="filter2", value2="222")
        //@Filters({@Filter(  value="filter1",value2="111" ),@Filter(  value="filter2", value2="222")}).注意:JDK8之前:1.没有@Repeatable2.采用本行“注解容器”写法
        public interface Filterable {        
        }
             
        public static void main(String[] args) {
            //获取注解后遍历打印值
            for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
                System.out.println( filter.value() +filter.value2());
            }
        }
    }
    
    

    3、新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)

    package com.zgldoing.base.jdk8Features;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.util.ArrayList;
    import java.util.Collection;
    
    import com.zgldoing.base.jdk8Features.Annotations.Holder;
    import com.zgldoing.base.jdk8Features.Annotations.NonEmpty;
    /**
     * @ClassName:Annotations
     * @Description:新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
     * @author zgldo
     *
     */
    public class Annotations {
          @Retention( RetentionPolicy.RUNTIME )
            @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
            public @interface NonEmpty {        
            }
                 
            public static class Holder< @NonEmpty T > extends @NonEmpty Object {
                public void method() throws @NonEmpty Exception {           
                }
            }
                 
            public static void main(String[] args) {
                final Holder< String > holder = new @NonEmpty Holder< String >();       
                @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();       
            }
    }
    
    

    相关文章

      网友评论

          本文标题:JDK1.8新特性总结 Day37 2018-12-27

          本文链接:https://www.haomeiwen.com/subject/onwllqtx.html