OpenJDK - JDK 8 Features (opens in a new tab)
Primitives
Float & Double
- Don't use
==
or!=
for comparison. UseFloat.compare
or>
,<
.
Enum
-
EnumSet
- All the methods in an
EnumSet
are implemented using arithmetic bitwise operations, therefore compact and efficient.
- All the methods in an
Convert to HEX representation
-
Integer.toHexString(int i)
-
Long.toHexString(int i)
-
Float.toHexString(float i)
-
Double.toHexString(double i)
-
Byte array
- Lowercase:
String.format("%02x", b)
- Uppercase:
String.format("%02X", b)
- Lowercase:
Control Statements
-
switch
- Better performance when there are a lot of cases, the more the faster
- Better readability
case
expression must be a constant, which means eagerness and static- Only the matched
case
gets executed, order does not matter - Without
break
statement, execution follows through
-
if
- Dynamic and expressive, as
switch
does not allowboolean
- When there is overlap, first case that meets the criteria will be executed, with the rest ignored
- Dynamic and expressive, as
Access Control
- Access level check is performed in the following order:
- access level of Class
- access level of members of the Class, such as constructors, fields, methods
Generics
-
Wildcard
-
Principle: Producer
Extends
, ConsumerSuper
-
extends
- Set the upper bound, which can be any type that is a descendant of the specified type, but only one at a time.
- For collection, not able to add element to it, as upper bound is set, while the specific class could be any descendent of the upper bound.
-
super
- Set the lower bound, which can be any type that is a ancestor of the specified type, which means it works for any type in runtime passes is-a relation.
// Any type that has an ancestor of Number class List<? super Number> list = new ArrayList<>(); list.add(3); list.add(123L); list.add(34.0F); list.add(34.0D);
-
Can be used in method parameters
-
Can be used as return type
-
Concrete types cannot be captured and used. In those cases, a type parameter must be used.
-
References
-
-
Troubleshoot compilation error about type inference:
- Extract variables from chaining method calls to make intermediate type explicit, so compiler can successfully deduce.
- Closely examine method signatures with type parameters and add type information to method calls when needed.
- Add omitted type information to variables of lambda expression when necessary.
- Replace lambda expression with method reference to supplement missing type information.
Annotations
Processing
- Runtime
- Reflection API
- Source code
- Java Language Model API (
javax.lang.model
) - Java compiler (Annotation Processor)
- Java Language Model API (
- Byte code
- ASM (opens in a new tab) (refer to JVM notes)
Collections
Map
-
HashMap
- Relies on
hashCode
andequals
to work.
- Relies on
-
SortedMap
(TreeMap
)- Keys must implement
Comparable
. - Uses
compareTo
(orcompare
ofComparator
if provided) instead ofequals
to decide equality. - Natural orderings should be consistent with
equals
. This is recommended not required, so we have to do it ourselves. Specifically, if we use custom objects as keys, bothcompareTo
andequals
methods must be overridden or implemented to ensure consistency.
- Keys must implement
-
Reference
Regular Expression
Example
<?xml version="1.0" encoding="UTF-8"?>
<folder name="c">
<folder name="program files">
<folder name="uninstall information" /></folder>
<folder name="users" />
</folder>
<(?:folder)[\s]+(name)="([\w\s]+)"\s*/?>
- Use parenthesis to enclose capture group such as
(name)
and([\w\s]+)
in the example. - Group zero denotes the entire pattern,
matcher.group()
is equivalent tomatcher.group(0)
. - Use numbers to access corresponding group like
matcher.group(1)
. - Use
(?:<pattern>)
to denote non-capturing group such as(?:folder)
in the example. Non-capturing groups are used for matching, but they are not captured.
NIO.2
Path
Path.startWith
is used to check path not filename. UsePath.getFileName().toString().startWith
instead to check file name.
Stream
allMatch
andnoneMatch
returntrue
when stream isempty
, whileanyMatch
returnsfalse
;- Stream backed by IO resources needs to be closed.
Parallel Stream
-
Resources
-
Parallelism (The Java Tutorials > Collections > Aggregate Operations) (opens in a new tab)
-
When to use parallel streams (opens in a new tab)
- The source collection is efficiently splittable.
- The total time to execute the sequential version exceeds a minimum threshold.
-
Concurrent Reduction
- Use
groupingByConcurrent
instead ofgroupingBy
. - Return a
ConcurrentHashMap
instead ofHashMap
. Usevar
to smooth the refactoring. - Use
parallel()
to enable parallel stream.
var map = Files.list(skuDir) .filter(Files::isRegularFile) .filter(IO::isJpg) .map(ifs::of) .filter(ImageFile::isNotEmpty) .filter(ImageFile::isCropped) .parallel() .collect(groupingByConcurrent(ImageFile::getSku));
- Use
Concurrency
-
Abstract
methods cannot besynchronized
. -
Resources
ThreadLocal
ThreadLocal
variable must be either astatic
variable or an instance variable of a singleton object to ensure single source of truth.- Can supply an initial value
- The value stored by a particular thread is only accessible by itself.
- The field usually has a type not
Thread
safe.
private static final ThreadLocal<DateFormat> dateFormat = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
InterruptedException
- Use
Thread.isInterrupted
instead ofThread.interrupted
to check interrupted status, asThread.interrupted
clears interrupted status. java.lang.InterruptedException
is intended for stopping the execution of a thread gracefully.- Any method throwing
java.lang.InterruptedException
is a blocking method.
Lock
- Avoid acquiring multiple locks. If you want to acquire multiple locks, make sure that they are acquired in the same order everywhere to avoid deadlocks.
ReentrantLock
basically has the same effect assynchronized
, but it's not structural and based on methods, therefore more flexible.
volatile
-
Modified by one thread, read by multiple threads
Best scenario
-
Modified by multiple threads
Use Atomic classes or locking
-
When you modify a
volatile
variable, its value is sent to the main memory. The value of all the variables modified previously by the same thread are sent too. -
Compilers can't reorder sentences that modify a
volatile
variable for an optimization purpose. It can reorder the previous operations and the later ones, but not the modifications of avolatile
variable. The changes that happen before these modifications will be visible to those instructions. -
volatile
variables only solve the visibility problem. They cannot be used to safely implement the atomic read-modify-write sequences that are necessary for safely implementing counters and other entities that require mutual exclusion.
ForkJoinPool
- All worker threads are
daemon
threads, which means they don't prevent the JVM from shutting down.
CompletableFuture
Use a custom Executor insteand of the default ForkJoinPool
CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
-
Resources
JSR 310: Date and Time API
-
All classes under
java.time
package are immutable and thread-safe, so don't forget to assign the result. -
Period: date-based
-
Duration: time-based
-
Month values are one-based, namely like realworld month values.
-
Formatter
java.time.format.DateTimeFormatter
isthread-safe
andimmutable
, so can be shared thus replacingjava.text.DateFormat
.
Maths
BigDecimal
- Don't use
double
constructor, which has precision and approximation issues. - Use
String
constructor as the first choice. - Use
valueOf()
method as the second choice as it still has precision issue. BigDecimal
s shouldn’t be compared using theequals()
method, but instead thecompareTo()
method should be used, because it compares the numerical values (x = 1; y = 1.0) represented by the two instances of BigDecimal.
- Don't use