JDK13,不如温习下Java8

JDK13于9月17号正式GA,版本新特性可参考: https://www.oschina.net/news/109934/jdk-13-released
html

虽然JDK更新迅速,但开发者貌似并不买帐,据统计,目前仍以JDK8使用最多,预计可能还会延续好长一段时间。虽然JDK版本已至13,但对Java8的新特性,掌握程度如何呢?
本文对Java8的主要特性进行了梳理。供温习参考。java

1. 接口默认方法

之前的接口只容许有抽象方法(没有实现体),java8中提供了接口默认方法支持,便可以提供方法的默认实现,实现类能够直接继承,也能够覆盖。默认方法主要解决接口的修改致使现有实现类不兼容的问题。python

@RunWith(SpringRunner.class) @SpringBootTest public class InterfaceDefaultFunctionTest { public interface MyFunction<T> { T func(T t); //默认方法
        default int func2(T t){ return t.hashCode(); } //静态方法
        static<T> void print(T t) { System.out.println(t); } } @Test public void testInterface(){ MyFunction<String> myFunction = new MyFunction<String>(){ @Override public String func(String s) { return s.toUpperCase(); } }; System.out.println(myFunction.func("abc")); System.out.println(myFunction.func2("abc")); LambdaTest.MyFunction.print("efg"); } }

 

默认方法经过关键字 default 声明。同时也能够在接口中定义静态方法。 数组

2. 函数式接口

函数式接口就是有且仅有一个抽象方法的接口(能够有其它非抽象方法),如1所示代码中 MyFunction 就是一个函数式接口,只有一个抽象方法 func, 其它非抽象方法如默认方法 func2, 静态方法 print 不影响其函数式接口的特性。安全

函数式接口可使用注解 @FunctionalInterface 标注,该注解会去检查接口是否符合函数式接口的规范要求,不符合的话IDE会给出提示。微信

java中内置了一些函数式接口,app

函数式接口 描述
Consumer 包含方法 void accept(T t), 对类型为T的对象t进行操做
Supplier 包含方法 T get(),返回类型为T的对象
Function<T,R> 包含方法 R apply(T t),对类型为T的对象进行操做,返回类型R的对象
Predicat 包含方法 boolean test(T t), 判断类型为T的对象是否知足条件

以及基于这些接口的其它变种或子接口,如BiConsumer<T,U>,BiFunction<T,U,R>等。还有如Runnable,Callable等接口,也属于函数式接口 —— 都只有一个抽象方法。ide

@FunctionalInterface public interface BiConsumer<T, U> { void accept(T t, U u); default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) { Objects.requireNonNull(after); return (l, r) -> { accept(l, r); after.accept(l, r); }; } }

 

3. Lambda表达式

lambda表达式实质就是一个匿名函数,在python中很常见,java到了jdk8提供了支持。函数

lambda表达式的格式形如: (参数) -> {方法体语句},当参数只有一个时,左边小括号能够省略,当方法体语句只有一条时,右边大括号能够省略。工具

Java的lambda表达式基本上是对函数式接口实现的一种简化 —— 用lambda表达式直接代替一个函数式接口的具体实现(抽象方法的实现)。当咱们使用jdk8在IDE中编写1中代码时,IDE会给出提示,
lambda-warn

匿名实现类能够用lambda表达式替换。上述代码使用lambda表达式替换可调整为,

@Test public void testInterface(){ MyFunction<String> myFunction = s -> s.toUpperCase(); System.out.println(myFunction.func("abc")); System.out.println(myFunction.func2("abc")); }

 

 lambda表达式甚至可做为方法参数传入(实质也是做为一个函数式接口的实现类实例)

@FunctionalInterface public interface MyFunction<T> { T func(T t); } public void print(MyFunction<String> function, String s){ System.out.println(function.func(s)); } @Test public void testInterface(){ //将lambda表达式做为方法参数传入
   print((String s) -> s.toUpperCase(), "abc"); }

 

局部变量在lambda表达式中是只读的,虽可不声明为final,但没法修改。如

@Test public void testInterface(){ int i = 1; //lambda表达式中没法修改局部变量i,将报编译错误
    print((String s) -> {i = i+10; return s.toUpperCase();}, "abc"); }

 

4. 方法引用 

当须要使用lambda表达式时,若是已经有了相同的实现方法,则可使用方法引用来替代lambda表达式,几种场景示例以下。

@RunWith(SpringRunner.class) @SpringBootTest public class FunctionReferenceTest { @Test public void testFunctionReference() { // 实例::实例方法
        Consumer<String> consumer = s -> System.out.println(s); //lambda表达式
        Consumer<String> consumer2 = System.out::println; //方法引用
        consumer.accept("abc"); consumer2.accept("abc"); //类::静态方法
        Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y); //lambda表达式
        Comparator<Integer> comparator2 = Integer::compare; //方法引用
        System.out.println(comparator.compare(10, 8)); System.out.println(comparator2.compare(10, 8)); //类::实例方法, 当引用方法是形如 a.func(b)时,用类::实例方法的形式
        BiPredicate<String, String> biPredicate = (a, b) -> a.equals(b); //lambda表达式
        BiPredicate<String, String> biPredicate2 = String::equals; //方法引用
        System.out.println(biPredicate.test("abc", "abb")); System.out.println(biPredicate2.test("abc","abb")); //type[]::new 数组引用
        Function<Integer,Integer[]> fun= n-> new Integer[n]; //lambda表达式
        Function<Integer,Integer[]> fun2=Integer[]::new; //方法引用
        System.out.println(fun.apply(10)); System.out.println(fun2.apply(10)); //构造器引用
        Function<String,String> func = n-> new String(n); //lambda表达式
        Function<String,String> func2 = String::new; //方法引用
        System.out.println(func.apply("aaa")); System.out.println(func2.apply("aaa")); } }

 

5. Stream API 

Stream与lambda应该是java8最重要的两大特性。Stream 对集合的处理进行了抽象,能够对集合进行很是复杂的查找、过滤和映射等操做。提供了一种高效的且易于使用的处理数据的方式。
Stream的三个特性:

  • Stream自己不会存储元素
  • Stream不会改变操做对象(即集合),会返回一个新的Stream
  • Stream的中间操做不会马上执行,而是会等到须要结果的时候才执行

Java8 的Collection接口包含了两个方法 stream(), parallelStream(), 分别返回一个顺序流与一个并行流,全部Collection类型(如List, )的对象能够调用这两个方法生成流。
Java8 的Arrays类也提供了 stream(T[] array)等方法用以生成流。也可使用静态方法 Stream.iterate() 和 Stream.generate() 来建立无限流。

Stream的中间操做包括

操做 描述
filter(Predicate p) 接收 Lambda , 从流中过滤出知足条件的元素
distinct() 经过hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使元素的个数不超过给定数量
skip(long n) 跳过前面的n个元素,若流中元素不足n个,则返回一个空流
map(Function f) 将每一个元素使用函数f执行,将其映射成一个新的元素
mapToDouble(ToDoubleFunction f) 将每一个元素使用f执行,产生一个新的DoubleStream流
mapToInt(ToIntFunction f) 将每一个元素使用f执行,产生一个新的IntStream流
mapToLong(ToLongFunction f) 将每一个元素使用f执行,产生一个新的LongStream流
flatMap(Function f) 将流中的每一个值都经过f转换成另外一个流,而后把全部流链接成一个流
sorted() 按天然顺序排序,产生一个新流
sorted(Comparator comp) 根据比较器排序,产生一个新流
allMatch(Predicate p) 判断是否匹配全部元素
anyMatch(Predicate p) 判断是否匹配至少一个元素
noneMatch(Predicate p) 判断是否没有匹配任意元素
findFirst() 返回第一个元素
findAny() 返回任意一个元素
reduce(T iden, BinaryOperator b) 对流中的元素进行reduce操做,返回T类型对象
reduce(BinaryOperator b) 对流中的元素进行reduce操做,返回Optional对象

Stream的终止操做包括

操做 描述
count() 返回元素总数
max(Comparator c) 返回最大值
min(Comparator c) 返回最小值
forEach(Consumer c) 内部迭代调用Consumer操做
collect(Collector c) 将流转换为其余形式,通常经过Collectors来实现

Stream使用示例

@Test public void testStream() { List<User> list = new ArrayList<>(); //转换为List,这里没啥意义,仅作示范
    List<User> users = list.stream().collect(Collectors.toList()); //转换为Set
    Set<User> users1 = list.stream().collect(Collectors.toSet()); //转换为Collection
    Collection<User> users2 = list.stream().collect(Collectors.toCollection(ArrayList::new)); //计数
    long count = list.stream().collect(Collectors.counting()); //求和
    int total = list.stream().collect(Collectors.summingInt(User::getAge)); //求平均值
    double avg= list.stream().collect(Collectors.averagingInt(User::getAge)); //获取统计对象,经过该统计对象可获取最大值,最小值之类的数据
    IntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(User::getAge)); //将值经过","拼接
    String str= list.stream().map(User::getName).collect(Collectors.joining(",")); //最大值
    Optional<User> max= list.stream().collect(Collectors.maxBy(Comparator.comparingInt(User::getAge))); //最小值
    Optional<User> min = list.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getAge))); //从累加器开始,对指定的值,这里是年龄,进行sum的reduce操做
    int t =list.stream().collect(Collectors.reducing(0, User::getAge, Integer::sum)); //对转换的结果再进行处理
    int how = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); //分组
    Map<String, List<User>> map= list.stream().collect(Collectors.groupingBy(User::getName)); //根据条件进行分区
    Map<Boolean,List<User>> vd= list.stream().collect(Collectors.partitioningBy(u -> u.getName().startsWith("W"))); }

 

6. Optional类 

Optional是一个容器类,能够避免显式的null判断,基本使用示例以下

@RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void testOptional(){ // of 不容许传入null值,不然抛出NPE
        Optional<Integer> optional = Optional.of(new Integer(10)); System.out.println(optional.get()); // ofNullable 容许传入null,可是直接调用get会抛出NoSuchElementException异常, // 可经过isPresent判断是否存在值
        Optional<Integer> optional1 = Optional.ofNullable(null); if(optional1.isPresent()) { System.out.println(optional1.get()); }else{ System.out.println("optional1 is empty"); } // orElse 判断是否存在值,存在则返回,不存在则返回参数里的值
        Integer value = optional1.orElse(new Integer(0)); // map方法,若是optional有值,则对值进行处理返回新的Optional, // 若是没有值则返回Optional.empty()
        optional = optional.map(x -> x*x); System.out.println(optional.get()); // 与map相似,只是要求返回值必须是Optional,进一步避免空指针
        optional = optional.flatMap(x ->Optional.of(x*x)); System.out.println(optional.get()); } }

 

7. Base64 

在java8中,Base64成为了java类库的标准,可直接使用

import java.util.Base64; @RunWith(SpringRunner.class) @SpringBootTest public class Base64Test { @Test public void testBase64(){ //base64编码
        String encode = Base64.getEncoder().encodeToString("abc".getBytes()); System.out.println(encode); //base64解码
        System.out.println(new String(Base64.getDecoder().decode(encode))); } }

 

8. 日期时间类 

之前的Date类是非线程安全的,而且一些经常使用的日期时间运算须要本身编写util工具类。java8推出了java.time包,里面包含了如 LocalDate, LocalTime, LocalDateTime等类,可方便地进行日期时间的运算,如日期间隔、时间间隔,日期时间的加减,格式化等等。

 

—————————————————————————————
做者:空山新雨
欢迎关注个人微信公众号:jboost-ksxy
微信公众号

原文出处:https://www.cnblogs.com/spec-dog/p/11555352.html

相关文章
相关标签/搜索