Author: I don’t have three hearts
The original link: mp.weixin.qq.com/s/qZgRTe0Q2…
Features overview
Here are some of the new features introduced in Java 8. A more detailed description of the new Java 8 features can be found here.
-
Interface default methods and static methods
-
Lambda expressions
-
Functional interface
-
Method references
-
Stream
-
Optional
-
Date/Time API
-
Repeated notes
-
Extended annotation support
-
Base64
-
JavaFX
-
other
- The JDBC 4.2 specification
- Better type inference mechanism
- HashMap performance improved
- IO/NIO improvements
- JavaScript engine Nashorn
- Concurrency
- Class dependent parser JDEPS
- The JVM’s PermGen space has been removed
Interface default methods and static methods
Default interface methods
In Java 8, it is possible to provide a default implementation for interface methods. Such a method must be marked with the default modifier, such as the Iterator interface in the JDK:
public interface Iterator<E> { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationExceition("remove"); }}Copy the code
This will be very useful! If you implement an iterator, you need to provide hasNext() and next() methods. There is no default implementation for these methods — they depend on the data structure you are iterating through. However, if your iterator is read-only, you don’t need to worry about implementing the remove() method.
The default method can also call other methods. For example, we can modify the Collection interface to define a convenient isEmpty() method:
public interface Collection { int size(); // an abstract method default boolean isEmpty() { return size() == 0; }}Copy the code
In this way, programmers implementing the Collection need not worry about implementing the isEmpty() method.
In the JVM, the implementation of default methods is very efficient and provides support for method invocation through bytecode instructions. The default method allows existing Java interfaces to continue to be used while maintaining normal compilation. A good example of this is the number of methods added to the java.util.collection interface: stream(), parallelStream(), forEach(), removeIf(), and so on. While default methods are powerful, there is one thing to be careful about when using them: Before declaring a default method, think carefully about whether it is really necessary.
Resolve default method conflicts
What happens if you define a method as the default method in one interface, and then define the same method in a class or another interface?
Public interface TestInterface1 {default void sameMethod() {system.out.println ("Invoke TestInterface1" Method!" ); Public interface TestInterface2 {default void sameMethod() {system.out.println ("Invoke TestInterface2 ") Method!" ); Public class TestObject implements TestInterface1, TestInterface2 {@ Override public void sameMethod () {/ / here you can also choose a default implementation / / in the two interfaces such as: TestInterface1. Super. The sameMethod (); System.out.println("Invoke Object method! ); Public class Tester {public static void main(String[] args) {TestObject TestObject = new TestObject(); testObject.sameMethod(); }}Copy the code
Test output:
Invoke the Object method.Copy the code
➡️ for languages like Scale or C++, the rules for dealing with such ambiguities are complicated. Java’s rules are much simpler:
- Class is a plus. If this class provides a concrete method that conforms to the signature, the default method in the interface with the same name and argument list is ignored.
- Interface conflict. If one interface provides a default method, another interface provides a method with the same name and argument list(Same order and type), the method must be overridden to resolve the conflict(In the case of code, the compiler will not compile without overwriting it.) ;
Java designers put more emphasis on consistency, and it seems reasonable to leave such ambiguities to programmers themselves. If at least one interface provides an implementation, the compiler reports an error and the programmer must resolve the ambiguity. (If neither interface provides a default implementation for shared methods, there is no conflict, either implemented or not implemented..)
➡️ We only discussed naming conflicts for two interfaces. Now consider another case where a class inherits from a class and implements an interface, but the methods inherited from the parent class and the interface have the same method signature.
Public interface TestInterface {default void sameMethod() {system.out.println ("Invoke TestInterface Method! ); Public class Father {void sameMethod() {system.out.println ("Invoke Father Method! ); }} public class extends Father implements TestInterface {@override public void sameMethod() { System.out.println("Invoke Son Method! ); Public class Tester {public static void main(String[] args) {new Son().samemethod (); }}Copy the code
Program output:
COPYInvoke Son Method!Copy the code
Remember how we talked about method calls (looking for a method of this class and then looking for a parent class)? Add in the “class first” principle mentioned here, which is invoked directly by the square rule in this class, and it’s easy to understand!
“
Never let a default method redefine a method in an Object class. For example, you can’t define a default method for toString() or equals(), although this might be attractive for an interface like List, due to class precedence, such a method can never go beyond Object.tostring () or object.equals ().
Interface static method
In Java 8, static methods (concrete methods that allow direct use of objects without building them) are allowed in the interface. In theory, there is no reason why this should be illegal, except that it defeats the purpose of interfaces as abstract specifications.
Example:
Public interface StaticInterface {static void method() {system.out.println (" Java8 static method ") {system.out.println (" Java8 static method ") ); }}Copy the code
Call:
public class Main { public static void main(String[] args) { StaticInterface.method(); This is a static method in the Java8 interface! }}Copy the code
So far, the common practice has been to put static methods in companion classes, which can be understood as utility classes that operate on inherited interfaces. In the standard library, you can see interfaces and utility classes in pairs, such as Collection/ Collections or Path/ Paths.
In Java 11, the Path interface provides a method equivalent to its utility class Paths.get(), which constructs a URI or sequence of strings as a Path to a file or directory:
COPYpublic interface Path { public static Path of(String first, String... more) { ... } public static Path of(URI uri) { ... }}Copy the code
In this way, the Paths class is no longer necessary. Similarly, if you implement your own interface, there is no reason to provide an additional utility class with utility methods.
➡️ Additionally, in Java 9, methods in interfaces can be private. Private methods can be static or instance methods. Because private methods can only be used within the methods of the interface itself, their use is limited and can only be used as auxiliary methods to other methods in the interface.
Lambda expressions
Lambda expressions (also known as closures) are the most anticipated change at the Java language level in the entire Java 8 release. Lambda allows functions to be passed as arguments to a method, i.e. behavior parameterization.
What is a Lambda expression
We know that we can assign a “value” to a Java variable.
If you want to assign a “block of code” to a Java variable, what do you do?
For example, I want to assign the code block on the right to a Java variable called blockOfCode:
This was not possible before Java 8, but with the advent of Java 8, with the Lambda feature, it will be possible.
Of course, this is not a very neat way to write it, so to make the assignment more elegant, we can remove some unnecessary declarations.
Thus, we have successfully assigned a “block of code” to a variable in a very elegant way. And the “piece of code,” or “function assigned to a variable,” is a Lambda expression.
But there is still a question, what type should the variable blockOfCode be?
In Java 8, ** All Lambda types are interfaces, and the Lambda expression itself, the “code,” needs to be an implementation of that interface. ** This is a key to understanding Lambda, in short, that Lambda expressions themselves are implementations of interfaces. It might be a little confusing to say this directly, but let’s go ahead and look at some examples. We add a type to the above blockOfCode:
This type of interface, in which only one interface function needs to be implemented, is called a functional interface.
To prevent this interface from becoming a “non-functional interface”, we can add a declaration @functionalInterface to this interface, so that no one can add new functions to it:
Thus, we have a complete Lambda expression declaration:
What Lambda expressions do
The e most intuitive use of Lambda is to clean up code.
We can compare Lambda expressions with a traditional Java implementation of the same interface:
So these two things are essentially equivalent. But Java 8 is clearly more elegant and concise. And since Lambda can be assigned directly to a variable, we can pass Lambda directly to a function as an argument, whereas traditional Java must have a well-defined interface implementation to initialize.
In some cases, this interface implementation only needs to be used once. Whereas traditional Java 7 would require you to define a “dirty” interface to implement MyInterfaceImpl, Java 8’s Lambda is much cleaner.
Functional interface
As mentioned above, there is only one type of interface that needs to be implemented. We call this a “functional interface”. Lambda expressions with functional interfaces make our code much cleaner.
The Java 8 API includes a number of built-in functional interfaces, such as the Comparator or Runnable interfaces used in older Java, that are annotated with the @functionalInterface annotation for use on Lambda.
The Java 8 API also offers a number of new functional interfaces to make things easier, some of them from the Google Guava library. Even if you’re familiar with them, it’s worth looking at how they extend to Lambda.
1-comparator (Comparator interface)
The Comparator is a classic interface from older Java, and Java 8 adds a variety of default methods on top of it. Source code and use examples are as follows:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
Copy the code
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName); Person p1 = new Person("John", "Doe"); Person p2 = new Person("Alice", "Wonderland"); comparator.compare(p1, p2); // > 0 comparator.reversed().compare(p1, p2); / / < 0Copy the code
2 – Consumer interface
The Consumer interface represents performing operations on a single parameter. Source code and use examples are as follows:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Copy the code
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
Copy the code
More Consumer interfaces
- BiConsumer: accept(T T, U U); : a binary function that takes two arguments
- DoubleConsumer: void Accept (double value); : a unary function that takes a double
- IntConsumer: void Accept (int value); : a unary function that takes an int
- LongConsumer: void Accept (long value); : a unary function that takes a long argument
- ObjDoubleConsumer: void Accept (T T, double value); : a binary function that takes a generic argument and a double argument
- ObjIntConsumer: void Accept (T T, int value); : a binary function that takes a generic argument and an int argument
- ObjLongConsumer: void Accept (T T, long value); : a binary function that takes a generic argument and a long argument
3 – Supplier (Supplier interface)
The Supplier interface takes no arguments and returns a value of an arbitrary paradigm. Its neat declaration makes it look like it’s not a function. The declaration of this abstract method, in contrast to Consumer, is a function that declares only the return value and takes no arguments. That is, Supplier expresses not the ability to map from a parameter space to a result space, but the ability to generate, because our common scenario involves not only consume (Consumer) or simple map (Function), but also the action of new. Supplier expresses this ability. Source code and use examples are as follows:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Copy the code
Supplier<Person> personSupplier = Person::new;
personSupplier.get(); // new Person
Copy the code
More Supplier Interface
- BooleanSupplier: Boolean getAsBoolean (); : Returns a Boolean parameterless function
- DoubleSupplier: double getAsDouble (); : a no-argument function that returns double
- IntSupplier: int getAsInt (); : a no-argument function that returns int
- LongSupplier: long getAsLong (); : returns a parameterless function of long
4 – Predicate (Predicate interface)
The Predicate interface has only one argument and returns a Boolean type. This interface contains default methods for grouping Predicate into other complex logic (such as and, or, or not). The filter method of Stream accepts Predicate as an input parameter. More on this later when we use Stream. Source code and use examples are as follows:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Copy the code
Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo"); // true
predicate.negate().test("foo"); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Copy the code
More Predicate interfaces
- BiPredicate: Boolean test(T T, U U); : a binary assertion function that takes two arguments
- DoublePredicate: Boolean test(double value); : an assertion function that takes double
- IntPredicate: Boolean test(int value); : an assertion function that takes an int
- LongPredicate: Boolean test(long value); : an assertion function with an argument of long
5-function (Functional interface)
The Function interface takes one argument and returns a result, along with some default methods that can be combined with other functions (compose, andThen). Source code and use examples are as follows:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Copy the code
Function<String, Integer> toInteger = Integer::valueOf; Function<String, String> backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); / / "123"Copy the code
More Function interfaces
- BiFunction: R apply(T T, U U); BiFunction: R apply(T T, U U); : takes two arguments and returns a value representing a binary function;
- Double function: R apply(double value); : Handles only unary functions of type double;
- IntFunction: R apply(int value); : unary functions that handle only int arguments;
- LongFunction: R apply(long value); : unary functions that handle only long arguments;
- ToDoubleFunction: double applyAsDouble(T value) : a unary function that returns double;
- ToDoubleBiFunction: double applyAsDouble(T T, U U); : a binary function that returns double;
- ToIntFunction: int applyAsInt(T value); : a unary function that returns int;
- ToIntBiFunction: int applyAsInt(T T, U U); : a binary function that returns int;
- ToLongFunction: long applyAsLong(T value); : unary function that returns long;
- ToLongBiFunction: long applyAsLong(T T, U U); : returns a binary function of long;
- DoubleToIntFunction: int applyAsInt(double value) : unary function that accepts double as an int;
- DoubleToLongFunction: Long applyAsLong(double value); : unary function that takes a double and returns a long;
- IntToDoubleFunction: double applyAsDouble(int value); : unary function that takes int and returns double;
- IntToLongFunction: long applyAsLong(int value); : unary function that takes int and returns long;
- Double applyAsDouble(long value); : unary function that accepts long to return double;
- LongToIntFunction: int applyAsInt(long value); : unary function that accepts long to return int;
6 – Operator
Operator is Function, and functions are sometimes called operators. The operator interface description in Java8 is more like a supplement to functions, similar to many of the above type mapping functions. Operators include UnaryOperator and BinaryOperator. Corresponding to single (one) primitive operator and binary operator respectively.
The interface declaration of the operator is as follows:
@FunctionalInterface public interface UnaryOperator<T> extends Function<T, T> { static <T> UnaryOperator<T> identity() { return t -> t; }}Copy the code
@FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T,T,T> { public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; } public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; }}Copy the code
Operator simply declares a generic parameter T. The following is an example:
UnaryOperator<Integer> increment = x -> x + 1; System.out.println(" increment :" + increment.apply(2)); 3 BinaryOperator<Integer> add = (x, y) -> x + y; System.out.println(" add :" + add.apply(2, 3)); 5 BinaryOperator<Integer> min = binaryoperator. minBy((o1, O2) -> o1-O2); System.out.println(" min :" + min.apply(2, 3)); // Minimum output value :2Copy the code
More Operator interfaces
- LongUnaryOperator: long applyAsLong(long operand); : unary operator that operates on type long
- IntUnaryOperator: int applyAsInt(int operand); : unary operator that operates on int
- DoubleUnaryOperator: double applyAsDouble(double operand); : unary operator that operates on type double
- DoubleBinaryOperator: Double applyAsDouble(double left, double right); : a binary operator that operates on type double
- Int applyAsInt(int left, int right); : a binary operator that operates on int
- LongBinaryOperator: long applyAsLong(long left, long right); : binary operator that operates on type long
7 – Other functional interfaces
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
4. Method reference
1 – an overview
After learning about Lambda expressions, we often use Lambda expressions to create anonymous methods. However, sometimes we simply call an existing method. As follows:
Arrays.sort(strArray, (s1, s2) -> s1.compareToIgnoreCase(s2));
Copy the code
In Java 8, we can abbreviate existing methods in Lambda expressions directly through method references.
Arrays.sort(strArray, String::compareToIgnoreCase);
Copy the code
This feature is called Method Reference.
A method reference is an existing method or constructor that is used to directly access a class or instance. Method references provide a way to refer to methods without executing them, requiring a target-type context composed of compatible functional interfaces. When evaluated, the method reference creates an instance of the functional interface. When only one method call is performed in a Lambda expression, it is more readable to pass a method reference without Lambda expression. Method references are a more concise and understandable form of Lambda expression.
“
Note: a method reference is a Lambda expression where the method reference operator is the double colon ::.
Classification of 2 –
The standard form of a method reference is: class name :: method name. (Note: only the method name is needed, not the parentheses.)
There are four forms of method references:
- Refer to a static method: ContainingClass: : staticMethodName
- Reference to an object instance methods: containingObject: : instanceMethodName
- Refer to a type of any object instance methods: ContainingType: : methodName
- Reference constructor: ClassName::new
3 – the sample
The following is an example:
public class Person { String name; LocalDate birthday; public Person(String name, LocalDate birthday) { this.name = name; this.birthday = birthday; } public LocalDate getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } @Override public String toString() { return this.name; }}Copy the code
The test class:
public class MethodReferenceTest { @Test public static void main() { Person[] pArr = new Person[] { new Person("003", Of (2016,9,1)), new Person("001", localdate.of (2016,2,1)), new Person("002", localdate.of (2016,3,1)), New Person (" 004 ", LocalDate of,12,1 (2016))}; Array.sort (pArr, new Comparator<Person>() {@override public int compare(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); }}); Array.sort (pArr, (Person a, Person b) -> {return a.getBirthday().compareto (b.getBirthday()); }); Array.sort (pArr, Person::compareByAge); }}Copy the code
5. Stream Operation
Streams are a new addition to the API in Java8 that allows you to work with collections of data declaratively (expressed through query statements rather than writing an implementation AD hoc). It’s kind of like when we operate a database, for example if I want to look up the name of a dish that’s low in calories I can do something like this:
COPYSELECT name FROM dishes WHERE calorie < 400;
Copy the code
You see, we didn’t filter the attributes of the dish (like we did with iterators), we just expressed what we wanted. So why isn’t it possible to do this in Java collections?
On the other hand, what if we want to process a lot of data? Would you consider using multiple threads for concurrent processing? If so, it’s likely that writing code about concurrency is more complex than using the iterator itself, and debugging can be cumbersome.
With these considerations in mind, the Java designers introduced the concept of flow in the Java 8 release (which really brought functional programming style to Java) to help you save time! And with Lambda, the use of stream operations will be smoother!
1 – Stream operation features
Feature 1: Internal iteration
For now, you can think of it simply as an advanced Iterator or an advanced for loop, except that both are external iterations and streams are internal iterations.
To illustrate the difference between internal and external iteration, let’s take a real life example (from Java 8 In Action). Let’s say you want your two-year-old Sofia to put all her toys in a box. The following conversation might occur:
- You: “Sofia, let’s put the toys away. Are there any toys on the floor?”
- Sophia: “Yes, the ball.”
- You: “Ok, put the ball in the box, anything else?”
- Sophia: “Yes, that’s my doll.”
- You: “Ok, put the doll in, too, any more?”
- Sophia: “Yes, I have my book.”
- You: “Ok, put the book in too, any more?”
- Sophia: “No.”
- You: “Okay, we’re done.”
This is exactly what you need to do with Java collections every day. You iterate through a collection, explicitly taking out each item and processing it, but if you just say to Sophia, “Put all the toys on the floor in the box,” Then Sophia can choose to take the doll in one hand and the ball in the other, or choose to take the object closest to the box first and take the other items later.
With internal iteration, projects can be handled transparently in parallel, or in an optimized order that would have been difficult with Java’s old external iteration approach.
This may be a bit nitpicking, but that’s pretty much why Java 8 introduced Streams — internal iterations of the Streams library can automatically choose one to represent and implement in parallel with your hardware.
Feature 2: Traverses only once
Note that, like iterators, a stream can only be traversed once. When the stream is finished, it is said that the stream has been consumed. You can retrieve a new stream from the original data, but you are not allowed to consume the consumed stream. For example, the following code will throw an exception saying that the stream has been consumed:
List<String> title = Arrays.asList("Wmyskxz", "Is", "Learning", "Java8", "In", "Action"); Stream<String> s = title.stream(); s.forEach(System.out::println); s.forEach(System.out::println); / / run the above application will be submitted to the following error / * Exception in the thread "is the main" Java. Lang. An IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279) at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) at Test1.main(Tester.java:17) */Copy the code
Feature three: convenient parallel processing
Java 8 not only provides convenient streaming operations (filtering, sorting, etc.), but also has great support for parallel processing, just add.parallel()! For example, we use the following program to illustrate the convenience and speed of multi-threaded flow operation, and compared with a single thread:
COPYpublic class StreamParallelDemo {/** total */ private static int total = 100_000_000; Public static void main(String[] args) {system.out.println (String. %d", Runtime.getRuntime().availableProcessors())); // Generate 1000W Random numbers (1 to 100) to form a list Random Random = new Random(); List<Integer> list = new ArrayList<>(total); for (int i = 0; i < total; i++) { list.add(random.nextInt(100)); } long prevTime = getCurrentTime(); list.stream().reduce((a, b) -> a + b).ifPresent(System.out::println); Println (string. format(" Single thread calculation time: %d", getCurrentTime() -prevTime)); prevTime = getCurrentTime(); List.stream ().parallel().reduce((a, b) -> a + b).ifpresent (system.out ::println); Println (string. format(" Multithreaded computation time: %d", getCurrentTime() -prevTime)); } private static long getCurrentTime() { return System.currentTimeMillis(); }}Copy the code
The above program uses single-thread stream and multi-thread stream respectively to calculate the sum of 10 million random numbers, and the output is as follows:
The number of cores of this computer: 8 655028378 Single-thread calculation time: 4159 655028378 multi-thread calculation time: 540Copy the code
Internally, parallel streams use the default ForkJoinPool branch/merge framework, whose default number of threads is the number of processors you have. This value is obtained by Runtime.getruntime ().availableProcessors() (of course we can also set this value globally). We don’t have to worry too much about locking thread safety.
2 – Some important method description
- Stream: Returns a stream of data, with the collection as the origin
- ParallelStream: Returns parallel data streams, with collections as origins
- The filter: method is used to filter out elements that meet the criteria
- Map: the method is used to map the corresponding results for each element
- ForEach: the method iterates through each element in the stream
- The limit: method is used to reduce the size of a stream
- Sorted: The sorted method is used to sort elements in a stream
- AnyMatch: Whether any element satisfies the condition (returns a Boolean value)
- AllMatch: Whether all elements meet the criteria (returns a Boolean value)
- NoneMatch: Whether all elements do not satisfy the condition (return Boolean)
- Collect: The method is a terminal operation, which is the end of a marker stream that typically occurs at the end of a pipe transfer operation
3 – Some usage examples
The Filter to Filter
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
Copy the code
The Sort order
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
Copy the code
The Map mapping
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
Copy the code
Match Match
boolean anyStartsWithA = stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA = stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ = stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
Copy the code
Count Count
long startsWithB = stringCollection .stream() .filter((s) -> s.startsWith("b")) .count(); System.out.println(startsWithB); / / 3Copy the code
Reduce reduction
This is a final operation that allows multiple elements in a stream to be specified as one element, with the result of the specification expressed through the Optional interface. The code is as follows:
Optional<String> reduced = stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
Copy the code
“
Want to know more, please refer to: www.wmyskxz.com/2019/08/03/…
6. Optional
The notorious null-pointer exception is by far the most common cause of Java application failure. In the past, Google’s famous Guava project introduced the Optional class to address null pointer exceptions. Guava encourages programmers to write cleaner code by checking for null values to prevent code contamination. Inspired by Google Guava, the Optional class has become part of the Java 8 class library.
Optional is actually a container: it can hold values of type T, or just NULL. Optional provides a number of useful methods so that we don’t have to explicitly null-check.
Let’s use two small examples to demonstrate how to use the Optional class: one that allows null values and one that does not.
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!" ));Copy the code
IsPresent () returns true if an instance of Optional is non-null, and false if not. To prevent Optional from being null, the orElseGet() method uses a callback function to generate a default value. The map() function converts the value of the current Optional and returns a new Optional instance. The orElse() method is similar to the orElseGet() method, but orElse accepts a default value instead of a callback function. Here is the output of the program:
Full Name is set? false
Full Name: [none]
Hey Stranger!
Copy the code
Let’s look at another 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();Copy the code
Here is the output of the program:
First Name is set? true
First Name: Tom
Hey Tom!
Copy the code
Lambda works with Optinal to elegantly resolve NULL
Here we assume we have a Person Object and an Optional wrapper for the Person Object:
Optional<T> If not used in conjunction with Lambda, it does not simplify the tedious NULL check.
Its true power comes only when Optional<T> is used in conjunction with Lambda!
Lambda + Optional<T> in Java 8 compared to traditional Java.
Case one: Existence continues
Case 2: return if it exists, otherwise it does not exist
Case 3: return if it exists, otherwise generated by the function
Case 4: Kill chain null check
It is clear from the above four cases that Optional<T> + Lambda will save us a lot of ifElse blocks. The new Optional<T> +Lambda is clean and concise, while the traditional Java method is verbose and confusing.
7. The Data/Time API
Java 8 includes a new set of date and time apis under the package java.time. The new date API is similar to, but not identical to, the open source Joda-Time library, and the following examples show some of the most important parts of the new API:
1-clock Indicates the Clock
The Clock class provides access to the current date and time. Clock is time-zone sensitive and can be used instead of System.CurrentTimemillis () to get the current microseconds. A specific point in time can also be represented using Instant classes, which can also be used to create old java.util.date objects. The code is as follows:
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();
Instant instant = clock.instant();
Date legacyDate = Date.from(instant); // legacy java.util.Date
Copy the code
2 – Timezones Indicates the time zone
In new AP I time zones are represented by ZoneId. Time zones can be easily obtained using the static method of. The time zone defines the time difference to UTS time, which is extremely important when converting from Instant point-in-time objects to local date objects. The code is as follows:
System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids
ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());
// ZoneRules[currentStandardOffset=+01:00]
// ZoneRules[currentStandardOffset=-03:00]
Copy the code
3 – LocalTime indicates the LocalTime
LocalTime defines a time with no time zone information, such as 10 p.m., or 17:30:15. The following example creates two local times using the time zone created in the previous code. Then compare the time and calculate the difference in hours and minutes. The code is as follows:
LocalTime now1 = LocalTime.now(zone1); LocalTime now2 = LocalTime.now(zone2); System.out.println(now1.isBefore(now2)); // false long hoursBetween = ChronoUnit.HOURS.between(now1, now2); long minutesBetween = ChronoUnit.MINUTES.between(now1, now2); System.out.println(hoursBetween); // -3 System.out.println(minutesBetween); / / - 239Copy the code
LocalTime provides a variety of factory methods to simplify object creation, including parsing time strings. The code is as follows:
LocalTime late = LocalTime.of(23, 59, 59); System.out.println(late); // 23:59:59 DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedTime(FormatStyle.SHORT) .withLocale(Locale.GERMAN); LocalTime leetTime = LocalTime.parse("13:37", germanFormatter); System.out.println(leetTime); / / 13:37Copy the code
4 – LocalData Indicates the local date
LocalDate indicates an exact date, such as 2014-03-11. This object value is immutable and is used almost identically to LocalTime. The following example shows how to add or subtract days/months/years to a Date object. Also note that these objects are immutable and the operation always returns a new instance. The code is as follows:
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
LocalDate yesterday = tomorrow.minusDays(2);
LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
System.out.println(dayOfWeek); // FRIDAY
Copy the code
Resolving a LocalDate type from a string is as easy as resolving LocalTime. The code is as follows:
DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(Locale.GERMAN); Parse ("24.12.2014", germanFormatter); System.out.println(xmas); / / 2014-12-24Copy the code
5 – LocalDateTime Indicates the local date and time
LocalDateTime represents both the time and the date, which is equivalent to merging the previous two sections into one object. LocalDateTime, like LocalTime and LocalDate, is immutable. LocalDateTime provides methods to access specific fields. The code is as follows:
LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59); DayOfWeek dayOfWeek = sylvester.getDayOfWeek(); System.out.println(dayOfWeek); // WEDNESDAY Month month = sylvester.getMonth(); System.out.println(month); // DECEMBER long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY); System.out.println(minuteOfDay); / / 1439Copy the code
This can be converted to a point-in-time Instant object, which can be easily converted to old-fashioned java.util.date, simply by attaching the time zone information. The code is as follows:
Instant instant = sylvester
.atZone(ZoneId.systemDefault())
.toInstant();
Date legacyDate = Date.from(instant);
System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014
Copy the code
Formatting LocalDateTime is the same as formatting time and date. In addition to using the predefined format, we can also define our own format. The code is as follows:
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern("MMM dd, yyyy - HH:mm");
LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
String string = formatter.format(parsed);
System.out.println(string); // Nov 03, 2014 - 07:13
Copy the code
Unlike java.text.NumberFormat, the new DateTimeFormatter is immutable, so it is thread-safe.
Repeat the notes
Since Java 5 introduced annotations, this feature has become very popular and widely used. However, one limitation of using annotations is that the same annotation can only be declared once in the same place, not more than once. Java 8 breaks this rule by introducing repeated annotations so that the same annotations can be declared multiple times in the same place.
The Repeatable annotation mechanism itself must be annotated with @REPEATable. In fact, this is not a language change, but more of a compiler trick, and the underlying principles remain the same. Let’s look at a quick start example:
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()); }}}Copy the code
As we can see, there is an annotation class Filter that uses the @REPEATable (Filters.class) annotation. Filters are just an array of Filters annotations, but the Java compiler doesn’t want the programmer to be aware of Filters. Thus, the interface Filterable has two Filter annotations (without mentioning Filter).
At the same time, Reflect this API provides a new function getAnnotationsByType () to return to repeat the annotation type (please note Filterable. Class. GetAnnotation (Filters. Class) by the compiler ` will return Filters instance).
Extended annotation support
Java 8 extends the context of annotations. You can now annotate almost anything: local variables, generic classes, superclasses, implementations of interfaces, even method exceptions. Here are some examples:
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<>(); }}Copy the code
Ten Base64.
In Java 8, Base64 encoding has become the standard for Java class libraries. It’s quite simple to use, so let’s look at an example:
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);
}
}
Copy the code
The program outputs the encoded and decoded characters on the console:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
Copy the code
The Base64 class also provides URL – and MIME-friendly encoders and decoders (base64.geturlencoder ()/base64.geturldecoder (), Base64.getMimeEncoder()/base64. getMimeDecoder()).
Eleven deployment headaches.
JavaFX is a powerful collection of graphics and multimedia processing toolkits that allows developers to design, create, test, debug, and deploy rich client applications, as cross-platform as Java. Since Java8, JavaFx has been built into the JDK. For more detailed documentation on JavaFx, refer to the JavaFx Documentation in Chinese.
Xii. Miscellaneous
1. JDBC4.2 specification
JDBC4.2 has the following major changes:
- Added support for REF Cursor
- Change the return value size range (update count)
- Added java.sql.DriverAction interface
- Added java.sql.SQLType interface
- Added a java.sql.JDBCtype enumeration
- Support for java.time package time types
2. Better type prediction mechanism
Java 8 has greatly improved type speculation. In many cases, the compiler can infer certain parameter types, which can make the code cleaner. Let’s look at an example:
public class Value<T> {
public static<T> T defaultValue() {
return null;
}
public T getOrDefault(T value, T defaultValue) {
return (value != null) ? value : defaultValue;
}
}
Copy the code
Here is the use of the Value<String> type.
public class TypeInference { public static void main(String[] args) { final Value<String> value = new Value<>(); value.getOrDefault("22", Value.defaultValue()); }}Copy the code
The parameter type of value.defaultValue () can be inferred, so it is not necessary to specify it. In Java 7, the same example will not compile; the correct way to write it is Value.<String>defaultValue().
3. Improved performance of HashMap
In Java 8, the internal implementation of HashMap also introduced a red-black tree, making the overall performance of HashMap significantly better than that of Java 7. The following is a comparison of performance with uniform and uneven Hash
Hash evenly
Performance comparison when Hash is uniform
Hash is extremely uneven
Performance comparison when Hash is uneven
“
To learn more about HashMap, click here: portal
4. IO/NIO improvement
Java 8 has also made some improvements to IO/NIO. The implementation of java.nio.charset.Charset is improved to improve the encoding and decoding efficiency, and the jre/lib/charsets. Jar package is simplified. Improved performance of String(byte[], *) constructors and string.getBytes () methods; New IO/NIO methods have also been added to retrieve streams (java.util.stream.stream) from files or input streams, simplifying text line processing, directory traversal, and file look-up by operating on streams.
The new apis are as follows:
- Bufferedreader.line (): Returns a text line Stream
- File.lines(Path, Charset): Stream
returns text lines
- File.list(Path): traverses files and directories in the current directory
- File.walk(Path, int, FileVisitOption): Traverses all files in a directory and subdirectories of a specified depth
- File.find(Path, int, BiPredicate, FileVisitOption…) : Finds the corresponding file
Here is a list of all files and directories in the current directory by streaming:
Files.list(new File(".").toPath()).forEach(System.out::println);
Copy the code
5. JavaScript engine Nashorn
Java 8 provides a new Nashorn javascript engine that allows us to run specific javascript applications on the JVM. Nashorn javascript engine just javax.mail. Script. A ScriptEngine another implementation, and rules, allow Java and javascript operation with each other. Here’s a quick example:
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;" ));Copy the code
The output is as follows:
jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2
Copy the code
6. Concurrency
Based on the mechanism of the new Stream with Lambda, in Java. Util. Concurrent. ConcurrentHashMap added some new methods to support the operation. . At the same time also in Java. Util. Concurrent ForkJoinPool classes to add some new methods to support the Shared resource pool (common pool) (please check our free classes about Java concurrency).
The new Java. Util. Concurrent. The locks. StampedLock class provides has been based on the capacity of the lock, This lock has three model to control the read and write operations, it is considered to be less famous Java. Util. Concurrent. The locks. The ReadWriteLock class replacement).
In Java. Util. Concurrent. Atomic package also adds the following categories:
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
7. Class dependent parser JDEPS
Jdeps is a powerful command-line tool that can help us show the dependencies of Java class files at the package or class level. It takes class files, directories, and JAR files as input, and jDEPS prints them to the console by default.
As an example, let’s take a look at the dependency reports for the libraries of the now popular Spring framework. To keep the report short, we will analyze only one JAR: org.springFramework.core-3.0.5.release.jar.
The jdeps org.springFramework.core-3.0.5.release.jar command produces a lot of output, and we’ll look at just a few of them. These dependencies are grouped by package, and if they are not found in the classpath, they are not found.
C: Program Files\Java\jdk1.8.0\jre\lib\rt.jar org.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 Found org. Springframework. Core. The annotation (org. Springframework. Core - 3.0.5. The jar) - > Java. Lang - > java.lang.annotation -> java.lang.reflect -> java.utilCopy the code
8. JVM PermGen space has been removed
The PermGen space was removed and replaced with Metaspace (JEP 122). JVM options -xx :PermSize and -xx :MaxPermSize were replaced by -xx :MetaSpaceSize and -xx :MaxMetaspaceSize, respectively.
The difference between:
- The meta-space is not in the virtual machine, but uses local memory
- By default, the size of a meta-space is limited only by local memory
- You can also specify the MetaspaceSize size by using -xx: MetaspaceSize