Preface: Java 8 has been released for some time, and all signs indicate that Java 8 is a major change in distribution. There are already many articles on Java Code Geeks that introduce new features of Java 8, such as Playing with Java 8 Lambdas and Concurrency, Java 8 Date Time API Tutorial: LocalDateTime and Abstract Class Versus Interface in the JDK 8 Era. This article also refers to some other information, such as: 15 Must Read Java 8 Tutorials and The Dark Side of Java 8. This article has compiled the above information and compiled it into a reference textbook on the new features of Java 8. I hope you will gain something.
1. Introduction
There is no doubt that Java 8 is the most important version of Java since Java 5 (released in 2004). This version contains more than a dozen new features in languages, compilers, libraries, tools, and JVM. In this article, we will learn these new features and use practical examples to illustrate what scenarios are suitable for use.
This tutorial contains several types of problems that Java developers often face:
language
Compiler
Library
tool
Runtime (JVM)
2. New features of Java language
Java 8 is a major version of Java. Some people believe that although these new features are expected by Java developers, it also requires a lot of effort to learn. In this section, we will introduce most of the new features of Java 8.
2.1 Lambda expressions and functional interfaces
Lambda expressions (also known as closures) are the biggest and most anticipated language changes in Java 8. It allows us to pass functions as parameters to a method, or to process the code itself as data: functional developers are very familiar with these concepts. Many languages on JVM platforms (Groovy, Scala, etc.) have supported Lambda expressions since their birth, but Java developers have no choice but to use anonymous internal classes instead of Lambda expressions.
Lambda's design took a lot of time and a lot of community efforts, and finally found a compromise implementation solution that could achieve a simple and compact language structure. The simplest Lambda expression can be composed of a comma-separated parameter list, -> symbol, and statement block, for example:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
In the above code, the type of parameter e is derived by the compiler reasoning, and you can also explicitly specify the type of parameter, for example:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
If a Lambda expression requires a more complex statement block, you can enclose the statement block with curly braces, similar to the body of a function in Java, for example:
Arrays.asList( "a", "b", "d" ).forEach( e -> {System.out.print( e );System.out.print( e );} );Lambda expressions can refer to class members and local variables (which will implicitly convert these variables into final), for example, the following two code blocks have exactly the same effect:
String separator = ",";Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.print( e + separator ) );
and
final String separator = ",";Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.print( e + separator ) );
Lambda expressions have return values, and the type of return values is also derived by the compiler inference. If the statement block in the Lambda expression has only one line, you can do not need to use the return statement. The following two code snippets have the same effect:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
and
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {int result = e1.compareTo( e2 ); return result;} );In order to make existing functions well compatible with Lambda expressions, Lambda designers considered many methods, so they came up with the concept of function interface. Function interface refers to an interface with only one function, and such an interface can be implicitly converted to a Lambda expression. java.lang.Runnable and java.util.concurrent.Callable are the best examples of functional interfaces. In practice, functional interfaces are very fragile: as long as a developer adds a function to the interface, the interface is no longer a functional interface, resulting in compilation failure. In order to overcome this code level vulnerability and explicitly state that an interface is a functional interface, Java 8 provides a special annotation @FunctionalInterface (all related interfaces in the Java library already have this annotation), to give a simple definition of a functional interface:
@FunctionalInterface public interface Functional { void method();}However, one thing to note is that the default method and static method will not destroy the definition of the functional interface, so the following code is legal.
@FunctionalInterface public interface FunctionalDefaultMethods { void method(); default void defaultMethod() { } }Lambda expressions, as the biggest selling point of Java 8, have the potential to attract more developers to join the JVM platform and use the concept of functional programming in pure Java programming. If you need to know more about Lambda expressions, you can refer to the official documentation.
2.2 Default and static methods of interfaces
Java 8 uses two new concepts to extend the meaning of an interface: the default method and the static method. The default method makes the interface a bit similar to traits, but the goals to be achieved are different. The default method allows developers to add new methods to existing interfaces without breaking binary compatibility, that is, not forcing classes that implement the interface to implement the newly added method at the same time.
The difference between a default method and an abstract method is that an abstract method needs to be implemented, while a default method does not. The default methods provided by the interface will be inherited or overwritten by the interface implementation class. The example code is as follows:
private interface Defaulable { // Interfaces now allow default methods, the implementer may or // may not implement (override) them. default String notRequired() { return "Default implementation"; } } private static class DefaultableImpl implements Defaulable {} private static class OverridableImpl implements Defaulable { @Override public String notRequired() { return "Overridden implementation";}}The Defaulable interface uses the keyword default to define a default method notRequired(). The DefaultableImpl class implements this interface and inherits the default methods in this interface by default; the OverridableImpl class also implements this interface, but overrides the default methods of the interface and provides a different implementation.
Another interesting feature brought by Java 8 is that static methods can be defined in interfaces. The example code is as follows:
private interface DefaulableFactory { // Interfaces now allow static methods static Defaulable create( Supplier< Defaulable > supplier ) { return supplier.get();}}The following code snippet integrates the usage scenarios of default methods and static methods:
public static void main( String[] args ) {Defaulaable default = DefaulableFactory.create( DefaultableImpl::new );System.out.println( defaultable.notRequired() );defaulaable = DefaulableFactory.create( OverridableImpl::new );System.out.println( defaultable.notRequired() );}The output of this code is as follows:
Default implementation
Overridden implementation
Since the implementation of the default method on the JVM provides support at the bytecode level, it is very efficient. The default method allows for improved interfaces without breaking the existing inheritance system. The application of this feature in the official library is: add new methods to the java.util.Collection interface, such as stream(), parallelStream(), forEach(), and removeIf(), etc.
Although default methods have so many benefits, they should be used with caution in actual development: in complex inheritance systems, default methods can cause ambiguity and compilation errors. If you want to know more details, please refer to the official documentation.
2.3 Method reference
Method reference allows developers to directly reference existing methods, Java class constructors or instance objects. Method references and Lambda expressions are used in conjunction with each other, making the constructor of the java class look compact and concise, without a lot of complex template code.
In Simon's example, the Car class is an example of different method references, which can help readers distinguish between four types of method references.
public static class Car { public static Car create( final Supplier< Car > supplier ) { return supplier.get();} public static void colleague( final Car car ) {System.out.println( "Collided " + car.toString() );} public void follow( final Car another ) {System.out.println( "Following the " + another.toString() );} public void repair() { System.out.println( "Repaired " + this.toString() );}}The type of reference for the first method is a constructor reference, the syntax is Class::new, or more general form: Class<T>::new. Note: This constructor has no parameters.
final Car car = Car.create( Car::new ); final List< Car > cars = Arrays.asList( car );
The type of reference of the second method is a static method reference, and the syntax is Class::static_method. Note: This method accepts a Car type parameter.
cars.forEach( Car::collide );
The third method references the type is a reference to a member method of a certain class, and the syntax is Class::method. Note that this method does not define the parameter:
cars.forEach( Car::repair);
The fourth method referenced type is a reference to a member method of an instance object, and the syntax is instance::method. Note: This method accepts a Car type parameter:
final Car police = Car.create( Car::new );cars.forEach( police::follow );
Run the above example and you can see the following output in the console (the Car instance may be different):
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
If you want to understand and learn more detailed content, you can refer to the official documentation
2.4 Repeat comments
Since the introduction of annotations in Java 5, this feature has become very popular and has been widely used in various frameworks and projects. However, annotations have a big limitation: the same annotation cannot be used multiple times in the same place. Java 8 breaks this limitation and introduces the concept of repeated annotations, allowing the same annotation to be used multiple times in the same place.
Defining repeated annotations in Java 8 using @Repeatable annotation is actually not a language-level improvement, but a trick made by the compiler, and the underlying technology is still the same. You can use the following code to explain:
package com.javacodegeeks.java8.repeatable.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public class RepeatingAnnotations { @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() );}}}}As we can see, the Filter class here uses the @Repeatable(Filters.class) annotation, and Filters is a container that stores Filter annotations. The compiler tries to block these details from developers. In this way, the Filterable interface can be annotated with two Filter annotations (no information about Filters is mentioned here).
In addition, the reflection API provides a new method: getAnnotationsByType(), which can return duplicate annotations of a certain type, for example
Filterable.class.getAnnoation(Filters.class) will return two Filter instances, and the content output to the console is as follows:
filter1
filter2
If you want to know more, you can refer to the official documentation.
2.5 Better type inference
The Java 8 compiler has made great improvements in type inference. In many scenarios, the compiler can deduce the data type of a certain parameter, making the code more concise. The example code is as follows:
package com.javacodegeeks.java8.type.inference; public class Value< T > { public static< T > T defaultValue() { return null; } public T getOrDefault( T value, T defaultValue ) { return ( value != null ) ? value : defaultValue;}}The following code is an application of type Value<String>:
package com.javacodegeeks.java8.type.inference; public class TypeInference { public static void main(String[] args) {final Value< String > value = new Value<>(); value.getOrDefault( "22", Value.defaultValue() );}}The type of parameter Value.defaultValue() is derived by the compiler and does not need to be explicitly specified. In Java 7, this code will have a compilation error unless Value.<String>defaultValue() is used.
2.6 Broaden the application scenarios of annotations
Java 8 broadens the application scenarios of annotations. Now, annotations can be used on almost any element: local variables, interface types, superclasses, and interface implementation classes, and even on exception definitions of functions. Here are some examples:
package com.javacodegeeks.java8.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collection; public class Annotations { @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<>(); }}ElementType.TYPE_USER and ElementType.TYPE_PARAMETER are two new annotations added to Java 8 to describe the usage scenarios of annotations. Java language
Yan also made corresponding changes to identify these newly added notes.
3. New features of Java compiler
3.1 Parameter name
In order to obtain the parameter names of methods in Java programs at runtime, older generations of Java programmers must use different methods, such as Paranamer liberary. Java 8 finally normalizes this feature, with support at the language level (using the reflection API and Parameter.getName() method) and bytecode level (using the new javac compiler and the -parameters parameter).
package com.javacodegeeks.java8.parameter.names; import java.lang.reflect.Method; import java.lang.reflect.Parameter; public class ParameterNames { public static void main(String[] args) throws Exception {Method method = ParameterNames.class.getMethod( "main", String[].class ); for( final Parameter parameter: method.getParameters() ) {System.out.println( "Parameter: " + parameter.getName() );}}}In Java 8, this feature is turned off by default, so if you compile the above code without the -parameters parameter and run it, the following results will be output:
Parameter: arg0
If the -parameters parameter is used, the following result will be output (correct result):
Parameter: args
If you use Maven for project management, you can configure the -parameters parameter in the maven-compiler-plugin compiler configuration item:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerArgument>-parameters</compilerArgument> <source>1.8</source> <target>1.8</target> </configuration> </plugin>
4. New features of Java official library
Java 8 has added many new tool classes (date/time classes) and extended existing tool classes to support modern concurrent programming, functional programming, etc.
4.1 Optional
The most common bug in Java applications is null value exceptions. Before Java 8, Google Guava introduced the Optionals class to solve NullPointerException, thereby avoiding the source code being contaminated by various null checks, so that developers can write cleaner code. Java 8 also adds Optional to the official library.
Optional is just an easy thing: store a value of type T or null. It provides some useful interfaces to avoid explicit null checking, and you can refer to the official Java 8 documentation for more details.
Next, let’s take a look at some example of using Optional: a value that may be empty or a value of a certain type:
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!" ) );
If the Optional instance holds a non-null value, the isPresent() method returns true, otherwise it returns false; orElseGet() method, and the Optional instance holds null, it can accept the default value generated by a lambda expression; the map() method can convert the value of the existing Optional instance into a new value; the orElse() method is similar to the orElseGet() method, but returns the passed default value when holding null.
The output results of the above code are as follows:
Full Name is set? false Full Name: [none] Hey Stranger!
Let's take a look at another simple example:
Optional< String > firstName = Optional.of( "Tom" ); System.out.println( "First Name is set? " + firstName.isPresent() ); System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) ); System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) ); System.out.println();
The output of this example is:
First Name is set? true First Name: Tom Hey Tom!
If you want to know more details, please refer to the official documentation.
4.2 Streams
The newly added Stream API (java.util.stream) introduces functional programming of the generated environment into the Java library. This is by far the largest improvement of Java libraries so that developers can write more efficient, concise and compact code.
The Steam API greatly simplifies collection operations (we will see more than just collections later). First, let’s take a look at this class called Task:
public class Streams { 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 );}}}The Task class has a concept of fraction (or pseudo-complexity), and there are two other states: OPEN or CLOSED. Now suppose there is a task collection:
final Collection< Task > tasks = Arrays.asList( new Task( Status.OPEN, 5 ), new Task( Status.OPEN, 13 ), new Task( Status.CLOSED, 8 ) );
First, let’s look at a question: How many OPEN state points are there in this task collection? Before Java 8, to solve this problem, you need to use foreach to loop over the task collection; however, in Java 8, you can use steams to solve it: include a list of a series of elements, and support sequential and parallel processing.
// 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( "Total points: " + totalPointsOfOpenTasks );
The console output for running this method is:
Total points: 18
There are many knowledge points worth talking about here. First, the tasks set is converted into a steam representation; second, the filter operation on the steam filters out all CLOSED tasks; third, the mapToInt operation converts the task stream into an Integer collection based on the Task::getPoints method of each task instance; finally, the sum is calculated by the sum method to obtain the final result.
Before learning the next example, you need to remember some knowledge points about steams (click here for more details). Operations above Steam can be divided into intermediate operations and late operations.
Intermediate operations will return a new steam - performing an intermediate operation (such as filter) will not perform the actual filtering operation, but create a new steam and put the elements that meet the conditions in the original steam into the newly created steam.
Late operations (such as forEach or sum) will traverse the steam and obtain results or accompanying results; after performing the late operations, the steam processing line has been processed and cannot be used. In almost all cases, late operations are traversing the steam immediately.
Another value of steam is creative support for parallel processing. For the above tasks collection, we can use the following code to calculate the sum of points of all tasks:
// Calculate total points of all tasks 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 );
Here we use the parallel method to process all tasks in parallel and calculate the final result using the reduce method. The console output is as follows:
Total points (all tasks): 26.0
For a collection, it is often necessary to group elements in it according to certain conditions. This type of task can be completed quickly using the API provided by steam. The code is as follows:
// Group tasks by their status final Map< Status, List< Task > > map = tasks.stream().collect( Collectors.groupingBy( Task::getStatus ) );System.out.println( map );
The output of the console is as follows:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
The last example question about tasks collection is: how to calculate the proportion of points of each task in the collection in the collection. The specific processing code is as follows:
// 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( weight -> ( long )( weight * 100 ) ) // LongStream .mapToObj( percentage -> percentage + "%" ) // Stream< String> .collect( Collectors.toList() ); // List< String > System.out.println( result );
The console output results are as follows:
[19%, 50%, 30%]
Finally, as mentioned earlier, the Steam API can not only act on Java collections, but traditional IO operations (read data from a file or a network line by line) can benefit from steam processing. Here is a small example:
final Path path = new File( filename ).toPath(); try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );}Stream method onClose returns an equivalent Stream with an extra handle. This handle will be executed when the Stream's close() method is called. The Stream API, Lambda expressions, and method references supported by interface default methods and static methods are Java 8's response to the modern paradigm of software development.
4.3 Date/Time API (JSR 310)
Java 8 introduces a new Date-Time API (JSR 310) to improve the processing of time and dates. Time and date management has always been the most painful problem for Java developers. java.util.Date and later java.util.Calendar have not solved this problem (even more confused by the developers).
Because of the above reasons, the third-party library Joda-Time was born, which can replace Java's time management API. The new time and date management API in Java 8 is deeply influenced by Joda-Time and has absorbed a lot of the essence of Joda-Time. The new java.time package contains all classes about date, time, time zone, Instant (similar to date but precise to nanoseconds), duration (duration), and clock operations. The newly designed API seriously considers the invariance of these classes (lectures learned from java.util.Calendar), and returns a new object if an instance needs to be modified.
Let's take a look at the key classes and their respective usage examples in the java.time package. First, the Clock class uses a time zone to return the current nanosecond time and date. Clock can replace System.currentTimeMillis() and TimeZone.getDefault().
// Get the system clock as UTC offset final Clock clock = Clock.systemUTC();System.out.println( clock.instant() );System.out.println( clock.millis() );
The output of this example is:
2014-04-12T15:19:29.282Z 1397315969360
Second, focus on LocalDate and LocalTime classes. LocalDate only contains the date part in the ISO-8601 calendar system; LocalTime only contains the time part in the calendar system. Objects of both classes can be built using Clock objects.
// Get the local date and local time final LocalDate date = LocalDate.now();final LocalDate dateFromClock = LocalDate.now( clock );System.out.println( dateFromClock ); // Get the local date and local time final LocalTime time = LocalTime.now();final LocalTime timeFromClock = LocalTime.now( clock );System.out.println( time );System.out.println( timeFromClock );
The output results of the above example are as follows:
2014-04-12 2014-04-12 11:25:54.568 15:25:54.568
The LocalDateTime class contains information about LocalDate and LocalTime, but does not contain time zone information in the ISO-8601 calendar system. Here are some examples about LocalDate and LocalTime:
// Get the local date/time final LocalDateTime datetime = LocalDateTime.now(); final LocalDateTime datetimeFromClock = LocalDateTime.now( clock ); System.out.println( datetime ); System.out.println( datetimeFromClock );
The output results of the above example are as follows:
2014-04-12T11:37:52.309 2014-04-12T15:37:52.309
If you need data/time information for a specific time zone, you can use ZoneDateTime, which holds the date and time of the ISO-8601 date system, and has time zone information. Here are some examples of using different time zones:
// Get the zoned date/time final ZonedDateTime zonedDatetime = ZonedDateTime.now(); final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock ); final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) ); System.out.println( zonedDatetime ); System.out.println( zonedDatetimeFromClock ); System.out.println( zonedDatetimeFromClock ); System.out.println( zonedDatetimeFromZone );
The output of this example is:
2014-04-12T11:47:01.017-04:00[America/New_York] 2014-04-12T15:47:01.017Z 2014-04-12T08:47:01.017-07:00[America/Los_Angeles]
Finally, let’s look at the Duration class, which holds the time to seconds and nanoseconds. This makes it easy to calculate the difference between two dates, the example code is as follows:
// Get duration between two dates final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );final Duration duration = Duration.between( from, to );System.out.println( "Duration in days: " + duration.toDays() );System.out.println( "Duration in hours: " + duration.toHours() );
This example is used to calculate the number of days and hours between April 16, 2014 and April 16, 2015, and the output is as follows:
Duration in days: 365 Duration in hours: 8783
The overall impression of Java 8's new date and time is relatively positive, partly because of the positive impact of Joda-Time, and partly because the official finally listened to the needs of developers. If you want to know more details, you can refer to the official documentation.
4.4 Nashorn JavaScript Engine
Java 8 provides the new Nashorn JavaScript engine, allowing us to develop and run JS applications on the JVM. Nashorn JavaScript engine is another implementation version of javax.script.ScriptEngine. This type of Script engine follows the same rules and allows Java and JavaScript to be used interactively. The example code is as follows:
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;" ) );The output of this code is as follows:
jdk.nashorn.api.scripting.NashornScriptEngine Results: 2
4.5 Base64
Support for Base64 encoding has been added to the official Java 8 library, so that Base64 encoding can be performed without using a third-party library. The example code is as follows:
package com.javacodegeeks.java8.base64; import java.nio.charset.StandardCharsets; import java.util.Base64; public class Base64s { public static void main(String[] args) { 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 );}}The output of this example is as follows:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
The new Base64API also supports encoding and decoding of URLs and MINEs.
(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder()).
4.6 Parallel arrays
The Java8 version has added many new methods to support parallel array processing. The most important method is parallelSort(), which can significantly speed up array sorting on multi-core machines. The following example demonstrates the method of the parallelexXxx series:
package com.javacodegeeks.java8.parallel.arrays;import java.util.Arrays;import java.util.concurrent.ThreadLocalRandom; public class ParallelArrays { public static void main( String[] args ) { long[] arrayOfLong = new long [ 20000 ]; Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt( 1000000 ) );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();Arrays.parallelSort( arrayOfLong );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();}}The above codes use the parallelSetAll() method to generate 20,000 random numbers, and then sort them using the parallelSort() method. This program will output the out-of-order array and the first 10 elements of the sorted array. The result of the code output in the above example is:
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 Sorted: 39 220 263 268 325 607 655 678 723 793
4.7 Concurrency
Based on the newly added lambda expressions and steam features, a new method has been added to Java 8 for the java.util.concurrent.ConcurrentHashMap class to support focus operations; in addition, a new method has been added to the java.util.concurrentForkJoinPool class to support general thread pool operations (for more information, please refer to our concurrent programming course).
Java 8 also added a new java.util.concurrent.locks.StampedLock class to support capacity-based locks - there are three models for supporting read and write operations (this lock can be regarded as a replacement for java.util.concurrent.locks.ReadWriteLock).
Many tool classes have also been added to the java.util.concurrent.atomic package, listed as follows:
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
5. New Java Tools
Java 8 provides some new command line tools, which will explain some of the most useful tools for developers.
5.1 Nashorn Engine: jjs
jjs is a command line tool based on the standard Nashorn engine, which can accept js source code and execute. For example, we write a func.js file with the following content:
function f() { return 1; }; print( f() + 1 );You can execute this command on the command line: jjs func.js, and the console output result is:
2
If you need to know the details, you can refer to the official documentation.
5.2 Class dependency analyzer: jdeps
jdeps is a pretty awesome command line tool that displays Java class dependencies at package and class levels. It takes .class files, directories, or Jar files as input, and then outputs the dependencies to the console.
We can use jedps to analyze the Spring Framework library. In order to reduce the results, we can only analyze one JAR file: org.springframework.core-3.0.5.RELEASE.jar.
jdeps org.springframework.core-3.0.5.RELEASE.jar
This command will output a lot of results. Let's only look at some of them: Dependencies are grouped by packages. If the dependencies cannot be found on the classpath, it will display "not found".
org.springframework.core-3.0.5.RELEASE.jar -> C:/Program Files/Java/jdk1.8.0/jre/lib/rt.jarorg.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)-> java.io -> java.lang -> java.lang.annotation -> java.lang.ref -> java.lang.reflect -> java.util -> java.util.concurrent -> org.apache.commons.logging not found-> org.springframework.asm not found-> org.springframework.asm.commons not foundorg.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)-> java.lang -> java.lang.annotation -> java.lang.reflect -> java.util
For more details, please refer to the official documentation.
6. New features of JVM
Use Metaspace (JEP 122) instead of PermGen space. In terms of JVM parameters, use -XX:MetaSpaceSize and -XX:MaxMetaspaceSize instead of the original -XX:PermSize and -XX:MaxPermSize.
7. Conclusion
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。