[new language features] - Java 8

Posted by kiowa_jackson on Mon, 24 Jan 2022 18:07:59 +0100

summary

java8 has brought us many new features in the java language:

Faster speed, less code (new syntax Lambda expression is added), powerful Stream API, easy parallelism, minimizing null pointer exceptions, Optional, etc

Faster

Java8 updates the underlying data structure, the core of which is HashMap. In java7, the underlying data structure of HashMap is array plus single Necklace table, while java8 adopts array plus linked list plus red black tree.

Modified the garbage collection mechanism!

Less code

Lambda expressions are added in the new Java 8, which is an exciting language change. The code designed with it will be more concise and readable. Most importantly, the amount of code will be reduced.

Powerful Stream API

The improvement of collections is also a highlight of Java 8, and the core component that makes collections better and better is "Stream". It is similar to Java InputStream and OutputStream in io package are completely different concepts. It is a new concept. Don't confuse them.

Easy parallel

Parallel flow is to divide a content into multiple data blocks and process each data block with different threads.

Java 8 optimizes the parallel flow, and we can easily operate the data in parallel.

Minimize null pointer differences

The Optional class is introduced into the class of Java 8 to wrap the object to solve the NullPointerException.

Lambda expression

What is a Lambda expression?

Lambda completely replaces anonymous inner classes. Lambda is a function expression composed of parameters, arrow symbols and method calls. Lambda expressions can have parameters and no parameters, return values and no return values. A lambda expression can replace the entire anonymous inner class. Lambda expressions are usually used with functional interfaces. Using lambda expressions can write more concise and flexible code. As a more compact code style, the language expression ability of Java has been improved.

Functional interface

There are four core functional interfaces in java

Interfacetypemethod
java.util.function.ConsumerConsumption typevoid accept(T t)
java.util.function.SupplierProduction typeT get()
java.util.function.FunctionFunctional typeR apply(T t)
java.util.function.PredicateAssertion typeboolean test(T t)

There can only be one abstract method in a functional interface, and the special interface is usually used with Lambda expressions.

Example:

@FunctionalInterface
public interface MyLambda {
	//There can only be one abstract method
   String test(String name);
}

Syntax format of Lambda expression

Premise:
Operators introduced in Java 8: - > (arrow operator)
->The arrow operator divides the Lambda expression into two parts
Left: parameter list
Right: the implementation of the method (only represents the implementation of the method)
There is no need to specify the data type in the parameter list, and the jvm will infer the type according to the context.

()-> Method calling code

Use of Lambda expressions

Lambda expression sub whole represents the original whole anonymous function. However, many abstract methods in an abstract class or interface cannot be implemented like anonymous functions. It can only be used in the case of an abstract class or interface with only one abstract method. For the case of multiple abstract methods, please refer to the next chapter [functional methods].

Lambda doesn't have a very fixed way of writing. However, its changes are inseparable from its origin, that is, the left side must be parameters and the right side must be method execution. The parameters on the left serve the methods on the right. Several situations are listed below.

No parameter, no return value.

 //No parameter, no return value.
   public static void test() {
   		//The run method is implemented
       Runnable runnable = () -> System.out.println("Lambda");
       runnable.run();//Lambda
    }
    

No parameter, return value.

     // String test(); Abstract methods implemented

        MyLambda myLambda = () -> {
            return "lambda";
        };
        System.out.println(myLambda.test());

There is one parameter and no return value

When there is only one parameter, parentheses can be omitted. But usually, we should write parentheses for ease of reading.
      // System.out.println(x) is a hypothetical method implementation,
        Consumer consumer = (x) -> System.out.println(x);
        consumer.accept("Lambda expression");
        

There is a parameter with a return value

	// String test(String name); Implemented abstract methods
	
   MyLambda myLambda = (a) -> {
            return a;
        };

        System.out.println(myLambda.test("Lambda"));

    }

There are two or more parameters and return values.

 Comparator comparator = (x,y) -> {return Integer.compare((int)x,(int)y);};

        System.out.println(comparator.compare(1, 2));

Method reference

We usually use Lambda's method body to implement the method body of the abstract method, and this part of the implementation is usually composed of the methods already implemented in the java class library. If the contents of the method body have been implemented by other methods, but the premise is that the methods referenced in the method body must be the same as the return value and formal parameter list of the abstract method, In this case, I can use three special reference methods, which can reduce the amount of code and facilitate reading.

Object reference instance

When an object calls a method in the body of a Lambda expression.

Example:

      Consumer consumer = (x) ->System.out.println(x);

      /*The above can be replaced by method reference
       System.out What you get is an object
       println is the referenced method,
       */

      Consumer consumer1 = System.out::println;
      consumer1.accept("lambda");//lambda

Class references static methods

When a class calls a static method in the body of a Lambda expression.

Example:

        Comparator comparator = (x,y) -> Integer.compare(x,y);

        /*
            Where Integer is a class and compare is a static method
             Can write
        */
        Comparator comparator1 = Integer::compare;
        

Class reference example method

When a class calls the sample method in the body of a Lambda expression.
Premise: when the first parameter in the parameter list is the caller of the instance method and the second parameter is the parameter of the instance method,

Example:

 BiPredicate biPredicate = (x,y) -> x.equals(y);
        /*
            In the above method, the first parameter in the parameter list is used to call the method to pass another parameter,
            x Instance method for object equals()
         */
        BiPredicate biPredicate1 = Object::equals;
        

Constructor reference

We usually use Lambda's method body to implement the method body of the abstract method. When this part of the implementation contains the operation of the new object, we can use the constructor reference. When there are multiple constructor overloads, when using the constructor reference, java will select the constructor matching the extraction method for us, and java will infer according to the generic type of the variable type. This can reduce the amount of code and make it easy to read.

Example:

	Supplier supplier = () -> new Employee();
       /*
       At this time, the Employee may have multiple overloads and generic inference.
        */
        Supplier<Employee> supplier1 = Employee::new;
        supplier1.get();

Stream API

java.util.stream.Stream interface is a brand-new API provided in java8. Stream stream mainly uses Lambda expression implementation method in stream to perform various intermediate operations for data,
It is convenient for us to get the desired results from the original data.

Create Stream stream

  • You can use the stream() or parallstream() provided by the Collection series Collection.
		List list = new ArrayList();
       
        Stream stream = list.stream();
        Stream stream1 = list.parallelStream();
        
  • Get the array stream through the static method stream() in Arrays
	User[] user = new User[10]; 
        Arrays.stream(user);
  • Through the static method of() in the Stream class
Stream<String> stream = Stream of("aa","bb","cc");

Intermediate operation

Screening and slicing

methodsummary
Stream filter(Predicate<? super T> predicate)Returns a stream consisting of elements of this stream that match this given predicate.
Stream limit(long maxSize)Returns a stream composed of elements of this stream. The truncated length cannot exceed maxSize.
Stream skip(long n)After discarding the first N element of the stream, the stream composed of the n elements of the stream is returned.
Stream distinct()Returns a stream composed of different elements of the stream according to Object.equals(Object)).

Example:

  List<User> list = Arrays.asList(
                new User("Li Si", 41,false),
                new User("Wang Wu", 60,false),
                new User("March", 22,true),
                new User("Zhang San", 53,false),
                new User("Sun Yi", 61,true),
                new User("Ge Ba", 85,false)
        );
        Stream<User> stream = list.stream();
           Stream<User>stream1 = stream
                   .filter( (user) -> { return user.getAge()>20 ;})//Filter to exclude some qualified elements in the flow
                   .skip(2)//Discard some data,
                   .limit(3)//Truncation makes the filtered results less than a few
                   .distinct();//De duplication. The elements participating in de duplication need to implement hashCode and equals
           stream1.forEach(System.out::println);

mapping

methodsummary
Stream map(Function<? super T,? extends R> mapper)Returns a stream consisting of the results of the elements that the given function applies to this stream., Convert elements to other forms or extract information. Receive a function as an argument, which is applied to each element and mapped to a new element
Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper)Returns a stream that replaces the result of each element of the stream with the content of the mapping stream generated by applying the provided mapping function to each element. Take a function as a parameter, replace each value in the stream with another stream, and then connect all streams into one stream

Example:

//This example is incomplete
List l = Arrays.asList("a","b","c","d","e");
       Stream stream = l.stream()
          .map((str) -> str.toUpperCase());
        Stream stream1 = l.stream()
                .map(filterMap);

sort

methodsummary
Stream sorted()Returns a stream consisting of the elements of this stream, sorted in natural order.
Stream sorted(Comparator<? super T> comparator)Returns the stream composed of the elements of the stream, sorted according to the provided Comparator.

Example:

 List l = Arrays.asList("a","d","b","c","e");
            l.stream()
                    .sorted()// Natural sorting
                    .forEach(System.out::println);
            
            //Custom sorting

termination

Find and match

methodsummary
boolean allMatch(Predicate<? super T> predicate)Check that all of these elements match
boolean anyMatch(Predicate<? super T> predicate)Check to see if at least one element matches
boolean noneMatch(Predicate<? super T> predicate)Check that no elements match
Optional findFirst()Returns the first element
Optional findAny()Returns any element in the current stream
long count()Returns the total number of elements in the stream
Optional max(Comparator<? super T> comparator)Returns the maximum value in the stream
Optional min(Comparator<? super T> comparator)Returns the minimum value in the stream

Reduction and collection

You can combine the elements in the flow repeatedly to get a value. For example, add all the numeric elements in an array

methodsummary
Optional reduce(BinaryOperator accumulator)Use the associated accumulation function to perform reduction on the elements of this stream and return Optional (if any) describing the reduced value.
T reduce(T identity, BinaryOperator accumulator)Use the provided identity value and associated accumulation function to perform reduction on the elements of this flow and return the reduced value.
U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator combiner)Perform reduction on the elements in this flow, using the provided identity, accumulation and combination functions.
<R,A> R collect(Collector<? super T,A,R> collector)

Reduction example:

 List l = Arrays.asList(1,2,3,4,5);
        l.stream().reduce(0,(x,y) -> x+y);//Take 0 as the starting value. After adding it to the starting value, the resulting value is x, and then add it to other values

Collection example:

        list.stream()
                .map(User::getName)//Take out all names
                .collect(Collectors.toList());//Put the collected elements into the List
                

 		 list.stream()
                .map(User::getName)//Take out all names
                .collect(Collectors.toCollection(HashSet::new));//Put the collected elements into the specified container
                

		list.stream()
                .collect(Collectors.groupingBy(User::getSxe));//grouping
        list.stream()
                .collect(Collectors.groupingBy(User::getSxe),
                        Collectors.groupingBy(Regroup rules));//After grouping, the group is grouped again
        list.stream()
                .collect(Collectors.groupingBy(Satisfied and unsatisfied));//Partition elements, true and false                         

Parallel stream and serial stream

Parallel flow is a flow that divides a content into multiple data blocks and processes each data block with different threads. Java 8 optimizes parallelism, and we can easily operate data in parallel. The Stream API can declaratively switch between parallel and sequential streams through parallel() and sequential(). Column, such as splitting a complex task according to the critical value (no splitting rule), pushing it into the thread queue, and finally join ing and merging the results obtained by the split tasks,

Optional

Java. util. The Optional class is a container class that represents the existence or nonexistence of a value. Originally, null was used to represent the nonexistence of a value. Now Optional can better express this concept. And you can avoid null pointer exceptions.

Common methods:

methodsummary
static Optional of(T value)Returns an Optional that is not null. If it is null, a null pointer exception will be thrown
static Optional empty()Returns an empty Optional container that does not contain any elements
static Optional ofNullable(T value)If the passed in parameter is null, an empty Optional container is returned; otherwise, an Optional container containing this value is returned
boolean isPresent()Determine whether there is a value in this optional container
T orElse(T other)If this optional container contains a value, the value in the container is returned; otherwise, the specified value is returned
T orElseGet(Supplier<? extends T> other)If this optional container contains a value, the value in the container is returned; otherwise, the result of the get operation of this value is returned
Optional map(Function<? super T,? extends U> mapper)If the result of this function is not null and does not contain a value, an empty Optional container containing no elements is returned; otherwise, an Optional container containing the processing result of this function is returned
Optional flatMap(Function<? super T,Optional> mapper)If the result of this function is not null and does not contain a value, an empty Optional container without any elements is returned; otherwise, a value of Object type containing the processing result of this function is returned

Example:

import java.util.Optional;

public class OptionalTest {

    public static void main(String[] args) {

        /*Returns an empty Optional container that does not contain any elements*/
        Optional optional1 = Optional.empty();

        /*If the parameter passed in is null, an empty Optional container will be returned; otherwise, an Optional container containing this value will be returned*/
        Optional.ofNullable(null);

        /*Returns an Optional that is not null. If it is null, a null pointer exception will be thrown*/
        Optional optional = Optional.of(null);
        /*Determine whether there is a value in this optional container*/
        optional.isPresent();
        /*If this optional container contains a value, the value in the container is returned; otherwise, the specified value is returned*/
        optional.orElse(new User());
        /*If this optional container contains a value, the value in the container is returned; otherwise, the result of the get operation of this value is returned*/
        optional.orElseGet(() -> new User());
        /*If the result of this function is not null and does not contain a value, an empty Optional container without any elements is returned,
        Otherwise, an optional container containing the processing results of this function is returned*/
        optional.map((e) -> e.toString());
        /*If the result of this function is not null and does not contain a value, an empty Optional container without any elements is returned,
        Otherwise, a value of Object type containing the processing result of this function is returned*/
        optional.flatMap((e) -> e.toString());





    }

}

Interface

In Java 8, new functions are provided for interfaces: default methods and static methods with implementation.
.

Default method

"Class first" principle of interface default method
When a default method is defined in an interface and a method with the same name is defined in another parent class or interface, the method in the parent class will be selected.
If a parent class provides a concrete implementation, the default method with the same name and parameters in the interface will be ignored. If a parent interface provides a default method and another interface provides a method with the same name and parameter list (whether the method is the default method or not), the method must be overridden to resolve the conflict.

public interface myInterface {

    public default String a(){
        return "square accounts in every detail";
    }

}

Static method

The processing rules for static methods and default methods with the same name are the same.

public interface myInterface {

   static void b(){
       
   }
   
}

New time date API

Java 8 brings us a new time API. Different from the past time API, the new time API has more convenient operation, less code, high readability, can perform mathematical operations, and there is no thread safety problem.

Main related categories:

classsummary
java.time.LocalDateIncluded hour minute second date object
java.time.LocalTimeDate object containing year month day, and other date fields, such as date, day of the week, and week
java.time.LocalDateTimeTime Date object containing year month day hour minute second
java.time.InstantInstant on the timeline (timestamp)
java.time.DurationThis class establishes quantity or time in seconds and nanoseconds, (time)
java.time. PeriodEstablish quantity or time quantity in year, month and day, (date)
java.time.temporalThe adjuster is a key tool for modifying time objects
java.time.DateTimeFormatterFormatter for printing and parsing date time objects.
java.time. ZonedDate/ZonedTime/ZonedDateTimetime zone
java.time.ZoneIdInclude time zone ID

Instances of Localdate, Localtime and Loca| Datetime classes are immutable objects. Any operation on the object will get a new immutable object, representing the date, time, date and time using the ISO-8601 calendar system respectively. They provide a simple date or time and do not contain time information several years ago. It also does not contain time zone related information.

LocalDateTime

The following only demonstrates the common methods of LocalDateTime. The methods of time class and date class are roughly the same as those of LocalDateTime class.

Example:

import java.time.*;

public class MyLocalDateTime {

    public static void main(String[] args) {

        //Gets the current date from the system clock in the default time zone.
        LocalDateTime defaultLocalDateTime =  LocalDateTime.now();
        System.out.println(defaultLocalDateTime);//21-06-25T23:49:41.512

        //Create a datetime object
        LocalDateTime localDateTime = LocalDateTime.of(2016, 1, 2,15,52,6,555);
        System.out.println(localDateTime);//2016-01-02T15:52:06.000000555

        //Operate on time objects
        System.out.println(localDateTime.plusYears(5));//Plus five years
        System.out.println(localDateTime.minusMonths(5));//Minus may
        //Gets the value of the time date object
        System.out.println(localDateTime.getMonth());//Gets the month of the object
        System.out.println(localDateTime.getYear());//Gets the year of the object

        //Get timestamp
        Instant instant = Instant.now();//This time is UTC time. China time is obtained by offset
        System.out.println(instant);
        //Offset time
        OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));//Offset 8 hours
        System.out.println(offsetDateTime);

        //Gets the value of the difference between two times
        Duration duration = Duration.between(Instant.now(),instant);
        //Convert to seconds
        long duration1 = duration.toMillis();//millisecond
        System.out.println(duration1);

    }
}

Time corrector

Adjust the time to a certain time

Example:

        LocalDateTime localDateTime =LocalDateTime.now();
        //Assign a date to a day
        System.out.println(localDateTime.withDayOfMonth(10));

        //Get next Sunday
        localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));

        //Gets the next specified date
        localDateTime.with(
                (l) -> {
                    LocalDateTime localDateTime1 = (LocalDateTime) l;
                    DayOfWeek dayOfWeek = localDateTime1.getDayOfWeek();
                    if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {//If it's Friday
                        return localDateTime1;
                    }
                    return l;
                }
        );

Format date or time

 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_DATE_TIME;//Standard for formatting

        LocalDateTime.now().format(dateTimeFormatter);//Format according to this standard

        //Create a time date object in your own format
        DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy MM dd HH mm ss");

time zone

Java 8 adds support for time zones. The time zones are ZonedDate, ZonedTime, and ZonedDateTime Each time zone corresponds to an ID, and the region ID is in the format of "{region} / {City}". Zoned: this class contains all the time zone information.

Example:

 //Get all time zones.
        Set set =ZoneId.getAvailableZoneIds();
        set.forEach(System.out::println);

        //Build a time object for the specified time zone
        LocalDateTime DateTime = LocalDateTime.now(ZoneId.of("US/Pacific"));
        //or
        LocalDateTime dateTime1 = LocalDateTime.now();
        dateTime1.atZone(ZoneId.of("US/Pacific"));

annotation

Java 8 provides improved annotations, providing repeatable annotations that can be used for type annotations

Repeatable annotation

To make annotations edible, you need to create an annotation container.

Steps: create duplicate annotation container - > create annotation - > reuse annotation

Example of creating a repeating annotation container:

@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationContainer {

    MyAnnotation[] value();
}

Create annotation:

@Repeatable(value = MyAnnotationContainer.class)
@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {


}

Reuse notes:

public class Test {
    @MyAnnotation
    @MyAnnotation
    public static void main(String[] args) {

    }
}

Annotations available for types

Java 8 provides two new values for Annotation's meta Annotation @ Retention

Retention valuesummary
TYPE_PARAMETERIt indicates that the annotation can be written in the declaration statement of type parameters, and the parameter type can be declared by type restriction on the parameters of the method
TYPE_USEIndicates that annotations can be used wherever types are used

TYPE_PARAMETER:

first:

//Need to add in @ Target
@Target({METHOD,TYPE, PARAMETER,TYPE_PARAMETER})
public void test(@MyAnnotation("qwe") String name){

    }

TYPE_USE:

Can be used at any declared type, including annotations and enumerations.