The original article was first published on my blog: /post/
expression
Lambda expressions are an important update in Java 8. Lambda expressions can use simpler code to create an instance of an interface (functional interface) with only one abstract method, making it easier to create objects of anonymous inner classes.
Grammar and usage
The basic syntax of lambda expression isFormal parameter list (type can be omitted),arrow,as well ascode block,For example() -> {}
,or(x, y) -> {}
, if there is only one parameter, then the parentheses()
Can be omitted, if the code block has only one statement, then the curly braces of the code block{}
Can be omitted altogether, if there is only one place in the code blockreturn
,Soreturn
It can also be omitted altogether.
example:TreeSet
The constructor of the class needs to pass in aComparator
An anonymous class object is put in for sorting, so the program implements an anonymous inner class to encapsulate the processing behavior, and has to use the syntax of the anonymous inner class to encapsulate the object.
@Test
public void test() {
TreeSet<Integer> treeSet = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return (o1, o2);
}
});
(20);
(78);
(-98);
(treeSet);
}
Comparator
The interface is a functional interface, so lambda expressions can be used to simplify the creation of anonymous inner class objects, so the above code can be modified like this
@Test
public void test() {
TreeSet<Integer> treeSet = new TreeSet<>((Integer x, Integer y) -> {
return (y);
});
(20);
(78);
(-98);
(treeSet);
}
Further simplification: parameter types can be omitted, and if the code block has only one statement, the curly braces of the code block{}
Can be omitted altogether, if there is only one place in the code blockreturn
,Soreturn
Can also be omitted altogether
@Test
public void test() {
TreeSet<Integer> treeSet = new TreeSet<>((x, y) -> (y));
(20);
(78);
(-98);
(treeSet);
}
The logic is exactly the same as the code above, except that it is no longer needednew Xxx() {}
This cumbersome syntax does not need to indicate the name of the overridden method, nor does it need to give the return value type of the overridden method. It only needs to give the parentheses of the overridden method and the formal parameter variables within the parentheses. It is expressed with lambda The code block replaces the method body of the anonymous inner class abstract method, and the lambda expression is like an anonymous method here.
Method references and constructor references
As mentioned before, if there is only one code in the curly braces, the curly braces can be omitted. Not only that, you can also use method references and constructor references to make lambda expressions more concise. The syntax of method references and constructor references is two English colon::
, supports the following usage methods
type | grammar | illustrate | How to write lambda expression |
---|---|---|---|
class method | Class name::Class method | All parameters of the abstract method are passed to a method of the class as parameters | (a,b,...) -> class name.class method (a,b,...) |
Specific object instance methods | Specific object::instance method | All parameters of the abstract method are passed to the method as parameters | (a,b,...) -> Specific object.Instance method (a,b,...) |
object instance method of a certain class | Class name::Instance method | The first parameter of the abstract method serves as the caller, and all subsequent parameters are passed to the method as parameters. | (a,b,c,...) -> a.Instance method (b,c,...) |
Constructor | Class name::new | All parameters of the abstract method are passed to the constructor as parameters | (a,b,...) -> new class name (a,b,...) |
example:Class name::Class method
@FunctionalInterface
interface Convert {
Integer fun(String s);
}
@Test
public void test8() {
Convert convert = from -> (from);
(("150") + 1);
}
@Test
public void test9() {
Convert convert = Integer::valueOf;
(("150") + 1);
}
example:Specific object::instance method
@FunctionalInterface
interface Convert {
Integer fun(String s);
}
@Test
public void test8() {
Convert convert = from -> "".indexOf(from);
(("zi"));
}
@Test
public void test9() {
Convert convert = ""::indexOf;
(("zi"));
}
example:Class name::Instance method
@FunctionalInterface
interface Fun {
String test(String a, int b, int c);
}
@Test
public void test8() {
Fun fun = (a, b, c) -> (b, c);
String s = ("abcdefghi", 3, 5);
(s);
}
@Test
public void test9() {
Fun fun = String::substring;
String s = ("abcdefghi", 3, 5);
(s);
}
example:Class name::new
@FunctionalInterface
interface Fun {
BigDecimal test(String n);
}
@Test
public void test8() {
Fun fun = (n) -> new BigDecimal(n);
BigDecimal b = ("45.64");
(b);
}
@Test
public void test9() {
Fun fun = BigDecimal::new;
BigDecimal b = ("45.64");
(b);
}
2. Functional interface
In Java 8, the concept of functional interface was introduced. A functional interface is an interface with only one abstract method. It is usually used for Lambda expressions and method references. Functional interfaces can have multipleDefault methodorstatic method, but mustThere is only one abstract method
definition
@FunctionalInterface
public interface MyPredicate<T> {
boolean fun(T obj);
default void other() {
("hello world");
}
static void staticMethod() {
("static method");
}
}
@FunctionalInterface
Annotation: This is an optional annotation. It can help the compiler check whether the interface meets the requirements of a functional interface during compilation, that is, whether there is only one abstract method. If it does not comply, adding this annotation will cause the compiler to report an error.
use
Write an entity classEmployee
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Employee {
private String name;
private Double salary;
private Integer age;
public Employee(Integer age) {
= age;
}
public Employee(Integer age, String name) {
= age;
= name;
}
}
Add a new method to filter by conditionsfilter
,WillList<Employee>
As the first parameter, the functional interfaceMyPredicate<Employee>
Passed in as the second parameterfilter()
Method, the method body loop passes each Employee object one by one as a parameter to the abstract method of the interfacefun()
, and call it to determine whether to filter out based on the Boolean value obtained after running the abstract method.
private List<Employee> filter(List<Employee>employees, MyPredicate<Employee> predicate) {
List<Employee>list = new ArrayList<>();
for (Employee e : employees) {
if ((e)) {
(e);
}
}
return list;
}
Declare a collection of employeesemployees
, insert 5 objects, and then callfilter()
method, willemployees
Pass it in as the first parameter, and then directly create a new implementationMyPredicate
The anonymous inner class of the interface abstract method is passed in as the second parameter. In this way, the target method is notified when callingfilter()
The data to be processed isemployees
, and also provide the specific data processing rules.() > 16
It tells the target method that there can be countless strategies for processing data by calling the same method. This is actually a typicalstrategy pattern, in fact, Java8 has already written a functional interface for strategy mode for us.
private List<Employee> employees = (
new Employee("soo", 8547.322, 17),
new Employee("lili", 1000D, 15),
new Employee("Wang Meng", 2154D, 16),
new Employee("Zhang Fan", 8547.322, 22),
new Employee("goog", 353D, 12)
);
@Test
public void test3() {
List<Employee>list = filter(employees, new MyPredicate<Employee>() {
@Override
public boolean fun(Employee obj) {
return () > 16;
}
});
(list);
}
In Java8, the strategy interface implementation is abbreviated asLambda
The expression method can make the syntax more concise
List<Employee>list2 = filter(employees, (e) -> () < 16);
Built-in functional interface
Java8 provides some predefined functional interfaces, located atIn the bag
-
Consumption
-
supply
-
function
-
assertion
-
Not commonly used
-
Not commonly used
Write 4 methods that take functional interfaces as parameters
private void testConsumer(String str, Consumer<String>consumer) {
(str);
}
private String testSupplier(Supplier<String>supplier) {
return ();
}
private Integer testFunction(String str, Function<String, Integer>function) {
return (str);
}
private boolean testPredicate(String str, Predicate<String>predicate) {
return (str);
}
Call these methods respectively, implement the abstract method of the functional interface through the lambda expression writing method of the anonymous inner class according to the business logic, and pass it in as a parameter
@Test
public void test4() {
testConsumer("hello lambda", (x) -> (x));
String str = testSupplier(() -> { return "hello world"; });
(str);
Integer integer = testFunction("66", (x) -> (x));
(integer);
boolean b = testPredicate("hello", (e) -> ("hello"));
(b);
}
Get running results
hello lambda
hello world
66
true
You can also modify the call more concisely through the method reference and constructor reference of the lambda expression.
@Test
public void test2() {
testConsumer("hello lambda", ::println);
Integer integer = testFunction("66", Integer::valueOf);
}
API
Stream is a new feature introduced in Java 8. It is a data stream. It provides a declarative method to process data from data sources such as collections and arrays. It can process data in a more concise and functional way. It will not Change the data source itself and instead return a new Stream or the final result.
Common streaming APIs introduced in Java 8 include:
inIt is a general stream interface. The other element types representing streams are
long
,int
,double
Stream operations are delayed, which means they wait until the results are needed. Stream operations can be called in a chain, and are generally divided into two types of operations: intermediate operations and termination operations.
Create Stream
from collection typestream()
Method creation
List<String> list = new ArrayList<>();
Stream<String> stream = ();
Create from array
Employee[] employees = new Employee[10];
Stream<Employee> employeeStream = (employees);
Create a stream through the static method of Stream
Employee[] employees = new Employee[10];
Stream<Employee> employeeStream1 = (employees);
Iteratively create infinite streams, based on seed and consumption interfaces
(10, (x) -> x + 2)
.limit(10)
.forEach(::println);
random number
(Math::random)
.limit(20)
.forEach(::println);
passbuilder()
Create an int stream
@Test
public void test5() {
IntStream intStream = ()
.add(1)
.add(2)
.add(3)
.add(4).build();
//The following aggregation method can only execute one row at a time
(().getAsInt());
//(().getAsInt());
//(());
//(());
//(());
}
Stream operations
Stream operations includeintermediate operationsandTerminate operation, in the book "Crazy Java Lecture Notes", teacher Li Gang also calls it the intermediate method and the terminal method. The intermediate operation allows the stream to remain open and allows subsequent methods to be called directly. The return value of the intermediate method is another stream. The termination method is the final operation on a stream. After a termination operation is performed on a stream, the entire stream will no longer be available.
Common intermediate operations
-
filter(Predicate predicate)
Filter elements in the stream that do not match the predicate -
mapToXxx(ToXxxFunction mapper)
Use ToXxxFunction to perform a one-to-one conversion of elements in the stream. The new stream returned contains all elements generated by the ToXxxFunction transformation -
peek(Consumer action)
Some operations are performed on each element in turn, and the returned stream contains the same elements as the original stream (mostly used for debugging) -
distinct()
Exclude all duplicate elements in the stream, the judgment criterion isequals()
returntrue
-
sorted()
This method is used to sort -
sorted(Comparator comparator)
This method is used to sort according to custom rules -
limit(long maxSize)
Intercept the first part of the streammaxSize
elements -
skip(long n)
skip previous in streamn
elements -
map(Function mapper)
Map each element to another form -
flatMap(Function mapper)
Convert each element to a stream, then merge multiple streams into a single stream
Common termination operations
-
collect(Collector collector)
Collect elements from a stream into a container (such as a collection, list, map, etc.) -
count()
Returns the number of elements in the stream -
forEach(Consumer action)
Traverse all elements in the stream and perform actions on each element -
toArray()
Convert all elements in the stream to an array -
reduce()
Combine elements in a stream through some operation -
min()
Returns the minimum value of the elements in the stream -
max()
Returns the maximum value of elements in the stream -
anyMatch(Predicate predicate)
Returns true if any element in the stream matches the given condition -
allMatch(Predicate predicate)
Returns true if all elements in the stream match the given condition -
noneMatch(Predicate predicate)
Returns true if no element in the stream matches the given condition -
findFirst()
Returns the first element in the stream -
findAny()
Return any element in the stream
The intermediate operation returns a new Stream, and the intermediate operation is executed lazily, and the calculation is not triggered until the operation is terminated.
Here are the use cases:
data:
private List<Employee> employees = (
new Employee("soo", 8547.322, 17),
new Employee("lili", 1000D, 18),
new Employee("Wang Meng", 2154D, 16),
new Employee("Zhang Fan", 8547.322, 22),
new Employee("Zhang Fan", 8547.322, 22),
new Employee("Zhang Fan", 8547.322, 22),
new Employee("goog", 353D, 12)
);
private List<String> list = ("aaa", "bbb", "ccc", "ddd", "eee");
Example: stream+limit filters slices to satisfy() > 16
Stop iteration when the number of condition objects reaches two, instead of returning to the first two after iterating once, which improves efficiency. Terminate operationforEach()
Not triggered, intermediate operationfilter()
,limit()
Nor will it be enforced.
@Test
public void test() {
()
.filter((e) -> {
//Intermediate operations
("Intermediate operation");
return () > 16;
})
.limit(2) //Intermediate operation
.forEach(::println); // Terminate operation
}
Running results:
intermediate operations
Employee(name=soo, salary=8547.322, age=17)
intermediate operations
Employee(name=lili, salary=1000.0, age=18)
Example: Skip the first part of the streamn
elements, andlimit
on the contrary
@Test
public void test2() {
().skip(2).forEach(::println);
}
Running results
Employee(name=Wang Meng, salary=2154.0, age=16)
Employee(name=Zhang Fan, salary=8547.322, age=22)
Employee(name=Zhang Fan, salary=8547.322, age=22)
Employee(name=Zhang Fan, salary=8547.322, age=22)
Employee(name=goog, salary=353.0, age=12)
Example: Remove duplicates, based onequals
,hashCode
, the prerequisite for successful deduplication in this example isEmployee
Class needs to be rewrittenequals
,hashCode
//Employee class overrides equals hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != ()) return false;
Employee employee = (Employee) o;
if (!(name, )) return false;
if (!(salary, )) return false;
return (age, );
}
@Override
public int hashCode() {
int result = name != null ? () : 0;
result = 31 * result + (salary != null ? () : 0);
result = 31 * result + (age != null ? () : 0);
return result;
}
@Test
public void test3() {
().distinct().forEach(::println);
}
Running results
Employee(name=soo, salary=8547.322, age=17)
Employee(name=lili, salary=1000.0, age=18)
Employee(name=Wang Meng, salary=2154.0, age=16)
Employee(name=Zhang Fan, salary=8547.322, age=22)
Employee(name=goog, salary=353.0, age=12)
example:flatMap
Convert each value in the stream into another stream, and then connect all streams into one
The following procedure will first"aaa"
converted into 3'a'
constitutedList<Character>
, thenList<Character>
Convert toStream<Character>
,"bbb"
and"ccc"
In the same way, the three finally converted intoStream<Character>
Combined into a 9-elementStream<Character>
, and then call the end methodcollect()
Turn it into a 9-elementList<Character>
, print the output in sequence.
@Test
public void test5() {
List<String> list = ("aaa", "bbb", "ccc");
Function<String, Stream<Character>> function = (e) -> {
List<Character> characters = new ArrayList<>();
for (char c : ()) {
(c);
}
return ();
};
List<Character> collect = ()
.flatMap(function)
.collect(());
(::println);
}
Running results
a
a
a
b
b
b
c
c
c
example:map
Mapping, getting an element in the stream and processing it to form a new stream
@Test
public void test4() {
().map((e) -> ()).forEach(::println);
}
Running results
soo
lily
Wang Meng
Zhang Fan
Zhang Fan
Zhang Fan
google
example:sorted()
natural ordering
@Test
public void test() {
().sorted().forEach(::println);
}
Running results
aaa
bbb
ccc
ddd
eee
example:sorted(Comparator c)
Custom sorting
@Test
public void test2() {
()
.sorted((e1, e2) -> () - ())
.forEach(::println);
}
Running results
Employee(name=goog, salary=353.0, age=12)
Employee(name=Wang Meng, salary=2154.0, age=16)
Employee(name=soo, salary=8547.322, age=17)
Employee(name=lili, salary=1000.0, age=18)
Employee(name=Zhang Fan, salary=8547.322, age=22)
Employee(name=Zhang Fan, salary=8547.322, age=22)
Employee(name=Zhang Fan, salary=8547.322, age=22)
example:xxxMatch
,findXXX
,count()
,max()
,min()
@Test
public void test3() {
boolean b = ().allMatch((e) -> () > 10);
(b);
b = ().anyMatch((e) -> () > 100);
(b);
b = ().noneMatch((e) -> () > 100);
(b);
Optional<Employee> first = ().findFirst();
(());
Optional<Employee> any = ().findAny();
(());
long count = ().count();
(count);
Optional<Employee> max = ()
.max((Employee::getAge));
(());
Optional<Integer> maxAge = ()
.map(Employee::getAge)
.max(Integer::compare);
(());
}
Running results
true
false
true
Employee(name=soo, salary=8547.322, age=17)
Employee(name=soo, salary=8547.322, age=17)
7
Employee(name=Zhang Fan, salary=8547.322, age=22)
twenty two
example:reduce()
Repeatedly combine the elements in the stream to get a new value. First, use the starting value as x, and take a value from the stream as y.
@Test
public void test() {
List<Integer> list = (1,2,3,4,5,6,7,8,9);
Integer sum = ().reduce(0, Integer::sum);
(sum);
Optional<Double> reduce = ().map(Employee::getSalary)
.reduce(Double::sum);
(());
}
Running results
45
37696.288
example:.collect(())
.collect(())
collect as set
@Test
public void test2() {
List<String> names = ()
.map(Employee::getName)
.collect(());
//.collect((LinkedList::new))
(::println);
}
Running results
soo
lily
Wang Meng
Zhang Fan
Zhang Fan
Zhang Fan
google
example:collect(())
average
@Test
public void test5() {
Double avg = ()
.collect((Employee::getSalary));
(avg);
}
example:collect(())
Concatenate multiple strings with the same content, which is very suitable for parameter splicing scenarios such as SQL
@Test
public void test() {
String collect = ().collect((","));
(collect);
}
Running results
aaa,bbb,ccc,ddd,eee
Example: Collect as Map()
Add together the prices of tickets and luggage for the same airline and ticket number
public class TestGroupBy {
private List<Detail> details = new ArrayList<>();
@Before
public void mock() {
(new Detail(1, "001", "123456789", new BigDecimal("120.00")));
(new Detail(2, "001", "123456789", new BigDecimal("99.32")));
(new Detail(3, "003", "333222111", new BigDecimal("27.32")));
(new Detail(4, "003", "333222111", new BigDecimal("36.00")));
(new Detail(5, "003", "123456789", new BigDecimal("48.32")));
(new Detail(6, "101", "123456789", new BigDecimal("53.32")));
(new Detail(7, "101", "123456789", new BigDecimal("10.32")));
(new Detail(8, "102", "333222111", new BigDecimal("3.32")));
(new Detail(9, "103", "123456789", new BigDecimal("9.00")));
(new Detail(10, "103", "123456789", new BigDecimal("12.12")));
}
@Test
public void test() {
Map<String, List<Detail>> groupByAir = ().collect((Detail::getAir));
((air, sameAirs) -> {
Map<String, List<Detail>> groupByDoc = ().collect((Detail::getDocument));
((doc, sameDocs) -> {
Optional<BigDecimal> reduce = ().map(Detail::getPrice).reduce(BigDecimal::add);
(e -> {
(air + " "+ doc + " " + e);
});
});
});
}
@Data
@AllArgsConstructor
public static class Detail {
/**
*ID
*/
privateInteger id;
/**
*Airline code
*/
private String air;
/**
*ticket number
*/
private String document;
/**
*Air ticket price
*/
private BigDecimal price;
}
}
Running results
001 123456789 219.32
101 123456789 63.64
102 333222111 3.32
003 333222111 63.32
003 123456789 48.32
103 123456789 21.12
example:peek()
Real-time printing and debugging to see what the elements in each step of stream processing look like
@Test
public void test6() {
List<String> names = ("liuzijian", "liutongtong", "zhaoying", "wangwendi");
()
.filter(name -> ("liu"))
.peek(name -> ("After filtering: " + name))
.map(String::toUpperCase)
.peek(name -> ("After changing to uppercase: " + name))
.collect(());
}
Running results
After filtering: liuzijian
After changing to upper case: LIUZIJIAN
After filtering: liutongtong
After changing to upper case: LIUTONGTONG
Parallel and serial streams
In Java8, streams can be divided into parallel streams and serial streams. The main difference between the two is the way data is processed.
Java8stream()
The default is a serial stream, that is, the data is processed one by one in order, which can be passedparallel()
Method to convert a serial stream to a parallel stream, or use it directly on stream creationparallelStream()
The bottom layer of parallel streaming is based on JavaForkJoinPool
Implemented, this pool manages multiple threads to process data in parallel. The elements of the stream are split into multiple subtasks and assigned to different threads for processing, and finally the results are merged.
Parallel streams by themselves do not guarantee order. However, in some operations, such as()
, it will ensure the order of the merged results, which is achieved through the design of the collector.
Example: Parallel stream traversal printing
@Test
public void test() {
().forEach(::println);
}
Running results
ccc
eee
ddd
bbb
aaa
Example: Parallel stream multithreading to add 0 to 100
(0, 100000000000L)
Created from0
arrive100000000000L
a stream of all integers between, thenreduce()
The stream is first divided into multiple sub-streams. Each sub-stream calculates the local sum and performs it in a different thread. Each thread calculates a part of the sum separately. After the calculation is completed, the calculation results of each sub-task are combined to obtain the calculation result.932356074711512064
public static void main(String[] args) {
long reduce = (0, 100000000000L)
.parallel() // Convert to parallel stream, the bottom layer is fork-join
.reduce(0, Long::sum);
(reduce);
}
The above is all about Java8 StreamAPI.
4.Default method of interface
Interfaces before Java 8 could only have two members, global static constants and abstract methods. Java 8 introduced default methods and static methods of interfaces as new features. They were introduced to enhance the functionality of the interface, especially the scalability of the interface. and flexibility.
The default method in the interface, usedefault
Modifier modification can have an implementation. The implementation class can directly inherit and use it. The implementation class can choose to override the default method or use it directly.
Static methods in an interface can only be called through the interface name, and cannot be called through the implementation class or instance of the interface. They provide related tool functions for the interface without relying on specific implementation classes. Static methods will not be inherited by the implementation class, nor will they be inherited by the implementation class. Cannot be overridden by implementing classes.
Default and static methods of interfaces
Write an interface, has two default methods
test()
,hello()
and a static methodhelloworld()
package ;
public interface MyInterface {
default String test() {
("default");
return "default";
}
default void hello() {
("my interface");
}
static void helloworld() {
("hello java8!!!");
}
}
write a class, implement the interface
MyInterface
package ;
public class SubClass implements MyInterface {
public static void main(String[] args) {
SubClass subClass = new SubClass();
();
();
}
}
Does not implement the interfacehello()
Methods can also directly call the default methodhello()
, and you can directly call the static method of the interface through the interface namehelloworld()
, program output:
my interface
hello java8!!!
method conflict
Write another interface, and implement a default method
hello
package ;
public interface OtherInterface {
default void hello() {
("other interface");
}
}
command classImplement another interface
OtherInterface
, this interface contains and interfaceMyInterface
Default method defined in the same wayhello()
, an interface conflict occurs. When multiple interfaces implemented have default methods with the same signature, the subclass must explicitly override the conflicting method.hello()
, the final program output is: "sub hello!"
package;
public class SubClass implements MyInterface, OtherInterface {
/**
* Multiple implementation methods conflict, the implementation class must implement
**/
@Override
public void hello() {
("sub hello!");
}
public static void main(String[] args) {
SubClass subClass = new SubClass();
();
}
}
Class first
write a class, there is a method in it
String test()
, and letSubClass
class inherits it and executes();
, get the output result: "class", butSubClass
implemented interfaceMyInterface
There is also a method in itString test()
, but was not executed, but the method in the class was executed, indicating that the class takes precedence. If the method implementation has been provided in the class or its parent class, the implementation of the class will be used first instead of the default method of the interface.
package ;
public class MyClass {
public String test() {
("class");
return "class";
}
}
package;
public class SubClass extends MyClass implements MyInterface, OtherInterface {
// Multiple implementation methods conflict, the implementation class must implement it
@Override
public void hello() {
("sub hello!");
}
public static void main(String[] args) {
SubClass subClass = new SubClass();
// Class priority principle, inherit class methods
();
}
}
5. New date and time API ()
Thread safety issues with old APIs
Old date and time utility classThere are thread safety issues. For example, SimpleDateFormat is thread unsafe. It relies internally on a Calendar instance to parse and format dates, and Calendar is thread unsafe. Multi-threaded formatting will concurrently update the Calendar state, causing exceptions.
The following code uses 100 threads to concurrently call a format object for date parsing operations, which will cause errors.
package ;
import ;
public class Test1 {
public static void main(String[] args) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
Runnable r = new Runnable() {
@Override
public void run() {
try {
(("20191231"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
for (int i=0; i<100; i++) {
new Thread(r, "t"+i).start();
}
}
}
It can be solved by using synchronized blocks, threads holding format objects separately, and using ThreadLocal in the thread pool. When using synchronized code blocks, only one thread can execute the parse method, which can avoid thread safety issues.
package ;
import ;
public class Test1 {
public static void main(String[] args) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
Runnable r = new Runnable() {
@Override
public void run() {
synchronized (format) {
try {
(("20191231"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
};
for (int i=0; i<100; i++) {
new Thread(r, "t"+i).start();
}
}
}
The solution is to use the thread to hold the format object independently. Each thread creates a format object when executing, and each thread holds it separately to prevent thread safety issues.
package ;
import ;
public class Test1 {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
try {
(new SimpleDateFormat("yyyyMMdd").parse("20191231"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
for (int i=0; i<100; i++) {
new Thread(r, "t"+i).start();
}
}
}
Thread pool + ThreadLocal, 10 threads dispatch 100 formatting tasks at the same time. Each thread can be bound to a format object and used separately, which can also avoid thread safety issues.
package ;
import ;
import ;
import ;
public class Test1 {
public static void main(String[] args) {
ExecutorService executorService = (10);
ThreadLocal<SimpleDateFormat> threadLocal = (() -> new SimpleDateFormat("yyyyMMdd"));
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
(().parse("20191231"));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
};
for (int i=0; i<100; i++) {
(runnable, "t"+i);
}
();
}
}
New datetime API
Java 8 further enhances date and time processing by releasing the new Date-TimeAPI (JSR310).
First of all, in the old version of Java, there are many problems with the date and time API. First of allNot thread-safe, all date classes are mutable.
Secondly, the definition of Java's date/time class is not consistent.and
There are date classes in the package, and the classes responsible for formatting and parsing are defined in the package.
contains both date and time, whereas
It only contains the date, and it is unreasonable to put it under the sql package.
Moreover, time zones cannot be handled better, the date class cannot be internationalized, and there is no time zone support, so Java introducedand
, but they still have the same problem.
So Java8 introduced a new date and time API, located atUnder the package, there are several important classes under the package:
-
Timestamp
-
time difference
-
Contain only the date, e.g.
2011-07-11
-
Contains only the time, e.g.
09:00:01
-
Contain both date and time, e.g.
2024-11-30 04:09:45
-
time period
-
Date and time with time zone offset are a combination of LocalDateTime and ZoneOffset, and are more suitable for scenarios that require precise time and offset, especially when all you care about is the offset of a certain time point relative to UTC. For example, OffsetDateTime is more suitable when dealing with time differences that need to be represented (such as timestamps, system logs, etc.).
-
time zone offset, e.g.
+8:00
-
Date and time with time zone, yes
LocalDateTime
andZoneId
In combination, ZonedDateTime is more suitable for scenarios where complex issues such as time zone history and daylight saving time need to be considered. For example, if you need to represent the time in a specific time zone (such as America/New_York) and you want to deal with daylight saving time, ZonedDateTime will be more accurate -
clock
package;
import ;
import .*;
import ;
import ;
import .*;
import ;
public class Test4 {
/**
* java8 API gets the current time
*/
@Test
public void current() {
Instant instant = ();
LocalDate localDate = ();
LocalTime localTime = ();
LocalDateTime localDateTime = ();
ZonedDateTime zonedDateTime = ();
(instant);
(localDate);
(localTime);
(localDateTime);
(zonedDateTime);
}
/**
* Common methods of Instant
*/
@Test
public void testInstant() {
//Get the current timestamp through Instant, Greenwich time
Instant now = ();
(now);
//Add time zone and convert to time with time zone: OffsetDateTime
OffsetDateTime us = ((-4));
(us);//US
//Set offset
OffsetDateTime offsetDateTime = ((+8));
(offsetDateTime);//CN
(((+9)));//JP
(((+10)));//AU
//Create an Instant object based on the given Unix timestamp (i.e. the number of seconds since January 1, 1970 00:00:00 UTC)
Instant instant = (1);//Start in 1970
(instant);
//Set time zone
ZonedDateTime zonedDateTime = (("GMT+9"));
LocalDateTime localDateTime = ();
(localDateTime);
}
/**
* Common methods and uses of LocalDateTime LocalDate LocalTime
*/
@Test
public void testLocalDateTime() {
// Get the current time
LocalDateTime now = ();
(now);
//Construction time
LocalDateTime localDateTime = (2019,8,8,12,23,50);
(localDateTime);
//Construct time from LocalDate and LocalTime
(((), ()));
// Get year, month, day, hour, minute and second
(());
(());
(());
//Week
DayOfWeek dayOfWeek = ();
(dayOfWeek);
//The nanosecond part of the current time represents the fine time within this time point
(());
//Time calculation
(().plusMonths(2));
(().minusYears(2));
(().plusHours(24));
(().plusNanos(500));
(().plusYears(2).plusMonths(8).plusDays(9));
// Used to create a Period object representing a specific time interval
(().plus((3, 5, 20))); ;
// represents ten years
(().plus(3, )) ;
// time modification
(().withMonth(2));
(().withDayOfMonth(25));
(().withSecond(22));
(().with(ChronoField.DAY_OF_MONTH, 2));
(().with(ChronoField.MONTH_OF_YEAR, 8));
// LocalDate LocalTime
((2020, 1, 19));
((2020, , 19));
((2020, (12), 19));
((20, 0));
(().withMonth(8));
((2020, , 19).plusDays(5));
((2020, (12), 19));
( (20, 0).plusHours(8) );
// LocalDate method to determine whether the current year is a leap year
(().isLeapYear());
}
/**
* TemporalAdjusters time corrector
*/
@Test
public void testTemporalAdjusters() {
//Next Thursday
LocalDateTime dateTime = ();
(());
(dateTime);
(());
(dateTime);
(());
(dateTime);
(());
(dateTime);
(().with(()));
// Get the first day of the month
(().with(()));
(().with(()));
// Customize to calculate the next working day
LocalDateTime nextWorkDay = ().with((e) -> {
LocalDateTime temp = (e);
DayOfWeek dayOfWeek = ();
if (()) {
return (3);
} else if (()) {
return (2);
} else {
return (1);
}
});
(nextWorkDay);
}
public void test() {
(());
(());
(());
}
/**
* Calculation time interval: how many days and hours Wuhan was closed
*/
@Test
public void testChronoUnit() {
LocalDateTime from = (2020, , 23, 10, 0,0);
LocalDateTime to = (2020, , 8, 0, 0,0);
long days = (from, to);
long hours = (from, to);
(days);
(hours);
}
/**
* Use TemporalQuery to calculate the hour difference between the current time and a specified time point (10:00:00 on January 19, 2020),
* and return it as a long value
*/
@Test
public void testTemporalQuery() {
long l = ().query(new TemporalQuery<Long>() {
@Override
public Long queryFrom(TemporalAccessor temporal) {
LocalDateTime now = (temporal);
LocalDateTime from = (2020, , 19, 10, 0,0);
return (from, now);
}
});
(l);
}
/**
* Duration class, can only calculate time difference
*/
@Test
public void testDurationPeriod() {
LocalTime start = (20, 0);
LocalTime end = (21, 30);
// time interval
Duration between = (start, end);
(());
(());
}
/**
* Format DateTimeFormatter
*/
@Test
public void testDateTimeFormatter() {
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
(().format(formatter));
LocalDate localDate = ("2009-12-31", ("yyyy-MM-dd"));
(localDate);
LocalDateTime localDateTime = ("2009-12-31 01:01:02", ("yyyy-MM-dd HH:mm:ss"));
(localDateTime);
// Sunday, December 1, 2024
(().format(()));
// December 1, 2024
(().format(()));
//24-12-1
(().format(()));
// 2024-12-1
(().format(()));
}
@Test
public void getAvailableZoneIds() {
//Current system time zone
(());
//Print all supported time zones in java8
().forEach(::println);
}
/**
*OffsetDateTime
*/
@Test
public void testOffsetDateTime() {
OffsetDateTime offsetDateTime = new Date().toInstant().atOffset(("-4"));
(offsetDateTime);
(());
OffsetDateTime of = ((), ("-4"));
(of);
}
/**
* ZonedDateTime
*/
@Test
public void testZonedDateTime() {
// What is the current time converted to Tokyo time?
ZonedDateTime zonedDateTime = (("Asia/Tokyo"));
(zonedDateTime);
(());
ZonedDateTime of = ((), ("Asia/Tokyo"));
(of);
//Add time zone to current time
ZonedDateTime tokyo = ().atZone(("Asia/Tokyo"));
(tokyo);
(());
//Convert one time zone time to another time zone time at the same moment
ZonedDateTime beijing = (("GMT+8"));
(beijing);
ZonedDateTime usa = ()
.atZone(())
.withZoneSameInstant(("GMT-4"));
(usa);
}
}
Conversion between new API and old Date
package;
import ;
import ;
import .*;
import ;
import ;
public class Test5 {
/**
* Combine LocalDateTime with the system default time zone and convert it to ZonedDateTime
* Then convert the ZonedDateTime to an Instant, which is an object containing a UTC timestamp.
* (): Convert Instant to object
*/
@Test
public void toDate() {
LocalDateTime localDateTime = ();
Instant instant = (()).toInstant();
Date date = (instant);
(date);
}
@Test
public void toLocalDateTime() {
Date date = new Date();
LocalDateTime dateTime = ((), ());
(dateTime);
}
/**
* Convert LocalDateTime
*/
@Test
public void sqlDate() {
date = new (());
LocalDate localDate = ();
(localDate);
Timestamp timestamp = new Timestamp(());
LocalDateTime localDateTime = ();
(localDateTime);
}
/**
* Calendar conversion LocalDateTime
*/
@Test
public void calendarToLocalDateTime() {
Calendar calendar = ();
ZonedDateTime zonedDateTime = ((), ().toZoneId());
(());
}
}
Is a container class used to represent objects that may or may not contain values. It provides an elegant way to avoid null pointers, helping developers handle potentially NULL values more safely and clearly.
create
Wraps a non-null value if the variable passed in isnull
A null pointer exception will be thrown directly. If it is written directlynull
Enter, IDEA may compile directly and make errors.
Optional<String> optional = ("Hello, World!");
ofNullable
Method allows filling in a possibly empty value
Optional<String> optional = (null);
emptyOptional
object
Optional<String> optional = ();
examine
Can be usedisPresent()
method judgment
Optional<String> optional = ();
if (()) {
("Value: " + ());
} else {
("No value present");
}
You can also useifPresent()
avoidif
explicit call
(value -> ("Value: " + value));
default value
If empty, provide a default value
Optional<String> optional = ();
String value = ("Default Value");
You can also use the providedSupplier
Functional interface generates default values
Optional<String> optional = ();
String value = (() -> "Generated Default Value");
// (String::new);
Custom exception can be thrown if the value does not exist
Optional<String> optional = ();
String value = (() -> new RuntimeException("Value is missing!"));
Convert
map()
If there is a value, process it and return the processedOptional
object, otherwise return()
Empty value, no output is performed
Optional<String> optional = ();
Optional<String> upperCase = (String::toUpperCase);
(::println);
Non-empty, return new after processingOptional
, output:HELLO WORLD
Optional<String> optional = ("hello world");
Optional<String> upperCase = (String::toUpperCase);
(::println);
useflatMap()
To further prevent null pointer exceptions, if the value in optional is null,flatMap()
Return directly()
, otherwise, it returns a value containing()
Optional object
Employee employee = new Employee();
("XXX");
Optional<Employee> optional = (employee);
Optional<String> s = ((e) -> (()));
(::println);
7. Repeating Annotations
1. First create a container annotation. This annotation type contains an annotation array to store multiple annotations of the same type.
package ;
import ;
import ;
import ;
import static ;
import static ;
import static .LOCAL_VARIABLE;
import static ;
import static ;
import static ;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention()
public @interface MyAnnotations {
MyAnnotation[] value();
}
2. Define a repeat annotation and use@Repeatable
mark
package;
import ;
import ;
import ;
import ;
import static ;
import static ;
import static .LOCAL_VARIABLE;
import static ;
import static ;
import static ;
import static .TYPE_PARAMETER; // Type annotation
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})
@Retention()
@Repeatable()
public @interface MyAnnotation {
String value() default "hello world";
}
3. Test, access the annotations on the method through reflection. Since MyAnnotation is a repeated annotation, adding multiple annotations to one method will not cause a syntax error, and then extract multiple MyAnnotation annotations.
package ;
import ;
import ;
public class TestAnnotation {
@MyAnnotation("hello")
@MyAnnotation("world")
public void test(String s) {
}
public static void main(String[] args) {
Class<TestAnnotation> clazz = ;
try {
Method method = ("test", );
MyAnnotation[] annotations = ();
(annotations).map(MyAnnotation::value).forEach(::println);
}
catch (Exception e) {
();
}
}
}
JavaScript engine
This is not commonly used, to be continued