zoukankan      html  css  js  c++  java
  • 浓缩版java8新特性


    一、Lambda

    1、定义/设计原因

    官方解释:允许把函数作为一个方法的参数。使代码变的更加简洁紧凑。表达式免去了使用匿名方法的麻烦。
    个人解释:用来创建匿名方法

    2、结构

    Lambda表达式可由逗号分隔的参数列表、->符号和语句块组成

    • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
    • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
    • 可选的大括号:如果主体只有一个语句,就不需要使用大括号。
    • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号必须指定return值

    3、规则

    表达式用到的局部变量不管是不是final,都会被变成final,后面代码不可以改它了。全局变量则不受限。

    4、使用

    
    public class Java8Tester {
        @FunctionalInterface
        interface MathOperation {
            int operation(int a, int b);
    
        }
        
        @FunctionalInterface
        interface GreetingService {
            void sayMessage(String message);
        }
    
        public static void main(String args[]){
            //老版本写法
            MathOperation oldAdd = new MathOperation() {
                @Override
                public int operation(int a, int b) {
                    return a+b;
                }
            };
    
            // jdk8类型声明
            MathOperation addition = (int a, int b) -> a + b;
    
            // 不用类型声明
            MathOperation subtraction = (a, b) -> a - b;
    
            // 大括号中的返回语句
            MathOperation multiplication = (int a, int b) -> { return a * b; };
    
            // 没有大括号及返回语句
            MathOperation division = (int a, int b) -> a / b;
    
            // 不用括号
            GreetingService greetService1 = message ->
                    System.out.println("Hello " + message);
    
            // 用括号
            GreetingService greetService2 = (message) ->
                    System.out.println("Hello " + message);
    
            greetService1.sayMessage("Runoob");
            greetService2.sayMessage("Google");
        }
        
       
    }
    
    

    二、函数式接口

    1、定义

    官方解释:函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式和方法引用。
    @FunctionalInterface,用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。

    2、设计原因

    lambda语法只能用函数式接口,为方便检查函数式接口,给了这个@FunctionalInterface注解

    3、使用

    @FunctionalInterfaceinterface 
    GreetingService {
        void sayMessage(String message);
    }
    
    //使用Lambda表达式来表示该接口的一个实现
    GreetingService greetService1 = message -> System.out.println("Hello " + message);
    

    三、方法引用

    1、定义/设计原因

    官方解释:通过方法的名字来指向一个方法。可以使语言的构造更紧凑简洁,减少冗余代码。
    个人解释:方法引用是一种更简洁易懂的Lambda表达式。直接访问类或者实例的方法或者构造方法。

    2、使用

    先创建个接口和类

     //函数式接口
    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
    
    class Car {
    
        public static Car create(final Supplier<Car> supplier) {
            return supplier.get();
        }
    
        public static void collide(final Car car) {
            System.out.println("Collided " + car.toString());
        }
        
        public void repair() {
            System.out.println("Repaired " + this.toString());
        }
    }
    

    开始使用方法引用:

       @Test
        public void MethodReferenceTest() {
    //        1、旧方法写法
    //        final Car oldCar = Car.create(new Supplier<Car>() {
    //            @Override
    //            public Car get() {
    //                return new Car();
    //            }
    //        });
    //        2、jdk8的lambda
    //        final Car oldCar1 = Car.create(() -> new Car());
    
            //3、jdk8写法
            //构造器引用  Class::new
            final Car newCar = Car.create(Car::new);
    
            //该方法返回的 List 与传入数组是映射关系(视图):set/get 操作直接作用于数组;直接修改数组,list 也会改变
            final List<Car> cars = Arrays.asList(newCar);
    /*-----------------------------分割线---------------------------------------*/
    
    //       1、旧方法写法
    //        for (Car car : cars) {
    //            Car.collide(car);
    //        }
    //        2、jdk8的forEach
    //        cars.forEach(new Consumer<Car>() {
    //            @Override
    //            public void accept(Car car) {
    //                Car.collide(car);
    //            }
    //        });
    //        3、jdk8的lambda
    //        cars.forEach(car -> Car.collide(car));
    
            //4、jdk8方法引用写法
            //类名的方法引用 Class::static_method 或者 Class::method
            cars.forEach(Car::collide);
    /*-----------------------------分割线----------------------------------------*/
    
            final Car police = Car.create(Car::new);
    
    //       1、jdk8的lambda
    //        cars.forEach(car -> police.follow(car));
    
            //2、jdk8方法引用写法
            //特定对象的方法引用  instance::method
            cars.forEach(police::follow);
        }
     /*-----------------------------分割线---------------------------------------*/
    
            String[] stringsArray= {"4","5"};
    //       1、旧方法写法
    //        Arrays.sort(stringsArray, new Comparator<String>() {
    //            @Override
    //            public int compare(String s1, String s2) {
    //                return s1.compareToIgnoreCase(s2);
    //            }
    //        });
    //        2、jdk8的lambda
    //        Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2));
    
            //3、jdk8的方法引用
            Arrays.sort(stringsArray, String::compareToIgnoreCase);
    

    四、接口的默认方法

    1、定义

    官方解释:默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个 default 关键字即可实现默认方法。

    2、设计原因

    问:为什么要有这个特性?
    答:首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

    3、使用

    (1)接口的默认方法
    最基本使用,接口通过default定义默认方法,实现类无需实现该方法

    interface Vehicle {
        default void print() {
            System.out.println("我是一辆车!");
        }
    }
    
    class Car implements Vehicle{
    
    }
    

    如果一个类实现了多个接口,且这些接口有相同的默认方法,则可以通过
    重写接口的默认方法,或者使用 super 来调用指定接口的默认方法

    public interface Vehicle {
       default void print(){
          System.out.println("我是一辆车!");
       }
    }
    
    public interface FourWheeler {
       default void print(){
          System.out.println("我是一辆四轮车!");
       }
    }
    
    //创建自己的默认方法,来覆盖重写接口的默认方法
    public class Car implements Vehicle, FourWheeler {
       default void print(){
          System.out.println("我是一辆四轮汽车!");
       }
    }
    
    //使用 super 来调用指定接口的默认方法
    public class Car implements Vehicle, FourWheeler {
       public void print(){
          Vehicle.super.print();
       }
    }
    

    (2)接口的静态默认方法
    不实例化接口的时候也可以用,个人感觉失去了接口的本质,但是妥协旧代码是这样的啦

    public interface Animal {
     static void putUpHands(){
         System.out.println("举手");
     }
    }
    
    class Cat {
        void catPutUpHands(){
            Animal.putUpHands();
        }
    }
    

    五、Stream

    1、定义

    官方解释:让你以一种声明的方式处理数据。将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
    个人解释:把数组通过.stream()或者.parallelStream()转换成流,然后通过filter、map、distinct、sorted、limit等方法进行中间处理,最后通过collect、forEach、count得出最终结果。

    2、设计原因

    简化数组处理

    2、使用

    public static void main(String[] args) {
            List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
            List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5, 1, 9, 10, 4, 1);
            /*=======================返回Stream<T>==========================*/
            //1、filter过滤
            strings.stream().filter(string -> !string.isEmpty());
            //2、map映射每个元素到对应的结果
            numbers.stream().map(i -> i * i);
            //3、distinct去重
            numbers.stream().distinct();
            //4、sorted排序
            numbers.stream().sorted();
            //4、limit限制数量
            numbers.stream().limit(5);
            //5、并行处理,主要适用于大数据量的数组
            strings.parallelStream();
    
            /*=======================对流进行转换或处理的==========================*/
            //1、collect转换成列表或字符串,Collectors 可将流转换成集合和聚合元素
            List<String> collect = strings.stream().collect(Collectors.toList());
            String collectStr = strings.stream().collect(Collectors.joining(", "));
            //2、forEach迭代流中的每个数据
            strings.stream().forEach(System.out::println);
            //3、count统计数量
            long count = strings.stream().count();
    
             /*=======================拓展IntStream,LongStream,DoubleStream==========================*/
    
             //1.1、mapToInt将Stream转换成IntStream, summaryStatistics是对IntStream数据进行汇总统计的方法,(LongStream,DoubleStream同理)
            IntSummaryStatistics summary = numbers.stream().mapToInt(x ->x).summaryStatistics();
            System.out.println(summary.getAverage());
            System.out.println(summary.getCount());
            System.out.println(summary.getMax());
            System.out.println(summary.getMin());
            System.out.println(summary.getSum());
    
            //1.2、IntStream,LongStream创建区间方式是一样的
            int[] range1 = IntStream.rangeClosed(13, 15).toArray();//生产区间 [a,b]      range1=[13,14,15]
            int[] range2 = IntStream.range(13, 15).toArray();//生产区间 [a,b)     range2=[13,14]
            double[] doubles = DoubleStream.of(5.33, 2.34, 5.32, 2.31, 3.51).toArray(); //doubles=[5.33, 2.34, 5.32, 2.31, 3.51]
    
            //1.2、IntStream的统计方法(LongStream,DoubleStream同理)
            double average = IntStream.range(13, 15).average().getAsDouble();//average=13.5
            int max = IntStream.range(13, 15).max().getAsInt(); //max=14
            int min = IntStream.range(13, 15).min().getAsInt(); //min=13
            int sum = IntStream.range(13, 15).sum(); //sum=27
        }
    

    六、Optional 类

    1、定义

    官方解释:Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。

    个人解释:换言之就是把变量转成Optional对象,其中null都转成Optional.empty()(就是一个空的Optional对象),然后就可以对Optional对象进行操作。好处就是发现null可选择抛出异常。

    2、设计原因

    不用显式进行空值检测。解决空指针异常。避免null。

    3、使用

    public class OptionalTest {
        public static void main(String args[]){
            handleParam(null,1);//后面两个参数自己随意写
    
        }
        //处理参数举例子
        static void handleParam(String a,Integer b){
            //对于处理string参数
            Optional<String> aOpt = Optional.ofNullable(a); //允许参数空。如果非空返回一个包含引用Optional实例,否则返回Optional.empty()。
            System.out.println(aOpt.isPresent());//输出aOpt是否为空
            a=aOpt.orElse("defaultValue");//如果为空,就给一个默认值defaultValue
            System.out.println(a);
    
            //同理对于处理int参数
            Optional<Integer> bOpt = Optional.of(b);//不允许参数空,不然会抛出异常
            b=bOpt.get();//get的时候不允许变量为空
            System.out.println(b);
        }
    }
    

    六、日期时间类

    1、定义

    加强对日期与时间的处理。

    2、设计原因

    在旧版的 Java 中,日期时间 API 存在诸多问题

    • (1)非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
    • (2)设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。
      java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
    • (3)时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

    3、使用

     public static void testTime() {
    
            // 当前详细时间
            LocalDateTime currentTime = LocalDateTime.now(); //currentTime =  2019-05-30T15:05:46.408
            // 当前年月日
            LocalDate date1 = currentTime.toLocalDate(); //date1 = 2019-05-30
    
            //获取详细时间的月日秒
            Month month = currentTime.getMonth();//month=MAY
            int day = currentTime.getDayOfMonth();//day=30
            int seconds = currentTime.getSecond();//seconds=46
    
            //替换详细时间的年月
            LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);//date2 = 2012-05-10T15:05:46.408
    
            //自定义年月日
            LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);//date3= 2014-12-12
    
            //自定义时分
            LocalTime date4 = LocalTime.of(22, 15); //date4 = 22:15
    
            //解析字符串
            LocalTime date5 = LocalTime.parse("20:15:30"); //date5 = 20:15:30
            LocalDateTime date6 = LocalDateTime.parse("2019-05-30T15:05:46.408");//date6 = 2019-05-30T15:05:46.408
    
    
            // 获取当前时间日期
            ZonedDateTime date7 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");//date6=2015-12-03T10:15:30+08:00[Asia/Shanghai]
            // 获取时区ID
            ZoneId id = ZoneId.of("Europe/Paris");//id= Europe/Paris
            //获取默认时区
            ZoneId defaultZone = ZoneId.systemDefault();//defaultZone=sia/Shanghai
        }
    

    七、Base64

    1、定义

    官方解释:Base64工具类提供了一套静态方法获取下面三种BASE64编码器和解码器:

    • 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
    • URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
    • MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用' '并跟随' '作为分割。编码输出最后没有行分割。

    2、使用

    public static void main(String args[]) {
           try {
    
               // 使用基本编码
               String base64encodedString = Base64.getEncoder().encodeToString("我是测试字符串".getBytes("utf-8"));//base64encodedString = "5oiR5piv5rWL6K+V5a2X56ym5Liy"
    
               // 解码
               byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
               String base64decodedStr= new String(base64decodedBytes, "utf-8");//base64decodedStr = "我是测试字符串"
    
               //使用URL编码
               String urlEncodedString = Base64.getUrlEncoder().encodeToString("testUrl?java8".getBytes("utf-8"));//urlEncodedString = "VHV0b3JpYWxzUG9pbnQ_amF2YTg="
    
    
               //使用MIME编码
               String mimeEncodedString = Base64.getMimeEncoder().encodeToString(("QWERTYUIOP-ASDFGHJKL-ZXCVBNM").getBytes("utf-8"));//mimeEncodedString = "UVdFUlRZVUlPUC1BU0RGR0hKS0wtWlhDVkJOTQ=="
    
           }catch(UnsupportedEncodingException e){
               System.out.println("Error :" + e.getMessage());
           }
       }
    
  • 相关阅读:
    第二周作业
    20172331 《Java程序设计》第1周学习总结
    20172316《程序设计与数据结构》(下)课程总结
    哈夫曼编码测试博客
    20172316 2017-2018-2 《程序设计与数据结构》实验三报告
    20172316 2018-2019-1《程序设计与数据结构》第九周学习总结
    20172316 2018-2019-1 《程序设计与数据结构》实验二报告
    2018-2019-1 20172316《程序设计与数据结构》第八周学习总结
    20172316 2018-2019-1《程序设计与数据结构》第七周学习总结
    20172316 《程序设计与数据结构》第六周学习总结
  • 原文地址:https://www.cnblogs.com/ranandrun/p/java8.html
Copyright © 2011-2022 走看看