赞
踩
Java 8(又称为 jdk 1.8)是 Java 语言开发个一个主要版本
Java 8 是 oracle公司与 2014年3月发布,可以 看成是自 Java 5 以后来最有革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
JDK7及以前:只能定义全局常量和抽象方法
全局常量:
public static final的,但是书写时,可以省略不写
抽象方法:public abstract的
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
Java 8 中,你可以为接口添加 静态方法 和 默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用静态方法。你可以在标准库中找到对象 Collection/Collections或者 Path/Paths这样对的接口和类。
默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对 Collection、List、Comparator等接口提供了丰富的默认方法。
代码演示:
public class SuperClass { public void method3(){ System.out.println("SuperClass类method3 的方法"); } } /** * 除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法 */ public interface CompareA { //静态方法 public static void method1(){//public 可以省略 System.out.println("CompareA接口method1 的静态方法"); } //默认方法 public default void method2(){//public 可以省略 System.out.println("CompareA接口method2 的默认方法"); } default void method3(){ System.out.println("CompareA接口method3 的默认方法"); } default void method4(){ System.out.println("CompareA接口method4 的默认方法"); } } public interface CompareB { default void method4(){ System.out.println("CompareB接口method4 的默认方法"); } }
public class SubClassTest { public static void main(String[] args) { SubClass subClass = new SubClass(); // subClass.method1(); // 接口实现类的静态方法不能使用 //知识1:接口定义静态方法,只能通过接口来调用 CompareA.method1(); //知识2:通过实现类的对象,可以调用接口中的默认方法 //如果实现类重写接口的默认方法,调用时,仍然调用的是重写的方法 subClass.method2(); //知识3:如果子类(或者实现类)继承的父类和实现的接口中声明了同名同参数的默认方法, // 那么子类在没有重写此方法的情况下,默认调用的是父类的同名同参数的方法。 ---> 类优先原则 subClass.method3(); //知识4:如果实现类实现了多个接口,而这个接口定义了同名同参的默认方法, // 那么在实现类没有重写此方法的情况下,报错(因为实现类不知道找哪个默认方法) ---> 接口冲突 // 这就需要我们必须在实现类中重写此方法,或者 父类声明了同名同参数的方法, subClass.method4(); } } class SubClass extends SuperClass implements CompareA,CompareB { public void method2(){ System.out.println("SubClass类method2 的重写方法"); } public void method4(){ System.out.println("SubClass类method4 的重写方法"); } //知识5:然后在子类(实现类)的方法中调用父类、接口中被重写的方法 public void myMethod(){ method3();//调用自己定义的重写方法 super.method3();//调用父类中声明的方法 //调用接口中的默认方法 CompareA.super.method3(); CompareB.super.method4(); } }
执行效果:

可重复注解:
1、在MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
2、MyAnnotation的注解 和 MyAnnotations上的注解必须都有
代码演示:
@Inherited @Repeatable(MyAnnotations.class)//可重复注解 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE}) public @interface MyAnnotation { String value() default "hello"; } @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE}) public @interface MyAnnotations { MyAnnotation[] value(); } //jdk8之前的写方法: //@MyAnnotations({@MyAnnotation(value = "hi"),@MyAnnotation(value = "abc")}) @MyAnnotation(value = "hi") @MyAnnotation(value = "abc") public class Test { }
类型注解
JDK1.8之后,关于元注解 @Target的参数类型 ElementType枚举值多了两个:
TYPE_PARAMETER、TYPE_USE

在Java 8 之前,注解只是在声明的地方所使用,Java8开始,注解可以应用在任何地方
ElementType.TYPE_PARAMETER表示该注解能写在类型变量的声明语句中(如:泛型声明)ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中@Target({ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
public @interface MyAnnotation {
String value() default "hello";
}
class Generic<@MyAnnotation T>{
public void show() throws @MyAnnotation RuntimeException{
ArrayList<@MyAnnotation String> list = new ArrayList<>();
int num = (@MyAnnotation int)10L;
}
}
为什么使用 Lambda 表达式
Lambda 是一个 匿名函数,我们可把 Lambda 表达式理解为一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,是java的语言表达能力得到了提升。
代码举例:
/** * Lambda 表达式的使用举例 */ public class LambdaTest { @Test public void test1(){ Runnable r1 = new Runnable() { @Override public void run() { System.out.println("我爱中国"); } }; r1.run(); System.out.println("======= Lambda 表达式 ============="); Runnable r2 = () -> System.out.println("我爱祖国"); r2.run(); System.out.println("**************************"); Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; int compare1 = com1.compare(12, 21); System.out.println(compare1); System.out.println("======= Lambda 表达式 ============="); //Lambda表达式的写法 Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2); int compare2 = com2.compare(32, 21); System.out.println(compare2); //方法引用 Comparator<Integer> com3 = Integer :: compare; int compare3 = com3.compare(32, 32); System.out.println(compare3); } }
执行效果:

Lambda具体介绍举例:
/** * Lambda表达式的使用 * * 1. 举例:(o1,o2) -> Integer.compare(o1,o2) * 2. 格式: * -> : Lambda操作符 或 箭头操作符 * -> 左边:lambda形参列表 (其实就是接口中的抽象方法形参列表) * -> 右边:lambda体 (其实就是重写抽象方法的方法体) * * 3. Lambda表达式的使用:(分为6种情况介绍) * * 总结: * -> 左边:lambda形参列表的参数类型可以省略(类型推断):如果lambda形参列表只有一个参数,其一对()也可以省略 * -> 右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是 return语句),可以省略一对{}和 return关键字 * * 4. Lambda表达式的本质:作为函数式接口的实例 * * 5. 如果一个接口中,只声明一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface注解, * 这样可以检查它是否是一个函数式接口 * * 6. 所以以前用匿名实现类表示的现在都可以使用Lambda表达式来写 */ public class LambdaTest1 { //语法一:无参,无返回值 @Test public void test1(){ Runnable r1 = new Runnable() { @Override public void run() { System.out.println("我爱中国"); } }; r1.run(); System.out.println("======= Lambda 表达式 ============="); Runnable r2 = () -> { System.out.println("我爱祖国"); }; r2.run(); } //语法格式二:Lambda 需要一个参数,当没有返回值 @Test public void test2(){ Consumer<String> con = new Consumer<String>(){ @Override public void accept(String s){ System.out.println(s); } }; con.accept("小明和小花"); System.out.println("======= Lambda 表达式 ============="); Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("小喵和小狗"); } //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为 “类型推断” @Test public void test3(){ Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("小喵和小狗"); System.out.println("==========="); Consumer<String> con2 = (s) -> { System.out.println(s); }; con2.accept("小喵和小狗"); ArrayList<String> list = new ArrayList<>();//类型推断 int[] arr = {1,2,3,4};//类型推断 } //语法格式四:Lamda 若只需要一个参数时,参数的小括号可以省略 @Test public void test4(){ Consumer<String> con1 = (s) -> { System.out.println(s); }; con1.accept("小喵和小狗"); System.out.println("*****************"); Consumer<String> con2 = s -> { System.out.println(s); }; con2.accept("小喵和小狗"); } //语法格式五:Lambda 需要两个以上的参数,多条件执行语句,并且可以有返回值 @Test public void test5(){ Comparator<Integer> comp1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(comp1.compare(12,21)); System.out.println("*****************"); Comparator<Integer> comp2 = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(comp2.compare(23,13)); } //语法格式六:当 Lambda 体只有一条语句时,return 与大括号如有,都可以省略 @Test public void test6(){ Comparator<Integer> comp1 = (o1,o2) -> { return o1.compareTo(o2); }; System.out.println(comp1.compare(23,13)); System.out.println("*******************"); Comparator<Integer> comp2 = (o1,o2) -> o1.compareTo(o2); System.out.println(comp2.compare(36,48)); } }
自定义函数式接口:
/**
* 自定义函数式接口
*/
@FunctionalInterface//函数式接口注解
public interface MyInterface {
void method1();
//void method2();//报错
}
@FunctionalInterface注解,这样可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。在 java.util.funtion 包下定义了 Java 8 的丰富函数式接口| 函数式接口 | 参数类型 | 返回类型 | 用途 |
|---|---|---|---|
Consumer<T>消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier<T>供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get() |
Function<T,R>函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t) |
Predicate<T>断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t) |
/** * java内置4大核心函数式接口 * * 消费类接口 Consumer<T> void accept(T t) * 供给型接口 Supplier<T> T get() * 函数型接口 Function<T,R> R apply(T t) * 断定型接口 Predicate<T> boolean test(T t) */ public class LambdaTest2 { @Test public void test1(){ happyTime(500, new Consumer<Double>() { @Override public void accept(Double aDouble) { System.out.println("学习太累了,去消费火锅:" + aDouble); } }); System.out.println("============================="); happyTime(400, moeny -> System.out.println("学习太累了,去消费烤鱼:" + moeny)); } public void happyTime(double money, Consumer<Double> con){ con.accept(money); } @Test public void test2(){ List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京"); List<String> filterString = filterString(list, new Predicate<String>() { @Override public boolean test(String s) { return s.contains("京"); } }); System.out.println(filterString); System.out.println("================="); List<String> filterStr = filterString(list, str -> str.contains("京")); System.out.println(filterStr); } //根据给定规则,过滤集合中的字符串。此规则有Predicate的方法决定 public List<String> filterString(List<String> list, Predicate<String> pre){ ArrayList<String> filterList = new ArrayList<>(); for(String str : list){ if(pre.test(str)){ filterList.add(str); } } return filterList; } }
执行效果:

| 函数式接口 | 参数类型 | 返回类型 | 用途 |
|---|---|---|---|
BiFunction<T,U,R> | T,U | R | 对类型为 T,U 参数引用操作,返回 R 类型的结果。包含方法:R apply(T t, U u) |
UnaryOperator<T>(Function子接口) | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法:T apply(T t) |
BinaryOperator<T>(BiFunction子接口) | T,T | T | 对类型为T的对象进二元运算,并返回T类型的结果。包含方法:T apply(T t1, T t2) |
BiConsumer<T,U> | T,U | void | 对类型为T,U 参数引用操作。包含方法:void accept(T t, U u) |
BiPredicate<T,U> | T,U | boolean | 包含方法:boolean test(T t, U u) |
ToIntFunction<T>ToLongFunction<T>ToDoubleFunction<T> | T | intlongdouble | 分别计算int、long、double值的函数 |
IntFunction<R>LongFunction<R>DoubleFunction<R> | intlongdouble | R | 参数分别为int、long、double类型的函数 |
::” 将类(或对象)与方法名分割开来对象 :: 实例方法名类 :: 静态方法名类 :: 实例方法名/** * 方法引用的使用 * * 1. 使用情境:当要传递给 Lambda体的操作,已经有实现的方法了,可以使用方法引用 * * 2. 方法引用,本质上就是Lambda表达式,而lambda表达式作为函数式接口的实例。所以 * 方法引用,也是函数式接口的实例 * * 3. 使用格式: 类(对象) :: 方法名 * * 4. 具体分为如下的三种情况: * 情况1 对象 :: 非静态方法 * 情况2 类 :: 静态方法 * * 情况3 类 :: 非静态方法 * * 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的 * 形参列表和返回值类型相同! */ @SuppressWarnings({"all"}) public class MethodRefTest { // 情况一:对象 :: 实例化 // Consumer中的 void accept(T t) // PrintStream中的 void println(T t) @Test public void test1(){ Consumer<String> con1 = str -> System.out.println(str); con1.accept("中国你好"); System.out.println("================="); PrintStream ps = System.out; Consumer<String> con2 = ps ::println; con2.accept("中国,hello"); } // Supplier中的 T get() // Employee中的 String getName() @Test public void test2(){ Employee emp = new Employee(1001,"Tom",23,5000); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("================="); Supplier<String> sup2 = emp :: getName; System.out.println(sup2.get()); } // 情况二:类 :: 静态方法 // Comparator中的 int compare(T t1,T t2) // Integer中的 int compare(T t1,T t2) @Test public void test3(){ Comparator<Integer> com1 = (o1,o2) -> Integer.compare(o1,o2); System.out.println(com1.compare(12,23)); System.out.println("================="); Comparator<Integer> com2 = Integer ::compare; System.out.println(com1.compare(25,13)); } // Function中的 R apply(T t) // Math中的 Long round(Double d) @Test public void test4(){ //原写法 Function<Double,Long> fun = new Function<Double, Long>() { @Override public Long apply(Double aDouble) { return Math.round(aDouble); } }; //lambda写法 Function<Double,Long> fun1 = d -> Math.round(d); System.out.println(fun1.apply(12.3)); System.out.println("================="); //方法引用 Function<Double,Long> fun2 = Math ::round; System.out.println(fun2.apply(12.6)); } // 情况三:类 :: 实例方法 (有难度) // Comparator中的 int compare(T t1,T t2) // String中的 int t1.compareTo(t2) @Test public void test5(){ Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("================="); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } // BiPredicate中的 boolean test(T t1,T t2) // String中的 boolean t1.equals(t2) @Test public void test6(){ BiPredicate<String,String> pre1 = (s1, s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("================="); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } // Function中的 R apply(T t) // Employee中的 String getName() @Test public void test7(){ Employee emp = new Employee(1002,"jack",23,6000); Function<Employee,String> fun1 = e -> e.getName(); System.out.println(fun1.apply(emp)); System.out.println("================="); Function<Employee,String> fun2 = Employee :: getName; System.out.println(fun2.apply(emp)); } }
加数组引用
/** * 一、构造器应用 * 和方法引用类似,函数式接口的抽象方法的形参和构造器的形参列表一致。 * 抽象方法的返回值类型即为构造器所属的类的类型 * * 二、数组引用 * 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。 */ @SuppressWarnings({"all"}) public class ConstructorRefTest { //构造器引用 //Supplier中的 T get() //Employee的空参构造器:Employee() @Test public void test1(){ Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get() { return new Employee(); } }; System.out.println(sup.get()); System.out.println("**********************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*********构造器引用*************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //Function中的 R apply(T t) @Test public void test2(){ Function<Integer,Employee> fun1 = id -> new Employee(id); Employee employee = fun1.apply(1001); System.out.println(employee); System.out.println("*********构造器引用*************"); Function<Integer,Employee> fun2 = Employee :: new; Employee employee1 = fun1.apply(1002); System.out.println(employee1); } //BiFunction中的 R apply(T t,U u) @Test public void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"tom")); System.out.println("*********构造器引用*************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"jack")); } //数组引用 //Function中的 R apply(T t) @Test public void test4(){ Function<Integer,String[]> fun1 = length -> new String[length]; String[] arr1 = fun1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*********数组引用引用*************"); Function<Integer,String[]> fun2 = String[] :: new; String[] arr2 = fun1.apply(10); System.out.println(Arrays.toString(arr2)); } }
执行结果:

Java8 中有两大最为重要的改变。第一个是 Lambda 表达式;另一个是 Stream API
Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为 Stream API 可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码
Stream 是 Java8 中处理集合的关系抽象的概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行操作,就类似与使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效率且易于使用的处理数据的方法。
为什么要使用 Stream API
实际开发中,项目中多数据源都来自于 Mysql,Oracle等。但现在数据源可以更多,有 MongDB,Radis等,而这些NoSQL的数据库就需要Java层面去处理
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
什么是 Stream
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,Stream讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,它们会返回一个持有结果的新Stream
③Stream 操作是延迟执行的。这意味着它们会等到需要结果的时候才执行。
1、创建 Stream
一个数据源(如:集合、数组),获取一个流
2、中间操作
一个中间操作链,对数据源的数据进行处理
3、终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

Java8 中 Collection 接口被扩展,提供了两个获取流的方法:
default Stream<E> stream(): 返回一个顺序流default Stream<E> parallelStream():返回一个并行流Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static <T> Stream<T> stream(T[] array):返回一个流重载形式,能够处理对应基本类型的数组:
public static InStream stream(int[] array)public static LongStream stream(long[] array)public static DoubleStream stream(double[] array)可以调用 Stream类静态方法 of(),通过显示值创建一个流。它可以接收任意数量的参数
public static<T> Stream<T> of(T... values):返回一个流可以使用静态方法 Stream.iterate() 和 Stream.generate(),创建无限流
迭代
public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f)
生成
public static<T> Stream<T> generate(Supplier<T> s)
/** * 1. Stream 关注的是对数据的运算,与CPU打交道 * 集合关注的是数据存储,与内存打交道 * * 2. * ①Stream 自己不会存储元素。 * ②Stream 不会改变源对象。相反,它们会返回一个持有结果的新Stream * ③Stream 操作是延迟执行的。这意味着它们会等到需要结果的时候才执行 * * 3. Stream 执行流程: * ①Stream的实例化 * ②一系列的中间操作(过滤、映射、..) * ③终止操作 * * 4. 说明: * 4.1 一个中间操作链,对数据源的数据进行处理 * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用 * * 测试Stream的实例化 * */ public class StreamAPITest { //创建 Stream方式一:通过集合 @Test public void test1(){ List<Employee> employees = EmployeeDate.getEmployees(); // default Stream<E> stream(): 返回一个顺序流 Stream<Employee> stream = employees.stream(); // default Stream<E> parallelStream():返回一个并行流 Stream<Employee> employeeStream = employees.parallelStream(); } //创建 Stream方式二:通过数组 @Test public void test2(){ int[] arr = new int[]{1,2,3,4,5}; //调用Arrays类的 static <T> Stream<T> stream(T[] array):返回一个流 IntStream stream = Arrays.stream(arr); Employee emp1 = new Employee(1001,"tom"); Employee emp2 = new Employee(1002,"mary"); Employee[] arr1 = new Employee[]{emp1,emp2}; Stream<Employee> stream1 = Arrays.stream(arr1); } //创建 Stream方式三:通过Stream的 of() @Test public void test3(){ //public static<T> Stream<T> of(T... values):返回一个流 Stream<Integer> of = Stream.of(1,2,3,4,5); } //创建 Stream方式四:通过无限流 @Test public void test4(){ //迭代 //public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f) //遍历前10个偶数 Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out :: println); //生成 //public static<T> Stream<T> generate(Supplier<T> s) Stream.generate(Math::random).limit(10).forEach(System.out :: println); } }
执行效果:

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”
1 - 筛选与切面
| 方法 | 描述 |
|---|---|
filter<Predicate p> | 接收 Lambda,从流中排除某些元素 |
distinct() | 筛选,通过流所产生元素的 hashCode() 和 equals() 去除重复的元素 |
limit<long maxSize> | 截断流,使其元素不超过给定的数量 |
skip<long n> | 跳过元素,返回一个扔掉了前n个元素的流。若流中的元素不足n个,则返回一个空流。与 limit(n) 互补 |
2 - 映 射
| 方法 | 描述 |
|---|---|
map<Function f> | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
| mapToDouble<ToDoubleFunction f> | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
| mapToInt<ToIntFunction f> | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
| mapToLong<ToLongFunction f> | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
flatMap<Function f> | 接收一个函数作为参数,该流中的每个值换成另外一个流,然后把所有流连接成一个流。 |
3 - 排 序
| 方法 | 描述 |
|---|---|
sorted() | 产生一个新流,其中按自然排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
/** * 测试stream的中间操作 */ @SuppressWarnings({"all"}) public class StreamAPITest1 { //1 - 筛选与切面 @Test public void test1() { List<Employee> list = EmployeeDate.getEmployees(); //filter<Predicate p> 接收 Lambda,从流中排除某些元素 Stream<Employee> stream = list.stream(); //练习:查询员工中薪资大于 7000的员工信息 stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println); System.out.println(); //limit<long maxSize> 截断流,使其元素不超过给定的数量 list.stream().limit(3).forEach(System.out::println); System.out.println(); //skip<long n> 跳过元素,返回一个扔掉了前n个元素的流。若流中的元素不足n个,则返回一个空流。与 limit(n) 互补 list.stream().skip(3).forEach(System.out::println); System.out.println(); //distinct() 筛选,通过流所产生元素的 hashCode() 和 equals() 去除重复的元素 list.stream().distinct().forEach(System.out::println); } //映射 @Test public void test2() { //map<Function f> 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); //练习1:获取员工姓名长度大于3的员工的姓名 List<Employee> employees = EmployeeDate.getEmployees(); Stream<String> namesStream = employees.stream().map(Employee::getName); namesStream.filter(name -> name.length() > 3).forEach(System.out::println); System.out.println(); //练习2: Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream); streamStream.forEach(s -> { s.forEach(System.out::println); }); System.out.println(); //flatMap<Function f> 接收一个函数作为参数,该流中的每个值换成另外一个流,然后把所有流连接成一个流。 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); } //将字符串中的多个字符构造成的集合转为为对应的Stream的实例 public static Stream<Character> fromStringToStream(String str) {//aa ArrayList<Character> list = new ArrayList<>(); for (Character character : str.toCharArray()) { list.add(character); } return list.stream(); } //3 - 排 序 @Test public void test3() { //sorted() 产生一个新流,其中按自然排序 List<Integer> list = Arrays.asList(12,43,65,87,0,-98,7); list.stream().sorted().forEach(System.out::println); System.out.println(); //List<Employee> employees = EmployeeDate.getEmployees(); // 抛异常,原因:Employee没有实现Comparable接口 // employees.stream().sorted().forEach(System.out::println); //sorted(Comparator com) 产生一个新流,其中按比较器顺序排序 List<Employee> employees = EmployeeDate.getEmployees(); // employees.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge())) // .forEach(System.out::println); //多条件 employees.stream().sorted((e1,e2) -> { int compare = Integer.compare(e1.getAge(), e2.getAge()); if(compare != 0){ return compare; }else { return -Double.compare(e1.getSalary(), e2.getSalary());//薪资 大到小 } }).forEach(System.out::println); } }
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void
流进行了终止操作后,不能再次使用
1 - 匹配与查找
| 方法 | 描述 |
|---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFist() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代 — 它帮你把迭代做了) |
/** * 测试Stream 的终止操作 */ public class StreamTest2 { //1 - 匹配与查找 @Test public void test1(){ List<Employee> employees = EmployeeDate.getEmployees(); //allMatch(Predicate p) 检查是否匹配所有元素 //练习:是否所有员工的年龄都大于18岁 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println(allMatch); //anyMatch(Predicate p) 检查是否至少匹配一个元素 //练习:是否存在员工的工资大于 10000 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000); System.out.println(anyMatch); //noneMatch(Predicate p) 检查是否没有匹配所有元素 //练习:是否存在员工姓 “雷” boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷")); System.out.println(noneMatch); //findFist() 返回第一个元素 Optional<Employee> optional = employees.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge())) .findFirst(); System.out.println(optional); //findAny() 返回当前流中的任意元素 Optional<Employee> any = employees.parallelStream().findAny(); System.out.println(any); } @Test public void test2(){ List<Employee> employees = EmployeeDate.getEmployees(); //count() 返回流中元素总数 long count = employees.stream().filter(e -> e.getSalary() > 5000).count(); System.out.println(count); //max(Comparator c) 返回流中最大值 //练习:返回最高的工资 Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> max = salaryStream.max(Double::compare); System.out.println(max); //min(Comparator c) 返回流中最小值 //练习:返回最低工资的员工 Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(min); //forEach(Consumer c) 内部迭代 employees.stream().forEach(System.out::println); //使用集合的遍历操作 //employees.forEach(System.out::println); } }
2 - 归约
| 方法 | 描述 |
|---|---|
reduce(T iden,BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional<T> |
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因为 Google 用它进行网络搜索而出名。
//2 - 归约 @Test public void test3(){ //reduce(T iden,BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T //练习:计算1-10的自然数 List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream().reduce(0, Integer::sum); System.out.println(sum); //reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T> //练习:计算公司所有员工工资的总和 List<Employee> employees = EmployeeDate.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary); //Optional<Double> sumMoney = salaryStream.reduce(Double::sum); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney); }
3 - 收集
| 方法 | 描述 |
|---|---|
collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现,用于给 Stream中元素做汇总 |
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外,Collectors 实用类通过了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
| 方法 | 返回类型 | 作用 | 示例 |
|---|---|---|---|
toList | List<T> | 把流中元素收集到List | List<Employee> emps = list.stream().collect(Collectors.toList()); |
toSet | Set<T> | 把流中元素收集到Set | Set<Employee> emps = list.stream().collect(Collectors.toSet()); |
toCollection | Collection<T> | 把流中元素收集到创建的集合 | Collection<Employee> emps = list.stream().collect(Collectors.toCollection(ArrayList::new)); |
| counting | Long | 计算流中的个数 | long count = list.stream().collect(Collectors.counting()); |
| summingInt | Integer | 对流中元素的整数属性求和 | int total = list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
| averagingInt | Double | 计算流中元素Integer属性平均值 | double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
| summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值。如:平均值 | int summaryStatisticsiss = list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
| joining | String | 连接流中每个字符串 | String str = list.stream().map(Employee::getName).collect(Collectors.joining()); |
| maxBy | Optional<T> | 根据比较器选择最大值 | Optional<Emp> max = list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
| minBy | Optional<T> | 根据比较器选择最小值 | Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
| reducing | 归约产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator与流中元素逐个结合,从而归约成单个值 | int total = list.stream().collect(Collectors.reducing(0,Employee::getSalary,Integer::sum)); |
| collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器。对其结果转换函数 | int how = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),List::size)); |
| groupingBy | Map<K,List<T>> | 根据某属性值对流分组,属性为K,结果为V | Map<Emp.Status,List<Emp>> map = list.stream().collect(Collectors.groupingBy(Employee::getManage)); |
| partitioningBy | Map<Boolean,List<T>> | 根据true或者false进行分区 | Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
//3 - 收集 @Test public void test4(){ List<Employee> employees = EmployeeDate.getEmployees(); //collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给 Stream中元素做汇总 //练习: 查找工资大于6000的员工,结果返回一个list或set //List<Employee> collect = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); Set<Employee> collect = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); collect.forEach(System.out::println); } //5 - joining("分隔符")(集合拼接指定字符串) @Test public void test4(){ List list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); String str = (String) list.stream().collect(Collectors.joining(",")); System.out.println(str);//a,b,c }
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经称为Java 8类库的一部分。
Optional<T>类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent() 方法会返回 true,调用 get() 方法会返回该对象。
Optional提供很多有用的方法,这样我们就不用显式进行空指针检测。
创建Optional类对象的方法:
Optional.of(T t):创建一个 Optional 实例,t 必须非空Optional.empty():创建一个空的 Optional 实例Optional.ofNullable(T t):t 可以为 null判断Optional容器中是否包含对象:
boolean isPresent():判断是否包含对象void isPresent(Cousumer<? super T> consumer):如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它获取Optional容器的对象:
T get():如果调用对象包含值,返回该值,否则抛出异常T orElse(T other):如果有值则将其返回,否则返回指定的other对象T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回有Supplier接口实现通过的对象。T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出有Supplier接口通过的异常。/** * Optional类:为了在程序中避免出现空指针异常而创建 * 常用方法:ofNullable(T t) * orElse(T t) */ public class OptionalTest { /** * Optional.of(T t):创建一个 Optional 实例,t 必须非空 * Optional.empty():创建一个空的 Optional 实例 * Optional.ofNullable(T t):t 可以为 null */ @Test public void test1(){ Girl girl = new Girl(); girl = null; //of(T t):保证t是非空的 //Optional<Girl> optional1 = Optional.of(girl); //ofNullable(T t):t 可以为 null Optional<Girl> optionalGirl = Optional.ofNullable(girl); System.out.println(optionalGirl);//Optional.empty //orElse(T t1):如果当前的Optional内部封装的t是非空的,则返回内部的t // 如果内部t是空的,则返回orElse()方法中的参数t1 Girl girl1 = optionalGirl.orElse(new Girl("小芳")); System.out.println(girl1);//Girl{name='小芳'} } //可能会报空指针异常 public String getGirlName(Boy boy){ return boy.getGirl().getName(); } @Test public void test2(){ Boy boy = new Boy(); String girlName = getGirlName(boy); System.out.println(girlName); } //优化以后的getGirlName public String getGirlName1(Boy boy){ if(boy != null){ Girl girl = boy.getGirl(); if(girl != null){ return girl.getName(); } } return null; } @Test public void test3(){ Boy boy = new Boy(); String girlName = getGirlName1(boy); System.out.println(girlName); } //使用Optional类的getGirlName public String getGirlName2(Boy boy){ Optional<Boy> boyOptional = Optional.ofNullable(boy); //此时的boy1一定非空 Boy boy1 = boyOptional.orElse(new Boy(new Girl("小翠"))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); //此时的girl1一定非空 Girl girl1 = girlOptional.orElse(new Girl("小花")); return girl1.getName(); } @Test public void test4(){ Boy boy = null;//小翠 boy = new Boy();//小花 boy = new Boy(new Girl("小倩"));//小倩 String girlName = getGirlName2(boy); System.out.println(girlName); } }
自从2017年9月21日 Java 9 正式发布之时,Oracle 就宣布今后会按照每个六月一次的节奏进行更新,在过去的几个月中,我们见证了其兑现了诺言,但万万没想到,苦了大批迎头而上的开发者们。
oracle 理念 与 小步快跑,快速迭代
官网提供的新特性列表:
https://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUID-C23AFD78-C777-460B-8ACE-58BE5EA681F6
或参考 Open JDK
http://openjdk.java.net/projects/jdk9/
在线Oracle JDK 9 Documentation
http://docs.oracle.com/javase/9/


模块将由通常的类和新的模块声明文件(module-info.java)组成。该文件是位于java代码块结构的顶层,该模块描述符明确地定义了我们的模块需要什么依赖关系,以及哪些模块被外部使用。在exports子句中未提供及的所有包默认情况下将封装在模块中,不能在外部使用。

在模块1服务,创建一个类,然后生成一个 module-info.java
package com.zzpedu.bean;
public class Person {
private String name;
private int age;
...
}
module-info.java对外暴露出去
module chapter {
exports com.zzpedu.bean;
}
在模块2服务,创建测试类,然后生成一个 module-info.java
module-info.java 引用 模块1服务
module java9test {
requires chapter;
}
测试类:
package com.zzpedu;
import com.zzpedu.bean.Person;
public class ModuleTest {
public static void main(String[] args) {
Person person = new Person("tom",18);
System.out.println("person=" + person);
}
}
执行效果:

产生背景
像 Python 和 Scala 之类的语言早就有交互编程环境 REPL(read - evaluate - print - loop)了,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码,就可以在编译前获得对程序的反馈。而之前的Java版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现
设计理念
即写即得、快速运行
实现目标
Java 9 中终于拥有了 REPL工具:jShell。让Java可以像脚本语言一样运行,从控制台启动jShell,利用jShell在没有创建类的情况下直接声明变量,计算表达式,执行语句。即开发时可以在命令行里直接运行Java的代码,而无需创建Java文件,无需跟人解释 “public static void main(String[] args){}”
jShell也可以从文件中加载语句或者将语句保存到文件中
jShell也可以是tab键进行自动补全和自动添加分号。

获取相关命令的列表:
/help intro
导入指定的包
import java.util.*;
默认已经导入如下的所有包:(包含java.lang包)
/imports
Tips: 在 jShell 环境下,语句末尾的 “;” 是可以可选的。但推荐还是最后加上。提高代码可读性。
一些命令演示:

可以覆盖以前的变量或者方法等

只需要按下 Tab 键,就能自动补全代码
列出当前session 里所有有效的代码片段
使用 /open命令调用执行 .java文件
/open D:\xx\xx.java
没有受检异常(编译异常)
说明:本来应该强迫我们捕获一个 IOException,但没有出现。因为jShell在后台为我们隐藏了。
退出jShell
/exit
Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程读度上,扩展了接口的功能,此时的接口更像是一个抽象类。
在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为 private 的了,此时方法将不会成为你对为暴露的API的一部分。
public interface MyInterface {
//如下的三个方法的权限修饰符都是 public
void methodAbstract();
static void methodStatic(){
System.out.println("我是接口中的静态方法");
}
default void methodDefault(){
System.out.println("我是接口中的默认方法");
methodPrivate();
}
// jdk9 中允许接口中定义私有方法
private void methodPrivate(){
System.out.println("我是接口中的私有方法");
}
}
public class MyInterfaceImpl implements MyInterface{ @Override public void methodAbstract() { } @Override public void methodDefault() { MyInterface.super.methodDefault(); System.out.println("实现类重写接口中的默认方法methodDefault"); } public static void main(String[] args) { //接口中的静态方法只能由接口自己调用 MyInterface.methodStatic(); //接口的实现类不能调用接口的静态方法 // MyInterfaceImpl.methodStatic(); MyInterfaceImpl impl = new MyInterfaceImpl(); impl.methodDefault(); //不能调用接口中私有方法 //impl.methodPrivate(); } }
执行效果:

我们将能够于匿名实现类共同使用钻石操作符(diamond operator)
在Java 8 中如下操作是会报错的:
Comparator<Object> com = new Comparator<>() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
编译报错信息:Cannot use “<>” with anonymous inner classes.
Java 9中如下操作可以正常执行通过:
// anonymous classes can now use type inference
Comparator<Object> com = new Comparator<>() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
代码示例:
//java9特性五:钻石操作符的升级
@Test
public void test1(){
//钻石操作符与匿名内部类在jdk 8中不能共存(正确写法:new Comparator<>(Object))。在java 9可以
Comparator<Object> com = new Comparator<>() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
//jdk7中的新特性:类型推断
ArrayList<String> list = new ArrayList<>();
}
Java 8中,可以实现资源的自动关闭,但是要求执行后必须关闭所有资源必须在try子句中初始化,否则编译不通过。如下例所示:
try(InputStreamReader reader = new InputStreamReader(System.in)){
//读取数据细节操作
}catch (IOException e) {
e.printStackTrace();
}
Java 9中,用资源语句编写try将更容易,我们可以在try子句中使用已经初始化过的资源,此时的资源是final的:
InputStreamReader reader = new InputStreamReader(System.in)
OutputStreamWriter writer = new OutputStreamWriter(System.out);
try(reader; writer ){
//reader是final的,不可再被赋值
//reader = null;
//具体读写操作...
}catch (IOException e) {
e.printStackTrace();
}
代码示例:
public static void main(String[] args) { //java 8之前的资源关闭的操作 // InputStreamReader reader = null; // try { // //读取控制台的输入,然后再输出 // reader = new InputStreamReader(System.in); // char[] chars = new char[20]; // int len; // if((len = reader.read(chars)) != -1){ // String str = new String(chars,0,len); // System.out.println(str); // } // } catch (IOException e) { // e.printStackTrace(); // }finally { // if(reader != null){ // try { // reader.close(); // } catch (IOException e) { // e.printStackTrace(); // } // } // } //java 8中资源关闭操作:Java 8中,可以实现资源的自动关闭 //要求自动关闭的资源的实例化必须放在try的一对小括号中 // try (InputStreamReader reader = new InputStreamReader(System.in)){ // char[] chars = new char[20]; // int len; // if((len = reader.read(chars)) != -1){ // String str = new String(chars,0,len); // System.out.println(str); // } // }catch (IOException e) { // e.printStackTrace(); // } //java 9中资源关闭操作:需要自动关闭的资源的实例化可以放在try的一对小括号外。 //此时的资源属性是常量,声明为final的,不可修改 InputStreamReader reader = new InputStreamReader(System.in); try (reader){ char[] chars = new char[20]; int len; if((len = reader.read(chars)) != -1){ String str = new String(chars,0,len); System.out.println(str); } //reader = null;//错误 不能修改 } catch (IOException e) { e.printStackTrace(); } }
Motivation
The current implementation of the String class stores characters in a char array, using two bytes (sixteen bits) for each character. Data gathered from many different applications indicates that strings are a major component of heap usage and, moreover, that most String objects contain only Latin-1 characters. Such characters require only one byte of storage, hence half of the space in the internal char arrays of such String objects is going unused.
诱因:String类的当前实现将字符存储在字符数组中,每个字符使用两个字节(16位)。从许多不同的应用程序收集的数据表明,字符串是堆使用的主要组成部分,而且大多数字符串对象只包含拉丁字符。这样的字符只需要一个字节的存储空间,因此这样的字符串对象的内部字符数组中有一半的空间没有使用。
Description
We propose to change the internal representation of the String class from a UTF-16 char array to a byte array plus an encoding-flag field. The new String class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character), or as UTF-16 (two bytes per character), based upon the contents of the string. The encoding flag will indicate which encoding is used.
描述:我们建议将字符串类的内部表示形式从UTF-16字符数组更改为字节数组加上编码标志字段。新的String类将根据字符串的内容存储编码为ISO-8859-1/Latin-1(每个字符一个字节)或UTF-16(每个字符两个字节)的字符。编码标志将指示使用哪种编码。
java9之前的String存储结构,是使用private final char value[];char数组类型

Java 9的String存储结构,是使用private final byte[] value;byte数组类型:

结论:String 再也不用 char[] 来存储,改成 byte[] 加上编码标记,节约了一下空间。
那么StringBuffer 和 StringBuilder 是否仍然不动呢?
String-related classes such as AbstractStringBuilder, StringBuilder, and StringBuffer will be updated to use the same representation, as will the HotSpot VM’s intrinsic(固有的、内置的) string operations.
与字符串相关的类,如AbstractStringBuilder、StringBuilder和StringBuffer,将被更新为使用相同的表示形式,HotSpot VM的固有字符串操作也是如此。
要创建一个只读、不可改变的集合,必须构造和分配它,然后添加元素,最后包装成一个不可修改的集合。
List<String> list = new ArrayList<>();
list.add("jack");
list.add("tom");
list.add("milan");
list = Collections.unmodifiableList(list);
System.out.println(list);
缺点:我们一下子写了五行。即:它不能表达为单个表达式。
List<String> list = Collections.unmodifiableList(Arrays.asList("a","b","c"));
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a","b","c")));
//如下操作不适用于jdk 8 及之前版本,适用于 jdk 9
Map<String,Integer> map = Collections.unmodifiableMap(new HashMap<>(){
{
put("a",1);
put("b",2);
put("c",3);
}
});
map.forEach((k,v) -> System.out.println(k + ":" + v));
Java 9因此引入了方便的方法,这使得类似的事情更容易表达

List<Integer> list = List.of(1, 2, 3, 4, 5);
调用集合中静态方法of(),可以将不同数量的参数传输到此工厂方法中。此功能可用于 Set和List,也可以用于Map的类似形式。此时得到的集合,是不可变的:在创建后,继续添加元素到这些集合会导致UnsupportedOperationException。
由于Java 8中接口方法的实现,可以直接在List,Set和Map的接口内定义这些方法,便于调用。
代码示例:
public class Java9Test1 { //java8中的写法: @Test public void test1(){ List<String> list = new ArrayList<>(); list.add("jack"); list.add("tom"); list.add("milan"); list = Collections.unmodifiableList(list); //返回的list是一个只读的集合 //list.add("king");//运行报错 UnsupportedOperationException System.out.println(list); } @Test public void test2(){ List<String> list = Collections.unmodifiableList(Arrays.asList("a","b","c")); Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a","b","c"))); //如下操作不适用于jdk 8 及之前版本,适用于 jdk 9 Map<String,Integer> map = Collections.unmodifiableMap(new HashMap<>(){ { put("a",1); put("b",2); put("c",3); } }); map.forEach((k,v) -> System.out.println(k + ":" + v)); } @Test public void test3(){ //此时得到的集合list也是一个只读集合 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); //源码: /* AbstractList类 public void add(int index, E element) { throw new UnsupportedOperationException(); } */ // list.add(6);//抛出异常 UnsupportedOperationException 说明 Arrays.asList 是不可修改 } //java9新特性:集合工厂方法:快速创建只读集合 @Test public void test4(){ List<Integer> list1 = List.of(1, 2, 3, 4, 5); //不能添加 //list1.add(6);//抛出异常 UnsupportedOperationException System.out.println(list1); Set<Integer> set1 = Set.of(12, 23, 34); //不能添加 //set1.add(1); System.out.println(set1); Map<String, Integer> map1 = Map.of("tom", 23, "jack", 18); //不能添加 //map1.put("king",50); System.out.println(map1); Map<String, Integer> map2 = Map.ofEntries(Map.entry("tom", 12), Map.entry("jack", 21)); //不能添加 //map2.put("milan",24); System.out.println(map2); } }
InputStream 终于有了一个非常有用的方法:transferTo(),可以用来将数据直接传输到 OutputStream,这是在处理原数据时非常常见的一种用法,如下示例:
//java9新特性:InputStream的新方法:transferTo()
@Test
public void test5(){
ClassLoader c1 = this.getClass().getClassLoader();
try(InputStream is = c1.getResourceAsStream("hello.txt");
OutputStream os = new FileOutputStream("src\\hello1.txt")){
is.transferTo(os);//把输入流中的所有数据直接自动复制到输出流中
}catch (IOException e){
e.printStackTrace();
}
}
Java 的Stream API 是java标准库最好改进之一,让开发者能够快速运算,从而能够有效的利用数据并进行计算。Java 8 提供的 stream 能够利用多核架构实现声明式的数据处理。
在 Java 9 中,Stream API 变得更好,Stream 接口中添加了 4个新的方法:takeWhile, dropWhile, offNullable,还有个 iterate 方法的重载方法,可以让你提供一个 Predicate(判断条件)来指定什么时候结束迭代。
除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也得到了改进。现在可以通过 Optional 的新方法 stream() 将一个 Optional 对象转换为一个(可能是空的)Stream 对象。
takeWhile() 的使用
用于从 Stream 中获取一部分数据,接收一个 Predicate 来进行选择。在有序的 Stream 中,返回从开头开始的按照指定规则尽量多的元素。
List<Integer> list = Arrays.asList(45, 43, 76, 87, 42, 77, 90, 73, 67, 88);
list.stream().takeWhile(x -> x < 50).forEach(System.out :: println);
System.out.println();
list = Arrays.asList(1,2,3,4,5,6,7,8);
list.stream().takeWhile(x -> x < 5).forEach(System.out :: println);
dropWhile() 的使用
dropWhile 的行为与 takeWhile 相反,返回剩余的元素
ofNullable() 的使用
Java 8 中 Stream 不能完全为null,否则会报空指针异常。而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream。
// 报NullPointerException // Stream<Object> stream1 = Stream.of(null); // System.out.println(stream1.count()); // 不报异常,允许通过 Stream<String> strStream = Stream.of("AA","BB",null); System.out.println(strStream.count());// 3 // 不报异常,允许通过 List<String> list= new ArrayList<>(); list.add("AA"); list.add(null); System.out.println(list.stream().count());// 2 // ofNullable():允许值为null Stream<Object> stream1 = Stream.ofNullable(null); System.out.println(stream1.count());// 0 Stream<String> stream = Stream.ofNullable("hello world"); System.out.println(stream.count());// 1
iterate() 的使用
这个 iterate 方法的新重载方法,可以让你提供一个 Predicate(判断条件)来指定什么时候结束迭代
// 原来的控制终止方法
Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out :: println);
// 现在的终止方法
Stream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out :: println);
//java9新特性十:Stream API的加强 @Test public void test1(){ List<Integer> list = Arrays.asList(45, 43, 76, 87, 42, 77, 90, 73, 67, 88); //takeWhile 返回从开头开始的按照指定规则尽量多的元素。 //list.stream().takeWhile(x -> x < 50).forEach(System.out :: println); //与 takeWhile 相反,返回剩余的元素 list.stream().dropWhile(x -> x < 60).forEach(System.out :: println); System.out.println(); list = Arrays.asList(1,2,3,4,5,6,7,8); list.stream().takeWhile(x -> x < 5).forEach(System.out :: println); } @Test public void test2(){ //of()参数中的多个元素,可以包含多个null值 Stream<Integer> stream1 = Stream.of(1,2,3,null); stream1.forEach(System.out::println); //of()参数不能存储单个null值,否则 抛出异常 NullPointerException // Stream<Object> stream2 = Stream.of(null); // stream2.forEach(System.out::println); Integer i = 10; i = null; //ofNullable():形参变量可以为null值的单个元素 Stream<Integer> stream2 = Stream.ofNullable(i); long count = stream2.count(); System.out.println(count); } @Test public void test3(){ Stream.iterate(0,x -> x + 1).limit(10).forEach(System.out::println); //java9中新增的重载的方法 Stream.iterate(0,x -> x < 100,x -> x + 1).forEach(System.out::println); }
Optional类中stream() 的使用
List<String> list = new ArrayList<>();
list.add("tom");
list.add("jerry");
list.add("king");
Optional<List<String>> optional = Optional.ofNullable(list);
Stream<List<String>> stream = optional.stream();
stream.flatMap(x -> x.stream()).forEach(System.out :: println);
//java9新特性十一:Optional提供了新的方法stream()
@Test
public void test4(){
List<String> list = new ArrayList<>();
list.add("tom");
list.add("jerry");
list.add("king");
Optional<List<String>> optional = Optional.ofNullable(list);
Stream<List<String>> stream = optional.stream();
// long count = stream.count();
// System.out.println(count);//1
stream.flatMap(x-> x.stream()).forEach(System.out :: println);
}
Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的 Javascript 运行时。Nashorn 项目跟随 Netscape 的 Rhino 项目。目的是为了在 Java 中实现一个高性能但轻量级的 Javascript 运行时。Nashorn 项目使得 Java 应用能够嵌入 Javascript。它在 JDK 8 中为了 Java 提供一个 Javascript引擎。
JDK 9 包含一个用来解析 Nashorn 的 ECMAScript 语法树的 API。这个 API 使得 IDE 和服务端框架不需要依赖 Nashorn 项目内部实现类,就能够分析 ECMAScript 代码。

286:
Local-Variable Type Inference :局部变量类型推断
296: Consolidate the JDK Forest into a Single Repository :将JDK林整合到单个存储库中
304: Garbage-Collector Interface :统一的垃圾收集器接口
307: Parallel Full GC for G1 :为G1提供的并行的Full GC
310: Application Class-Data Sharing : 应用程序级数据(AppCDS)共享
312: Thread-Local Handshakes :线程本地握手
313: Remove the Native-Header Generation Tool (javah) : 移除JDK中附带的javah工具
314: Additional Unicode Language-Tag Extensions :使用附加的Unicode语言标记扩展
316: Heap Allocation on Alternative Memory Devices :能将堆内存占用分配给用户指定的备用内存设备
317: Experimental Java-Based JIT Compiler :使用基于Java的JIT编译器
319: Root Certificates :根证书
322: Time-Based Release Versioning :基于时间的发布版本
产生背景
开发者经常抱怨Java中引用代码的程度。局部变量的显示类型声明,常常被认为是是不必须的,给一个好听的名字经常可以很清楚的表达出下面应该怎么样继续。
好处
减少了啰嗦形式的代码,避免了信息冗余,而且对齐了变量名,更容易阅读
举例如下
LinkedHashSet<Integer> set = new LinkedHashSet<>();
Iterator<Map.Entry<Integer, Student>> iterator = set.iterator();
URL url = new URL("http://www.baidu.com");
URLConnection connection = url.openConnection();
Reader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
尽量IDE可以帮我们自动完成这些代码,但当变量总是跳来跳去的时候,可读性还是会受到影响,因为变量类型的名称有各种不同长度的字符组成。而且,有时候开发人员会尽力避免声明中间变量,因为太多的类型声明只会分散注意力,不会带来额外的好处。
使用于以下情况
//1. 局部变量额初始化
var list = new ArrayList<Integer>();
list.add(123);
//2. 增强for循环中索引
for(var v : list){
System.out.println(v);
}
//3. 传统for循环中
for (var i = 0; i < 100; i++) {
System.out.println(i);
}
在局部变量中使用时,如下情况不适用:
初始值为 null
var a = null;
错误:无法推断本地变量 a 类型
<变量初始化程序为 'null'>
方法引用
var r = System.out::println;
错误:无法推断本地变量 r 类型
<方法引用需要显式目标类型>
Lambda表达式
var r = () -> Math.randon();
错误:无法推断本地变量 r 类型
<Lambda 表达式需要显式目标类型>
为数组静态初始化
var arr = {"a","b","c"};
错误:无法推断本地变量 r 类型
<数组初始化程序需要显式目标类型>
/** * java10的新特性一:局部变量的类型推断 */ @Test public void test1(){ //1. 声明变量时,根据所附的值,推断变量的类型 var num = 10; var list = new ArrayList<Integer>(); list.add(123); //2. 遍历操作 for(var i : list){ System.out.println(i); System.out.println(i.getClass()); } //3. 普通的遍历操作 for (var i = 0; i < 100; i++) { System.out.println(i); } } @Test public void test2(){ //1.局部变量不赋值,就不能实现类型推断 //var num; //错误 //2.lambda表达式中, 左边的函数式接口不能声明为var Supplier<Double> sup = () -> Math.random(); //var sup = () -> Math.random();//错误 //3.方法引用中, 左边的函数式接口不能声明为var Consumer<String> con = System.out::println; //var con = System.out::println;//错误 //4.数组的静态初始化中,注意如下情况也不可以 int[] arr ={1,2,3}; //var arr ={1,2,3};//错误 }
不适用以下的结构中:
工作原理
在处理 var时,编译器先是查看表达式右边部分,并根据右边变量值的类型进行推断,作为左边变量的类型,然后将该类型写入字节码当中。
注意
var不是一个关键字
你不需要担心变量名或方法名会与 var发生冲突,因为var实际上并不是一个关键字,而是一个类型名,只有在编译器需要知道类型的地方才需要用到它。除此之外,它就是一个普通合法的标识符。也就是说,除了不能用它作为类名,其他的都可以,但极少人会用它作为类名。
这不是JavaScript
首先说明的是,var并不是改变Java是一门静态类型语言的事实。编译器负责推断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样。下面是使用 InteliJ(实际上是 Fernflower的反编译器)反编译器反编译出代码:
var url = new URL("http://www.baidu.com");
var connection = url.openConnection();
var reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
反编译后
URL url = new URL("http://www.baidu.com");
URLConnection connection = url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
从代码来看,就好像之前已经声明了这些类型一样。事实上,这一特性只发生在编译阶段,与运行时无关,所以对运行时的性能不会产生任何影响。所以请放心,这不是 JavaScript。
@Test public void test3(){ //情况1:没有初始化的局部变量声明 var r = null;//错误 //情况6:catch块 try { }catch (var e){//错误 e.printStackTrace(); } } //情况2:方法的返回类型 public var method1(){//错误 return 1; } //情况3:方法的参数类型 public void method2(var num){//错误 return 0; } //情况4:构造器的参数类型 public Java10Test(var i){//错误 } //情况5:属性 var num;//错误 @Test public void test4(){ try { var url = new URL("http://www.baidu.com"); var connection = url.openConnection(); var reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); } catch (IOException e) { e.printStackTrace(); } }
自Java 9 开始,jdk 里面为集合(List/Set/Map)都添加了 of(jdk9新增)和 copyOf(jdk10新增)方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别
//实例1
var list1 = List.of("Java","Python","C");
var copy1 = List.copyOf(list1);
System.out.println(list1 == copy1);//true
//示例2
var list2 = new ArrayList<String>();
var copy2 = List.copyOf(list2);
System.out.println(list2 == copy2);//false
//实例1和实例2代码基本一致,为什么一个true,一个为false?
//java10的新特性二:集合中新增的copyOf(),用于创建一个只读的集合 @Test public void test5(){ //实例1 var list1 = List.of("Java","Python","C"); var copy1 = List.copyOf(list1); System.out.println(list1 == copy1);//true //示例2 var list2 = new ArrayList<String>(); list2.add("aaa"); var copy2 = List.copyOf(list2); System.out.println(list2 == copy2);//false //实例1和实例2代码基本一致,为什么一个true,一个为false? //结论:copyOf(Xxx coll):如果参数coll本身就是一个只读集合,则copyOf()返回值即为当前的coll // 如果参数coll不是一个只读集合,则copyOf()返回一个新的集合,这个集合是只读的。 }
从源码分析,可以看出 copyOf() 方法会先判断来源集合是不是 AbstractImmutableList 类型,如果是,就直接返回,如果不是,则调用 of() 创建一个新的集合。
实例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf() 方法又创建了一个新的实例,所以为 false
注意:使用 of 和 copyOf 创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException异常。
上面演示了 List的 of 和 copyOf 方法,Set 和 Map 接口都有。
北京时间 2018年9月26日,Oracle官方宣布 Java 11 正式发布。这是 Java 大版本周期变化后的第一个长期支持版本,非常值得关注。从官网即可下载,最新发布的 Java11 将带来 ZGC Http Cilent 等重要特性,一共包括 17 个 JEP(JDK Enhancement Proposals,JDK 增强提案)。其实,总共更新不止17个,只是我们更关注如下17个JEP更新。
参阅官方文档:http://openjdk.java.net/projects/jdk/11/
官网公开的17个 JEP (JDK Enhancement Proposals,JDK 特性增强提议)
181: Nest-Based Access Control :基于嵌套的访问控制
309: Dynamic Class-File Constants :动态的类文件常量
315: Improve Aarch64 Intrinsics :改进 Aarch64 Intrinsics
318: Epsilon: A No-Op Garbage Collector :Epsilon垃圾回收器
320: Remove the Java EE and CORBA Modules :移除JavaEE和CORBA模块
321: HTTP Client (Standard) :HTTP客户端(标准)
323: Local-Variable Syntax for Lambda Parameters :Lambda参数的局部变量语法
324: Key Agreement with Curve25519 and Curve448 :与Curve25519和Curve448的关键协议
327: Unicode 10 :Unicode 10
328: Flight Recorder :飞行记录器
329: ChaCha20 and Poly1305 Cryptographic Algorithms :实现ChaCha20 和 Poly1305加密算法
330: Launch Single-File Source-Code Programs :启动单个Java源代码的程序
331: Low-Overhead Heap Profiling :低开销的堆分配采样方法
332: Transport Layer Security (TLS) 1.3 : 对 TLS 1.3 的支持
333: ZGC: A Scalable Low-Latency Garbage Collector
(Experimental) :ZGC:可伸缩的低延迟垃圾收回收器,处于实验阶段)
335: Deprecate the Nashorn JavaScript Engine : 弃用Nashorn JavaScript引擎
336: Deprecate the Pack200 Tools and API : 弃用使用Pack200工具和API
JDK 11 将是一个 企业不可忽视的版本。 从时间节点来看, JDK 11 的发布正处于在 JDK 8 免费更新到期的前夕,同时 JDK 9、10 也陆续称为 “历史版本”,其他java版本发现详情 https://www.oracle.com/java/technologies/java-se-support-roadmap.html
JDK 11 是一个长期支持版本(LTS,Long-Term-Support)
按照官方的说法,新的发布周期会严格遵循时间点,将于每年的3月份和9月份发布。所以 Java 11 的版本号是 18.9(LTS)。
不过与 Java 9 和 Java 10 这两个被称为“功能性的版本”不同(两者均只提供半年的技术支持),Java 11 不仅提供了长期支持服务,还将作为 Java 平台的参考实现。Oracle 直到2023年9月都会 Java 11 提供技术支持,而补丁和安全警告等扩展支持将持续到2026年。


| 描述 | 举例 |
|---|---|
| 判断字符串是否为空白 | " ".isBlank();//true |
| 去除首尾空白 | " Javastack ".strip();//"Javastack" |
| 去除尾部空白 | " Javastack ".stripTrailing();//" Javastack" |
| 去除首部空白 | " Javastack ".stripLeading();//"Javastack " |
| 复制字符串 | "Java".repeat(3);//"JavaJavaJava" |
| 行数统计 | "A\nB\nC".lines().count();//3 |
//java 11新增特性一:String中新增的方法 @Test public void test1(){ //isBlank():判断字符串是否为空白 System.out.println(" \t \t \n ".isBlank());//true //strip():去除首尾空白 System.out.println("----" + " \t abd \t \n ".strip() + "---");//----abd--- System.out.println("----" + " \t \t abc \n ".trim() + "---");//----abc--- //stripTrailing():去除尾部空白 System.out.println("----" + " \t \t abc \n ".stripTrailing() + "---");//---- abc--- //stripLeading():去除首部空白 System.out.println("----" + " \t \t abc \n ".stripLeading() + "---");//----abc //--- String str = "abc"; //repeat():复制字符串 System.out.println(str.repeat(3));//abcabcabc //lines().count():行数统计 String str2 = "abc\nde\nfg"; System.out.println(str2.lines().count());//3 }
执行效果;

Optional 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream,或这当一个空 Optional 时给它一个替代的。
| 新增方法 | 描述 | 新增版本 |
|---|---|---|
boolean isEmpty() | 判断是否为空 | JDK 11 |
ifPresentOrElse(Consumer<? super T> action,Runnable emptyAction) | value非空,执行参数1功能;如果value为空,执行参数2功能 | JDK 9 |
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) | value非空,返回对应的Optional;value为空,返回形参封装的Optional | JDK 9 |
Stream<T> stream() | value非空,返回仅包含此value的Stream;否则,返回一个空的Stream | JDK 9 |
T orElseThrow() | value非空,返回value;否则抛出异常NosuchElementException | JDK 10 |
//java 11新增特性二:Optional新增的方法 @Test public void test2(){ var op = Optional.empty(); System.out.println(op.isPresent());//java8 判断内部value是否存在 false System.out.println(op.isEmpty());//判断内部的value是否为空 true op = Optional.of("abc"); //orElseThrow():如果为空的 抛出异常java.util.NoSuchElementException: No value present var obj = op.orElseThrow(); System.out.println(obj);//abc //or():value非空,返回对应的Optional;value为空,返回形参封装的Optional Optional<String> op1 = Optional.of("hello"); //op = Optional.empty(); Optional<Object> op2 = op.or(() -> op1); System.out.println(op2);//Optional[abc] }
执行效果:

在 var 上添加注解的语法格式,在jdk10中是不能实现的。在jdk中加入了这样的语法。
//错误的形式:必须要有类型,可以加上var
//Consumer<String> con1 = (@Deprecated t) -> System.out.println(t.toUpperCase());
//正确的形式:
//使用var的好处是在使用lambda表达式时给参数加上注解
Consumer<String> con2 = (@Deprecated var t) -> System.out.println(t.toUpperCase());
//java 11新增特性三:局部变量类型推断的升级
@Test
public void test3(){
//错误的形式:必须要有类型,可以加上var
//Consumer<String> con1 = (@Deprecated t) -> System.out.println(t.toUpperCase());
//正确的形式:
//使用var的好处是在使用lambda表达式时给参数加上注解
Consumer<String> con2 = (@Deprecated var t) -> System.out.println(t.toUpperCase());
}
HTTP,用于传输网页的协议,早在1997年就被采用在目前的1.1版本中。直到 2015年,HTTP2才成为标准

HTTP/1.1 和 HTTP/2 的主要区别是如何在客户端和服务端之间构建和传输数据。HTTP/1.1依赖于请求/响应周期。HTTP/2允许服务器“push”数据;它可以发送比客户端请求更多的数据。这使得它可以优先处理并发对于首先加载网页至关重要的数据。
这是 Java 9 开始引入的一个处理 HTTP 请求的 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到这个 API
它将替代仅使用于 blocking 模式的 HttpURLConnection(HttpURLConnection是在 HTTP 1.0的时代创建的,并使用了协议无关的方法),并提供对 WebSocket 和 HTTP/2的支持。
//java 11新增特性四:httpClint替换原有的HttpURLConnection @Test public void test4() throws IOException, InterruptedException { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build(); HttpResponse.BodyHandler<String> responseBodyHandler = HttpResponse.BodyHandlers.ofString(); HttpResponse<String> response = client.send(request,responseBodyHandler); String body = response.body(); System.out.println(body); } @Test public void test5() throws ExecutionException, InterruptedException { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build(); HttpResponse.BodyHandler<String> responseBodyHandler = HttpResponse.BodyHandlers.ofString(); CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request,responseBodyHandler); sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println); // HttpResponse<String> response = sendAsync.get(); // String body = response.body(); // System.out.println(body); }
执行效果:

看下面代码
//编译
javac Javastack.java
//运行
java Javastack
在我们的认知里面,要运行一个 Java 源码必须先编译,在运行,两步执行动作。而在未来的 Java 11 版本中,通过一个 Java 命令就直接搞定了,如下所示:
java Javastack.java
一个命令编译运行源代码的注意点:
废弃Nashorn Javascript引擎,在之后续版本准备移除掉,有需要的可以考虑使用 GraaIVM
GC主要优势之一。然而,当GC停顿太长,就会开始影响应用的响应时间。消除或者减少GC停顿时长,java将对更广泛的应用场景是一个更有吸引力的平台。此外,现在系统中可用内存不断增长,用户和程序员希望JVM能够以高效的方式充分利用这些内存,并且无需长时间的GC暂停时间。
ZGC, A Scalable Low-Latency Garbage Collector(Experimental) ZGC,这应该是 JDK11最高瞩目的特性,没有之一。但是后面带了Experimental,说明这还不建议用到生产环境。
ZGC是一个开发,基于region,压缩型垃圾收集器,只有root扫描阶段会STW(stop the world),因此GC停顿时间不会随着堆的增长和存活对象的增长而变长。
优势:
ZGC 的设计目标是:支持TB级内存容量,暂时时间低(<10ms),对整个程序吞吐量的影响小于15%。将来还可以扩展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆。
一个标准化和轻量级的 JSON API
一个标准化和轻量级的JSON API被许多Java开发人员所青睐。但是由于资金问题无法在Java当前版本中见到,但并不会消减掉。Java平台首席架构师Mark Reinhold在JDK 9邮件列中说:“这个JEP将是平台上的一个有用的补充,但是在计划中,它并不像Oracle资助的其他功能那么重要,可能会重新考虑JDK 10或更高版本中实现。”
新的货币 API
对许多应用而言货币价值都是一个关键的特性,但JDK对此却几乎没有任何支持。严格来讲,现有的 java.util.Currency类只是代表了当前 ISO 4217货币的一个数据结构,但并没有关联的值或者自定义货币。JDK对货币的运算及转换也没有内建的支持,更别说一个能够代表货币值的标准类型了。
此前,Oracle 公布的 JSR 354定义了一套新的Java货币API:JavaMoney,计划会在Java中正式引入。但是目前没有出现在JDK新特性中。
不过,如果你用的是Maven的话,可以做如下的添加,即可使用相关的API处理货币:
<dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>0.9</version>
</dependency>
随着云计算和 AI 等技术浪潮,当前的计算模式和场景正在发送翻天覆地的变化,不仅对 Java 的发展速度提出了更高要求,也深刻影响者 Java 技术的发展方向。传统的大型企业或互联网应用,正在被云端、容器化应用、模块化的微服务甚至是函数(FaaS, Function-as-a-Service)所替代。
Java虽然标榜面向对象编程,却毫无顾虑的加入面向接口编程思想,又扯出匿名对象之概念,每增加一个新的东西,对Java的根本所在的面向对象思想的一次冲击。反观Python,抓住面向对象的本质,又能在函数编程思想方面游刃有余。Java对标C/C++,以抛掉内存管理为卖点,却又陷入JVM优化的噩梦。选择比努力更重要,选择Java的人更加需要对它有更清晰的认识。
Java 需要在新的计算创建下,改进开发效率。这话说的有点笼统,我谈一些自己的体会,Java 代码虽然进行 一些类型推断的改进,更易用的集合API等,但仍然给开发者留下了过于刻版、形式主义的印象,这是一个长期的改进方向。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。