1 Java Fundamentals
1.1 Create and use final classes
1.2 Create and use inner, nested and anonymous classes
-
member inner class
- Can be declared
public
,private
, orprotected
or use default access - Can
extend
any class andimplement
interfaces - Can be
abstract
orfinal
- Can access members of the outer class including
private
members by<OuterClassName>.this.<memberName>
- Instead of
new
operator, use<instance of outer class>.new
- Compiled bytecode file is named as
<OuterClassName>$<InnerClassName>.class
- Multiple level of member inner classes can be nested
- When used outside the outer class, its reference type must be refered to as
<OuterClassName>.<InnerClassName>
- Can be declared
-
local inner class
- Can only be
class
- Cannot be
interface
orenum
- Can be
abstract
orfinal
- Like a local variable, cannot be accessed outside of the method
- Like a local variable, cannot have access modifier
- Cannot be declared
static
- Can have
static final
fields, but notstatic
only fields - Cannot have
static
initialization blocks - Cannot have
static
methods - Cannot have
static
nested class - Cannot have inner
interface
orenum
- Can access members of the enclosing class
- Can only access
final
or effectively final local variables of the enclosing method
- Can only be
-
anonymous inner class
- Must extend an existing
class
or implement an existinginterface
- Can be converted to a lambda expression when implementing a functional interface
- Can have
static
fields - Cannot have
static
initialization blocks - Cannot have
static
methods - Cannot have
static
nested class - Cannot have inner
interface
orenum
- Can access members of the enclosing class
- Can only access
final
or effectively final local variables of the enclosing method - Can define new methods the same as other inner classes.
- Must extend an existing
-
static nested class
- member level
static
class - Can be referenced without enclosing type
- Cannot access members of the enclosing class without an instance
- Enclosing class can access its
static
members by using class name, evenprivate
ones - Can be imported by either
import
orimport static
- member level
1.3 Create and use enumerations
-
enum
-
enum
is essentially a class with enumerated, predefined class instances, which can have normal class elements, evenabstract
method. -
enum
literals must be on the first line of the body, and if there are any other content in theenum
, the last literal must be followed by semi-colon. -
enum
literals are actually instances of theenum
created beforehand, therefore they are static by nature. -
enum
constructors must be and areprivate
implicitly. -
enum
canimplement
interfaces but can'textend
any otherclass
orenum
. -
Best for creating singleton objects intended for reuse. While
enum
could have more behaviors, its data is meant to be read-only.enum Tiger { SIBERIAN(1), BENGAL(2); public static final int LEVEL = 9; private final double length; Tiger(double length) { this.length = length; } public double getLength() { return length; } public static void t() { System.out.println("t"); } private static void p() { System.out.println("p"); } public void u() { System.out.println("p"); } private void m() { System.out.println("p"); } private static class Cub { public static void print() { System.out.println("length: " + SIBERIAN.length); } } }
-
A
enum
constant can define an anonymous class. This anonymous class extends the enum type itself, and if the enum type has any method defined, it can be overridden in the anonymous class.enum Animal { DOG("happy") { @Override void communicate() { bark(); } private void bark() { System.out.println("woof"); } @Override public String toString() { return this.name; } }, CAT("aloof") { @Override void communicate() { meow(); } private void meow() { System.out.println("meow"); } @Override public String toString() { return this.name; } }; protected final String name; // Compiler error if private abstract void communicate(); public abstract String toString(); Animal(String n) { this.name = n; }
-
2 Java Interfaces
-
In
class
definition,extends
must go beforeimplement
.class Test1 implements Foo extends Bar {} // Compiler error class Test2 extends Bar implements Foo {} // OK
2.1 Create and use interfaces with default methods
-
default
methods are implicitlypublic
. -
can only be instance methods
-
can be overridden in subclasses
-
Cannot override
java.lang.Object
methods -
Multiple inheritance
- a class implementing 2 or more interfaces with 2 or more methods of the same signature, at least one of which is a
default
method, must resolve the conflict by overriding the method. This only applies to when one of those methods isdefault
method.
interface Q18_A { void m(); } interface Q18_B { default void m() { System.out.println("B"); } } abstract class Q18_C implements Q18_A, Q18_B { @Override public void m() { // Compiler error if the method is not overridden to resolve the conflict, and this cannot be delegated to subclasses. } } class D extends Q18_C { @Override public void m() { System.out.println("D"); } }
-
Multiple methods from different types with the same signature but different access level must be overridden in the first common descendant, and the access level must be the least restrictive one among the methods with the same signature.
abstract class Animal { abstract void fly(); } interface Flyable { void fly(); } public class Bird extends Animal implements Flyable { @Override public void fly() { System.out.println("fly"); } }
-
Multiple methods from different types with the same signature but incompatible return types must be overridden in the first common descendant, and the overriding method must have a covariant return type that accommodate all incompatible types.
abstract class Animal { abstract List<? extends Object> fly(); } interface Flyable { List<String> fly(); } public class Bird extends Animal implements Flyable { @Override public List<String> fly() { System.out.println("fly"); return null; } }
- a class implementing 2 or more interfaces with 2 or more methods of the same signature, at least one of which is a
-
With the same signature, in subclasses, non abstract method would take precedence over default methods
@Test public void a() { Child child = new Child(); child.m1(); // Print "C" } class C { public void m1() { System.out.println("C"); } } interface I { default void m1() { System.out.println("I"); } } class Child extends C implements I { }
-
static
and instance methods cannot have the same signature, and all instance methods are included:abstract
,default
andprivate
ones, in bothinterface
andclass
. -
When referring to an overridden method in a super-interface, we must use the format
<super interface name>.super.<method name>
.interface Q46_Foo { default void m() { System.out.println("Foo"); } } interface Q46_Bar extends Q46_Foo { default void m() { Q46_Foo.super.m(); // <super interface name>.super.<method name> } }
2.2 Create and use interfaces with private methods
- can be
static
methods - can be instance methods
static
method must be invoked on containinginterface
only.
3 Functional Interface and Lambda Expressions
3.1 Define and write functional interfaces
- The only difference between a
FunctionalInterface
and a regularinterface
is there can be only oneabstract
method in aFunctionalInterface
. - Any
interface
with only oneabstract
method is considered effectively aFunctionalInterface
.abstract
methods do not include inherited non-abstract instance methods fromObject
, such asString toString()
.abstract
methods include those inherited from superinterface
s.
3.2 Create and use lambda expressions including statement lambdas, local-variable for lambda parameters
-
When using lambda expression to implement a
FunctionalInterface
, the referencing variable must have an explicit type instead ofvar
, otherwise type inference would fail. -
Any local variable, method parameter, or exception parameter used but not declared in a lambda expression must either be declared
final
or be effectively final, or a compile-time error occurs where the use is attempted, effectively final meaning the variable cannot be assigned twice in the enclosing block. -
Variables of lambda expression cannot have the same name as enclosing local variable.
-
Limitations of
var
used in lambda expression(var s1, s2) -> s1 + s2 // Compiler error, all variables must have types or none of them have types (var s1, String s2) -> s1 + s2 // Compiler error, var cannot be mixed with explicit types
-
For a lambda returning void, an expression returning value as lambda body is allowed by the compiler. The return value of the expression is simply ignored.
Consumer<String> c = s -> s.toLowerCase(); // OK, result of s.toLowerCase() is ignored
4 Built-in Functional Interfaces
4.1 Use interfaces from the java.util.function package
4.2 Use core functional interfaces including Predicate, Consumer, Function and Supplier
-
Predicate<T>
boolean test(T t)
-
Consumer<T>
accept(T t)
-
Function<T, R>
R apply(T t)
-
Supplier<T>
T get()
4.3 Use primitive and binary variations of base interfaces of java.util.function package
5 Migration to a Modular Application
5.1 Migrate the application developed using a Java version prior to SE 9 to SE 11 including top-down and bottom-up migration, splitting a Java SE 8 application into modules for migration
-
A modular JAR placed on classpath is considered as a plain-old JAR.
-
Unnamed Module
- All classes of all JARs (modular or not) on the classpath becomes part of an unnamed module, and there's only one unamed module.
- The unnamed module exports all of its packages.
- The unnamed module reads all other modules.
- If a package is defined in both a named module and the unnamed module then the package in the unnamed module is ignored.
- An unamed module can refer to a named module, but a named module cannot declare a dependence upon an unnamed module, as it cannot be referenced in
module-info.java
.
-
Automatic Module
- To enable a unnamed module to be able to be referred to, the unnamed module can be converted to an automatic module by placing it on the module path rather than the class path.
- The name of an automatic module is derived from that of the JAR file.
- Automatic module abides by modularization rules and forbids duplicate packages on the module path, therefore packages with the same name can only remain on the class path.
- Able to read unamed module
5.2 Use jdeps
to determine dependencies and identify ways to address the cyclic dependencies
- output:
<package> -> <package> <module/JAR>
<module/JAR>
would displaynot found
if it cannot be found on the class path.
6 Concurrency
6.1 Create worker threads using Runnable, Callable and use an ExecutorService to concurrently execute tasks
-
java.lang.Thread
- Started by calling
start()
notrun()
- Started by calling
-
java.util.concurrent.Executor
void execute(Runnable command)
-
java.util.concurrent.Executors
-
API
- Single thread executor:
Executors.newSingleThreadExecutor()
- Scheduled thread executor:
Executors.newScheduledThreadPool(int corePoolSize)
- Cached thread executor:
Executors.newCachedThreadPool()
- Fixed number of threads executor:
Executors.newFixedThreadPool(int corePoolSize)
- Single thread executor:
-
-
java.util.concurrent.ExecutorService
-
API
Method Description <T> Future<T> submit(Callable<T> task)
<T> Future<T> submit(Runnable task, T result)
Future<?> Future<T> submit(Runnable task)
void shutdown()
No new tasks will be accepted. Invocation has no additional effect if already shut down. boolean isShutdown()
boolean isTerminated()
List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
Wait for all tasks to complete T invokeAny(Collection<? extends Callable<T>> tasks)
Wait for any task to complete -
Tasks are not guaranteed to be executed in the order they are submitted to
ExecutorService
.
-
-
java.util.concurrent.ScheduledExecutorService
- API
Method Description <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
supports Callable<V>
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
period calculated starting from initialDelay ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
delay is between the termination of one execution and the commencement of the next
- API
-
java.util.concurrent.Callable
-
Difference with
java.lang.Runnable
- Return value
- Exception declaration
java.util.concurrent.Callable: V call() throws Exception; java.lang.Runnable: void run();
-
-
java.util.concurrent.Callable
-
Difference with
java.util.function.Supplier
- Exception declaration
java.util.concurrent.Callable: V call() throws Exception; java.util.function.Supplier: T get();
-
6.2 Use java.util.concurrent collections and classes including CyclicBarrier and CopyOnWriteArrayList
-
LinkedBlockingQueue
-
A
BlockingQueue
does not accept null elements. Implementations throw NullPointerException on attempts to add, put or offer a null. A null is used as a sentinel value to indicate failure of poll operations. -
All queuing methods are thread-safe, but bulk Collection operations such as
addAll
are not necessarily thread-safe. -
Blocking operations
put(e)
take()
-
-
CyclicBarrier
- Once the specified number of threads have each called
await()
, the barrier is released and all threads can continue. await()
is a blocking method, it will wait until the barrier is released.- Once the barrier is released, calling
await()
won't have any effect. - Make sure that you set the number of available threads to be at least as large as your
CyclicBarrier
limit value. CyclicBarrier
can be reused by new threads after all current threads are released.
- Once the specified number of threads have each called
-
CopyOnWriteArrayList
-
iterator()
- Any
Iterator
established prior to a modification will not see the changes, but instead it will iterate over the original elements prior to the modification. - The returned
Iterator
does not support theremove
.
- Any
-
6.3 Write thread-safe code
6.4 Identify threading problems such as deadlocks and livelocks
- 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.
- livelock: Consider two threads t1 and t2. Assume that thread t1 makes a change and thread t2 undoes that change. When both the threads t1 and t2 work, it will appear as though lots of work is getting done, but no progress is made.
- lock starvation: occurs when low-priority threads starve for a long time trying to obtain the lock
- race condition: occurs when two threads try to complete a task at the same time, which should have been completed sequentially.
7 I/O (Fundamentals and NIO2)
7.1 Read data from and write console and file data using I/O Streams
-
Console
System.out
andSystem.err
arePrintStream
objects.
-
java.io.File
File[] listFiles()
- returning all files and directories if the given
File
object is a directory - returning
null
if the givenFile
object is a file
- returning all files and directories if the given
7.2 Use I/O Streams to read and write files
-
OutputStream
-
representing an output stream of bytes
-
BufferedInputStream
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int bufferSize)
-
ObjectInputStream
ObjectInputStream(InputStream in) throws IOException
-
FileInputStream
-
can only accept a file, not a
InputStream
-
FileInputStream(String name) throws FileNotFoundException
-
FileInputStream(File file) throws FileNotFoundException
-
-
PrintStream
- most methods don't throw
IOException
, including the common ones:write
append
flush
close
- Using
boolean checkError()
method to check if anIOException
has occurred. format
andprintf
have identical behaviors.
- most methods don't throw
-
-
Writer
-
writing to character streams
-
PrintWriter
-
PrintWriter(Writer out)
-
PrintWriter(Writer out, boolean autoFlush)
-
PrintWriter(OutputStream out)
-
PrintWriter(OutputStream out, boolean autoFlush)
-
PrintWriter(OutputStream out, boolean autoFlush, Charset charset)
-
PrintWriter(String fileName)
-
PrintWriter(Charset charset, File file)
-
PrintWriter(String fileName, String csn)
-
PrintWriter(String fileName, Charset charset)
-
PrintWriter(File file)
-
PrintWriter(File file, String csn)
-
PrintWriter(File file, Charset charset)
-
most methods don't throw
IOException
, including the common ones:write
append
flush
close
-
Using
boolean checkError()
method to check if anIOException
has occurred. -
format
andprintf
have identical behaviors.
-
-
7.3 Read and write objects by using serialization
-
java.io.Serializable
- In order to serialize objects using the
java.io
API, the class they belong to must implement thejava.io.Serializable
interface. - Any class,
abstract
,concrete
, orfinal
, can be markedSerializable
. java.io.Serializable
is a markerinterface
that has no method.- The purpose of implementing the
Serializable
interface is to inform any process attempting to serialize the object that you have taken the proper steps to make the object serializable, which involves making sure that the classes of all instance variables within the object are also markedSerializable
. - A process attempting to serialize an object will throw a
NotSerializableException
if the class or one of its contained classes does not properly implement theSerializable
interface. - Use
transient
keyword to mark instance variables that you don't want to serialize. - Recommended but not mandatory,
serialVersionUID
is used forclass
schema migration. Astatic
fieldserialVersionUID
is used to identifyuniquely a version of the class
, so that it knows whether the serialized data for an object will match the instance variable in the current version of the class. Without aserialVersionUID
field, any change to a class will make previously serialized versions unreadable. serialVersionUID
field should be updated anytime the instance variables in the class are changed.null
objects can be serialized and deserialized, therefore usinginstanceof
for every serialization and deserialization process.- When you deserialize an object, the constructor of the serialized class is not called. Java calls the no-arg constructor for the first nonserializable ancestor class, skipping the constructors of any serialized class in between.
- If The first non-serializable ancestor class does not have an accessible no-arg constructor, an
InvalidClassException
will be thrown during serialization and deserialization process. - Because of not being a part of a class instance, any
static
variables or initialization blocks or constructors are ignored during the serialization and deserialization process.
- In order to serialize objects using the
-
ObjectInputStream
readObject()
returns anObject
, therefore a cast is usually needed to restore the instance.
-
ObjectOutputStream
writeObject(Object o)
requires aSerializable
object, otherise it throws aNotSerializableException
.
7.4 Use the Path interface to operate on file and directory paths
-
Optional Arguments
LinkOption.NOFOLLOW_LINKS
FileVisitOption.FOLLOW_LINKS
StandardCopyOption.COPY_ATTRIBUTES
StandardCopyOption.REPLACE_EXISTING
StandardCopyOption.ATOMIC_MOVE
-
java.nio.file.Paths
-
static Path get(String first, String... more)
- equivalent to
static Path.of
first
is a relative path: it would be relative to working directory.first
could contain directory separator.more
would be appended tofirst
to concatenate a path separated by directory separator of the current OS.
- equivalent to
-
-
java.nio.file.Path
-
static of(String first, String... more)
- equivalent to
static Paths.get
- equivalent to
-
boolean equals(Object other)
- If the given object is not a
Path
, or is aPath
associated with a differentFileSystem
, then this method returnsfalse
. This method does not access the file system and the file is not required to exist. Where required, theisSameFile
method may be used to check if two paths locate the same file.
- If the given object is not a
-
Path getParent()
- Return the concatenated name elements from the second to last to the farthest
- Doesn't check the file system
Path.of("a", "b", "c").getParent() // "a\b" Path.of("log", "..").getParent() // "log" Path.of("/").getParent() // null Path.of(".").getParent() // null
-
int getNameCount()
- Returns the number of name elements in the path, or 0 if this path only represents a root component.
-
Path getName(int index)
- The element that is closest to the root in the directory hierarchy has index 0.
-
Path subpath(int beginIndex, int endIndex)
- Returns a relative path, and the returned
Path
object has the name elements that begin atbeginIndex
and extend to the element at indexendIndex - 1
.
- Returns a relative path, and the returned
-
Path relativize(Path other)
-
Basically it means how to reach
other
path from the invoking path, similar to usingcd
command in command line.var path1 = Path.of("a", "b"); var path2 = Path.of("c", "d"); System.out.println(path1.relativize(path2)); // "..\..\c\d"
-
Doesn't check the file system.
-
If both path values are relative, then the
relativize()
method computes the paths as if they are in the same current working directory. -
If both path values are absolute, then the method computes the relative path from one absolute location to another, regardless of the current working directory.
-
It will throw an
IllegalArgumentException
, if one path is relative path, and the other is an absolute path. -
It will throw an
IllegalArgumentException
, when on Windows-based systems, both absolute paths are not under the same drive.
-
-
Path resolve(Path other)
-
Intended for joining two
Path
s -
Doesn't check the file system.
-
Doesn't normalize path.
-
If an absolute path is provided as input to the method, such as
path1.resolve(path2)
, thenpath1
would be ignored and a copy ofpath2
would be returned.
-
-
Path resolveSibling(Path other)
- Resolve
other
path against the current path's parent.
- Resolve
-
Path normalize()
-
Doesn't check the file system.
-
Removing redundancies such as
.
or..
notation from aPath
-
Doesn't convert a relative path into an absolute path.
-
-
Path toAbsolutePath()
-
Doesn't check the file system.
-
If this path is already absolute then this method simply returns
this
path. -
If this path is relative then this method returns this path relative to the current directory.
-
-
Path toRealPath(LinkOption... options) throws IOException
-
Cleaning up a
Path
and check the file system to locate the file/directory. -
Converting a relative
Path
to an absolute one, similar totoAbsolutePath()
-
It throws an
IOException
when the file does not exist. -
Implicitly calling
normalize()
on the resulting aboslutePath
-
-
7.5 Use the Files class to check, delete, copy or move a file or directory
-
Symbolic files
- In POSIX-compliant operating systems, they are generally treated as an alias for the target.
-
Files
API-
boolean Files.exists(Path p, LinkOption... options)
-
boolean Files.isSameFile(Path p1, Path p2)
-
If both
Path
objects are equal then this method returnstrue
. -
If the two
Path
objects are associated with different providers then this method returnsfalse
. -
Checking the file system to see if both
Path
objects locate the same file.
-
-
Path Files.createDirectories(Path p, FileAttribute<?> attrs)
- Creating a directory several levels deep when one or more of the parent directories might not yet exist, with all non-existent directories created along the way
- Doesn't throw exception when directory path already exists, but if the specified path is a file, an exception would be thrown.
-
Path Files.createDirectory(Path p, FileAttribute<?> attrs)
- Creating one single directory, assuming all parent directories exist beforehand
-
long File.copy(Path source, Path target, CopyOption... options) throws IOException
-
long File.copy(InputStream in, Path target, CopyOption... options) throws IOException
-
long File.copy(Path source, OutputStream out) throws IOException
-
All three
copy
methodsthrows IOException
. -
Directory copies are shallow rather than deep, meaning that files and subdirectories within the directory are not copied.
-
By default, copying files and directories will traverse symbolic links, although it will not overwrite a file or directory if it already exists, nor will it copy file attributes. These behaviors can be altered by providing the additional
CopyOption...
.
-
-
Path Files.move(Path source, Path target, CopyOption... options) throws IOException
-
By default, the
move()
method will follow links, throw an exception if the file already exists, and not perform an atomic move. These behaviors can be altered by providing the additionalCopyOption...
. -
The method can be applied to non-empty directories only if they are on the same underlying drive. While moving an empty directory across a drive is supported, moving a non-empty directory across a drive will throw an NIO.2
DirectoryNotEmptyException
. -
Moving always preserves the metadata even if the
COPY_ATTRIBUTES
flag is not set.
-
-
void Files.delete(Path path) throws IOException
-
Deleting a file or empty directory within the file system
-
Throwing IOException
- if the path represents a non-empty directory, the operation will throw the runtime
DirectoryNotEmptyException
- if the path represents a non-empty directory, the operation will throw the runtime
-
If the target of the path is a symbol link, then the symbolic link will be deleted, not the target of the link.
-
-
boolean Files.deleteIfExists(Path path) throws IOException
- Almost identical to the
delete(Path)
method, except that it will not throw an exception if the file or directory does not exist, but instead it will return a boolean value of false.
- Almost identical to the
-
BufferedReader Files.newBufferedReader(Path path) throws IOException
-
BufferedReader Files.newBufferedReader(Path path, Charset cs) throws IOException
-
List<String> Files.readAllLines(Path path) throws IOException
- The entire file is read when
readAllLines()
is called, with the resultingString
array storing all of the contents of the file in memory at once. Therefore, if the file is significantly large, you may encounter anOutOfMemoryError
trying to load all of it into memory.
- The entire file is read when
-
boolean Files.isDirectory(Path path, LinkOption... options)
-
boolean Files.isRegularFile(Path path, LinkOption... options)
- By default, symbolic links are followed and the file attribute of the final target of the link is read.
- If the target doesn't exist, return
false
, instead of throwing anIOException
.
-
boolean Files.isSymbolicLink(Path path)
- If the target doesn't exist, return
false
, instead of throwing anIOException
.
- If the target doesn't exist, return
-
boolean Files.isHidden(Path path) throws IOException
-
boolean Files.isReadable(Path path)
-
boolean Files.isExecutable(Path path)
-
<A extends BasicFileAttributes> A Files.readAttributes(Path path, Class<A> type, LinkOption... options)
-
<V extends FileAttributeView> V Files.getFileAttributeView(Path path, Class<V> type, LinkOption... options)
-
A lot of other methods in
Files
rely on these two under the hood for getting file attributes. -
BasicFileAttributeView
and its subclasses can be used to modify various file attributes.
-
-
Path Files.writeString(Path path, CharSequence csq, OpenOption... options) throws IOException
- Write a
CharSequence
to a file, creating the file if it doesn't exist, overriding its content if the file exists.
- Write a
-
-
Methods that by default follow symbolic links
boolean Files.exists(Path p, LinkOption... options)
boolean Files.isDirectory(Path path, LinkOption... options)
boolean Files.isRegularFile(Path path, LinkOption... options)
<A extends BasicFileAttributes> A Files.readAttributes(Path path, Class<A> type, LinkOption... options)
<V extends FileAttributeView> V Files.getFileAttributeView(Path path, Class<V> type, LinkOption... options)
7.6 Use the Stream API with Files
-
Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options) throws IOException
-
Uses depth-first searching with a default maximum depth of
Integer.MAX_VALUE
. -
maxDepth
with a value of 0 indicates the current path record itself. -
When you create a
Stream<Path>
object usingFiles.walk()
, the contents of the directory have not yet been traversed. -
walk()
will not traverse symbolic links by default.
-
-
Stream<Path> find(Path start, int maxDepth, BiPredicate<Path, BasicFileAttributes> matcher, FileVisitOption... options)
maxDepth
must be >= 0, and0
means the starting directory itself, and1
means the items under the starting directory.
-
Stream<Path> list(Path dir) throws IOException
-
The listing is not recursive, only entries in the given directory.
-
similar to
File[] listFiles()
, but returns a lazily populated stream.
-
-
Stream<String> lines(Path path) throws IOException
- Unlike
Files.readAllLines()
's eager processing, this one returns a lazily processedStream
.
- Unlike
8 Database Applications with JDBC
-
JDBC interfaces
Driver
Connection
Statement
ResultSet
8.1 Connect to databases using JDBC URLs and DriverManager
-
JDBC URL
- The first piece is always the same. It is the protocol jdbc. The second part is the name of the database such as derby, mysql, or postgres. The third part is “the rest of it,” which is a database-specific format. Colons separate the three parts.
-
Starting with JDBC 4.0, driver implementations were required to provide the name of the class implementing Driver in a text file named
java.sql.Driver
in the directoryMETA-INF/services
.
var url = "jdbc:h2:mem:ocp;DB_CLOSE_DELAY=-1";
var query = "SHOW SCHEMAS";
try (var conn = DriverManager.getConnection(url);
var stmt = conn.createStatement();
var rs = stmt.executeQuery(query)) {
while (rs.next())
System.out.println(rs.getString(1));
} catch (SQLException e) {
e.printStackTrace();
}
- JDBC resources should be closed in the reverse order from that in which they were opened.
8.2 Use PreparedStatement to perform CRUD operations
-
Connection
-
Statement createStatement() throws SQLException
-
Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
-
resultSetType
int ResultSet.TYPE_FORWARD_ONLY
- default option
- cannot navigate backwards
int ResultSet.TYPE_SCROLL_INSENSITIVE
- can navigate both backwards and forwards
- static view of the
ResultSet
in spite of any change in the table after the query was made
int ResultSet.TYPE_SCROLL_SENSITIVE
- can navigate both backwards and forwards
- ongoing update to the
ResultSet
after the query was made to reflect the latest change - rarely used due to insufficient support from implementations, downgrade to
TYPE_SCROLL_INSENSITIVE
when unavailable
-
resultSetConcurrency
int ResultSet.CONCUR_READ_ONLY
- default option
- unable to update via
ResultSet
int ResultSet.CONCUR_UPDATABLE
- able to update via
ResultSet
- rarely used due to insufficient support from implementations, downgrade to
CONCUR_READ_ONLY
when unavailable
- able to update via
-
-
-
Statement
-
boolean execute(String sql) throws SQLException
- Returns
true
if the first result is aResultSet
object;false
if it is an update count or there are no results.
- Returns
-
ResultSet executeQuery(String sql) throws SQLException
- Returns a
ResultSet
object.
- Returns a
-
int executeUpdate(String sql) throws SQLException
- Returns updated row count.
-
A
Statement
automatically closes the openResultSet
when another SQL statement is run on the sameStatement
object.
-
-
ResultSet
-
boolean next() throws SQLException
- A ResultSet cursor is initially positioned before the first row; the first call to the method next makes the first row the current row.
- Returns
true
if the new current row is valid;false
if there are no more rows. - When a call to the next method returns
false
, the cursor is positioned after the last row. - Column index of
get
methods starts from1
.
-
boolean absolute(int row) throws SQLException
- The number of the row to which the cursor should move. A value of zero indicates that the cursor will be positioned before the first row.
- If the given row number is negative, the cursor moves to an absolute row position with respect to the end of the result set.
-
void beforeFirst() throws SQLException
-
void afterLast() throws SQLException
-
8.3 Use PreparedStatement and CallableStatement APIs to perform database operations
-
PreparedStatement
- storing a precompiled SQL statement, therefore when the
PreparedStatement
is executed, the DBMS can just run thePreparedStatement
SQL statement without having to compile it first - able to be supplied with parameters, index starting from 1, and placeholder being
?
- can be reused
PreparedStatement prepareStatement(String sql) throws SQLException
- storing a precompiled SQL statement, therefore when the
-
CallableStatement
- subclass of
PreparedStatement
intended for executing SQL stored procedures - in addition to being able to receive parameter values by index, supporting receiving parameter values by name, which must be identical with the name defined in the stored procedure
- parameter placeholders composed of IN, OUT and INOUT parameters.
- IN parameters are input parameters, and
set
methods are used to supply values. - OUT parameters are return values of the stored procedure, and
get
methods are used to retrieve OUT parameters. - All OUT parameters must be registered before a stored procedure is executed.
- INOUT parameters can serve both purposes, therefore they must be both
set
with values as input and registered as output before calling the stored procedure. CallableStatement prepareCall(String sql) throws SQLException
- subclass of
9 Annotations
9.1 Describe the purpose of annotations and typical usage patterns
- Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
- Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
- Runtime processing — Some annotations are available to be examined at runtime.
9.2 Apply annotations to classes and methods
-
Applied to declarations
- If there is just one element named
value
, then the name can be omitted@SuppressWarnings(value = "unchecked")
equals to@SuppressWarnings("unchecked")
- If no elements, the parentheses can be omitted
- If there is just one element named
-
Type Annotations (Java 8+)
-
Class instance creation expression:
new @Interned MyObject();
-
Type cast:
myString = (@NonNull String) str;
-
implements clause:
class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }
-
Thrown exception declaration:
void monitorTemperature() throws @Critical TemperatureException { ... }
-
9.3 Describe commonly used annotations in the JDK
-
java.lang
: annotations for common use@Deprecated
@Override
@SuppressWarnings
@SafeVarargs
@FunctionalInterface
-
java.lang.annotation
: meta-annotations-
@Retention
-
@Documented
-
@Target
-
@Inherited
-
@Repeatable
- An annotation to be used repeatedly must be marked with
@Repeatable
- Repeating annotations are stored in a container annotation, which is not used in code but intended for the compiler.
- The containing annotation type must have a value element with an array type, and the component type of the array type must be the repeatable annotation type.
import java.lang.annotation.Repeatable; // Repeatable Annotation @Repeatable(Schedules.class) // Specifying containing Annotation type public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; } // Containing Annotation Type public @interface Schedules { Schedule[] value(); // an array type of the repeatable Annotation } // Repeatable Annotation usage @Schedule(dayOfMonth="last") @Schedule(dayOfWeek="Fri", hour="23") public void doPeriodicCleanup() { ... }
- An annotation to be used repeatedly must be marked with
-
9.4 Declare custom annotations
- Annotation Type Elements (opens in a new tab)
- The return type of a method declared in an annotation type must be one of the following, or a compile-time error occurs:
- primitive
String
Class
or an invocation of Class (§4.5)- enum
- annotation
- An array type whose component type is one of the preceding types
- multi-dimentional array is not permitted.
- The return type of a method declared in an annotation type must be one of the following, or a compile-time error occurs:
10 Exception Handling and Assertions
10.1 Use the try-with-resources construct
-
try-with-resources can only have
AutoCloseable
variable declaration statements, separated by semi-colon if multiple. -
As the variables are local to the
try
block,var
can be used. -
catch
andfinally
block are both optional for try-with-resources. -
A user defined
finally
block gets executed after the implictfinally
block. -
Resources are closed after the
try
clause ends and before anycatch
/finally
clauses. -
Resources are closed in the reverse order from which they were created.
-
Checked exceptions thrown from variable declarations must be catched in corresponding
catch
blocks fortry-with-resources
. -
If both
Autocloseable
resources'close
method andtry
block throw exceptions, the ones fromclose
methods would be suppressed, and the one gets thrown is called primary exception.- Suppressed exceptions can be accessed by calling
getSuppressed()
on the primary exception.
- Suppressed exceptions can be accessed by calling
-
java.io.Closeable
extendsjava.lang.AutoCloseable
, throwing different exceptions.public interface Closeable extends AutoCloseable { void close() throws IOException; } public interface AutoCloseable { void close() throws Exception; }
-
When there are multiple
AutoCloseable
s declared- If more than one of the
close
methods throw exceptions, only the first one will be the primary exception, the rest suppressed. The first resource to close is the last one declared intry
statements. - Even if there are exceptions, all
close
methods would be run nonetheless. Program flow wouldn't be interrupted.
- If more than one of the
10.2 Create and use custom exception classes
10.3 Test invariants by using assertions
-
When an assertion evaluates to
false
, anAssertionError
is thrown. -
assert
forms-
assert
Expression1 -
assert
Expression1 : Expression2 ;- Expression1 is a
boolean
expression. - Expression2 is an expression that has a value (cannot be a method invocation returns void).
- The
String
representation of Expression2 would be passed toAssertionError
. - Expression1 can have optional enclosing parenthesis.
- Expression1 is a
-
-
By default, Java ignores assertions
-
-enableassertions
/-ea
to enable assertionsjava -enableassertions/-ea Rectangle
-
-ea:<package-name>...
to enable assertions for specific package and its subpackages. -
-ea:<class-name>
to enable assertions for specific package -
-da:<class-name>
to disable a specific class
11 Generics and Collections
11.1 Use wrapper classes, autoboxing and autounboxing
-
Wrapper types can only be assigned with corresponding primitives and its own wrapper types.
Double f = Integer.valueOf(43); // Compiler error
-
Primitive types can be assigned with wrapper types that can be implicitly converted after unboxing.
double d = Integer.valueOf(4); // OK
-
When a primitive value is compared with an instance of a wrapper class, this instance is unboxed automatically.
-
valueOf()
method of all integer wrapper types (Byte
,Short
,Integer
,Long
) cache values from-128
to127
, andCharacter
caches values from0
to127
11.2 Create and use generic classes, methods with diamond notation and wildcards
-
super
-
super
can only be used with wildcard, not to be used in type definition. -
Used to define lower bound (mainly of method parameter), only super types of the given type can be accepted.
-
Set the lower bound, which can be any type that is a ancestor of the specified type.
-
<? super Number>
means the set of all types extendingNumber
, but which type it will be in runtime is unknown, therefore if it's a generic container, only elements is-aNumber
could be added into the container.@Test public void wildcardSuper1() { List<Object> o1 = List.of(new Object(), new Object()); accept(o1); // OK List<Serializable> o2 = List.of("abc", "def"); accept(o2); // OK List<Number> n1 = List.of(1, 2, 3, 4); accept(n1); // OK List<Integer> n2 = List.of(1, 2, 3, 4); accept(n2); // Compiler error } private void accept(List<? super Number> nums) { System.out.println(nums); } @Test public void wildcardSuper2() { // The following lines compile successfully. List<? super Number> nums = new ArrayList<>(); nums.add(Short.valueOf("2")); nums.add(1); nums.add(1L); nums.add(1F); nums.add(1D); }
-
-
extends
- When used with wildcard, it defines upper bound (mainly of return type), only sub types of the given type can be returned, therefore meeting is-a relationship.
- When used with type parameter for generic type definition or generic method, it helps to restrict the captured type.
- 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.
-
Wildcard
- Stand for a set of types meeting the requirement, while a type parameter is used to indicate a specific type and can be captured.
- Can be used in method parameters
- Can be used as return type
- Cannot be used to instantiate object directly.
-
Object[]
is considered super class of other array type such asInteger[]
.
11.3 Describe the Collections Framework and use key collection interfaces
-
Set
retainAll
: Only keeps the intersection with the givenCollection
in thisSet
-
Map
-
merge
MapM<String, String> map = new LinkedHashMap<>(); BinaryOperator<String> operator = (s1, s2) -> null; map.put("John", "Teacher"); map.merge("John", "Doctor", operator); // Use remappingFunction to get a new value to replace "Doctor", because the new value is null, the entry is removed. map.merge("Jane", "Doctor", operator); // As there is no value for "Jane", "Doctor" is associated with "Jane". System.out.println(map); // {Jane=Doctor}
-
11.4 Use Comparator and Comparable interfaces
11.5 Create and use convenience methods for collections
12 Java Stream API
12.1 Describe the Stream interface and pipelines
-
java.util.stream.Stream
-
static methods
-
infinite stream
generate
: the supplier function determines what to be generated.iterate
- the initial element is given
- the supplier function applies to the previous item to generate the current item.
-
finite stream
of
: enumerated elements, equivalent toArrays.stream
.
-
-
instance methods
-
limit
: ensure the stream outputs elements no more than the specified max size. -
toArray
Object[] toArray()
<A> A[] toArray(IntFunction<A[]> generator)
: generator is a function accepting an int and returning an array
-
reduce
- identity must meet the requirement that
accumulator.apply(identity, t) == t
- identity must meet the requirement that
-
-
-
3 primitive streams
-
java.util.stream.IntStream
-
java.util.stream.LongStream
-
static methods
range
: start inclusive, end exclusinverangeClosed
: start inclusive, end inclusinve
-
-
java.util.stream.DoubleStream
-
-
java.util.function.BooleanSupplier
- A Supplier returns a
boolean
- A Supplier returns a
-
Consumer
- Two
Consumer
s chained together is considered accepting the input separately.
- Two
12.2 Use lambda expressions and method references
13 Lambda Operations on Streams
13.1 Extract stream data using map, peek and flatMap methods
13.2 Search stream data using search findFirst, findAny, anyMatch, allMatch and noneMatch methods
-
noneMatch
- When the Stream is empty, it returns
true
- When the Stream is empty, it returns
13.3 Use the Optional class
-
java.util.stream.Optional
-
T get()
- Throws
NoSuchElementException
when no value exists.
- Throws
-
Optional.of(T value)
- Throws
NullPointerException
when the parameter isnull
.
- Throws
-
-
3 primitive optional classes
-
java.util.stream.OptionalInt
int getAsInt()
-
java.util.stream.OptionalLong
long getAsLong()
-
java.util.stream.OptionalDouble
double getAsDouble()
-
13.4 Perform calculations using count, max, min, average and sum stream operations
- Only primitive streams have
average
,sum
methods. OptionalDouble average();
- All
sum
methods return their corresponding type. count
returnslong
.max
andmin
returnOptional<T>
13.5 Sort a collection using lambda expressions
13.6 Use Collectors with streams, including the groupingBy and partitioningBy operations
-
The
partitioningBy()
operation always returns a map with two Boolean keys, even if there are no corresponding values. By contrast,groupingBy()
returns only keys with associated values. -
Stream.collect(supplier, accumulator, combiner)
- The combiner function must fold the elements from the second result container into the first result container.
14 Services in a Modular Application
14.1 Describe the components of Services including directives
- Service provider doesn't need to export the package of the implementation class.
- Service provider
requires
the API package containing the interface.
// service provider
module com.mysql.jdbc {
requires java.sql;
requires org.slf4j;
provides java.sql.Driver with com.mysql.jdbc.Driver; // implementation
}
// service consumer
module java.sql {
requires public java.logging;
requires public java.xml;
exports java.sql;
exports javax.sql;
exports javax.transaction.xa;
uses java.sql.Driver; // interface
}
- If a JAR file defining an automatic module contains a folder
META-INF/services
, then each such entry is treated as if it were a correspondingprovides
clause in a hypothetical declaration of that module. An automatic module is considered touses
every available service.
14.2 Design a service type, load services using ServiceLoader, check for dependencies of the services including consumer and provider modules
-
JPMS
- For a plain JAR, it must have
META-INF/services
directory to declare services, every file under that directory has a file name of the service interface's full name. The content of the file is the full name of the service provider class. - For a module JAR, it uses
module-info.java
to declare services. - compile-time error: if more than one
provides
directive in a module declaration specifies the same service. - compile-time error: if the
with
clause of a givenprovides
directive specifies the same service provider more than once.
- For a plain JAR, it must have
-
ServiceLoader API
class ServiceLoader<S> implements Iterable<S>
, therefore it can be iterated directly.- The type parameter is the target service.
- The implementation class isn't involved in the client code.
ServiceLoader<Q12_MyService> service1 = ServiceLoader.load(Q12_MyService.class); Optional<Q12_MyService> service2 = ServiceLoader.load(Q12_MyService.class).findFirst(); Iterator<Q12_MyService> service3 = ServiceLoader.load(Q12_MyService.class).iterator(); Stream<ServiceLoader.Provider<Q12_MyService>> stream = ServiceLoader.load(Q12_MyService.class).stream();
15 Parallel Streams
15.1 Develop code that uses parallel streams
- Stream pipelines may execute either sequentially or in parallel, determined when the terminal operation is initiated.
15.2 Implement decomposition and reduction with streams
16 Secure Coding in Java SE Application (opens in a new tab)
16.1 Prevent Denial of Service in Java applications
-
Guideline 1-1 / DOS-1: Beware of activities that may use disproportionate resources
-
Guideline 1-2 / DOS-2: Release resources in all cases
-
Guideline 1-3 / DOS-3: Resource limit checks should not suffer from integer overflow
16.2 Secure confidential information in Java application
-
Guideline 2-1 / CONFIDENTIAL-1: Purge sensitive information from exceptions
-
Guideline 2-2 / CONFIDENTIAL-2: Do not log highly sensitive information
-
Guideline 2-3 / CONFIDENTIAL-3: Consider purging highly sensitive from memory after use
16.3 Implement Data integrity guidelines- injections and inclusion and input validation
-
Guideline 3-1 / INJECT-1: Generate valid formatting
-
Guideline 3-2 / INJECT-2: Avoid dynamic SQL
-
Guideline 3-3 / INJECT-3: XML and HTML generation requires care
-
Guideline 3-4 / INJECT-4: Avoid any untrusted data on the command line
-
Guideline 3-5 / INJECT-5: Restrict XML inclusion
-
Guideline 3-6 / INJECT-6: Care with BMP files
-
Guideline 3-7 / INJECT-7: Disable HTML display in Swing components
-
Guideline 3-8 / INJECT-8: Take care interpreting untrusted code
-
Guideline 3-9 / INJECT-9: Prevent injection of exceptional floating point values
16.4 Prevent external attack of the code by limiting Accessibility and Extensibility, properly handling input validation, and mutability
-
Guideline 4-1 / EXTEND-1: Limit the accessibility of classes, interfaces, methods, and fields
-
Guideline 4-2 / EXTEND-2: Limit the accessibility of packages
-
Guideline 4-3 / EXTEND-3: Isolate unrelated code
-
Guideline 4-4 / EXTEND-4: Limit exposure of ClassLoader instances
-
Guideline 4-5 / EXTEND-5: Limit the extensibility of classes and methods
-
Guideline 4-6 / EXTEND-6: Understand how a superclass can affect subclass behavior
16.5 Securely constructing sensitive objects
-
Guideline 7-1 / OBJECT-1: Avoid exposing constructors of sensitive classes
-
Guideline 7-2 / OBJECT-2: Prevent the unauthorized construction of sensitive classes
-
Guideline 7-3 / OBJECT-3: Defend against partially initialized instances of non-final classes
-
Guideline 7-4 / OBJECT-4: Prevent constructors from calling methods that can be overridden
-
Guideline 7-5 / OBJECT-5: Defend against cloning of non-final classes
16.6 Secure Serialization and Deserialization
-
Guideline 8-1 / SERIAL-1: Avoid serialization for security-sensitive classes
-
Guideline 8-2 / SERIAL-2: Guard sensitive data during serialization
-
Guideline 8-3 / SERIAL-3: View deserialization the same as object construction
-
Guideline 8-4 / SERIAL-4: Duplicate the SecurityManager checks enforced in a class during serialization and deserialization
-
Guideline 8-5 / SERIAL-5: Understand the security permissions given to serialization and deserialization
-
Guideline 8-6 / SERIAL-6: Filter untrusted serial data
17 Localization
17.1 Use the Locale class
-
Locale must be in the format of
en_US
, with language lowercase and country uppercase. -
Locale must have a language, but country/region is optional.
var l1 = new Locale.Builder() .setLanguage("en") .setRegion("US") .build(); var l2 = Locale.getDefault(); var l3 = new Locale("en"); var l4 = new Locale("en", "US");
17.2 Use resource bundles
-
Resource Bundle as a property file
- Value must be
String
, usingString ResourceBundle.getString(String key)
- Value must be
-
Resource Bundle as a Java class
- A class with the same name that you would use for a property file. Only the extension is different.
- Value can be any type, using
Object ResourceBundle.getObject(String key)
method. - Values of the properties can be created at runtime
- For the same
ResourceBundle
, Java class file takes precedence over the property file, the resources in property file would be ignored.
-
How to use Resource Bundle
-
Locate the Resource Bundle
-
If a Resource Bundle with specified Locale is found
- Both language and country are matched
- Only language is matched
-
If a Resource Bundle with specified Locale is not found
- It would fall back to default Locale.
-
-
Locate the property
-
Java isn’t required to get all of the keys from the same resource bundle. It can get them from any parent of the matching resource bundle.
-
When a Resource Bundle cannot be found, a RuntimeException
java.util.MissingResourceException
will be thrown.var fr = new Locale("fr", "CA"); var b = ResourceBundle.getBundle("Dolphins", fr); var s = b.getString("name"); // Property search order // 1. Dolphins_fr_CA.properties Look for the bundle matching both language and country. // 2. Dolphins_fr.properties If no match with both language and country is found, look for the bundle with matching language. // 3. Dolphins.properties If no match with language is found, look for the bundle with base name only.
-
Look for the bundle matching both language and country
-
Matching Java class comes before the matching property file.
-
If no match with both language and country is found, look for the bundle with matching language.
-
If no match with language is found, look for the bundle with base name only.
-
-
17.3 Format messages, dates, and numbers with Java
-
NumberFormat
-
parse
-
Leading whitespace is not allowed, which causes a
ParseException
, a checked exception. -
Any characters that follow the number in the string are simply ignored, so no exception is thrown.
NumberFormat nf = NumberFormat.getInstance(); String one = "456abc"; String two = "-2.5165x10"; String three = "x85.3"; System.out.println(nf.parse(one)); // 456 System.out.println(nf.parse(two)); // -2.5165 System.out.println(nf.parse(three));// throws ParseException
-
-
DecimalFormat
- Both the
#
and0
characters in a pattern string denote a digit. The difference between them is that if the digit is missing,#
is shown as absent while0
is shown as0
.
- Both the
-