New features of JDK9 (1)

New features of JDK9 (1)

Java 8 was launched in March 2014, and the tortuous Java 9 was finally released on September 21, 2017. After more than 3 years in the middle, Java 9 provides more than 150 new features, including the highly anticipated modular system , Interactive REPL tools: jshell, JDK compilation tools, Java public API and private code, as well as security enhancements, expansion enhancements, performance management improvements, etc. It can be said that Java 9 is a huge system project that has completely changed the whole. The most core new features in Java 8 are Lambda expressions and Stream API, so for Java 9 the core is the modular system and JShell commands.

Java's faster release cycle means that developers will no longer need to look at the main release version as before. This also means that developers will likely skip Java 9 and its immature modular features, and only need to wait another 6 months to usher in the new version, which may solve the developer's entanglement. Anyway, Java 11 has been officially supported for commercial use, and Java 11 will receive long-term support services provided by Oracle until September 2026. So if you want to use the stable and latest JDK, choose Java11. I will describe the new features of Java11 and some of the new features of Java14 in a later article.

You can see the new features of JavaSE9 "Overview of What's New in JDK 9" on this website

JEP and JSR

JEP (JDK Enhancement Proposals): JDK improvement proposals. Whenever a new idea is needed, JEP can propose an informal specification before or at the same time as the JCP (Java Community Process), and the officially recognized JEP is officially written into the JDK Development roadmap and assign a version number. JSR (Java Specification Requests): Java specification proposal. The specification of new features appears at this stage. It is a formal request for adding a standardized technical specification to the JCP (Java Community Process). Requests can come from proposals from groups/projects, JEP, JCP members, or Java community members, and each Java version is supported by the corresponding JSR.

  • Group: Organizations and individuals who have common interests in specific technical content, such as security, networking, HotSpot, etc.
  • Project: Write code, documentation and other work, sponsored by at least one team, such as the recent Lambda project, JigSaw project, etc.

Directory structure changes

Modular system

When it comes to Java 9, the first thing that comes to mind is the Jigsaw project. As we all know, Java has been developed for more than 20 years (originally released in 1995). While Java and related ecosystems are constantly enriched, some problems are also exposed:

  • Problem 1: The expansion and bloat of the Java runtime environment. At least 30-60MB of memory will be loaded every time the JVM starts. The reason is that the JVM needs to load rt.jar. Regardless of whether the class is loaded by the classloader or not, the entire jar will be loaded into the memory by the JVM (and modularity can be based on the needs of the module) Load the class required for the program to run)
  • Problem 2: When the code base is getting bigger and bigger, the creation is complicated. The cross-dependence of different versions of class libraries leads to headaches, which hinder the improvement of Java development and operational efficiency.
  • Problem 3: It is difficult to really encapsulate the code, and the system does not have a clear concept of the dependencies between different parts (that is, JAR files). Every public class can be accessed by any other public class under the classpath, which will lead to inadvertent use of APIs that are not intended to be publicly accessible.
  • Question 4: There are also problems with the classpath itself. How do you know that all the required JARs are already there, or will there be duplicates?

The concept of modularity is actually a layer outside the package, that is to say, the module is used to manage each package, which is exposed by declaring a package, and not declaring it is hiding by default. Therefore, modularization makes code organization safer, because you can specify which parts are exposed and which parts are hidden.

Modularization to achieve goals:

  • The main purpose is to reduce memory overhead
  • Only necessary modules, not all jdk modules, can simplify the development and maintenance of various class libraries and large-scale applications
  • Improve the JavaSE platform so that it can adapt to different sizes of computing devices
  • Improve its safety, maintainability, and performance

The following is a modular demo. I created a new project called java9news, and then through the new module function of IDEA, two modules were generated, one is java9demo and the other is java9test:

There are two simple classes Person and User in the java9demo module:

public class Person { private String name; private int age; //Getter/Setter/toString } public class User { private String name; private int age; //Getter/Setter/toString } Copy code

The java9test module cannot use the classes in java9demo. A module-info.java file must be imported. Module-info.java in the java9demo module:

module java9demo { //Point out the package we want to export exports xpu.tim.bean; } Copy code

Module-info.java in the java9test module:

module java9test { //Specify the module we want to import requires java9demo; //Import log module requires java.logging; } Copy code

Next, test it. The reason why User cannot be used is because the package xpu.tim.entity is not specified in module-info.java in the java9demo module to export, so the User class is used to report Error:

/** * Test the modularity of Java9 */ public class ModuleTest { private static final Logger LOGGER = Logger.getLogger("Tim"); public static void main(String[] args) { Person person = new Person("Tim", 20); System.out.println(person); //User user = new User();//Error LOGGER.info("This one log"); } } Copy code

Java's REpl tool jShell

Languages like Python and Scala have long had an interactive programming environment REPL (read-evaluate-print-loop) to evaluate statements and expressions in an interactive way. Developers only need to enter some code to get feedback on the program before compiling. In the previous Java version, if you want to execute code, you must create a file, declare a class, and provide a test method.

The realization goal of jShell>

1. The REPL tool is finally available in Java9: jShell. Use jShell to directly declare variables, calculate expressions, and execute statements without creating a class. That is, you can run Java code directly on the command line during development without creating a Java file or explaining it to others.

public static void main(String[] args)
This nonsense.

2. jShell can also load statements from files or save statements to files.

3. jShell can also use the tab key for automatic completion and automatic addition of semicolons.

Example of jShell usage, jShell can use Tab key to complete:

D:>jshell | Welcome to JShell - Version 9.0.1 | To get an overview of this version, type:/help intro jshell> System.out.println("HelloWorld") HelloWorld jshell> int i = 10; i ==> 10 jshell> int j = 20; j ==> 20 jshell> int k = i + j; k ==> 30 jshell> System.out.println(k) 30 jshell> public int add(int i, int j){ ...> return i + j; ...>} | Created method add(int,int) jshell> System.out.println(add(50, 100)) 150 jshell> add(10, 20) $8 ==> 30 Copy code

jshell>/help | Type a Java language expression, statement or statement. | Or type one of the following commands: |/list [<name or id>|-all|-start] | List the sources you type |/edit <name or id> | Edit source entries referenced by name or id |/drop <name or id> | Delete source entries referenced by name or id |/save [-all|-history|-start] <file> | Save the clip source to a file. |/open <file> | Open file as source input |/vars [<name or id>|-all|-start] | List declared variables and their values |/methods [<name or id>|-all|-start] | List declared methods and their signatures |/types [<name or id>|-all|-start] | List declared types |/imports | List imported items |/exit | Exit jshell |/env [-class-path <path>] [-module-path <path>] [-add-modules <module>] ... | View or change assessment context |/reset [-class-path <path>] [-module-path <path>] [-add-modules <module>]... | Restart jshell |/reload [-restore] [-quiet] [-class-path <path>] [-module-path <path>]... | Reset and replay related history records - current history or previous history (-restore) |/history | History of what you type |/help [<command>|<subject>] | Get information about jshell |/set editor|start|feedback|mode|prompt|truncation|format ... | Set jshell configuration information |/? [<command>|<subject>] | Get information about jshell |/! | Re-run the previous segment |/<id> | Rerun the fragment by id |/-<n> | Rerun the previous nth fragment | | For more information, type'/help' followed by | The name of the command or topic. | For example,'/help/list' or'/help intro'. theme: | | intro | Introduction to the jshell tool | shortcuts | Fragment and command input prompts, information access and | Button description for automatic code generation | context |/env/reload and/reset evaluation context options jshell> Copy code

jshell can also load source code from external files. For example, the following is a HelloWorld.java file on my desktop:

//Test load source code from external file void printHello() { System.out.println("Test load source code from external file"); } printHello(); Copy code

jShell has no checked exceptions (compiled exceptions), which should have forced us to catch an IOException, but it did not appear. Because jShell hides it for us in the background.

Multi-version compatible jar package

When a new version of Java appears, it will take years for users to switch to this new version. This means that the library has to be backward compatible with the oldest Java version you want to support (in many cases Java 6 or Java 7). This actually means that for a long time in the future, you will not be able to use the new features provided by Java 9 in the library. Fortunately, the multi-version compatible jar function allows you to create a class version that is only used by the library program in a specific version of the Java environment.

As shown in the figure above: root.jar can be used in Java9, but the A or B class does not use the two top-level root.A or root.B classes, but in

META-INF/versions/9
These two below. This is a class version specially prepared for Java 9, which can use the features and libraries provided by Java 9. At the same time, it is also possible to use this JAR in earlier versions of Java, because older versions of Java will only see the top-level class A or B.

The existing directory structure is as follows:

//Application.java in java import java.io.IOException; import java.util.List; import java.util.ArrayList; import java.util.Set; public class Application { public static void testMultiJar(){ Generator gen = new Generator(); System.out.println("Generated strings: "+ gen.createStrings()); } } //Generator.java in java import java.util.Set; import java.util.HashSet; public class Generator { public Set<String> createStrings() { Set<String> strings = new HashSet<String>(); strings.add("Java"); strings.add("8"); return strings; } } //Generator.java in java9 import java.util.Set; public class Generator { public Set<String> createStrings() { return Set.of("Java", "9"); } } Copy code

Now compile it into a Jar package:

javac -d build --release 8 src/main/java/*.javajavac -d build9 --release 9 src/main/java-9/*.javajar --create --main-class=Application --file multijar. jar -C build. --release 9 -C build9 . duplicated code

Next, call the methods in the Jar package in the JDK8 and JDK9 environments, and the results are as follows:

Private methods of the interface

Java 8 specifies that in addition to abstract methods, methods in interfaces can also define static methods and default methods. To a certain extent, the function of the interface is expanded, and the interface at this time is more like an abstract class.

In Java 9, the interface is more flexible and powerful. Even the access permission modifier of the method can be declared as private, and the method will not become part of the API that you expose to the outside world.

interface MyInterface {//JDK7 void method1();//JDK8: static method static void method2(){ System.out.println("method2");}//JDK8: default method default void method3(){ System.out .println("method3"); method4();}//JDK9: private method private void method4(){ System.out.println("method"); }}class MyInterfaceImpl implements MyInterface{ @Override public void method1() {}} public class MyInterfaceTest {public static void main (String [] args) {MyInterface myInterface = new MyInterfaceImpl (); myInterface.method3 ();//myInterface.method4 (); Error}} copy the code

Diamond operator use upgrade

We will be able to use the diamond operator together with the anonymous implementation class. The following operations in Java 8 will report errors:

public class MyOperatorTest {private List<String> flattenStrings(List<String>... lists) {Set<String> set = new HashSet<>(){}; for(List<String> list: lists) {set.addAll (list);} return new ArrayList<>(set); } Copy code

What about in JDK9? In fact, our anonymous subclasses and generics can be used together:

public class MyOperatorTest {public static void main(String[] args) {Set<String> set = new HashSet<>(){ @Override public int size() {return super.size() * 100;} }; set. addAll (Arrays.asList ( "AAA", "BBB", "CCC")); System.out.println (set.size ());//300}} copy the code

try statement upgrade

How to shut down resources in versions before JDK7? It is nothing more than a try-catch-finally structure to ensure that resources are closed in finally:

public class MyTryCatchTest { public static void main(String[] args) { InputStreamReader reader = null; reader = new InputStreamReader(System.in); try { //Data reading process.. reader.read(); } catch (IOException e) { e.printStackTrace(); }finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } Copy code

Try-with-resource appears in JDK7, there is no need to explicitly handle the closing of resources, but all resources that must be closed after execution must be initialized in the try clause, otherwise the compilation will not pass:

public class MyTryCatchTest { public static void main(String[] args) { try(InputStreamReader reader = new InputStreamReader(System.in)) { //Data reading process.. reader.read(); } catch (IOException e) { e.printStackTrace(); } } } Copy code

In JDK9, it is easier to write a try with a resource statement. We can use the initialized resource in the try clause. At this time, the resource is final:

public class MyTryCatchTest { public static void main(String[] args) { InputStreamReader reader = new InputStreamReader(System.in); OutputStreamWriter writer = new OutputStreamWriter(System.out); try(reader; writer) { //Data reading process.. reader.read(); //reader = null;//Error At this time reader and writer are final and cannot be assigned again } catch (IOException e) { e.printStackTrace(); } } } Copy code

UnderScore usage restrictions

UnderScore is actually an underscore. In Java 8, the identifier can be used independently

_
To name:

String _ = "HelloWorld"; Copy code

But it is stipulated in java 9

_
Identifiers can no longer be named individually. If used, an error will be reported:

String storage structure changes

String no longer uses char[] to store it, it is changed to byte[] with encoding mark, saving some space. You can see the description of the official document here: openjdk.java.net/jeps/254

Motivation (Modify 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. 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. 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. This is purely an implementation change, with no changes to existing public interfaces. There are no plans to add any new public APIs or other interfaces. The prototyping work done to date confirms the expected reduction in memory footprint, substantial reductions of GC activity, and minor performance regressions in some corner cases. Copy code

The current implementation of the String class stores characters in a char array, and each character uses two bytes (sixteen bits). Data collected from many different applications shows that strings are the main component of heap usage, and most String objects contain only Latin characters. Such characters only need to store one byte, so half of the space in the internal char array of such String objects is not used.

We recommend changing the internal representation of the String class from a UTF-16 character array to a byte array, plus an encoding flag field. The new String class stores string-based content as ISO-8859-1/Latin-1 (one byte per character) or UTF-16 (two bytes per character) characters. The encoding flag will indicate which encoding is used.

String-related classes (such as AbstractStringBuilder, StringBuilder and StringBuffer) will be updated to use the same representation, and the inherent string operations of HotSpot VM will also use the same representation. This is purely an implementation change and does not change the existing public interface. There are no plans to add any new public APIs or other interfaces.

The prototype work completed so far confirms the expected reduction in memory footprint, greatly reduced GC activity, and performance degradation expected under certain special circumstances.

Are StringBuffer and StringBuilder still indifferent? In fact, due to the changes in the underlying storage structure of the String class, StringBuffer and StringBuier will be affected. We saw the source code of StringBuffer and StringBuilder and found that the annotation @HotSpotIntrinsicCandidate was added:

/** * Constructs a string buffer with no characters in it and an * initial capacity of 16 characters. */ @HotSpotIntrinsicCandidate public StringBuffer() { super(16); } /** * Constructs a string builder with no characters in it and an * initial capacity of 16 characters. */ @HotSpotIntrinsicCandidate public StringBuilder() { super(16); } Copy code

In the JDK source code, the methods annotated by @HotSpotIntrinsicCandidate have a set of efficient implementations in HotSpot. This efficient implementation is based on CPU instructions. During runtime, the efficient implementation of HotSpot maintenance will replace the JDK source code implementation to achieve higher efficiency. . So it can be seen that both StringBuffer and StringBuilder are implemented efficiently through HotSpot, which is also implemented at the bottom through byte[].

Collection factory method: quickly create a read-only collection

To create a read-only, immutable collection, you must construct and allocate it, then add elements, and finally wrap it into an immutable collection. You can refer to these, openjdk.java.net/jeps/269 .

The way to create a read-only collection in JDK1.8:

public class CollectionTest {public static void main(String[] args) {List<String> namesList = new ArrayList<>(); namesList.add("Joe"); namesList.add("Bob"); namesList.add( "Bill"); namesList = Collections.unmodifiableList(namesList); List<String> namesList = new ArrayList<>(); namesList.addAll(Arrays.asList("Joe", "Bob", "Bill")); namesList = Collections.unmodifiableList (namesList);}} copy the code

But this can be done directly in JDK9 (in fact, many places refer to this design, such as the paging parameter in JPA is a typical example):

Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<>(){ { put("AAA", 1); put("BBB", 1); put("CCC", 1); } }); //Even the following is easier to write List<String> namesList = List.of("Joe", "Bob", "Bill"); Map<String, Integer> map = Map.of("AAA", 1, "BBB", 1, "CCC", 1); Copy code

After creation, continuing to add elements to these collections will result in UnsupportedOperationException. Due to the implementation of interface methods in Java 8, these methods can be defined directly in the interfaces of List, Set and Map for easy calling.

StreamAPI enhancement

Java's Steam API is one of the best improvements to the java standard library, allowing developers to perform fast calculations, thereby effectively using data parallel computing. Steam provided by Java 8 can use a multi-core architecture to achieve declarative data processing. In Java 9, the Stream API has become better. 4 new methods have been added to the Stream interface: dropWhile, takeWhile, ofNullable, and a new overload method of the iterator method that allows you to provide a Predicate (judgment condition) To specify when to end the iteration.

In addition to the extension of Stream itself, the combination between Optional and Stream has also been improved. It is now possible to convert an Optional object into a (possibly empty) Stream object through Optional's new method stream().

The use of takeWhile(): Used to get a part of the data from the Stream and receive a Predicate to select. In an ordered Stream, takeWhile returns as many elements as possible from the beginning.

public class StreamAPITest { public static void main(String[] args) { List<Integer> list = Arrays.asList(45, 43, 76, 87, 42, 77); 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); } } Copy code

Use of dropWhile(): The behavior of dropWhile is the opposite of takeWhile, returning the remaining elements.

public class StreamAPITest { public static void main(String[] args) { List<Integer> list = Arrays.asList(45, 43, 76, 87, 42, 77, 90, 73, 67, 88); list.stream().dropWhile(x -> x <50) .forEach(System.out::println); System.out.println(); list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8); list.stream().dropWhile(x -> x <5) .forEach(System.out::println); } } Copy code

The use of ofNullable(): Stream in Java 8 cannot be completely null, otherwise it will report a null pointer exception. The ofNullable method in Java 9 allows us to create a single-element Stream, which can contain a non-null element or create an empty Stream.

public class StreamAPITest { public static void main(String[] args) { Stream<String> stringStream = Stream.of("AA", "BB", null); System.out.println(stringStream.count());//3 List<String> list = new ArrayList<>(); list.add("AA"); list.add(null); System.out.println(list.stream().count());//2 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 } } Copy code

The use of iterator() overload:

public class StreamAPITest { public static void main(String[] args) { //The original control termination method: Stream.iterate(1,i -> i + 1).limit(10) .forEach(System.out::println); //The current termination method: Stream.iterate(1,i -> i <100,i -> i + 1) .forEach(System.out::println); } } Copy code

The use of stream() in the Optional class:

public class StreamAPITest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Tom"); list.add("Jerry"); list.add("Tim"); Optional<List<String>> optional = Optional.ofNullable(list); Stream<List<String>> stream = optional.stream(); stream.flatMap(x -> x.stream()).forEach(System.out::println); } } Copy code