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

  

 

 

 

 

 

 

 

 

 

 

  

  

 

转载于:https://www.cnblogs.com/lujiango/p/7837661.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值