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, orprotectedor use default access - Can
extendany class andimplementinterfaces - Can be
abstractorfinal - Can access members of the outer class including
privatemembers by<OuterClassName>.this.<memberName> - Instead of
newoperator, 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
interfaceorenum - Can be
abstractorfinal - 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 finalfields, but notstaticonly fields - Cannot have
staticinitialization blocks - Cannot have
staticmethods - Cannot have
staticnested class - Cannot have inner
interfaceorenum - Can access members of the enclosing class
- Can only access
finalor effectively final local variables of the enclosing method
- Can only be
-
anonymous inner class
- Must extend an existing
classor implement an existinginterface - Can be converted to a lambda expression when implementing a functional interface
- Can have
staticfields - Cannot have
staticinitialization blocks - Cannot have
staticmethods - Cannot have
staticnested class - Cannot have inner
interfaceorenum - Can access members of the enclosing class
- Can only access
finalor 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
staticclass - Can be referenced without enclosing type
- Cannot access members of the enclosing class without an instance
- Enclosing class can access its
staticmembers by using class name, evenprivateones - Can be imported by either
importorimport static
- member level
1.3 Create and use enumerations
-
enum-
enumis essentially a class with enumerated, predefined class instances, which can have normal class elements, evenabstractmethod. -
enumliterals 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. -
enumliterals are actually instances of theenumcreated beforehand, therefore they are static by nature. -
enumconstructors must be and areprivateimplicitly. -
enumcanimplementinterfaces but can'textendany otherclassorenum. -
Best for creating singleton objects intended for reuse. While
enumcould 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
enumconstant 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
classdefinition,extendsmust 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
-
defaultmethods are implicitlypublic. -
can only be instance methods
-
can be overridden in subclasses
-
Cannot override
java.lang.Objectmethods -
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
defaultmethod, must resolve the conflict by overriding the method. This only applies to when one of those methods isdefaultmethod.
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 { } -
staticand instance methods cannot have the same signature, and all instance methods are included:abstract,defaultandprivateones, in bothinterfaceandclass. -
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
staticmethods - can be instance methods
staticmethod must be invoked on containinginterfaceonly.
3 Functional Interface and Lambda Expressions
3.1 Define and write functional interfaces
- The only difference between a
FunctionalInterfaceand a regularinterfaceis there can be only oneabstractmethod in aFunctionalInterface. - Any
interfacewith only oneabstractmethod is considered effectively aFunctionalInterface.abstractmethods do not include inherited non-abstract instance methods fromObject, such asString toString().abstractmethods include those inherited from superinterfaces.
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
finalor 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
varused 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 foundif 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.Executorvoid 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
BlockingQueuedoes 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
addAllare 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
CyclicBarrierlimit value. CyclicBarriercan be reused by new threads after all current threads are released.
- Once the specified number of threads have each called
-
CopyOnWriteArrayList-
iterator()- Any
Iteratorestablished prior to a modification will not see the changes, but instead it will iterate over the original elements prior to the modification. - The returned
Iteratordoes 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
-
ConsoleSystem.outandSystem.errarePrintStreamobjects.
-
java.io.FileFile[] listFiles()- returning all files and directories if the given
Fileobject is a directory - returning
nullif the givenFileobject 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
-
BufferedInputStreamBufferedInputStream(InputStream in)BufferedInputStream(InputStream in, int bufferSize)
-
ObjectInputStreamObjectInputStream(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:writeappendflushclose
- Using
boolean checkError()method to check if anIOExceptionhas occurred. formatandprintfhave 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:writeappendflushclose
-
Using
boolean checkError()method to check if anIOExceptionhas occurred. -
formatandprintfhave identical behaviors.
-
-
7.3 Read and write objects by using serialization
-
java.io.Serializable- In order to serialize objects using the
java.ioAPI, the class they belong to must implement thejava.io.Serializableinterface. - Any class,
abstract,concrete, orfinal, can be markedSerializable. java.io.Serializableis a markerinterfacethat has no method.- The purpose of implementing the
Serializableinterface 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
NotSerializableExceptionif the class or one of its contained classes does not properly implement theSerializableinterface. - Use
transientkeyword to mark instance variables that you don't want to serialize. - Recommended but not mandatory,
serialVersionUIDis used forclassschema migration. AstaticfieldserialVersionUIDis 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 aserialVersionUIDfield, any change to a class will make previously serialized versions unreadable. serialVersionUIDfield should be updated anytime the instance variables in the class are changed.nullobjects can be serialized and deserialized, therefore usinginstanceoffor 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
InvalidClassExceptionwill be thrown during serialization and deserialization process. - Because of not being a part of a class instance, any
staticvariables or initialization blocks or constructors are ignored during the serialization and deserialization process.
- In order to serialize objects using the
-
ObjectInputStreamreadObject()returns anObject, therefore a cast is usually needed to restore the instance.
-
ObjectOutputStreamwriteObject(Object o)requires aSerializableobject, otherise it throws aNotSerializableException.
7.4 Use the Path interface to operate on file and directory paths
-
Optional Arguments
LinkOption.NOFOLLOW_LINKSFileVisitOption.FOLLOW_LINKSStandardCopyOption.COPY_ATTRIBUTESStandardCopyOption.REPLACE_EXISTINGStandardCopyOption.ATOMIC_MOVE
-
java.nio.file.Paths-
static Path get(String first, String... more)- equivalent to
static Path.of firstis a relative path: it would be relative to working directory.firstcould contain directory separator.morewould be appended tofirstto 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 aPathassociated 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, theisSameFilemethod 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
Pathobject has the name elements that begin atbeginIndexand extend to the element at indexendIndex - 1.
- Returns a relative path, and the returned
-
Path relativize(Path other)-
Basically it means how to reach
otherpath from the invoking path, similar to usingcdcommand 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
Paths -
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), thenpath1would be ignored and a copy ofpath2would be returned.
-
-
Path resolveSibling(Path other)- Resolve
otherpath 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
thispath. -
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
Pathand check the file system to locate the file/directory. -
Converting a relative
Pathto an absolute one, similar totoAbsolutePath() -
It throws an
IOExceptionwhen 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.
-
FilesAPI-
boolean Files.exists(Path p, LinkOption... options) -
boolean Files.isSameFile(Path p1, Path p2)-
If both
Pathobjects are equal then this method returnstrue. -
If the two
Pathobjects are associated with different providers then this method returnsfalse. -
Checking the file system to see if both
Pathobjects 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
copymethodsthrows 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_ATTRIBUTESflag 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 resultingStringarray storing all of the contents of the file in memory at once. Therefore, if the file is significantly large, you may encounter anOutOfMemoryErrortrying 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
Filesrely on these two under the hood for getting file attributes. -
BasicFileAttributeViewand its subclasses can be used to modify various file attributes.
-
-
Path Files.writeString(Path path, CharSequence csq, OpenOption... options) throws IOException- Write a
CharSequenceto 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. -
maxDepthwith 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)maxDepthmust be >= 0, and0means the starting directory itself, and1means 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
DriverConnectionStatementResultSet
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.Driverin 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-
resultSetTypeint ResultSet.TYPE_FORWARD_ONLY- default option
- cannot navigate backwards
int ResultSet.TYPE_SCROLL_INSENSITIVE- can navigate both backwards and forwards
- static view of the
ResultSetin 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
ResultSetafter the query was made to reflect the latest change - rarely used due to insufficient support from implementations, downgrade to
TYPE_SCROLL_INSENSITIVEwhen unavailable
-
resultSetConcurrencyint 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_ONLYwhen unavailable
- able to update via
-
-
-
Statement-
boolean execute(String sql) throws SQLException- Returns
trueif the first result is aResultSetobject;falseif it is an update count or there are no results.
- Returns
-
ResultSet executeQuery(String sql) throws SQLException- Returns a
ResultSetobject.
- Returns a
-
int executeUpdate(String sql) throws SQLException- Returns updated row count.
-
A
Statementautomatically closes the openResultSetwhen another SQL statement is run on the sameStatementobject.
-
-
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
trueif the new current row is valid;falseif 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
getmethods 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
PreparedStatementis executed, the DBMS can just run thePreparedStatementSQL 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
PreparedStatementintended 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
setmethods are used to supply values. - OUT parameters are return values of the stored procedure, and
getmethods 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
setwith 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
StringClassor 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
AutoCloseablevariable declaration statements, separated by semi-colon if multiple. -
As the variables are local to the
tryblock,varcan be used. -
catchandfinallyblock are both optional for try-with-resources. -
A user defined
finallyblock gets executed after the implictfinallyblock. -
Resources are closed after the
tryclause ends and before anycatch/finallyclauses. -
Resources are closed in the reverse order from which they were created.
-
Checked exceptions thrown from variable declarations must be catched in corresponding
catchblocks fortry-with-resources. -
If both
Autocloseableresources'closemethod andtryblock throw exceptions, the ones fromclosemethods 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.Closeableextendsjava.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
AutoCloseables declared- If more than one of the
closemethods throw exceptions, only the first one will be the primary exception, the rest suppressed. The first resource to close is the last one declared intrystatements. - Even if there are exceptions, all
closemethods 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, anAssertionErroris thrown. -
assertforms-
assertExpression1 -
assertExpression1 : Expression2 ;- Expression1 is a
booleanexpression. - Expression2 is an expression that has a value (cannot be a method invocation returns void).
- The
Stringrepresentation of Expression2 would be passed toAssertionError. - Expression1 can have optional enclosing parenthesis.
- Expression1 is a
-
-
By default, Java ignores assertions
-
-enableassertions/-eato 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-128to127, andCharactercaches values from0to127
11.2 Create and use generic classes, methods with diamond notation and wildcards
-
super-
supercan 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-aNumbercould 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
-
SetretainAll: Only keeps the intersection with the givenCollectionin thisSet
-
Map-
mergeMapM<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. -
toArrayObject[] 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
Consumers 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
NoSuchElementExceptionwhen no value exists.
- Throws
-
Optional.of(T value)- Throws
NullPointerExceptionwhen the parameter isnull.
- Throws
-
-
3 primitive optional classes
-
java.util.stream.OptionalIntint getAsInt()
-
java.util.stream.OptionalLonglong getAsLong()
-
java.util.stream.OptionalDoubledouble getAsDouble()
-
13.4 Perform calculations using count, max, min, average and sum stream operations
- Only primitive streams have
average,summethods. OptionalDouble average();- All
summethods return their corresponding type. countreturnslong.maxandminreturnOptional<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
requiresthe 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 correspondingprovidesclause in a hypothetical declaration of that module. An automatic module is considered tousesevery 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/servicesdirectory 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.javato declare services. - compile-time error: if more than one
providesdirective in a module declaration specifies the same service. - compile-time error: if the
withclause of a givenprovidesdirective 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.MissingResourceExceptionwill 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
#and0characters in a pattern string denote a digit. The difference between them is that if the digit is missing,#is shown as absent while0is shown as0.
- Both the
-