JDK 为什么在不断变化?

要么改变,要么衰亡。

Java8 为开发者提供更多的工具和概念。

更快、更简洁、更易于维护的处理编程问题。

主要变化

  • 方法作为一等公民

Scala、Groovy 等语言已经实践证明。

让方法作为一等值,可以扩充程序员的工具库,让编程变得简单。

  • lambda作为一等公民

Java8 还体现了更广义的将函数作为值得思想,将 lambda 作为一等值。

传统的 java 对于集合的创建和操作,相对来说比较麻烦。

且无法充分地利用多核CPU。

  • 默认方法

interface 接口中可以定义默认方法。

  • NULL 的处理

Optional<T> 类对于 null 的处理。

  • 模式匹配

可以将模式匹配看作是 switch 的扩展形式,同时将一个数据类型分解成元素。

New Features

Lambda

Lambda 表达式内容比较重要,放在单独的一篇进行讲解。

Lambda

Lambda expression is anonymous function.

  • old way
  [java]
1
2
3
4
5
6
7
8
public void old() { Runnable r = new Runnable() { public void run() { System.out.println("Hello World!"); } }; new Thread(r).start(); }
  • use lambda
  [java]
1
new Thread(() -> System.out.println("Lambda Hello World!")).start();

lambda expression grammar

  [plaintext]
1
2
3
4
5
6
(Type1 param1, Type2 param2, ..., TypeN paramN) -> { statment1; statment2; //............. return statmentM; }

1、No parameters.

  [plaintext]
1
() -> { //..... };

2、One parameter, can leave out the parameter type, javac can deduce it from the context.

  [plaintext]
1
2
3
4
5
6
param1 -> { statment1; statment2; //............. return statmentM; }

3、Only one statement, can leave out the {}.

  [plaintext]
1
param1 -> statment

4、Leave out the parameters’ type.

  [plaintext]
1
2
3
4
5
6
(param1,param2, ..., paramN) -> { statment1; statment2; //... return statmentM; }

demo

  • Person.java
  [java]
1
2
3
public interface Person { void say(String string); }
  • call it use lambda
  [java]
1
2
3
4
public static void main(String[] args) { Person h = str -> System.out.println(str); h.say("Hello World"); }
  • result
  [plaintext]
1
Hello World

interface default method

  • Person.java
  [java]
1
2
3
4
5
6
7
public interface Person { void say(); default void eat() { System.out.println("eat..."); } }
  • Student.java
  [java]
1
2
3
4
5
6
7
public class Student implements Person { @Override public void say() { System.out.println("say..."); } }
  • test
  [java]
1
2
3
4
5
public static void main(String[] args) { Student student = new Student(); student.say(); student.eat(); }
  • result
  [plaintext]
1
2
3
4
say... eat... Process finished with exit code 0

defaultmethods

method reference

1、static method: ClassName::methodName

  • test
  [java]
1
2
3
4
public static void main(String[] args) { List<String> strs = Arrays.asList("aa","bb","cc"); strs.forEach(System.out::println); }
  • result
  [plaintext]
1
2
3
4
5
aa bb cc Process finished with exit code 0

2、instance method: instanceRefence::methodName

  • HelloWorld.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld { void print(){ System.out.println("instanceRefence::methodName"); } public void printInfo(){ System.out.println("printInfo"); //instance method reference new Thread(this::print).start(); } }
  • test
  [java]
1
2
3
4
public static void main(String[] args) { HelloWorld helloWorld = new HelloWorld(); helloWorld.printInfo(); }
  • result
  [plaintext]
1
2
3
4
printInfo instanceRefence::methodName Process finished with exit code 0

3、constructor method: Class::new

  • User.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
public class User { String username; User(String username){ this.username = username; } public String getUsername(){ return username; } }
  • HelloWorld.java
public class HelloWorld {
    @FunctionalInterface
    interface UserFactory<T extends User> {
        T create(String username);
    }

    private void test() {
        UserFactory<User> uf = User::new;
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 5; ++i) {
            users.add(uf.create("user"+i));
        }
        users.stream().map(User::getUsername).forEach(System.out::println);
    }
}
  • test
  [java]
1
2
3
4
public static void main(String[] args) { HelloWorld helloWorld = new HelloWorld(); helloWorld.test(); }
  • result
  [plaintext]
1
2
3
4
5
6
7
user0 user1 user2 user3 user4 Process finished with exit code 0

methodreferences

Repeating Annotations

Repeating Annotations

  • RepeatingAnnotationsTest.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class RepeatingAnnotationsTest { @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("filter1") @Filter("filter2") public interface Filterable { } public static void main(String[] args) { for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) { System.out.println(filter.value()); } } }

result

  [plaintext]
1
2
filter1 filter2

Better Type Inference

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TypeInference<T> { public static <T> T defaultValue() { return null; } public T getOrDefault(T value, T defaultValue) { return (value != null) ? value : defaultValue; } @Test public void betterTest() { final TypeInference<String> value = new TypeInference<>(); String result = value.getOrDefault("22", TypeInference.defaultValue()); System.out.println(result); //22 } }

More useful annotation

Java 8拓宽了注解的应用场景。现在,注解几乎可以使用在任何元素上:局部变量、接口类型、超类和接口实现类,甚至可以用在函数的异常定义上。

  • MoreUsefulAnnotationTest.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MoreUsefulAnnotationTest { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) public @interface NonEmpty { } public static class Holder<@NonEmpty T> extends @NonEmpty Object { public void method() throws @NonEmpty Exception { } } @SuppressWarnings("unused") public static void main(String[] args) { final Holder<String> holder = new @NonEmpty Holder<String>(); @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>(); } }

Lib

Optional

Google Guava 引入了 Optionals 类来解决 NullPointerException,从而避免源码被各种 null 检查污染,以便开发者写出更加整洁的代码。Java 8也将Optional加入了官方库。

Optional仅仅是一个容器:存放T类型的值或者null。它提供了一些有用的接口来避免显式的null检查。

  • nullTest()
  [java]
1
2
3
4
5
6
7
@Test public void nullTest() { Optional< String > fullName = Optional.ofNullable( null ); System.out.println( "Full Name is set? " + fullName.isPresent() ); System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) ); }

result

  [plaintext]
1
2
3
Full Name is set? false Full Name: [none] Hey Stranger!
  • notNullTest()
  [java]
1
2
3
4
5
6
7
@Test public void notNullTest() { Optional< String > fullName = Optional.ofNullable( "ryo" ); System.out.println( "Full Name is set? " + fullName.isPresent() ); System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) ); }

result

  [plaintext]
1
2
3
Full Name is set? true Full Name: ryo Hey ryo!

Streams

新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中。 这是目前为止最大的一次对Java库的完善,以便开发者能够写出更加有效、更加简洁和紧凑的代码。

  • StreamsTest.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StreamsTest { private enum Status { OPEN, CLOSED } private static final class Task { private final Status status; private final Integer points; Task(final Status status, final Integer points) { this.status = status; this.points = points; } public Integer getPoints() { return points; } public Status getStatus() { return status; } @Override public String toString() { return String.format("[%s, %d]", status, points); } } }
  • test
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@Test public void totalTest() { final Collection<Task> tasks = Arrays.asList( new Task(Status.OPEN, 5), new Task(Status.OPEN, 13), new Task(Status.CLOSED, 8) ); // Calculate total points of all active tasks using sum() final long totalPointsOfOpenTasks = tasks .stream() .filter(task -> task.getStatus() == Status.OPEN) .mapToInt(Task::getPoints) .sum(); System.out.println(totalPointsOfOpenTasks); //18 } @Test public void parallelTest() { final Collection<Task> tasks = Arrays.asList( new Task(Status.OPEN, 5), new Task(Status.OPEN, 13), new Task(Status.CLOSED, 8) ); final double totalPoints = tasks .stream() .parallel() .map(task -> task.getPoints()) // or map( Task::getPoints ) .reduce(0, Integer::sum); System.out.println("Total points (all tasks): " + totalPoints); //Total points (all tasks): 26.0 } @Test public void groupByTest() { final Collection<Task> tasks = Arrays.asList( new Task(Status.OPEN, 5), new Task(Status.OPEN, 13), new Task(Status.CLOSED, 8) ); // Group tasks by their status final Map< Status, List< Task >> map = tasks .stream() .collect( Collectors.groupingBy( Task::getStatus ) ); System.out.println( map ); //{OPEN=[[OPEN, 5], [OPEN, 13]], CLOSED=[[CLOSED, 8]]} } @Test public void percentTest() { final Collection<Task> tasks = Arrays.asList( new Task(Status.OPEN, 5), new Task(Status.OPEN, 13), new Task(Status.CLOSED, 8) ); // Calculate total points of all tasks final double totalPoints = tasks .stream() .parallel() .map( task -> task.getPoints() ) // or map( Task::getPoints ) .reduce( 0, Integer::sum ); // Calculate the weight of each tasks (as percent of total points) final Collection< String > result = tasks .stream() // Stream< String > .mapToInt( Task::getPoints ) // IntStream .asLongStream() // LongStream .mapToDouble( points -> points / totalPoints ) // DoubleStream .boxed() // Stream< Double > .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream .mapToObj( percentage -> percentage + "%" ) // Stream< String> .collect( Collectors.toList() ); // List< String > System.out.println(result); //[19%, 50%, 30%] }

Date/Time API(JSR 310)

Java 8引入了新的Date-Time API(JSR 310)来改进时间、日期的处理。

Joda-Time,可以替代Java的时间管理API。

Nashorn JavaScript

Java 8提供了新的Nashorn JavaScript引擎,使得我们可以在JVM上开发和运行js应用。 Nashorn JavaScript引擎是 javax.script.ScriptEngine 的另一个实现版本,这类Script引擎遵循相同的规则,允许Java和javascript交互使用,例子代码如下:

  • NashornJSTest.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
public class NashornJSTest { @Test public void jsTest() throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName( "JavaScript" ); System.out.println( engine.getClass().getName() ); System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) ); } }

result:

  [plaintext]
1
2
jdk.nashorn.api.scripting.NashornScriptEngine Result:2.0

Base64

Base64编码的支持已经被加入到Java 8官方库中.

  • base64Test()
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test public void base64Test() { final String text = "Base64 finally in Java 8!"; final String encoded = Base64 .getEncoder() .encodeToString(text.getBytes(StandardCharsets.UTF_8)); System.out.println(encoded); final String decoded = new String( Base64.getDecoder().decode(encoded), StandardCharsets.UTF_8); System.out.println(decoded); }

result

  [plaintext]
1
2
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ== Base64 finally in Java 8!

ParallelArrays

  • parallelArraysTest()
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/** * 使用parallelSetAll()方法生成20000个随机数,然后使用parallelSort()方法进行排序。这个程序会输出乱序数组和排序数组的前10个元素。 */ @Test public void parallelArraysTest() { long[] arrayOfLong = new long[20000]; Arrays.parallelSetAll(arrayOfLong, index -> ThreadLocalRandom.current().nextInt(1000000)); Arrays.stream(arrayOfLong).limit(10).forEach( i -> System.out.print(i + " ")); System.out.println(); Arrays.parallelSort(arrayOfLong); Arrays.stream(arrayOfLong).limit(10).forEach( i -> System.out.print(i + " ")); System.out.println(); }

result:

  [plaintext]
1
2
395659 19377 864569 289077 710936 742196 922967 850922 701156 551843 7 17 49 111 116 173 194 260 344 396