zoukankan      html  css  js  c++  java
  • JDK1.8-新特性

    1. 背景

    Java8发行版是自Java5以来最具革命性的版本,Java8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。

    2. Java语言的新特性

    2.1 Lambda表达式与Functional接口

    Lambda表达式(称为闭包)是整个Java8发行版中最受期待的在Java语言层面上的改变。

    Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)或者把代码看成数据。

    在最简单的形式中,一个lambda可以由逗号分隔的参数列表、->符号与函数体三部分表示。

    String sep = ",";
    Arrays.asList("a", "b", "c").forEach((String e) -> { System.out.print(e + sep); });  

    Lambda可以引用类的成员变量与局部变量(如果这些变量不是final的话,它们会被隐含的转维final,这样效率更高),如上面代码中的变量sep。

    Lambda可能会返回一个值,返回值的类型也是有编译器推测出来的。如果Lambda的函数体只有一行的话,那么没有必要显式使用return语句。下面两个代码是等价的:

            Arrays.asList("a", "b", "c").sort((e1, e2) -> e1.compareTo(e2));
    
            Arrays.asList("a", "b", "c").sort((e1, e2) -> {
                int result = e1.compareTo(e2);
                return result;
            });  

    语言设计者思考如何使现有的函数友好地支持Lambda。最终采取的方法是:增加函数式接口的概念。

    函数式接口就是一个具有一个方法的普通接口。像这样的接口,可以被隐式转换为Lambda表达式。

    为了解决函数式接口的脆弱性,Java8增加了一种特殊的注解@FunctionalInterface(Java8中所有类库的已有接口都添加了@FunctionalInterface注解)

    @FunctionalInterface
    interface Functional {
        void method();
    }  

    注意:默认方法和静态方法并不影响函数式接口的契约。

    Lambda是Java8最大的卖点,它具有吸引越来越多程序员到Java平台的潜力,并且能够在纯Java语言环境中提供一种优雅的方式来支持函数式编程。

    2.2 接口的默认方法与静态方法

     Java8用默认方法与静态方法这两个新概念来扩展接口的声明。

    默认方法使得接口可以包含实现代码。但与传统的接口又有些不一样,它允许在已有的接口中添加新方法,而同时又保持了与旧版本代码的兼容性。

    默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是美容恩方法则没有这个要求。相反,每个接口都必须提供一个所谓的默认实现,这样所有的接口实现者将会默认继承它(如果有必要的话,可以覆盖这个默认实现)。

    Java8带来一个新特性是接口可以声明(并且提供实现)静态方法

    package com.jdk8.feature;
    
    import java.util.function.Supplier;
    
    interface Defaultable {
        default String notRequired() { // 用关键字default声明了一个默认方法
            return "Default Implements";
        }
    }
    
    class DefaultableImpl implements Defaultable {}// 没有覆盖notRequired方法
    
    class OverridableImpl implements Defaultable {
        @Override
        public String notRequired() {// 覆盖notRequired方法
            return "Override Implement";
        }
    }
    
    interface DefaultableFactory {
        static Defaultable create(Supplier<Defaultable> supplier) {
            return supplier.get();
        }
    }
    
    public class NewInterface {
    
        public static void main(String[] args) {
            Defaultable d = DefaultableFactory.create(DefaultableImpl::new);// 方法引用,后面提到
            System.out.println(d.notRequired());
    
            Defaultable d1 = DefaultableFactory.create(OverridableImpl::new);
            System.out.println(d1.notRequired());
    
        }
    
    }
    

    2.3 方法引用  

    方法引用提供了非常有用的语法,可以直接引用已有Java类或对象的方法或构造器。与Lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

    package com.jdk8.feature;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Supplier;
    
    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 follow(final Car another) {
            System.out.println("Following the " + another.toString());
        }
        
        public void repair() {
            System.out.println("Repaired " + this.toString());
        }
        
    }
    
    public class MethodRef {
    
        public static void main(String[] args) {
            Car car1 = Car.create(Car::new);
            List<Car> cars = Arrays.asList(car1);
            cars.forEach(Car::collide);
            cars.forEach(Car::repair);
            cars.forEach(Car.create(Car::new)::follow);
        }
    
    }  
    • 第一种方法引用是构造器引用,它的语法是Class::new,或更一般的Class<T>::new,请注意构造器没有参数;
    • 第二种方法引用是静态方法引用,它的语法是Class::static_method,方法接受一个Car类型的参数;
    • 第三种方法引用是特定类的任意对象的方法引用,它的语法是Class:method,这个方法没有参数;
    • 第四种方法引用是特定对象的方法引用,它的语法是instance::method,这个方法接受一个Car类型的参数。

    2.4 重复注解

    使用注解的一个限制是相同的注解在同一位置只能声明一次,不能声明多次。Java8引入了重复注解机制,这样相同的注解可以在同一地方声明多次。

    重复注解机制本身必须用@Repeatable注解,事实上,这并不是语言层面上的改变,更多的是编译期的技巧,底层的原理保持不变。

    package com.jdk8.feature;
    
    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;
    
    public class RepeatAnnotation {
        @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();
        }
        
        @Filter("f1")
        @Filter("f2")
        public interface Filterable {}
    
        public static void main(String[] args) {
            for (Filter f : Filterable.class.getAnnotationsByType(Filter.class)) {
                System.out.println(f.value());
            }
            System.out.println(Filterable.class.getAnnotation(Filters.class));
        }
    
    }

    运行截图:

    使用@Repeatable(Filters.class)注解的注解类Filter,Filters仅仅是Filter注解的数组,但Java编译期并不想让程序员意识到Filters的存在。这样接口Filterable就拥有了两次Filter注解。

    2.5 更好的类型推测机制

    Java8在类型推测方面有了很大的提高,在很多情况下,编译期可以推测出确定的参数类型,这样就能使代码更整洁。

    package com.jdk8.feature;
    
    public class GenericInference {
        static class Value<T> {
            public static <T> T defaultValue() {
                return null;
            }
            
            public T getOrDefault(T value, T defaultValue) {
                return (value != null) ? value : defaultValue;
            }
        }
    
        public static void main(String[] args) {
            Value<String> vs = new Value<>();
            String v = vs.getOrDefault("22", Value.defaultValue());
            System.out.println(v);
        }
    }
    

    Value.defaultValue()的参数类型可以被推测出,所有就不必明确给出。在Java7中,相同的例子将不会通过编译,正确的方式Value.<String>defaultValue().

    2.6 扩展注解的支持

    Java8扩展了注解的上下文,现在几乎可以为任何东西添加注解:局部变量,泛型类,父类与接口的实现,就连方法的异常也能添加注解。

    package com.jdk8.feature;
    
    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;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
    @interface NonEmpty {}
    
    class Holder<@NonEmpty T> extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {}
    }
    
    public class AnnotationExt {
    
        public static void main(String[] args) {
            final Holder<String> h = new @NonEmpty Holder<>();
            @NonEmpty Collection<@NonEmpty String> ss = new ArrayList<>();
            System.out.println(h + ", " + ss);
        }
    
    }
    

    ElementType.TYPE_USE和ElementType.TYPE_PARAMETER是两个新添加的用于描述适当的注解上下文的元素类型。  

    3. Java编译器的新特性  

     3.1 参数名称

    Java程序员一直在发明不同的方式使得方法参数的名字能保留在Java字节码中,并且能够在运行时获取它们。在Java8中把这个强烈要求的功能添加到语言层面(通过反射API与Parameter.getName()方法)与字节码文件(通过新版的javac -parameter选项)。

    package com.jdk8.feature;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    
    public class ParameterName {
    
        public static void main(String[] args) throws NoSuchMethodException, SecurityException {
            Method m = ParameterName.class.getMethod("main", String[].class);
            for (Parameter p : m.getParameters()) {
                System.out.println("Parameter: " + p.getName());
                System.out.println(p.isNamePresent());
            }
        }
    
    }  

     使用使用-parameter参数来编译这个类,然后运行这个类,结果如下:

     如果不使用-parameter参数来编译这个类,结果如下:

    isNamePresent()来验证是否可以获取参数的名字。

    可以通过eclipse设置编译选项: 

     4. Java类库的新特性

    Java8通过增加大量新类,扩展已有类的功能的方式来改善堆并发编程、函数式编程、日期/时间相隔操作以及其他更多方面的支持。

    4.1 Optional

    空指针异常是导致Java应用程序失败的最常见原因,收到Google Guava启发,Optional类称为Java8类库的一部分。

    Optional实际上是个容器:它可以保持类型T的值或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

    package com.jdk8.feature;
    
    import java.util.Optional;
    
    public class OptionalFeature {
    
        public static void main(String[] args) {
            Optional<String> fullName = Optional.ofNullable(null);
            System.out.println(fullName.isPresent());
            System.out.println(fullName.orElseGet(() -> "[none]"));
            System.out.println(fullName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!"));
        }
    
    }  

    如果Optional类的实例为非空值得花,isPresent返回true,否则返回false;

    4.2 Stream

    最新添加的Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,Stream API极大简化了集合框架的处理。

    package com.jdk8.feature;
    
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    enum Status {
        OPEN, CLOSED
    }
    
    final class Task {
        private final Status status;
        private final Integer points;
    
        public Task(final Status status, final Integer points) {
            this.status = status;
            this.points = points;
        }
    
        public Integer getPoints() {
            return this.points;
        }
    
        public Status getStatus() {
            return this.status;
        }
    
        @Override
        public String toString() {
            return String.format("[%s, %d]", status, points);
        }
    
    }
    
    public class StreamFeature {
    
        public static void main(String[] args) {
            final Collection<Task> tasks = Arrays.asList(new Task(Status.OPEN, 5), new Task(Status.OPEN, 13),
                    new Task(Status.CLOSED, 8));
            final long lg = tasks.stream().filter(task -> task.getStatus() == Status.OPEN).mapToInt(Task::getPoints).sum();
            System.out.println(lg);
            
            final double de = tasks.stream().parallel().map(task -> task.getPoints())
                    .reduce(0,  Integer::sum);
            System.out.println(de);
            
            final Map<Status, List<Task>> map = tasks.stream()
                    .collect(Collectors.groupingBy(Task::getStatus));
            System.out.println(map);
            
        }
    
    }  

    stream操作被分成了中间操作和最终操作这两种,中间操作返回一个新的stream对象,中间操作总是采用惰性求值方式,运行一个像filter这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生一个新的stream对象,这个新stream对象包含原始stream中符合给定谓词的所有元素。

    注意:Stream API、Lambda表达式与方法引用在接口默认方法与静态方法的配合下是Java8堆现代软件开发范式的回应。

    4.3 Date/Time API(JSR 310)

    Java8通过发布新的Date-Time API来进一步加强对日期与时间的处理。

    Java8新的Date-Time API在很大程度上收到Joda-Time的影响。

    新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

    package com.jdk8.feature;
    
    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;
    
    public class DateTimeExt {
    
        public static void main(String[] args) {
            final Clock now = Clock.systemUTC();
            System.out.println(now.instant());
            System.out.println(now.millis());
    
            final LocalDate d = LocalDate.now();
            System.out.println(d);
            final LocalDate d1 = LocalDate.now(now);
            System.out.println(d1);
    
            final LocalTime t = LocalTime.now();
            System.out.println(t);
            final LocalTime t1 = LocalTime.now(now);
            System.out.println(t1);
    
            final LocalDateTime dt = LocalDateTime.now();
            System.out.println(dt);
            final LocalDateTime dt1 = LocalDateTime.now(now);
            System.out.println(dt1);
    
            final ZonedDateTime zdt = ZonedDateTime.now();
            final ZonedDateTime zdt1 = ZonedDateTime.now(now);
            final ZonedDateTime zdt2 = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
            System.out.println(zdt);
            System.out.println(zdt1);
            System.out.println(zdt2);
    
            final LocalDateTime dtFrom = LocalDateTime.of(2014, Month.APRIL, 16, 0, 0, 0);
            final LocalDateTime dtTo = LocalDateTime.of(2015, Month.APRIL, 16, 23, 59, 59);
            final Duration dur = Duration.between(dtFrom, dtTo);
            System.out.println(dur.toDays());
            System.out.println(dur.toHours());
    
        }
    
    }
    

     执行截图:

    4.4 JavaScript引擎Nashorn

    Nashorn,一个新的JS引擎随着Java8一起发布,它允许在JVM上开发运行某些JS引用。Nashorn就是javax.script.ScirptEngine的另一种实现,并且它们俩遵循相同的规则,允许Java与JavaScript相互调用。

    package com.jdk8.feature;
    
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
    
    public class JavaScriptSupport {
    
        public static void main(String[] args) throws ScriptException {
            ScriptEngineManager sem = new ScriptEngineManager();
            ScriptEngine se = sem.getEngineByName("JavaScript");
            
            System.out.println(se.getClass().getName());
            System.out.println(se.eval("function f() {return 1;}; f() + 1"));
        }
    
    }  

    执行截图:

    4.5 Base64

    Base64编码已经成为Java8类库的标准。

    package com.jdk8.feature;
    
    import java.nio.charset.StandardCharsets;
    import java.util.Base64;
    
    public class Base64Support {
    
        public static void main(String[] args) {
            final String text = "Base64 fianlly in Java 8!";
            final String en = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
            System.out.println(en);
    
            final String de = new String(Base64.getDecoder().decode(en), StandardCharsets.UTF_8);
            System.out.println(de);
    
        }
    
    }  

     执行截图:

    Base64类同时还提供了堆URL,MIME友好的编码器与解码器:Base64.getUrlEncoder(),Base64.getUrlDecoder(),Base64.getMimeEncoder(),Base64.getMimeDecoder()

    4.6 并行数组

    Java8增加了大量的新方法来对数组进行并行处理,最重要的是parallelSort()方法,因为它可以在多核机器上极大提高数组排序的速度。

    package com.jdk8.feature;
    
    import java.util.Arrays;
    import java.util.concurrent.ThreadLocalRandom;
    
    public class ArrayParallel {
    
        public static void main(String[] args) {
            long[] arrayOfLong = new long[20000];
            Arrays.parallelSetAll(arrayOfLong, index -> ThreadLocalRandom.current().nextInt(1000000));
            Arrays.stream(arrayOfLong).limit(10).forEach(i -> System.out.println(i));
            System.out.println();
            
            Arrays.parallelSort(arrayOfLong);
            Arrays.stream(arrayOfLong).limit(10).forEach(i -> System.out.println(i));
        }
    
    }  

     执行截图:

    4.7 并发(Concurrency)

    在新增Stream机制和Lambda的基础之上,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持聚集操作。

    5. 新的Java工具

     5.1 Nashorn引擎:jjs

    jjs是个基于Nashorn引擎的命令行工具,它接受一些JS源代码为参数,并且执行这些源代码。

    5.2 类依赖分析器jdeps

    可以显示Java类的包级别或类级别的依赖。它接受一个.class文件,一个目录,或者一个jar文件作为输入。

    6. JVM的新特性

    PermGen空间被移除了,取而代之的是Metaspace。

    JVM选项-XX:PermSize与-XX:MaxPermSize分别被-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替。

     参考资料

     http://www.importnew.com/11908.html

      

      

      

  • 相关阅读:
    CodeForces 576E Paingting Edges
    CodeForces 1361D Johnny and James
    agc027_d Modulo Matrix
    agc046_f Forbidden Tournament
    MySQL架构及优化原理
    Ubuntu下无法安装pip
    WSL安装问题----wslregisterdistribution failed with error: 0x8007007b
    基础排序算法总结(一)
    透明代理、匿名代理、混淆代理、高匿代理
    常见“加密”算法之 base64
  • 原文地址:https://www.cnblogs.com/lujiango/p/7837661.html
Copyright © 2011-2022 走看看