JAVA8 新特性学习笔记

一行青芒  |  2017. 01. 29   |  阅读 1923 次
服务端

语法的新特性:

一、 接口默认方法和静态方法,给接口赋予了类的能力,增强了java的灵活性。

接口中可加入default默认方法或static静态方法。

可有多个但必须实现其方法体。Java接口中定义了默认方法,实现类不用每个方法都实现一遍了。而且不需要修改继承接口的实现类,就给接口添加了新的方法实现。其给接口赋予了抽象类的能力,增强了java的灵活性。

二、 方法引用

ClassName::methodName (静态方法 + 实例方法 都可使用)

super::methodName (超类上的实例方法使用)

Class::new (创建新实例时可使用)

TypeName[]::new 

三、 Lambda表达式(也叫做闭包)

    Collections.sort(names, (String a, String b) -> {
         return b.compareTo(a);
     });

    Collections.sort(names, (String a, String b) -> b.compareTo(a));

    Collections.sort(names, (a, b) -> b.compareTo(a));

Lambda 作用域

在lambda表达式中可以访问外层局部变量,但不能修改外层局部变量。(闭包的作用域)

和本地变量不同的是,lambda内部对于实例的字段或类的静态变量是即可读又可写。

四、 函数式接口

函数式接口有且仅有一个抽象方法,每一个lambda匹配这个抽象方法。因为static与defualt默认方法不是抽象的,所以可以在函数式接口中自由的添加。

函数式接口应该用@FunctionalInterface来注解接口,但可省略。

函数式接口的作用是使lambda表达式融入java的类型系统。每一个lambda相当于一种指定类型的函数式接口的实现。

   @FunctionalInterface
   public interface MyFuncitonalInterface {
       void fun();
   }

   MyFuncitonalInterface mf = () -> System.out.println(666);
           mf.fun();

常用javaAPI标准函数接口:

  1. Predicate 断言,一般用来判断是否满足某条件

  2. Consumer 接收一个参数,无返回值

  3. Function 接收T对象,返回R对象

  4. Supplier 类似工厂,调用时会返回一个指定类型的对象

  5. UnaryOperator 执行一元操作(与、或、非)

  6. BinaryOperator 接收两个参数,返回一个值

一些函数式接口的典型用例:

   public class Lambda {

       @FunctionalInterface
       interface Fun {
           void foo();
       }

       public static void main(String[] args) throws Exception {

           // Predicates

           Predicate<String> predicate = (s) -> s.length() > 0;

           predicate.test("foo");              // true
           predicate.negate().test("foo");     // false

           Predicate<Boolean> nonNull = Objects::nonNull;
           Predicate<Boolean> isNull = Objects::isNull;

           Predicate<String> isEmpty = String::isEmpty;
           Predicate<String> isNotEmpty = isEmpty.negate();

           // Functions

           Function<String, Integer> toInteger = Integer::valueOf;
           Function<String, String> backToString = toInteger.andThen(String::valueOf);//在toInteger加入apply结束后执行的方法,生成新的Function

           backToString.apply("123");     // "123"

           // Suppliers

           Supplier<Person> personSupplier = Person::new;
           personSupplier.get();   // new Person

           // Consumers

           Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
           greeter.accept(new Person("Luke", "Skywalker"));

           // Comparators

           Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

           Person p1 = new Person("John", "Doe");
           Person p2 = new Person("Alice", "Wonderland");

           comparator.compare(p1, p2);             // > 0
           comparator.reversed().compare(p1, p2);  // < 0

           // Runnables

           Runnable runnable = () -> System.out.println(UUID.randomUUID());
           runnable.run();

           // Callables

           Callable<UUID> callable = UUID::randomUUID;
           callable.call();
       }

   }

小结:

以上新增的语言特性使Java 能够实现一部分“函数式”的编程范式,可以用简单的函数式风格(例如filter和map)简化笨重的代码。但Java需要用类型来表示它们。如java.util.function中的Predicate、Function和Consumer接口。

java库的新特性

五、 Stream 流操作

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。

Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。

Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。

Stream的操作可以串行执行或者并行(parallelStream)执行。

常用操作:

forEach 遍历

stringCollection  
    .stream()
    .forEach(System.out::println);

Filter 过滤

过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。

stringCollection  
    .stream()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

Sort 排序

排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:

stringCollection  
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

Map 映射

中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。

stringCollection  
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);

Match 匹配

Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

boolean anyStartsWithA =  
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA);      // true  
boolean allStartsWithA =  
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA);      // false  
boolean noneStartsWithZ =  
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ);      // true  

Count 计数

计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

long startsWithB =  
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();

Reduce

这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规约后的结果是通过Optional接口表示的:

Optional<String> reduced =  
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);  

collect 获取集合

//获取年龄大于12的用户列表 
        List<User> list = users.parallelStream().filter(p -> p.age > 12) 
                .collect(Collectors.toList()); 
        System.out.println(list); 

六、 Optional 处理空指针问题

Optional内部封装了要处理的值value,对外提供了一下方法:

//============创建方法=====================================//
<T> Optional<T> empty() ;//返回空对象

<T> Optional<T> of(T value);//返回封装value的Optional对象,内部用Objects.requireNonNull,判断value为null的情况抛出NullPointerException

<T> Optional<T> ofNullable(T value);//返回一个封装value的Optional对象,value为null的情况返回空的Optional对象

//============使用方法=====================================//
T get();// 返回value,若value为nul抛出NoSuchElementException

boolean isPresent();//判断value是否存在

void ifPresent(Consumer<? super T> consumer) ;//若value非null,执行consumer.accept(value)方法

Optional<T> filter(Predicate<? super T> predicate);//若value存在并且value符合predicate的判定,返回Optional对象,否则返回空Optional对象

<U> Optional<U> map(Function<? super T, ? extends U> mapper);//若value存在执行mapper.apply(value)并返回Optional对象,否则返回空Optional对象。flatMap略(为空抛出NullPointerException)

T orElse(T other);//返回value,若value为null返回other

T orElseGet(Supplier<? extends T> other);//返回value,若value为null返回other.get()的返回值

<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X ;//返回value,若value为null抛出exceptionSupplier.get()的返回值  
//典型用例:
//获取字符串s,若为null返回""
String str = Optional.fromNullable(s).orElse("");  
//获取车辆对象,若为null则new一个
Car car = Optional.fromNullable(car).orElse(Car::new);  

其他:

  • Objects工具类, 对Object空指针问题进行了处理。

  • 数组工具类:Arrays.parallelSort 并行排序

  • Map新增方法:

map.forEach((id, val) -> System.out.println(val));

map.getOrDefault(42, "not found");  // 没有返回默认值

  • HashMap 内部结构变为:数组+链表+红黑树(解决hashCode冲突使用链地址法,将key加入链表,当链表长度大于8时转换为红黑树,引入红黑树利用红黑树快速增删改查的特点大程度优化了HashMap的性能。这个数据结构很屌但很复杂(⊙o⊙)?)

  • 多重Annotation:允许我们把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。还增加到两种新的target:@Target({ElementType.TYPEPARAMETER, ElementType.TYPEUSE})

  • 全新的时间日期API。

  • JVM的新特性:JVM内存永久区已经被metaspace替换(JEP 122)。JVM参数 -XX:PermSize 和 –XX:MaxPermSize被XX:MetaSpaceSize 和 -XX:MaxMetaspaceSize代替。

分享到

   
测试的定位