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
public void old() {
    Runnable r = new Runnable() {
        public void run() {
            System.out.println("Hello World!");
        }
    };
    new Thread(r).start();
}
  • use lambda
new Thread(() -> System.out.println("Lambda Hello World!")).start();

lambda expression grammar

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

1、No parameters.

() -> { //..... };

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

param1 -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

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

param1 -> statment

4、Leave out the parameters’ type.

(param1,param2, ..., paramN) -> {
  statment1;
  statment2;
  //...
  return statmentM;
}

demo

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

interface default method

  • Person.java
public interface Person {
    void say();

    default void eat() {
        System.out.println("eat...");
    }
}
  • Student.java
public class Student implements Person {
    @Override
    public void say() {
        System.out.println("say...");
    }

}
  • test
public static void main(String[] args) {
    Student student = new Student();
    student.say();
    student.eat();
}
  • result
say...
eat...

Process finished with exit code 0

defaultmethods

method reference

1、static method: ClassName::methodName

  • test
public static void main(String[] args) {
    List<String> strs = Arrays.asList("aa","bb","cc");
    strs.forEach(System.out::println);
}
  • result
aa
bb
cc

Process finished with exit code 0

2、instance method: instanceRefence::methodName

  • HelloWorld.java
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
public static void main(String[] args) {
    HelloWorld helloWorld = new HelloWorld();
    helloWorld.printInfo();
}
  • result
printInfo
instanceRefence::methodName

Process finished with exit code 0

3、constructor method: Class::new

  • User.java
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
public static void main(String[] args) {
    HelloWorld helloWorld = new HelloWorld();
    helloWorld.test();
}
  • result
user0
user1
user2
user3
user4

Process finished with exit code 0

methodreferences

Repeating Annotations

Repeating Annotations

  • RepeatingAnnotationsTest.java
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

filter1
filter2

Better Type Inference

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
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()
@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

Full Name is set? false
Full Name: [none]
Hey Stranger!
  • notNullTest()
@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

Full Name is set? true
Full Name: ryo
Hey ryo!

Streams

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

  • StreamsTest.java
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
@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
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:

jdk.nashorn.api.scripting.NashornScriptEngine
Result:2.0

Base64

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

  • base64Test()
@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

QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!

ParallelArrays

  • parallelArraysTest()
/**
 * 使用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:

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