Java#
Basics#
Conventions, Modifiers, Variables, Data Types, Enums, Operators, Loops, Switch
Numbers, Characters, Strings, Arrays, Date/Time, Regex, Methods, I/O, Exceptions
Interfaces, Packages, Metadata & Annotations, Framework Principles, Reflection
write once, run anywhere, can run on all Java supported platforms without recompilation
four platforms for Java: Standard Edition (Java SE), Enterprise Edition (Java EE), Micro Edition (Java ME), Java FX
multi-threaded, interpreted, high performance (uses JIT compilers), distributed and dynamic
object oriented: everything is object in Java
first compiled into platform independent byte code, which is then interpreted by JVM
object: has states and behaviors, is an instance of a class
class: template/blueprint that describes state that objects of its type supports
method: a behavior, a class can contain many behaviors
Conventions#
identifiers are case sensitive and start with letter, $ or _
Upper camel case for classes and interfaces, lower camel case for methods and variables
name of the file should match the class name
Modifiers#
- Access modifiers
default: visible to the package, no modifiers
private: visible to the class only
public: visible to the world
protected: visible to the package and all subclasses
- Non-access modifiers
static: creating class methods and variables
final: finalizing the implementation of classes, methods and variables
abstract: for abstract classes and methods
strictfp: strict floating-point, to get same floating-point result on any platforms
synchronized: for threads, guarantees both atomicity and visibility, can be applied to blocks or methods
volatile: for threads, guarantees visibility, can only be applied to variables
Variables#
- Local
declared in methods, constructors or blocks and visible only within
destroyed once it exits the block
cannot use access modifiers
implemented at stack level
no default value and must be initialized
- Instance/Non-static
declared in a class, but outside a method, constructor or any block
created when object is created and destroyed as well
when object is created in the heap, space for each variable is also allocated in it
can be declared in class level before or after use
access modifiers can be given
visible for all methods, constructors and blocks in the class
have default values, but can be assigned during declaration or in constructor
can be accessed directly inside the class, but should called using
Object.Variable
in static methods
- Class/Static
declared with static keyword in class, but outside a method, constructor or block
only one copy per class, no matter how many objects are created
rarely used other than as constants
stored in static memory
created when program starts and destroyed when it stops
similar visibility to instance variables, but mostly declared public for users of the class
same default values as instance variables, and can be assigned in static initializer blocks
can be called using
ClassName.VariableName
should be all upper case when declaring as
public static final
Data Types#
- Primitive
byte: 8-bit signed
short: 16-bit signed
int: 32-bit signed
long: 64-bit signed
float: single-precision 32-bit
double: double-precision 64-bit
boolean: 1-bit true/false
char: single 16-bit Unicode
- Reference/Object
created using constructors of classes, to access objects
class objects and various type of array variables are reference data type
default value is null
- Literals
decimal, hexadecimal, octal
string
Enums#
restrict a variable to have one of only predefined values
enums are classes and should follow the conventions for classes
enum Level {LOW, MEDIUM, HIGH} Level l; // l can be only one of the 3 values
Operators#
- Arithmetic
+, -, *, /, %, ++, –
- Relational
==, !=, >, <, >=, <=
- Bitwise
&, |, ^, ~ (complement), <<, >>, >>> (zero fill right shift)
- Logical
&&, ||, !
- Assignment
=, +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |=
- Misc
?: (conditional)
instance of
(only for object reference variables, check object is of or compatible with particular type)
Loops#
while
while (true) { // do this }for
// update statement can be left blank for (int i = 0; i < 9; ++i) { if (i == 2) continue; // skip the body // do this if ( i == 4) break; // break out of loop } // foreach loop, since Java 5 for (int x : intArray) { // do this }do…while
// execute at least once do { // do this } while (true);loop control statements:
break
,continue
Switch#
values must be of an int, byte, short, char, strings and enums
value for a case must be same data type as the one in the switch, must be constant or literal
not every case needs to contain
break
int a = 2; switch (a) { case 1: // do this break; case 2: // do this break; default : // optional // do this }
Numbers#
wrapper classes such as Integer, Long, Byte, Double, Float, Short are subclasses of the abstract class Number
boxing: converting primitive data types into object
unboxing: converting wrapper object back to primitive data type
the compiler takes care of boxing and unboxing
Number
class is part ofjava.lang
packageInteger x = 5; // box int to Integer object x = x + 10; // unbox Integer to int
xxxValue()
convert value of the Number object to primitive data type and return
byte, short, int, long, float, double
x.byteValue()
compareTo()
compare Number object to the argument
two different types cannot be compared
return 1 if greater, 0 if equal, -1 if less than the argument
x.compareTo(3)
equals()
determine if Number object is equal to the argument object
argument can be of any object
return true if argument is not null and is an object of same type with same numeric value
extra requirements for Double and Float
x.equals(y)
valueOf()
: return relevant Number Object holding the value of the argument passed, argument can be primitive data type, String, etc.Integer.valueOf(9); // 9 Double.valueOf(9); // 9.0 Float.valueOf("80"); // 80.0 // 16 is called radix, to decide the value of returned Integer based on the String Integer.valueOf("444", 16); // 1092
toString()
: to get String object with value of Number object, take primitive data type as an argument and return String object, x.toString() or Integer.toString(12)
parseXxx()
: to get primitive data type of certain String, is a static method and can have one argument or twoInteger.parseInt("9"); // 9 Double.parseDouble("5") // 5.0 Integer.parseInt("444,", 16) // 1092
abs()
: return absolute value of argument, that can be int, float, long, double, short, byte, Math.abs(-8) return 8
ceil()
: return smallest integer greater than or equal to the argument,Math.ceil(100.82)
return101.0
floor()
: return largest integer less than or equal to the argument,Math.floor(100.82)
return 100.0
rint()
: return integer that is closest in value to the argument,Math.rint(100.82)
return 101.0 and Math.rint(100.20) return 100.0
round()
: return closest long or int,Math.round(100.5)
return101.0
min()
: return smaller of two arguments, which can be int, float, long, double,Math.min(1.3, 2)
return1.3
max()
: return maximum of two arguments, which can be int, float, long, double,Math.max(1.3, 2)
return2.0
exp()
: return e to the power of the argument,Math.exp(2)
is e<sup>2</sup> andMath.E
return Euler’s number
log()
: return natural logarithm of argument,Math.log(Math.E)
return1.0
pow()
: return value of first argument raised to the power of second,Math.pow(2, 2)
return 4.0
sqrt()
: return square root of argument,Math.sqrt(2)
sin()
: return sine of specified double value,Math.sin(2.0)
cos()
: return cosine of specified double value:Math.cos(2.0)
tan()
: return tangent of specified double value,Math.tan(2.0)
asin()
: return arcsine of specified double value,Math.asin(Math.sin(2.0))
acos()
: return arccosine of specified double value,Math.acos(Math.cos(2.0))
atan()
: return arctangent of specified double value,Math.atan(Math.tan(2.0))
atan2()
: convert rectangular coordinates(x, y)
to polar coordinate(r, theta)
,Math.atan2(1, 2)
toDegrees()
: convert argument to degrees,Math.toDegrees(45.0)
toRadians()
: convert argument to radians,Math.toRadians(45.0)
random()
: to generate random number between0.0
and1.0
(exclusive),Math.random()
Characters#
wrapper class for primitive data type char
has methods to manipulate characters
autoboxing: compiler auto convert to object if necessary
Character ch = 'a'; char c = test('x'); // primitive 'x' is autoboxed and return is unboxed
- escape sequences
t (tab), b (backspace), n, r (carriage return), f (form feed), ', ", \
isLetter()
: true if char is a letter,Character.isLetter('a')
isDigit()
: true if char is a digit,Character.isDigit('5')
isWhitespace()
: true if char is space, tab or new line,Character.isWhitespace('\t')
isUpperCase()
: true if char is uppercase,Character.isUpperCase('A')
isLowerCase()
: true if char is lowercase,Character.isUpperCase('a')
toUpperCase()
: return uppercase form,Character.toUpperCase('a')
toLowerCase()
: return lowercase form,Character.toLowerCase('A')
toString()
: return one-character String object,Character.toString('a')
Strings#
to create and manipulate strings
has 11 constructors to provide initial value using different sources, such as array of chars
immutable, created string object cannot be changed, use String Buffer and String Builder classes if needed
String s = "hello"; // create using char array char[] charArray = { 'h', 'e', 'l', 'l', 'o'}; String s = new String(charArray);
length()
: return length of string,s.length()
concat()
: concatenate two strings and return new string,s1.concat(s2)
or"hello".concat("world")
format()
: create reusable formatted stringString s; s = String.format("%.3f, %d, %s", floatVar, intVar, stringVar);
charAt()
: return char at index,s.charAt(8)
compareTo()
: compare string to another object/string, return 0 if equal, < 0 if argument is greater, > 0 if argument is less than, s1.compareTo(s2)
compareToIgnoreCase()
: compare two strings, ignoring case,s1.compareToIgnoreCase(s2)
contentEquals()
: return true if and only if this String represents same sequence of chars as specified in StringBuffer, s.contentEquals(new StringBuffer())
copyValueOf()
: return string as in the argument array,s1.copyValueOf(char[] s2)
ors1.copyValueOf(char[] s2, startIndex, length)
endsWith()
: check if string ends with specified suffix,s.endsWith("some string")
equals()
: return true if equal,s1.equals(s2)
equalsIgnoreCase()
: return true if equal ignoring case,s1.equalsIgnoreCase(s2)
getBytes()
: encodes string into byte array,s.getBytes("UTF-8")
ors.getBytes("ISO-8859-1")
getChars()
: copy chars from string to char array,s.getChars(start, end, dst, dstBegin)
hashCode()
: return hash code for string,s.hashCode()
indexOf()
: return index of first occurrence of char or substring, -1 if not found,s.indexOf('a')
ors.indexOf("abc", startIndex)
intern()
: return canonical representation,s.intern()
,s.intern() == t.intern()
if and only if s.equals(t), interning ensure all strings having same contents share same memory
lastIndexOf()
: return index of last occurrence of char or substring, -1 if not found,s.lastIndexOf('a')
ors.lastIndexOf("abc", startIndex)
matches()
: return if string match regex or not,s.matches("*abc*")
, same asPattern.matches(regex, str)
regionMatches()
: check if two string regions are equal,s1.regionMatches(boolean ignoreCase, startIndex, s2, startIndexIns2, numOfCharToCompare)
replace()
: return new string after replacing all occurrences of char,s.replace(old, new)
replaceAll()
: return new string after replacing each substring that matches regex,s.replaceAll(regex, replaceWithThis)
replaceFirst()
: return new string after replacing first substring that matches regex,s.replaceFirst(regex, replaceWithThis)
split()
: return array of strings after splitting that matches regex,s.split(",")
ors.split(",", limitToReturn)
starsWith()
: check if string starts with specified prefix,s.starsWith("abc")
ors.starsWith("abc", startIndex)
subSequence()
: return new character sequence,s.subSequence(startIndex, endIndex)
subString()
: return new substring,s.subString(start)
ors.subString(start, end)
toCharArray()
: return new char array,s.toCharArray()
toLowerCase()
: convert all chars to lower,s.toLowerCase()
, which is same ass.toLowerCase(Locale.getDefault())
toString()
: return itself a string,s.toString()
toUpperCase()
: converts all chars to upper,s.toUpperCase()
, which is same ass.toUpperCase(Locale.getDefault())
trim()
: return copy string after removing leading and trailing whitespace,s.trim()
valueOf()
: return string representation of argument,String.valueOf(new long(123))
Arrays#
as arrays are reference types and can only be dynamically allocated, they are objects on the heap
int[] myArray = new int[9]; // preferred way int[] myArray = {1, 2, 3}; // initialized int myArray[];
passing arrays to methods
public void printArray(int[] myArray);returning arrays from methods
public int[] printArray(int[] myArray);
binarySearch()
: find number and return index of sorted array,Arrays.binarySearch(myArray, numToSearch)
equals()
: true if two arrays have same number of elements,Arrays.equals(a1, a2)
fill()
: set specified value to each element,Arrays.fill(myArray, 1)
sort()
: sort the array in ascending order,Arrays.sort(myArray)
Date/Time#
in
java.util
package, use [Java8 Date/Time](#java8-date-time) for updated API
Date()
: initialize object with current date and time
Date(long ms)
: accept argument of number of milliseconds since midnight Jan 1, 1970
after()
: true if this Date object is later than argument,d1.after(d2)
before()
: true if this Date object is earlier than argument,d1.before(d2)
clone()
: shallow copy of this Date object,Object d2 = d1.clone()
compareTo()
: 0 if equal, < 0 if this Date is earlier, > 0 if this Date is later,d1.compareTo(d2)
equals()
: true if same time and date,d1.equals(d2)
getTime()
: return number of ms since Jan 1, 1970,d.getTime()
hashCode()
: return hash code for this Date,d.hashCode()
setTime()
: set time and date specified by in ms from Jan 1, 1970,d.setTime(long time)
toString()
: convert and return this Date as string,d.toString()
- SimpleDateFormat
concrete class to format and parse dates
allow user-defined patterns for date-time formatting using date format codes
Date d = new Date(); SimpleDateFormat ft = new SimpleDateFormat("E yyyy.MM.dd 'at' hh:mm:ss a zzz"); System.out.println(ft.format(d)); // parse(), parse string according to the format stored in SimpleDateFormat object Date t = ft.parse(input);can use
printf
to format date-time using date-time conversion charactersString s = String.format("%tc", d); System.out.printf(s); // can use index to be formatted System.out.printf("%1$s %2$tB", "Date: ", d); // can use < flag System.out.printf("%s %tB %<te", "Date: " d);
Thread.sleep()
: sleep for any period,Thead.sleep(time in ms)
System.currentTimeMillis()
: get current time in ms, used for measuring elapsed time
- GregorianCalendar
concrete implementation of Calendar class in Gregorian
GregorianCalendar()
: initialize default GregorianCalendar using current time in default time zone and default locale orGregorianCalendar(int yr, int month, int date)
orGregorianCalendar(int yr, int month, int date, int hr, int min, int second)
orGregorianCalendar(Locale l)
orGregorianCalendar(TimeZone zone)
orGregorianCalendar(TimeZone z, Locale l)
add(int field, int amnt)
: add amount of time to given field,c.add(GregorianCalendar.MONTH, 2)
computeFields()
: converts UTC as ms to time field values
computeTime()
: Overrides Calendar Converts time field values to UTC as ms
equals()
: true if equal,c1.equals(c2)
get()
: get value of given time field,c.get(Calendar.YEAR)
getActualMaximum()
: get max value a field can have,c.getActualMaximum(Calendar.YEAR)
getActualMinimum()
: get minimum value a field can have,c.getActualMinimum(Calendar.YEAR)
getGreatestMinimum()
: get highest minimum value of a field,c.getGreatestMinimum(Calendar.AM_PM)
getGregorianChange()
: get date change from Julian Calendar to Gregorian
setGregorianChange()
: set Gregorian Calendar change date,c.setGregorianChange(new Date())
getLeastMaximum()
: get lowest max value of a field,c.getLeastMaximum(Calendar.PM)
getMaximum()
: get max value for a field,c.getMaximum(Calendar.YEAR)
getTime()
: get this Calendar current time,c.getTime()
getTimeInMillis()
: get this Calendar current time in ms,c.getTimeInMillis()
getTimeZone()
: return TimeZone object,TimeZone t = c.getTimezone()
hashCode()
: get hash code,c.hashCode()
isLeapYear()
: true if argument is leap year,c.isLeapYear(2000)
roll()
: add/subtract single unit of time on given field,c.roll(Calendar.YEAR, true)
increase year by one,c.roll(Calendar.YEAR, false)
decrease year by one
set()
: set time field with given value,c.set(Calendar.YEAR, 22)
orc.set(yr, month, date)
orc.set(yr, month, date, hr, min)
orc.set(yr, month, date, hr, min, second)
setTime()
: set this Calendar current time with given Date object,c.setTime(Date())
setTimeinMillis()
: set this Calendar current time in given long value
setTimeZone()
: set time zone with given TimeZone object,c.setTimeZone(TimeZone)
toString()
: return string representation of this Calendar
Regex#
in
java.util.regex
package
- Pattern Class
compiled representation of regex, no public constructors
must invoke
compile()
, which returns Pattern object
- Matcher Class
interpret pattern and preform match operations on input string
no public constructors, must invoke
matcher()
on Pattern object
groupCount()
: return number of capturing groups, does not include group 0,m.groupCount()
capturing group 0 represents entire expression
start()
: return start index of previous match,m.start()
orm.start(int group)
, which returns start index of subsequence captured by given group
end()
: return offset after the last char matched,m.end()
orm.end(int group)
, which returns offset after the last char of subsequence captured by given group
lookingAt()
: true if pattern is matched, starting at the beginning,m.lookingAt()
find()
: true if next subsequence of matched pattern is found,m.find()
orm.find(int start)
, which find next subsequence at given index
matches()
: true if entire region matches the pattern
appendReplacement(StringBuffer, String)
: non-terminal append and replace return Matcher object,m.appendReplacement()
appendTail(StringBuffer)
: terminal append and replace, return StringBuffer object
replaceAll(String)
: replace every subsequence that matches with given string, return String object
replaceFirst(String)
: replace first subsequence,m.replaceFirst()
, return String
quoteReplacement(String)
: return literal replacement String for specified String, act as intermediate in replace methods,m.quoteReplacement()
- PatternSyntaxException
unchecked exception that indicates syntax error in regex
getDescription()
: return description of error
getIndex()
: return error index
getPattern()
: return incorrect regex pattern
getMessage()
: return description of syntax error and index, incorrect regex pattern and visual indication of error index within patternString line = "hello abc hello"; String pattern = "(.*)(abc)(.*)"; Pattern p = Pattern.compile(pattern); Matcher m = p.matcher(line); if (m.find()) { // pattern found } else { // pattern not found }
Methods#
modifier: define access type of the method, optional
void
: method does not return any value// modifier returnType methodName (ParameterList) public static int myMethod (int a) {}
pass by value
public static void swap(int a, int b) { int tmp = a; a = b; b= tmp; } public static void main(String[] args) { int x = 1, y = 2; swap(x, y); // calling swap does not change x and y values }
- method overloading
a class having two or more methods with same name but different parameters
not same as overriding, which has same name, type, number of parameters, etc.
overloading make program more readable
public static int myFunc(int a) {} public static double myFunc(double a) {}
- command-line arguments
stored as strings in String array passed to
main()
public static void main(String[] args) { System.out.println(args.length); }
- this
used as reference to the object of current class, only within instance method or constructor
can refer the members of class such as constructors, variables and methods
to differentiate instance variables from local variables that have same names
class MyClass { int x; /* explicit constructor invocation: calling one type of constructor, such as parameterized constructor or default from other in a class */ MyClass() { this(2); // invoke MyClass(int x) } MyClass(int x) { this.x = x; } }
- var-args
JDK 1.5 enables to pass variable number of same type arguments to a method
public static void MyFunc(int... numbers) { System.out.println(numbers.length); } // both valid MyFunc(1, 2, 3, 4); MyFunc(new int[] {1, 2, 3, 4});
I/O#
in
java.io
packageInputStream: read data from source
OutputStream: write data to destination
classes of streams: File, ByteArray, Filter (Buffered, Data), Object
- byte streams
for I/O of 8-bit bytes
FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("input.txt"); out = new FileOutputStream("output.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } }
- character streams
for I/O of 16-bit unicode
FileReader
andFileWriter
useFileInputStream
andFileOutputStream
internally, but read and write 2 bytes at a timeFileReader in = null; FileWriter out = null;
- standard streams
standard input: input from user,
System.in
standard ouput: output from program to user,
System.out
standard error: output error from program to user,
System.err
InputStreamReader cin = null; try { cin = new InputStreamReader(System.in); System.out.println("Enter input, 'q' to quit."); char c; do { c = (char) cin.read(); System.out.print(c); } while (c != 'q'); } finally { if (cin != null) cin.close(); }
- FileInputStream
for reading data from files
objects can be created, and several types of constructors available
all methods throw IOException
close()
: close file input stream,in.close()
finalize()
: protected method, clean the connection to the file, ensureclose()
is called when there are no more references to the stream
read(int r)
: read specified byte of data from InputStream, return the next byte of data or -1 if end of file
read(byte[] r)
: read r.length bytes from InputStream into array, return total number of bytes read or -1 if end of file
available()
: return number of bytes that can be read from the file input streamInputStream in = new FileInputStream("filename"); // using File object File f = new File("filename"); InputStream in = new FileInputStream(f);
- FileOutputStream
to create file and write data into it
will create new file if not exist, before opening it for output
all methods throw IOException
close()
: file file output stream,out.close()
finalize()
: protected method, clean the connection to the file, ensureclose()
is called when there are no more references to the stream
write(int w)
: write specified byte to output stream
write(byte[] w)
: write w.length bytes from byte array to OutputStreamOutputStream out = new FileOutputStream("filename") // using file object File f = new File("filename") OutputStream out = new FileOutputStream(f);
- Directories
can use File object to create directories and list files in a directory
mkdir()
: create a directory, return true on success and false on failure, which means path specified already exists or entire path does not exist yet,d.mkdir(/foo)
mkdirs()
: create both directory and parents of the directory,d.mkdirs(/foo/bar)
path separators of UNIX and Windows are resolved correctly by Java
list()
: list all filesFile d = new File("/foo/bar"); d.mkdirs(); String[] paths = d.list(); for (String p : paths) { System.out.println(p); }
- ByteArrayInputStream
allow buffer in memory to be used as InputStream, byte array as input source
ByteArrayInputStream(byte[] a)
orByteArrayInputStream(byte[] a, int off, int len)
: constructor take byte array, first byte to be read and number of bytes to be read
read()
: read next byte from InputStream, return int as next byte of data, -1 if end of file
read(byte[] r, int off, int len)
: read from input stream starting from off till len into an array, return total number of bytes read or -1 if end of file
available()
: return number of readable bytes from input stream
mark(int r)
: set current marked position in the stream, max limit of readable bytes as argument
skip(long n)
: skip n numbers of bytes from stream, return actual number of bytes skippedByteArrayInputStream bInput = new ByteArrayInputStream(byte[] b); for (int i = 0; i < 1; ++i) { while ((c = bInput.read()) != -1) System.out.println(Character.toUpperCase((char) c)); bInput.reset(); }
- ByteArrayOutputStream
create buffer in memory, all data sent to the stream is stored in the buffer
ByteArrayOutputStream()
: create ByteArrayOutputStream having buffer of 32 bytes
ByteArrayOutputStream(int s)
: having buffer of given size
reset()
: reset number of valid bytes of the stream to zero, all output in the stream is discarded
toByteArray()
: return newly allocated byte array, with size and content of current output stream
toString()
: return buffer content as string
write(byte[] b)
: write given array to output stream
write(byte[] b, int off, int lent)
: write len of bytes starting from off
writeTo(OutputStream o)
: write entire content of this Stream to given streamByteArrayOutputStream bOutput = new ByteArrayOutputStream(12); while (bOutput.size() != 10) bOutput.write("hello".getBytes()); byte[] b = bOutput.toByteArray();
- DataInputStream
to read primitives
DataInputStream(InputStream in)
: create InputStream objectall methods throw IOException
read(byte[] b)
: read bytes from input stream into the byte array, return total number of bytes read or -1 if end of file
read(byte[] b, int off, int len)
: read len of bytes starting from off
readBoolean()
,readByte()
,readShort()
,readInt()
: read bytes from the contained InputStream, return next two bytes of InputStream as specific primitive type
readLine()
: read next line of text from InputStream, read successive bytes by converting each into char, until line terminator or end of file, return chars read as StringDataInputStream dataIn = new DataInputStream(new FileInputStream("filename")); while (dataIn.available() > 0) { System.out.print((char) dataIn.read()); }
- DataOutputStream
write primitives to output source
DataOutputStream(OutputStream out)
: create OutputStream objectall methods throw IOException
write(byte[] w)
: write number of bytes to output stream, return number bytes written to buffer
write(byte[] w, int off, int len)
: write len bytes from byte array at starting point off
writeBoolean()
,writeByte()
,writeShort()
,writeInt()
: write specific primitive type data into output stream as bytes
flush()
: flush data output stream
wrtieBytes(String s)
: write the string to output stream as sequence of bytes, by discarding each char high eight bitsDataOutputStream dataOut = new DataOutputStream(new FileOutputStream("filename")); dataOut.writeUTF("hello");
- File
class to create files and directories, file searching, file deletion, etc.
File object represents actual file/dir on the disk
File(File parent, String child)
: create File instance from parent abstract pathname and child pathname
File(String pathname)
: create File instance by converting given pathname into abstract pathname
File(String parent, String child)
: create File instance from parent and child pathname string
File(URI uri)
: create File instance by converting given URI into abstract pathname
getName()
: return name of file or dir
getParent()
: return pathname’s parent or null if parent dir not exist
getParentFile()
: return abstract pathname of pathname’s parent, null if parent dir does not exist
getPath()
: return pathname string
isAbsolute()
: true if pathname is absolute
getAbsolutePath()
: return absolute pathname string
canRead()
: true if and only if file exists and can be read
canWrite()
:true if and only if file exists and is allowed to write
exists()
: true if file or dir exists
isDirectory()
: true if and only if pathname exists and is a dir
isFile()
: true if and only if file exists and is normal file, which is not a dir and satisfy other system-dependent criteria
lastModified()
: return last modified time in ms since epoch (Jan 1, 1970)
length()
: return length of file, return unspecified value if pathname is dir
createNewFile()
: auto create new, empty file only if it does not exist, return true if file not exist and created successfully, throw IOException
delete()
: delete file or dir, dir must be empty, return true if success
deleteOnExit()
: request file or dir be deleted when the VM terminates
list()
: return array of strings of files and dirs
list(FilenameFilter f)
: return array of strings of files and dirs that satisfy given filter
listFiles()
orlistFiles(FileFilter)
: return array of File objects
mkdir()
: create dir, return true if dir is created
mkdirs()
: create dir, with necessary parent dirs if not exist, return true if dir with parent dirs is created
renameTo(File f)
: rename the file, return true if success
setLastModified(long time)
: set last modified time of file or dir, return true if success
setReadOnly()
: mark file or dir read only, return true if success
createTempFile(String prefix, String suffix)
orcreateTempFile(String prefix, String suffix, File dir)
: create empty file in default tmp or specified directory, using prefix and suffix to generate name, return abstract >pathname of created empty file
compareTo(File)
: compare two abstract pathnames lexicographically, return 0 if equal, < 0 if argument is greater, > 0 if argument is less
compareTo(Object)
: compare abstract pathname to another object, return 0 if equal, < 0 if argument is greater, > 0 if argument is less
equals(Object)
: true if argument is not null and same file or dir
toString()
: return pathname string, which is just the string returned bygetPath()
File f = null; f = new File("filename"); boolean bool = f.canExecute(); String s = f.getAbsolutePath();
- FileReader
inherits from InputStreamReader, used to read streams of characters
FileReader(File)
,FileReader(FileDescriptor)
,FileReader(String)
: create FileReader with given argument to read from
read()
: read single char, return int of char read, throws IOException
read(char[] c, int off, int len)
: read chars into array, return number of chars readFileReader fr = new FileReader(new File("filename")); char[] a = new char[50]; fr.read(a); // read content to the array fr.close();
- FileWriter
inherits form OutputStreamWriter, used to write streams of characters
FileWriter(File)
,FileWriter(File,boolean append)
,FileWriter(FileDescriptor)
,FileWriter(String)
,FileWriter(String, boolean append)
: create FileWriter object, accept boolean to append data or not
write(int c)
: write single char, throw IOException
write(char[] c, int off, int len)
: write len of array of chars starting from off
write(String s, int off, int len)
: write len of String starting from offFileWriter writer = new FileWriter(new File("filename")); writer.write("hello"); writer.flush(); writer.close();
Exceptions#
in
java.lang.Exception
packageexceptions should be handled not to let programs terminate abnormally
exceptions can be caused by users, programmer or other resources errors
- checked exceptions
checked by the compiler at compilation time, aka compile time exceptions
cannot be ignored and must be taken care of immediately
e.g
FileNotFoundException
when creatingFileReader
object and the file doesn’t exist
ClassNotFoundException
,CloneNOtSupportedException
,IllegalAccessException
InstantiationException
,InterruptedException
,NoSuchFieldException
NoSuchMethodException
- unchecked exceptions
occurs at the time of program execution, aka runtime exceptions
ignored at compile time, such as bugs and logic errors
e.g
ArrayIndexOutOfBoundsException
only shows up when running the program
ArithmeticException
,ArrayIndexOutOfBoundsException
,ArrayStoreException
ClassCastException
,IllegalArgumentException
,IllegalMonitorStateException
IllegalStateException
,IllegalThreadStateException
,IndexOutOfBoundsException
NegativeArraySizeException
,NullPointerException
,NumberFormatException
SecurityException
,StringIndexOutOfBounds
,UnsupportedOperationException
- errors
not exceptions, but beyond control of user or programmer
ignored at compile time, generated by runtime environment
e.g a stack overflow, JVM out of memory
- JVM exceptions
thrown by the JVM
e.g
NullPointerException
,ArrayIndexOutOfBoundsException
,ClassCastException
- programmatic exceptions
thrown by the application or API
e.g
IllegalArgumentException
,IllegalStateException
Exception
andError
classes are subclasses ofThrowable
class
IOException
andRuntimeException
are two main subclasses ofException
class
- Throwable
getMessage()
: return detail message about exception
getCause()
: return cause of exception as Throwable object
toString()
: return name of the class fromgetMessage()
printStackTrace()
: print result oftoString()
with stack trace toSystem.err
getStackTrace()
: return array with elements on stack trace, index 0 being the top of the call stack
fillInStackTrace()
: fill the stack trace of this Throwable object with current stack trace, return Throwable object
- try/catch
placed around the code that might generate exception
code within the block is called protected code
every
try
block should be immediately followed bycatch
orfinally
, which is optionalone
try
can have multiplecatch
blockscode in
finally
block always execute, even if exception does not occurcan use
finally
to do cleanup, no matter what happens in the protected code, e.g closing a filetry { // protected code } catch (Exception e) { System.out.println(e); } catch (ExceptionType1 | ExceptionType2 e) { // can handle multiple exception in single catch block since Java 7 System.out.println(e); } finally { // at the end of catch blocks // always execute }
- throws/throw
used when a method does no handle a checked exception
throws
: used to postpone the handling of checked exception
throw
: used to invoked exception explicitly// can declare to throw more than one exception public void MyFunc() throws Exception1, Exception2 { throw new Exception3(); }
- try-with-resources
automatic resource management, introduced in Java 7
auto close the resources used within try/catch block
a class should implement
AutoCloseable
interface to be used withcan have multiple classes in
try
statement, which will be closed in reverse orderresources declared in
try
statement are instantiated before the start oftry
block, and are implicitly declared asfinal
try (FileReader fr = new FileReader("filename")) { // no need to invoke fr.close(); } catch (IOException e) { e.printStackTrace(); }
- custom exceptions
must be a child of
Throwable
need to extend
Exception
class for a checked exception that is auto enforced by the Handle or Declare Ruleneed to extend
RuntimeException
for runtime exceptionpublic class MyException extends Exception {}
Interfaces#
contract between objects on how to communicate with each other, a reference type
define methods a subclass should use, but implementation is up to the subclass
can contain constants, default and static methods and nested types
only default and static methods can have method body
all methods need to be defined in the class unless the class is abstract
cannot be instantiated, no constructors, all methods are abstract
cannot contain instance fields, except declared
static
andfinal
interface is implicitly abstract, methods in it are implicitly abstract and public, so the keywords can be omitted
cannot declare checked exceptions other than the ones declared by the interface
must maintain signature of interface method and same return type or subtype
interface MyInterface { void foo(); } interface OtherInterface extends MyInterface { // inherits 'foo()' void bar(); } class A implements OtherInterface { // 'A' must implement both 'foo()' and 'bar()' // must be public public void foo() {} public void bar() {} } // no need to implement 'foo()' abstract class B implements MyInterface {}
a class can extend only one class but implement more than one interface
interface MyInterface { void foo(); } interface OtherInterface { void bar(); } class A implements MyInterface, OtherInterface { public void foo() {} public void bar() {} }an interface can extend multiple interfaces
interface MyInterface { void foo(); } interface OtherInterface { void bar(); } interface MultipleInterface extends MyInterface, OtherInterface {}
- tagging interface
interface with no methods in it
to create common parent among group of interfaces
to add data type to a class: implementing class does not need to define any methods, but becomes an interface type through polymorphism
Packages#
categorizing the classes, interfaces, enumerations and annotation types for easy search and usage
to prevent naming conflicts and to control access
a grouping of related types for access protection and namespace management
e.g
java.lang
,java.io
package statement should be the first line in source file
only one package statement per source file
if no package statement is used, will be placed in current default package
use
javac -d DEST file.java
to compile programs with package statements// MyPackage.java package MyPackage; interface MyInterface { void foo(); } // MyClass.java package MyPackage; public class MyClass implements MyInterface { public void foo() {} public static void main(String[] args) {} }
classes in the same package find each other without any special syntax
if different packages, use
import
orpackageName.Class
// A.java package MyPackage; public class A {} // B.java package OtherPackage; public class B {} // MyClass.java package MyPackage; import OtherPackage.*; public class MyClass implements MyInterface { public static void main(String[] args) { A a = new A(); // can use 'A' class since same package B b = new B(); // ok, since import statement is used // or OtherPackage.B b = new B(); // if import statement is not used } }the name of the package must match the directory structure of bytecode
when compiled there will be separate files for each class, interface and enumerations
// A.java package foo.bar.MyPackage; public class A {} class B {} // './foo/bar/MyPackage/A.class' // './foo/bar/MyPackage/B.class'compiled
.class
files and.java
source files do not need to have same pathcan give access to others without revealing source files
- CLASSPATH
full path of the classes directory can be set with
CLASSPATH
system variableif class path is
path/classes
and package isfoo.MyPackage
, compiler and JVM will look for.class
files inpath/classes/foo/MyPackage
can have several class paths separated by semicolon (Windows) or colon (Unix)
by default, compiler and JVM will search current directory and JAR files containing Java platform classes
Metadata & Annotations#
metadata and annotations allow developers to encapsulate crucial information about classes, methods, and other components
- Metadata
help with complicated processes such as converting Java entities to XML files or databases
e.g. conversion between Java
camelCase
and databasesnake_case
XML is used to manage metadata, as shown in Java Persistence API (JPA)
the XML file is dynamically interpreted at runtime, and serves as a blueprint and a channel for generating real-time metadata
<entity>
: map Java class
<table>
: define table name in the database associated with the entity
<attributes>
: describe class attributes
<id>
: primary key attribute
<basic>
: non-composite attributes
<column>
: details for database mappingSpring and Jakarta EE offer code-centric approach to defining metadata, e.g.
@Entity
,@Table(name="table")
,@Column(name="col")
public class Person { private String id; private String name; }<entity class="entity.Person" name="Person"> <table name="Person"/> <attributes> <id name="id"/> <basic name="name"> <column name="NAME" length="100"/> </basic> </attributes> </entity>
- Annotations
introduced in Java 5 through Java Specification Request (JSR)
remove the need for a separate configuration file
essential information can be within the Java class
can read and process annotations dynamically at runtime using reflection, or statically at build time using tools such as Java annotation processor
Framework Principles#
- Framework
provide pre-defined components, tools, and design patterns for app development
e.g. Spring, Hibernate, JSF
simplify development, encourage code reuse, and maintain best practices
always consider the trade-off between existing framework and creating a custom one
convention over configuration reduce the need for explicit configuration when developers follow to established patterns
documentation helps users understand, aiding in adoption and reducing learning curve
testing ensures reliability and robustness of the framework
- API Design
significantly influence framework usability and adoption
Declarative API: focus expressing the desired outcome and promote readability
Imperative API: step-by-step approach, giving more control
need to ensure ease of use and long-term maintainability
- Executability
consider to choose reflection, avoid reflection for efficiency within JVM, or execute outside JVM, such as building native images
reflection gives dynamic capabilities, but have performance cost
- Service Provider Approach
allow developers to extend or modify framework behaviour
promote plug-and-play (PnP), enabling users to add more functions or customise the framework without changing the core code base
Reflection#
inspect and manipulate classes, methods and fields at runtime
enable dynamic class instantiation and configuration in dependency injection (DI) containers, object-relational mapping (ORM) and testing frameworks
enable dynamic loading and manipulation of objects and classes in serialisation and deserialisation libraries, GUI development tools, and Java core libraries
Introspection: enabling programs to examine and adapt to their structure
can have performance cost, slower execution times, and reduce code safety, as errors might only be discovered at runtime
reflective code can be challenging to read and maintain
reflection might be platform-dependent, and certain operations may behave differently on different JVMs
- Reflection in a Framework
framework initialises and reflection engine is loaded
annotations are processed during code compilation
as classes are loaded into runtime environment, reflection engine is aware of classes and their structure
reflection engine reads and interprets the annotations
reflection engine generates a dependency tree, outlining the relationship between classes, methods and fields
code is dynamically executed based on the dependency tree
- Dynamic Proxy
enable creation of objects at runtime, especially when the structure of objects or interfaces is not known until runtime
can reduce boilerplate code, and inject custom logic, as they can intercept method invocations
can have execution delay compared to direct method calls
as dynamic proxies are interface-based, they may limit where class-based proxies may be more fitting
Annotation Processors#
static analysing and generating code based on annotations at compile time
improved performance, early error detection, and code maintainability than reflection
- Processor in a Framework
configuration is loaded is parsed by identifying annotations and classes for metadata
dependencies are analysed based on the loaded classes dynamically
dependency tree is built and pre-processed to create the framework
the app is packaged by ensuring the absence of reflection, generating bytecode or native app
- Including in Maven Project
provided
scope: processor will be available during compilation, but not bundled with the final app
annotationProcessorPaths
with Maven compiler plugin: configuration-centric approach, specifying annotation processors for the compiler plugin
Classes#
Class Variables, Constructors, Singleton Class, Class Rules, Import
Abstract Class, Non-static Nested Class, Static Nested Class
blueprint to create objects
use
new
keyword to create new objectsa top level class cannot be associated with
private
access modifiervariables of a class can have another class as its member
nested class: class written within a class, Non-static nested class and Static nested class
outer class: the class that holds the inner class
public class MyClass {
int x;
public int getX() {
return x;
}
public static void main (String []args) {
MyClass c1 = new MyClass();
System.out.println(c1.x); // accessing instance variable
}
}
Class Variables#
local: defined inside methods, constructors or blocks
instance: within a class, outside any method, can be accessed from any method
class: declared within a class, outside any method, with static keyword
public static void main (String []args) { MyClass c1 = new MyClass(); System.out.println(c1.x); // accessing instance variable }
Constructors#
at least one constructor is invoked each time a new object is created
should have the same name as the class
a class can have more than one constructor
no explicit return type
compiler builds a default one if not defined explicitly, initializing member variables to zero
public class MyClass { public MyClass () {} // constructor public MyClass (int y) {} // constructor with one parameter }
Singleton Class#
can only create one instance of the class
Class Rules#
only one public class per source file, but can have multiple non-public classes
public class name should be the same as file name
package statement should be the first in the source file if the class is declared inside a package
import statements must be written between package statements and class declaration
import and package statements will imply to all classes present in the source file
cannot declare different import or package statements to different classes in the file
Import#
to give the compiler the location to find a particular class
import java.io.*; // load all classes in directory java_installation/java/io
Abstract Class#
contains
abstract
keyword in declarationmay or may not have abstract methods, methods without body
class must be abstract if it has at least one abstract method
abstract classes cannot be instantiated
must inherit from another class with implementations of abstract methods to use abstract class
all abstract methods must be implemented once inherited
abstract class A { public abstract void hello(); } class B extends A { public void hello() { } // 'B' must implement 'hello()' or itself must be abstract } A a = new A(); // error A a = new B(); // ok
Non-static Nested Class#
also called inner class, used for security mechanism
inner class can be made
private
and used to access private members of a classif
private
, it cannot be accessed from object outside the classclass Outer { private class Inner { public void innerHello() { System.out.println("Hello from Inner Class"); } } void callInner() { Inner i = new Inner(); i.innerHello(); } } public class MyClass { public static void main(String[] args) { Outer o = new Outer(); o.callInner(); } }
can use inner class methods to access private members of a class
class Outer { private int outerNum = 9; public class Inner { public int getOuterNum() { return outerNum; } } } public class MyClass { public static void main(String[] args) { Outer o = new Outer(); Outer.Inner i = o.new Inner(); System.out.println(i.getOuterNum()); } }
- Method-local Inner Class
class within a method, scope of the class is restricted within the method
can only be instantiated within the method, where it is defined
public class MyClass { void myFunc() { class MethodInner { public void helloFromMethodInner() { System.out.println("Hello from method inner class"); } } MethodInner mi = new MethodInner(); mi.helloFromMethodInner(); } public static void main(String[] args) { MyClass m = new MyClass(); m.myFunc(); } }
- Anonymous Inner Class
inner class without class name, declare and instantiate at the same time
used to override the method of a class or interface
abstract class AnonymousInner { public abstract void anonymousInnerMethod(); } public class MyClass { public static void main(String[] args) { AnonymousInner i = new AnonymousInner() { public void anonymousInnerMethod() { System.out.println("Hello from AnonymousInner"); } }; i.anonymousInnerMethod(); } }
can pass anonymous inner class as argument to a method that accepts object of an interface, abstract class or a concrete class
interface MyInterface { void foo(); } public class MyClass { public void bar(MyInterface i) { i.foo(); } public static void main(String[] args) { MyClass m = new MyClass(); m.bar(new MyInterface() { public void foo() { System.out.println("hello from foo"); } }); } }
Static Nested Class#
static member of outer class
can be accessed without instantiating the outer class
does not have access to instance variables and methods of outer class
can access static members of outer class, but must use outer object to access non-static members
public class Outer { int outer = 1; static staticOuter = 2; static class StaticNested { public void foo(Outer o) { System.out.println("hello from static inner"); // can access static member of outer class normally System.out.println(staticOuter); // need outer object to access non-static members System.out.println(o.outer); } } public static void main(String[] args) { Outer o = new Outer(); StaticNested i = new StaticNested(); // can also instantiate with 'Outer.StaticNested i = new Outer.StaticNested()'; i.foo(o); } }
Object Oriented Programming#
four fundamentals of OOP: abstraction, encapsulation, inheritance and polymorphism
Inheritance#
one class acquiring properties of another
subclass: class that inherits properties of other, aka derived class, child class
superclass: class whose properties are inherited, aka base class, parent class
extends
: to inherit properties of a class, except private onesclass Super { int superX = 9; public void superMethod() {} } // Sub inherits all of Super's methods and fields class Sub extends Super { public void subMethod() {} } Sub s = new Sub(); s.superMethod(); s.superX; s.subMethod();
when an object of subclass is created, a copy of contents of superclass is made within it
can instantiate using superclass reference variable, but can’t access subclass properties
Super s = new Sub(); s.superMethod(); // ok s.superX; // ok s.subMethod(); // errorsubclass inherits all members, such as fields, methods and nested class from its superclass, but not constructors, as they are not members
super
: to invoke constructor of superclass, similar tothis
keyword, use to differentiate members of superclass from members of subclass if same namesclass Super { int i = 9; public void hello() { System.out.println("hello from super"); } } class Sub extends Super { int i = 8; public void hello() { System.out.println("hello from sub"); } public void subMethod() { Sub s = new Sub(); s.hello(); // print "hello from sub" super.hello(); // print "hello from super" System.out.println("i from sub: " + s.i); // print 8 System.out.println("i from super: " + super.i); // print 9 } } // can use to call parameterized constructor of superclass class Super { int i; Super(int i) { this.i = i; } } class Sub extends Super { Sub(int i) { super(i); // can now call Super parameterized constructor } } Sub s = new Sub(8); // ok
instanceof
: to check an object is is an instance of specified typeclass A {} class B extends A {} class C extends B {} B b = new B(); C c = new c(); System.out.println(b instanceof A); // true System.out.println(c instanceof B); // true System.out.println(c instanceof A); // true
implements
: to inherit properties of an interface, interface can’t be extended by classinterface A {} class B implements A {} class C extends B {} System.out.println(b instanceof A); // true System.out.println(c instanceof B); // true System.out.println(c instanceof A); // truesingle inheritance:
B extends A
multi level inheritance:
B extends A
,C extends B
hierarchical inheritance:
B extends A
,C extends A
multiple inheritance:
C extends A, B
(not supported by Java, as it can lead to diamond problem)a class can implement more than one interfaces:
C implements A, B
- Overriding
overriding function of existing method
allow subclass to implement parent class method based on its requirements
argument list must be same, return type must be same or subtype of the one declared in original overridden method
access level cannot be more restrictive than original, e.g if original method is
public
, overriding method cannot beprivate
orprotected
can only override instance methods if subclass inherits them
method with
final
cannot be overriddenmethod with
static
cannot be overridden, but can be re-declaredcannot override if method cannot be inherited
if same package as superclass, can override any method, unless
private
orfinal
if different package as superclass, can only override
public
orprotected
non-final methodsoverriding method can throw unchecked exceptions, even if the original method does not
overriding method should not throw checked exceptions that are new or broader than the ones in original method
constructors cannot be overridden
class A { public void hello() { System.out.println("hello from A"); } } class B extends A { public void hello() { System.out.println("hello from B"); } public void onlyB() { System.out.println("B specific method"); } } A a = new A(); B b = new B(); a.hello(); // A's hello() b.hello(); // B's hello() b.onlyB(); // ok // A as b's reference type does not have methods in B A b = new B(); b.hello(); // B's hello() b.onlyB(); // error
Polymorphism#
object having many forms, occur when parent class reference is used to refer child class object
all objects are polymorphic in Java as any object will pass
instanceof
test for their own type and class Objectreference variable can be of only one type and cannot be changed once declared
reference variable can be reassigned to other objects if it not declared
final
reference variable can be declared as class or interface
public interface A {} public class B {} public class C extends B implements A {} // C is polymorphic C c = new C(); c instanceof A // true c instanceof B // true c instanceof C // true c instanceof Object // true A a = c; // ok B b = c; // ok Object o = c; // ok
Virtual Methods: overridden methods being invoked at run time, no matter what reference type is used at compile time
class B { public void hello() { System.out.println("hello from B"); } } class C extends B { public void hello() { System.out.println("hello from C"); } } C c = new C(); // 'C' object instantiated using 'B' reference 'b' B b = new C(); c.hello(); // print "hello from C" // compiler use 'hello()' from 'B', but JVM invoke 'hello()' from 'C' during run time b.hello(); // print "hello from C"
Abstraction#
hiding implementation from the user, except functionality
user will have information about what the object does, not how it does it
achieved using [Abstract Class](#abstract-class) and [Interface](#interfaces)
Encapsulation#
wrapping variables and methods as single unit, aka data hiding
variables are declared
private
, define getter and setter methods to access variablesby encapsulating, can set fields of a class read/write only and a class can have total control over what is stored in its fields
class A { private int num = 1; public int getNum() { return num; } public void setNum(int n) { num = n; } } a.getNum(); // 1 a.setNum(9); a.getNum(); // 9
Collections Framework#
Map, Map.Entry, SortedMap, HashMap, TreeMap, WeakHashMap, LinkedHashMap, IdentityHashMap
in
java.util
packageunified architecture for representing and manipulating collections
contain interfaces, implementations/classes and algorithms
Collection#
AbstractCollection
: implement most of the Collection interfacefoundation interface on which collections framework is built
all collections implement Collection interface
several methods can throw
UnsupportedOperationException
add(Object)
: adds argument to this collection, return true if added, false if object is already member or if the collection doesn’t allow duplicates
addAll(Collection)
: add all elements of argument to this collection, true if added
clear()
: remove all elements from this collection
contains(Object)
: true if object is element of this collection
containsAll(Collection)
: true if this collection contain all elements of argument
equals(Object)
: true if this collection and argument are equal
hashCode()
: return hash code for this collection
isEmpty()
: true if this collection is empty
iterator()
: return iterator for this collection
remove(Object)
: remove one instance of object from this collection, true if removed
removeAll(Collection)
: remove all elements of argument from this collection, return true if removed
retainAll(Collection)
: remove all elements from this collection except those in argument, true if removed
size()
: return number of elements of this collection
toArray()
: return array of all elements of this collection, elements are copies
toArray(Object[])
: return array with elements whose type match argument
List#
AbstractList
: extend AbstractCollection and implement most of the List interface
AbstractSequentialList
: extend AbstractList for collection of sequential accessstores sequence of elements
can insert or access elements by position, zero-based index can contain duplicate element
has methods define by Collection interface and other own methods
methods throw
UnsupportedOperationException
if not modifiable,ClassCastException
if one object is incompatible with another
add(int, Object)
: insert object into this list at given index, existing elements at or beyond are shifted and not overwritten
addAll(int, Collection)
: insert all elements of argument at given index, existing elements are shifted and not overwritten, return true if this list changes
get(int)
: return object stored at given index
indexOf(Object)
: return index of last instance of given object in this list, return 1 if not found
listIterator()
: return iterator to the start of this list
listIterator(int)
: return iterator of this list that start at given index
remove(int)
: remove element at given index, return removed element, list is compacted, indexes of subsequent elements are decremented by one
set(int, Object)
: assign given object at given index
subList(int start, int end)
: return list from start to end (exclusive), returned elements are also referenced by the invoking objectList<Integer> l1 = new ArrayList<Integer>(); l1.add(1); l1.add(2); System.out.println(l1); List<String> l2 = new LinkedList<String>(); l2.add("hello"); l2.add("world"); System.out.println(l2);
LinkedList#
implemented by extending AbstractSequentialList
LinkedList()
,LinkedList(Collection)
: create empty linked list or initialized with elements of argument
add(int, Object)
: add object at given index, can throwIndexOutOfBoundsException
add(Object)
: add object at end of the list, return true on success
addAll(Collection)
: add all elements of argument at the end of the list, throwNullPointerException
if given collection is null
addAll(int, Collection)
: add all elements of argument at given index
addFirst(Object)
: add element at the start of the list
addLast(Object)
: add element at the end of the list
clear()
: remove all elements
clone()
:return shallow copy of this linked list
contains(Object)
: true if list contain given argument
get(int)
: return element at given index, throwIndexOutOfBoundsException
getFirst()
: return first element, throwNoSuchElementException
getLast()
: return last element, throwNoSuchElementException
indexOf(Object)
: return index of first occurrence of given argument, -1 if not found
lastIndexOf(Object)
: return index of last occurrence of given argument, -1 if not found
listIterator(int)
: return list iterator at given position, throwIndexOutOfBoundsException
remove(int)
: remove and return element at given index, throwNoSuchElementException
remove(Object)
: remove first occurrence of argument and return true if success, throwIndexOutOfBoundsException
removeFirst()
: remove and return first element
removeLast()
: remove and return last element
set(int, Object)
: replace element at given index with given object
size()
: return size of the list
toArray()
: return an array with elements of the list, throwNullPointerException
toArray(Object[])
: return an array with elements of the list, with runtime type as specifiedLinkedList<Integer> l = new LinkedList<Integer>(); l.add(1); l.add(2); int x = l.remove(1); // x = 2
ArrayList#
extend AbstractList and implement List interface, support dynamic array
ArrayList()
,ArrayList(Collection)
,ArrayList(int)
: create empty array list or initialized with elements or specific capacity
add(int, Object)
: add given element at given index
add(Object)
: add element at end, return true on success
addAll(Collection)
: add all elements of argument at end
addAll(int, Collection)
: add all elements of given collection at specific index
clear()
: remove all elements
clone()
: return shallow copy
contains(Object)
: return true if list contain argument
ensureCapacity(int)
: increase capacity to have at least specified capacity
get(int)
: return element at given index
indexOf(Object)
: return index of first occurrence of argument, -1 if not found
lastIndexOf(Object)
: return index of last occurrence of given argument, -1 if not found
remove(int)
: remove and return element at given index
removeRange(int start, int end)
: remove elements from start to end, exclusive
set(int, Object)
: replace element at given index with given argument
size()
: return size
toArray()
: return an array with elements of the list
toArray(Object[])
: return an array with elements of the list, with runtime type as
trimToSize()
: trim the capacity to current sizeArrayList<Integer> l = new ArrayList<Integer>(); l.add(1); l.add(2); int x = l.remove(1); // x = 2
Set#
AbstractSet
: extend AbstractCollection and implement most of the Set interfaceno duplicate elements, only methods inherited from Collection
add(Object)
: add object to the collection
clear()
: remove all objects
contains(Object)
: true if object exists
isEmpty()
: true if no elements
iterator()
: return iterator object for the collection
remove(Object)
: remove specific object
size()
: return number of elementsSet<Integer> s = new HashSet<Integer>(); s.add(1); s.add(1); s.add(2); s.add(3); s.size(); // 3
Sorted Set#
extend Set, elements in ascending order
has implementations in various classes like TreeSet
most methods throw
NoSuchElementException
when no items in the setthrow
ClassCastException
for incompatible objectsnull is not allowed and throw
NullPointerException
comparator()
: return this set’s comparator, return null for natural ordering
first()
: return first element
headSet(Object)
: return sorted set with elements less than argument, returned elements are also referenced by invoking object
last()
: return last element
subSet(Object start, Object end)
: return sorted set with elements between start and end, exclusive, returned elements are also referenced by invoking object
tailSet(Object)
: return sorted set with elements greater than argument, returned elements are also referenced by invoking objectSortedSet<Integer> s = new TreeSet<Integer>(); s.add(3); s.add(2); s.add(9); s.add(1); java.util.Iterator<Integer> it = s.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
HashSet#
extend AbstractSet and implement Set interface
HashSet()
,HashSet(Collection)
,HashSet(int)
: create default hash set or initialize with elements or specific capacity
HashSet(int, float)
: create hash set with given capacity and fill ratio/load capacity, fill ratio must be between 0.0 and 1.0, which determine how full the set can be before resized
add(Object)
: add element, return true if success and element is not present
clear()
: remove all elements
clone()
: return shallow copy, elements are not cloned
contains(Object)
: return true if contain argument
isEmpty()
: return true if empty
iterator()
: return iterator
remove(Object)
: remove given argument, return true if success and element is present
size()
: return number of elementsHashSet<Integer> h = new HashSet<>(); System.out.println(h.add(1)); System.out.println(h.add(1)); // false
LinkedHashSet#
extend HashSet, but no new member is added
maintain linked list of entries, allow insertion-order iteration
HashSet()
,HashSet(Collection)
: create default hash set
LinkedHashSet(int)
: create linked hash set with specific capacity
LinkedHashSet(int, float)
: create linked hash set with specific capacity and fill ratioLinkedHashSet<Integer> h = new LinkedHashSet<>(); System.out.println(h.add(1)); System.out.println(h.add(1)); // false
TreeSet#
implement Set interface that uses a tree for storage
objects are sorted and ascending order
access time is fast, suited for large amount of information that must be found quickly
TreeSet()
,TreeSet(Collection)
: create empty tree set or initialize with argument
TreeSet(Comparator)
: create empty tree set that uses specific comparator
TreeSet(SortedSet)
: create tree set with elements of argument
add(Object)
: add given element
addAll(Collection)
: add all elements of given collection
clear()
: remove all elements
clone()
: return shallow copy
comparator()
: return comparator used, null if natural ordering
contains(Object)
: return true if element present
first()
: return first element
headSet(Object)
: return sorted set with elements less than argument
isEmpty()
: return true if empty
iterator()
: return iterator
last()
: return last element
remove(Object)
: remove specific element if present, return true on success
size()
: return size
subSet(Object start, Object end)
: return sorted set of elements range from start to end, exclusive
tailSet(Object)
: return sorted set of elements greater than or equal to argumentTreeSet<Integer> h = new TreeSet<>(); System.out.println(h.add(1)); System.out.println(h.remove(1)); System.out.println(h.remove(1)); // false
Map#
AbstractMap
: implement Map interfacemaps unique keys to values
has implementations in various classes like HashMap
most methods throw
NoSuchElementException
when no items in the mapthrow
ClassCastException
for incompatible objectsnull is not allowed and throw
NullPointerException
UnsupportedOperationException
for changing unmodifiable map
clear()
: remove all key-value pairs
containsKey(Object)
: true if contain argument as key
containsValue(Object)
: true if contain argument as value
entrySet()
: return a Set with entries of the map, set-view of the map
equals(Object)
: true if given object is a map and contain same entries
get(Object)
: return value of given key argument
hashCode()
: return hash code for this map
isEmpty()
: true if map is empty
keySet()
: return a Set with keys of this map, set-view of keys of the map
put(Object k, Object v)
: add key-value pair, existing value is overwritten, return null if key does not already exist, otherwise previous value is returned
putAll(Map)
: put all entries from given map
remove(Object)
: remove entry whose key equals argument
size()
: return size of map
values()
: return a collection with values in the map, collection-view of values of mapMap<String, Integer> m = new HashMap<String, Integer>(); m.put("hello", 1); m.put("world", 2); System.out.println(m);
Map.Entry#
interface to enable working with a map entry
Map.entrySet()
return a set, which is a Map.Entry object
equals(Object)
: true if argument is Map.Entry and key-value pairs are equal to this object
getKey()
: return key for this map entry
getValue()
: return value for this map entry
hashCode()
: return hash code for this map entry
setValue(Object)
: set value for this map entry to argument,ClassCastException
if argument is not correct type, NullPointerException if argument is null and map does not allow null keys, UnsupportedOperationException if map cannot be changedMap<String, Integer> m = new HashMap<String, Integer>(); m.put("hello", 1); m.put("world", 2); Set<Map.Entry<String, Integer>> s = m.entrySet(); System.out.println(s);
SortedMap#
extends Map, entries are in ascending key order
has implementations in various classes like TreeMap
most methods throw
NoSuchElementException
ClassCastException
for incompatible object
NullPointerException
when null object is used and map does not allow
comparator()
: return this map’s comparator, null if natural ordering is used
firstKey()
: return first key in this map
headMap(Object)
: return sorted map with keys that are less than argument
lastKey()
: return last key of this map
subMap(Object start, Object end)
: return sorted map with keys greater than or equal to start and less than end
tailMap(Object)
: return sorted map with keys greater than or equal to argumentTreeMap<Integer, String> m = new TreeMap<Integer, String>(); m.put(1, "hello"); m.put(4, "bar"); m.put(2, "world"); m.put(3, "foo"); Set<Map.Entry<Integer, String>> s = m.entrySet(); Iterator<Map.Entry<Integer, String>> i = s.iterator(); while (i.hasNext()) { System.out.println(i.next()); }
HashMap#
use hashtable, constant time for basic operations
HashMap()
,HashMap(Map)
,HashMap(int)
: create default hash map or initialize with given map or capacity
HashMap(int, float)
: create hash map with specific capacity and fill ratio
clear()
: clear all mappings
clone()
: return shallow copy, keys and values are not cloned
containsKey(Object)
: return true if contain argument as key
containsValue(Object)
: return true if one or more keys map to argument
entrySet()
: return a set of mappings
get(Object)
: return value of given key argument
isEmpty()
: return true if empty
keySet()
: return a set of keys
put(Object k, Object v)
: associate given value to given key
putAll(Map)
: copies all mappings from argument, existing same mappings are replaced
remove(Object)
: remove mapping of given key, return old value if present
size()
: return size
values()
: return a collection of valuesHashMap<Integer, String> m = new HashMap<Integer, String>(); m.put(1, "hello"); m.put(2, "world"); System.out.println(m.remove(1)); // hello System.out.println(m.remove(1)); // null
TreeMap#
implement Map by using tree, efficient way of storing key-value pairs in ascending order
TreeMap()
,TreeMap(Comparator)
,TreeMap(Map)
,TreeMap(SortedMap)
: create empty tree map or with given comparator or initialize with entries from Map or SortedMap
clear()
: remove all mappings
clone()
: return shallow copy
comparator()
: return comparator, null if natural order is used
containsKey(Object)
: return true if contain argument as key
containsValue(Object)
: return true if one or more keys map to argument
entrySet()
: return set view of mappings
firstKey()
: return first key
get(Object)
: return value of given key argument
headMap(Object)
: return sorted map whose keys are less than argument
keySet()
: return set view of keys
lastKey()
: return last key
put(Object k, Object v)
: associate given value to given key
putAll(Map)
: copy all mappings from argument
remove(Object)
: remove mapping for the key, return object if present, else null
size()
: return size
subMap(Object start, Object end)
: return sorted map with keys range from start to end, exclusive
tailMap(Object)
: return sorted map with keys greater than or equal to argument
values()
: return a collection of valuesTreeMap<Integer, String> m = new TreeMap<Integer, String>(); m.put(1, "hello"); m.put(3, "foo"); m.put(2, "world"); System.out.println(m.keySet()); // [1, 2, 3]
WeakHashMap#
only store weak references to keys, allowing key-value pair to be garbage-collected when the key is no longer referenced outside
useful for registry-like data structures
function same as HashMap, except entry is removed once memory manager no longer has strong reference to key object
weak reference: garbage collector can reclaim object’s memory at any time, no need to wait until system out of memory
WeakHashMap()
: create default WeakHashMap with capacity of 16 and load factor of 0.75
WeakHashMap(int)
: create empty WeakHashMap with specific capacity
WeakHashMap(int, float)
: create WeakHashMap with specific capacity and load factor
WeakHashMap(Map)
: create WeakHashMap with given mappings
clear()
: clear all mappings
containsKey(Object)
: return true if contain argument as key
containsValue(Object)
: return true if one or more keys map to argument
entrySet()
: return set of mappings
get(Object)
: return value of given key
isEmpty()
: return true if empty
keySet()
: return a set of keys
put(Object k, Object v)
: associate given value to given key
putAll(Map)
: copy all mappings from argument, existing same mappings will be replaced
remove(Object)
: remove mapping for the key, return object if present, else null
size()
: return size
values()
: return a collection of valuesWeakHashMap<Integer, String> m = new WeakHashMap<Integer, String>(); m.put(1, "hello"); m.put(3, "foo"); m.put(2, "world"); System.out.println(m.containsKey(3)); // true
LinkedHashMap#
extend HashMap, use linked list of entries in order inserted
can also create LinkedHashMap that return elements in access order
LinkedHashMap()
: create default LinkedHashMap
LinkedHashMap(int)
: create LinkedHashMap with specific capacity
LinkedHashMap(int, float)
: create LinkedHashMap with specific capacity and fill ratio
LinkedHashMap(Map)
: create LinkedHashMap with given mappings
LinkedHashMap(int, float, boolean)
: create LinkedHashMap with specific capacity, fill ratio and storing order, insertion if false, last access if true
clear()
: remove all mappigs
containsKey(Object)
: return true if contain argument as key
get(Object)
: return value of given key
removeEldesEntry(Map.Entry)
: return true if map should remove eldest entryLinkedHashMap<Integer, String> m = new LinkedHashMap<Integer, String>(3, 0.5f, true); m.put(1, "hello"); m.put(2, "world"); m.put(3, "foo"); m.get(2); for (String v : m.values()) { System.out.println(v); // "hello, foo, world" instead of "hello, world, foo" }
IdentityHashMap#
implement AbstractMap, similar to HashMap, but uses reference equality when comparing elements
not general purpose Map implementation
use rarely where reference equality is required
constant time for basic operations assuming hash function disperse elements properly
IdentityHashMap()
: create empty IdentityHashMap with default maximum size of 21
IdentityHashMap(int)
,IdentityHashMap(Map)
: create IdentityHashMap with specific maximum size or initialized with mappings
clear()
: remove all mappings
clone()
: return shallow copy, key-value pairs are not cloned
containsKey(Object)
: return true if contain argument as key
containsValue(Object)
: return true if one or more keys map to argument
entrySet()
: return a set of mappings
equals(Object)
: compare argument with this map for equality
get(Object)
: return value of given key
hashCode()
: return hash code value for this map
isEmpty()
: return true if empty
keySet()
: return an identity-based set of keys
put(Object k, Object v)
: associate given value to given key
putAll(Map)
: copy all mappings from argument, existing same mappings will be replaced
remove(Object)
: remove mapping for the key, return object if present, else null
size()
: return size
values()
: return a collection of valuesIdentityHashMap<Integer, String> m1 = new IdentityHashMap<Integer, String>(); m1.put(1, "hello"); m1.put(2, "world"); m1.put(3, "foo"); IdentityHashMap<Integer, String> m2 = new IdentityHashMap<Integer, String>(); m2.put(1, "test"); m1.putAll(m2); // "1 = test"
Enumeration#
define methods to enumerate elements in a collection
legacy interface, superceded by Iterator
still used by methods in classes such as Vector, Properties and other API classes
hasMoreElements()
: must return true while there are more elements to extract
nextElement()
: must return next object in enumeration as generic Object reference
Algorithms#
static methods, most methods throw
ClassCastException
orUnsupportedOperationException
binarySearch(List, Object, Comparator)
: search for value according to comparator, return position of value or -1 if not found
binarySearch(List, Object)
: search for value in sorted list
copy(List l1, List l2)
: copy elements of l2 to l1
enumeration(Collection)
: return enumeration over argument
fill(List, Object)
: assign object to each element of list
indexOfSubList(List, List subList)
: search list for first occurrence of subList, return index of first match or -1 if not found
lastIndexOfSubList(List, List subList)
: search list for last occurrence of subList, return index or -1 if not found
list(Enumeration)
: return ArrayList with elements of argument
max(Collection)
: return max element in collection
max(Collection, Comparator)
: return max element in collection, determined by comparator
min(Collection)
: return min element in collection
min(Collection, Comparator)
: return min element in collection, determined by comparator
nCopies(int, Object)
: return specific copies of Object, as immutable list
replaceAll(List, Object old, Object new)
: replace all occurrences of old with new, return true if at least one replacement
reverse(List)
: reverse the list
reverseOrder()
: return reverse comparator
rotate(List, int)
: rotate specific places to right, provide negative value for left
shuffle(List)
: shuffle elements in the list
shuffle(List, Random)
: shuffle elements using given argument as source of random numbers
singleton(Object)
: return argument as immutable set, easy way to convert single object into set
singletonList(Object)
: return argument as immutable list, easy way to convert single object into list
singletonMap(Object k, Object v)
: return as immutable map, easy way to convert single key-value pair into map
sort(List)
: sort elements by natural ordering
sort(List, Comparator)
: sort elements by comparator
swap(List, int i1, int i2)
: swap elements at i1 and i2
synchronizedCollection(Collection)
: return thread-safe collection
synchronizedList(List)
: return thread-safe list
synchronizedMap(Map)
: return thread-safe map
synchronizedSet(Set)
: return thread-safe set
synchronizedSortedMap(SortedMap)
: return thread-safe sorted map
synchronizedSortedSet(SortedSet)
: return thread-safe sorted set
unmodifiableCollection(Collection)
: return unmodifiable collection
unmodifiableList(List)
: return unmodifiable list
unmodifiableMap(Map)
: return unmodifiable map
unmodifiableSet(Set)
: return unmodifiable set
unmodifiableSortedMap(SortedMap)
: return unmodifiable sorted map
unmodifiableSortedSet(SortedSet)
: return unmodifiable sorted set
Iterator#
to cycle through elements in a collection
implement Iterator or ListIterator interface
ListIterator extend Iterator for bidirectional traversal of list and modification
each collection class provide
iterator()
use a loop with a call to
hasNext()
andnext()
to access each element
hasNext()
: return true if more elements remain
next()
: return next element, throwNoSuchElementException
if not exist
remove()
: remove current element, throwIllegalStateException
ifnext()
is not called after
- ListIterator
add(Object)
: insert argument in front of element returned bynext()
hasNext()
: return true if next element exist
hasPrevious()
: return true if previous element exist
next()
: return next element
nextIndex()
: return index of next element, return size of list if no next element
previous()
: return previous element
previousIndex()
: return index of previous index, return -1 if not exist
remove()
: remove current element
set(Object)
: assign argument to current element, which is the element returned bynext()
orprevious()
ArrayList<Integer> a = new ArrayList<>(); a.add(1); a.add(2); a.add(3); ListIterator<Integer> itr = a.listIterator(); while (itr.hasNext()) { Integer e = itr.next(); itr.set(e + 1); } itr = a.listIterator(); while (itr.hasNext()) { System.out.println(itr.next()); // 2, 3, 4 }
Comparator#
interface to define what sorted order means in collections
compare(Object o1, Object o2)
: compare two elements for order, return 0 if equal, > 0 if o1 is greater, < 0 if o2 is greater
equals(Object o1, Object o2)
: return true if equal
Generics#
allow to specify a set of related methods or types with single method or class declaration
also provide compile-time type safety
Generic Methods#
can have a single generic method declaration called with arguments of different types
compiler handles each method call based on argument types
all declarations must have a type parameter section
each type parameter section contain one or more type parameters
type parameters can be used to declare return type
type parameters can only represent reference types, not primitive types
public static <E> void printArray(E[] inputArray) { for (E e : inputArray) { System.out.println(e); } } Integer[] intArray = { 1, 2, 3 }; String[] stringArray = { "hello", "world" }; printArray(intArray); printArray(stringArray);
can restrict types that are allowed to be passed to type parameter
public static <E extends Number> void printArray(E[] inputArray) { for (E e : inputArray) { System.out.println(e); } } Integer[] intArray = { 1, 2, 3 }; Double[] doubleArray = { 1.0, 2.2, 3.3 }; String[] stringArray = { "hello", "world" }; printArray(intArray); // ok printArray(doubleArray); // ok printArray(stringArray); // error
Generic Classes#
class name followed by a type parameter section
can have one or more type parameters separated by commas, called parameterized class or parameterised types
class Generic<T> { private T t; Generic(T t) { this.t = t; } public T get() { return t; } } Generic<Integer> i = new Generic<Integer>(1); Generic<String> s = new Generic<String>("hello"); System.out.println(i.get()); // 1 System.out.println(s.get()); // "hello"
Serialization#
ObjectInputStream, ObjectOutputStream, Serializing, Deserializing
object represented as sequence of bytes, including data, type and types of data
serialized object written to a file can be read and deserialized to recreate object in memory
serialization and deserialisation is JVM independent, operations can be on different platform
ObjectInputStream#
readObject()
: get next object out of the stream and deserialize it, return an Object
ObjectOutputStream#
writeObject(Object)
: serialize an object and send it to output stream
Serializing#
class must implement
java.io.Serializable
interface and all fields must be serialisable or marked transientconvention to serialize an object to a file with
.ser
extensionclass S implements java.io.Serializable { public String name; public transient int age; } S s = new S(); s.name = "foo"; s.age = 9; try { FileOutputStream f = new FileOutputStream("serializable.ser"); ObjectOutputStream o = new ObjectOutputStream(f); o.writeObject(s); o.close(); f.close(); } catch (IOException e) { e.printStackTrace(); }
Deserializing#
S s = null; try { FileInputStream f = new FileInputStream("serializable.ser"); ObjectInputStream i = new ObjectInputStream(f); s = (S) i.readObject(); // need to cast proper type i.close(); f.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { // need to catch as it is declared by "readObject()" e.printStackTrace(); } System.out.println(s.name); // "foo" System.out.println(s.age); // 0, value is not sent to output stream as it is transient
Networking#
in
java.net
package
Socket Programming#
sockets provide communication mechanism using TCP
server instantiate ServerSocket object with port number
server inovkes
accept()
to wait until client connectsclient instantiate Socket object with server name and port number to connect to
after connected, client will have Socket object that can communicate with the server
server’s
accept()
return reference to new socket that is connected to client’s socketafter connection, communication can be done using I/O streams, as each socket has OutputStream and InputStream
client’s OutputStream is connected to server’s InputStream, and server’s InputStream to client’s OutputStream
as it use TCP, data can be sent across both streams at the same time
- ServerSocket
use by server applications, all constructors throw
IOException
ServerSocket()
: create unbound server socket, must usebind()
when ready
ServerSocket(int)
: create server socket with given port
ServerSocket(int port, int backlog)
: create server socket with given port and backlog, number of incoming clients to store in a wait queue
ServerSocket(int port, int backlog, InetAddress addr)
: create server socket with local IP address to bind to, InetAddress is used for servers that may have multiple IP
getLocalPort()
: return listening port
accept()
: wait for incoming client, block until client connect or socket time out, must set timeout value withsetSoTimeout()
, return Socket object if client connect, throwIOException
setSoTimeout(int)
: set timeout value duringaccept()
bind(SocketAddress, int backlog)
: bind socket to given SocketAddress object
- Socket
use by both client and server, client instantiate one and server obtain one from
accept()
all constructors, except default one, throw
IOException
Socket(String, int)
: connect to given server at given port, throwUnknownHostException
Socket(InetAddress, int)
: connect to given server, using InetAddress, at given port
Socket(String host, int port, InetAddress local, int localPort)
: connect to given host and port, creating socket on local host at given address and port
Socket(InetAddress host, int port, InetAddress local, int localPort)
: connect to given host, using InetAddress, and port, creating socket on local host at given address and port
Socket()
: create unconnected socket, need to useconnect()
connect(SocketAddress, int timeout)
: connect socket to given host, throwIOException
getInetAddress()
: return address to which the socket is connected
getPort()
: return port of the socket bounded on remote machine
getLocalPort()
: return port of socket on local machine
getRemoteSocketAddress()
: return address of remote socket
getInputStream()
: return InputStream of socket, which is connected to OutputStream of remote socket, throwIOException
getOutputStream()
: return OutputStream of socket, which is connected to InputStream of remote socket, throwIOException
close()
: close the socket, throwIOException
- InetAddress
InetAddress(byte[])
: create object with raw IP address, return InetAddress
getByAddress(String, byte[])
: create object with given host name and IP address, return InetAddress
getByName(String)
: return IP address, as InetAddress object, of given host
getHostAddress()
: return IP address string
getHostName()
: get host name string
getLocalHost()
: return local host as InetAddress
toString()
: convert IP address to stringClient Example
public class SocketClient { public static void main(String[] args) { String serverName = args[0]; int port = Integer.parseInt(args[1]); try { System.out.println("Connecting to " + serverName + " on port " + port); Socket client = new Socket(serverName, port); System.out.println("Connected to " + client.getRemoteSocketAddress()); OutputStream outToServer = client.getOutputStream(); DataOutputStream out = new DataOutputStream(outToServer); out.writeUTF("Hello from " + client.getLocalSocketAddress()); InputStream inFromServer = client.getInputStream(); DataInputStream in = new DataInputStream(inFromServer); System.out.println("Reply from server: " + in.readUTF()); client.close(); } catch (IOException e) { e.printStackTrace(); } } }Server Example
public class SocketServer extends Thread { private ServerSocket serverSocket; public SocketServer(int port) throws IOException { serverSocket = new ServerSocket(port); serverSocket.setSoTimeout(10000); } public void run() { while (true) { try { System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "..."); Socket server = serverSocket.accept(); System.out.println("Connected to " + server.getRemoteSocketAddress()); DataInputStream in = new DataInputStream(server.getInputStream()); System.out.println(in.readUTF()); DataOutputStream out = new DataOutputStream(server.getOutputStream()); out.writeUTF("Disconnecting from " + server.getLocalSocketAddress()); server.close(); } catch (SocketTimeoutException e) { System.out.println("Socket timed out!"); break; } catch (IOException e) { e.printStackTrace(); break; } } } public static void main(String[] args) { int port = Integer.parseInt(args[0]); try { Thread t = new SocketServer(port); t.start(); } catch (IOException e) { e.printStackTrace(); } } }
URL Processing#
url:
protocol://host:port/path?query#ref
host is also called authority, path is also called filename
example protocols: HTTP, HTTPS, FTP, File
all constructors throw
MalformedURLException
URL(String protocol, String host, int port, String file)
: create URL
URL(String protocol, String host, String file)
: create URL with default port for given protocol
URL(String url)
: create URL from given string
URL(URL context, STring url)
: create URL by parsing arguments
getPath()
: return path of URL
getQuery()
: return query part of URL
getAuthority()
: return authority/host of URL
getPort()
: return port of URL
getDefaultPort()
: return default port for the protocol of URL
getProtocol()
: return protocol of URL
getHost()
: return host of URL
getFile()
: return filename/path of URL
getRef()
: return reference part of URL
openConnection()
: open connection to URL for client communication, return URLConnection object, throw IOExceptionURL url = new URL("https://www.amrood.com/index.htm?language=en#j2se"); System.out.println(url.getProtocol()); System.out.println(url.getAuthority()); System.out.println(url.getFile()); System.out.println(url.getHost()); System.out.println(url.getPort()); System.out.println(url.getDefaultPort());
- URLConnection
getContent()
,getContent(Class[])
: return contents as object
getContentEncoding()
: return content-encoding header field as string
getConentLength()
: return content-length header field as int
getContentType()
: return content-type header field as string
getLastModified()
: return last-modified header field as int
getExpiration()
: return expired header field as long
getIfModifiedSince()
: return ifModifiedSince field as long
setDoInput(boolean)
: default argument true to denote the connection will be used for input
setDoOutput(boolean)
: default argument false to denote connection will not be used for output
getInputStream()
: return InputStream of connection for reading from the resource, throwIOException
getOutputStream()
: return OutputStream of connection for writing to resource, throwIOException
getURL()
: return URL object of connection
Email#
need JavaMail API and JAF (Java ACtivation Framework) installed
in
javax.mail
andjavax.activation
;Simple Email
String from = "foo@mail.com"; String to = "bar@mail.com"; String host = "localhost"; Properties properties = System.getProperties(); properties.setProperty("mail.smtp.host", host); Session session = Session.getDefaultInstance(properties); try { MimeMessage message = new MimeMessage(session); message.setFrom(new InternextAddress(from)); message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); message.setSubject("Mail subject"); message.setText("Mail text message"); Transport.send(message); System.out.println("Message sent"); } catch (MessagingException e) { e.printStackTrace(); }Html Email
// same as simple mail, with "setContent()" message.setContent("<h1>Mail message</h1>");Sending with Attachment
// same as simple mail, with additional message parts BodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setText("Message body"); Multipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBodyPart); messageBodyPart = new MimeBodyPart(); String filename = "file.txt"; DataSource source = new FileDataSource(filename); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(filename); multipart.addBodyPart(messageBodyPart); message.setContent(multipart);Setting Authentication
// set system properties Properties properties = System.getProperties(); properties.setProperty("mail.user", "user"); properties.setProperty("mail.password", "password");
Threads#
Java thread priorities range between MIN_PRIORITY, constant 1, and MAX_PRIORITY, constant 10
every thread has default NORM_PRIORITY, constant 5
thread priorities cannot guarantee execution order and platform dependent
Multithreading: subdividing specific operations within single application into individual threads
Thread Life Cycle#
- New
new thread begins its life cycle in new state and remain in it until started
also called born thread
- Runnable
becomes runnable when newly born thread is started
runnable thread is considered to be executing its task
- Waiting
waiting for another thread to perform a task
change back to runnable state when another thread signal
- Timed Waiting
runnable thread can enter this state for specified time
transition back to runnable when time expire or a waiting event occur
- Terminated
thread enter terminated/dead state when a task is completed or it is terminated
Creating Thread#
- Using Runnable
implement Runnable interface to execute class as a thread
must implement
run()
provided by Runnable, which is an entry point for a thread, need to put complete business logic inside the methodinstantiate a Thread object,
Thread(Runnable, String name)
can start the created Thread object with
start()
class MyRunnable implements Runnable { private Thread t; private String threadName; MyRunnable(String name) { threadName = name; System.out.println("Creating " + threadName); } public void run() { System.out.println("Running " + threadName); try { for (int i = 1; i < 5; ++i) { System.out.println("Thread: " + threadName + ", " + i); Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Thread " + threadName + " interrupted"); } System.out.println("Thread " + threadName + " exiting"); } public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); } } } MyRunnable r1 = new MyRunnable("Thread1"); r1.start();
- Using Thread
extending Thread class provide more flexibility
override
run()
, which is an entry point for a thread, need to put complete business logic inside the methodcall
start()
once Thread object is created
start()
: start the thread in a separate path of execution, then invokerun()
run()
: invoked when this Thread object is instantiated using separate Runnable target
setName(String)
: change the name of Thread object
getName()
: return name of Thread object
setPriority(int)
: set thread priority, between 1 and 10
setDaemon(boolean)
: this Thread is daemon thread if argument is true
join(long)
: invoked on second thread, current thread is blocked until the second thread terminate or given time in ms passes
interrupt()
: interrupt this thread, will continue execution if it was blocked
isAlive()
: return true if alive, which is any time after the thread has been startedbelow static methods perform operation on currently running thread
yield()
: yield to any other threads of same priority that are waiting to be scheduled
sleep(long)
: block current thread for given amount of time in ms
holdsLock(Object)
: return true if current thread hold the lock on argument
currentThread()
: return reference to this thread
dumpStack()
: print stack trace for current thread, useful for debugging multithreaded applicationsclass MyThread extends Thread { private Thread t; private String threadName; MyThread(String name) {} public void run() {} public void start() {} } MyThread t1 = new MyThread("Thread1"); t1.start();
Thread Synchronization#
synchronizing multiple threads to make sure only one can access the same resource at a given point in time, implemented using monitors concept
each object in Java is associated with a monitor, which a thread can lock or unlock
only one thread at a time may hold a lock on a monitor
can create and sync threads by using
synchronized
blocks, in which shared resources are placed
synchronized(objectID)
: objectID is a reference to an object whose lock associates with the monitor that the synchronized statement representsclass MyThread extends Thread { private Thread t; private String threadName; DemoObject d; MyThread(String name, DemoObject d) { threadName = name; this.d = d; } public void run() { synchronized (d) { d.foo(); } System.out.println("Thread " + threadName + " exiting"); } public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); } } } DemoObject d = new DemoObject(); ThreadDemo t1 = new ThreadDemo("Thread-1", d); ThreadDemo t2 = new ThreadDemo("Thread-2", d); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (Exception e) { System.out.println("Interrupted"); }
Interthread Communication#
important for applications where two or more threads exchange information
all three methods are
final
, available in all classes, and can only be called from within synchronized context
wait()
: this thread wait until another invokenotify()
notify()
: wake up a single thread that is waiting on this object’s monitor
notifyAll()
: wake up all threads that callwait()
on the same objectclass Chat { boolean flag = false; public synchronized void Question(String msg) { if (flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(msg); flag = true; notify(); } public synchronized void Answer(String msg) { if (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(msg); flag = false; notify(); } } class T1 implements Runnable { Chat m; String[] s1 = { "Hi", "How are you?", "I am also doing fine" }; public T1(Chat m1) { this.m = m1; new Thread(this, "Question").start(); } public void run() { for (int i = 0; i < s1.length; i++) { m.Question(s1[i]); } } } class T2 implements Runnable { Chat m; String[] s2 = { "Hi", "I'm good, and you?", "Great!" }; public T2(Chat m2) { this.m = m2; new Thread(this, "Question").start(); } public void run() { for (int i = 0; i < s2.length; i++) { m.Answer(s2[i]); } } } Chat m = new Chat(); new T1(m); new T2(m);
Thread Deadlock#
deadlock: two or more threads blocked forever, waiting for each other, occur when multiple threads need same locks but obtain them in different order
synchronized
keyword causes the executing thread to block while waiting for the lock or monitordeadlock example
public class DeadLockDemo { public static Object Lock1 = new Object(); public static Object Lock2 = new Object(); public static void main(String[] args) { ThreadDemo1 t1 = new ThreadDemo1(); ThreadDemo2 t2 = new ThreadDemo2(); t1.start(); t2.start(); } private static class ThreadDemo1 extends Thread { public void run() { synchronized (Lock1) { System.out.println("Thread 1: Holding lock 1..."); try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println("Thread 1: Waiting for lock 2..."); synchronized (Lock2) { System.out.println("Thread 1: Holding lock 1 & 2..."); } } } } private static class ThreadDemo2 extends Thread { public void run() { synchronized (Lock2) { System.out.println("Thread 2: Holding lock 2..."); try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println("Thread 2: Waiting for lock 2..."); synchronized (Lock1) { System.out.println("Thread 2: Holding lock 1 & 2..."); } } } } }Deadlock Solution Example
// changing the order of locks prevent deadlock situation private static class ThreadDemo1 extends Thread { public void run() { synchronized (Lock1) { synchronized (Lock2) {} } } } private static class ThreadDemo2 extends Thread { public void run() { synchronized (Lock1) { synchronized (Lock2) {} } } }
Thread Control#
allow threads to be suspended, resumed or stopped completely
suspend()
: put thread in suspended state, can be resumed withresume()
stop()
: stop thread completely
resume()
: resume a suspended thread usingsuspend()
wait():
cause current thread to wait until another thread invokenotify()
notify()
: wake up single thread that is waiting on this object’s monitorlatest versions of Java has deprecated
suspend()
,resume()
andstop()
Javadoc#
a tool that comes with JDK, used to generate documentation in HTML from source code
// normal text comment
/* normal text comment */
/**
* <h1>can include html tags</h1>
* This is a doc comment
*
* @author author-name
* @version version of doc
* @since doc released date
* {@code} display text in code font
* {@docRoot} relative path to generate doc root directory
* @deprecated deprecated-text
* @exception add Trhows subheading
* {@inheritDoc} inherit comment from nearest superclass
* {@link} insert in-line link
* {@linkplain} link is displayed in plain text
* @param add parameter
* @return add return section
* @see add See Also heading with link or text entry
* @serial for default serializable field
* @serialData data written by writeObject() or writeExternal()
* @serialField ObjectStreamField component
* @throws same as @exception
* {@value} to display value of static field
*/
Java8#
Lambda#
optional type declaration, compile can inference from the value
can omit parenthesis for single parameter
optional curly braces for single statement in expression body
compiler can auto return if body has single expression
used to define inline implementation of functional interface, which only has single method
eliminate the need of anonymous class and provide functional programming capability
can refer to any final variable
throw compilation error if a variable is assigned a value second time
interface Operation { int operation(int a, int b); } interface Print { void foo(String msg); } public class MyClass { private int operate(int a, int b, Operation o) { return o.operation(a, b); } public static void main(String[] args) { MyClass t = new MyClass(); Operation addition = (int a, int b) -> a + b; Operation multiplication = (int a, int b) -> a * b; System.out.println(t.operate(1, 2, addition)); // 3 System.out.println(t.operate(1, 2, multiplication)); // 2 Print p = m -> System.out.println(m); p.foo("hello"); // "hello" } }
Method References#
allow to point methods by names
can use on static, instance and constructors using new operators
ArrayList<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.forEach(System.out::println); // pass "println()" as static method reference
Functional Interfaces#
in
java.util.function
, only have single functionalitye.g Predicate interface has
test(Object)
that test object to be true or falseimport java.util.function.Predicate; public static void printEven(List<Integer> l, Predicate<Integer> p) { for (Integer n : l) { if (p.test(n)) { System.out.println(n); } } } List<Integer> l = Arrays.asList(1, 2, 3, 4); System.out.println("Even numbers:"); printEven(l, n -> n % 2 == 0); // print "2, 4"
Default Methods#
capability added for backward compatibility so that old interfaces can be used to leverage lambda expression
when a class implement two interfaces with same default methods, it needs to override the method or call one using super
starting form Java 8, interface can have static helper methods
interface A { default void hello() { System.out.println("Hello from A"); } static void foo() { System.out.println("Foo"); } } interface B { default void hello() { System.out.println("Hello from B"); } } class C implements A, B { public void hello() { A.super.hello(); A.foo(); } }
Streams#
abstract layer to process data in a declarative way
- Stream Characteristics
provide sequence of elements of specific type
take sources, such as Collections, Arrays or I/O resources, as input sources
support aggregate operations such as filter, reduce, find, etc.
results can be pipelined as most operations return stream itself
stream operations do iterations internally
Collection interface has
stream()
andparallelStream()
to generate a StreamList<Integer> myList = Arrays.asList(1, 2, 3); // forEach: iterate each element myList.forEach(System.out::println); // map: map each element to its correspond result myList.stream().map(n -> n * n).forEach(System.out::println); // 1, 4, 9 // filter: eliminate elements based on criteria List<Integer> oddList = myList.stream().filter(n -> n % 2 != 0).collect(Collectors.toList()); oddList.forEach(System.out::println); // 1, 3 // limit: reduce size of the stream myList.stream().limit(2).forEach(System.out::println); // 1, 2 // sorted: sort the stream myList = Arrays.asList(2, 3, 1); myList.stream().sorted().forEach(System.out::println); // 1, 2, 3 // parallelStream: alternative of stream for parallel processing myList.parallelStream().map(n -> n * n).forEach(System.out::println);
- Collectors
used to combine result of processing on elements of a stream
can be used to return a list or a string
can use statistics collectors to calculate all statistics when stream processing is done
List<Integer> myList = Arrays.asList(1, 2, 3); List<String> myStringList = Arrays.asList("hello", "world"); List<Integer> squareList = myList.stream().map(n -> n * n).collect(Collectors.toList()); squareList.forEach(System.out::println); // 1, 4, 9 String myListString = myStringList.stream().collect(Collectors.joining(", ")); System.out.println(myListString); // "hello, world" // statistics collectors IntSummaryStatistics stats = myList.stream().mapToInt(n -> n).summaryStatistics(); System.out.println(stats.getMax()); // 3 System.out.println(stats.getMin()); // 1 System.out.println(stats.getSum()); // 6 System.out.println(stats.getAverage()); // 2.0
Optional Class#
in
java.util.Optinal
container object used to represent null with absent value
has methods to handle values as available or not available instead of checking null values
inherits methods from
java.lang.Object
empty()
: return empty optional instance
equals(Object)
: true if argument is equal to this Optional
filter(Predicate)
: return Optional describing the value if it is present and matches argument, return empty Optional otherwise
flatMap(Function)
: apply argument if a value is present and return the result, otherwise return empty Optional
get()
: return value if present in this Optional, otherwise throwNoSuchElementException
hashCode()
: return hash code value of present value, 0 if no value
ifpresent(Consumer)
: invoke argument with value if present, otherwise do nothing
isPresent()
: return true if value is present
map(Function)
: apply argument if value if present, return Optional describing the result if result is non-null
of(T)
: return Optional with given present non-null value
ofNullable(T)
: return Optional describing argument if non-null, otherwise return empty Optional
orElse(T)
: return value if present, otherwise return argument
orElseGet(Supplier)
: return value if present, otherwise invoke argument and return the result
orElseThrow(Supplier)
: return the contained value if present, otherwise throw exception to be created by the argument
toString()
: return non-empty string of this Optional suitable for debuggingimport java.util.Optional; public class MyClass { public static void main(String[] args) { MyClass t = new MyClass(); Integer x = null; Integer y = Integer.valueOf(10); Optional<Integer> a = Optional.ofNullable(x); Optional<Integer> b = Optional.ofNullable(y); System.out.println(t.sum(a, b)); // 10 } public Integer sum(Optional<Integer> a, Optional<Integer> b) { System.out.println("First parameter: " + a.isPresent()); // false System.out.println("Second parameter: " + b.isPresent()); // true Integer x = a.orElse(Integer.valueOf(0)); Integer y = b.get(); return x + y; } }
Date/Time API#
in
java.time
immutable, no setter methods and more utility methods
- Local Date-Time
simplified and time-zones are not required
LocalDateTime currentTime = LocalDateTime.now(); System.out.println("Current DateTime: " + currentTime); LocalDate d1 = currentTime.toLocalDate(); System.out.println("date: " + d1); Month m = currentTime.getMonth(); int day = currentTime.getDayOfMonth(); System.out.println("Month: " + m + " day: " + day); LocalDateTime d2 = currentTime.withDayOfMonth(10).withYear(2000); System.out.println("Date 2: " + d2); LocalDate d3 = LocalDate.of(2000, Month.DECEMBER, 12); System.out.println("Date 3: " + d3); LocalTime d4 = LocalTime.of(22, 15); System.out.println("Date 4: " + d4); LocalTime d5 = LocalTime.parse("20:15:22"); System.out.println("Date 5: " + d5);
- Zoned Date-Time
to use with time zone
ZoneId id = ZoneId.of("Africa/Abidjan"); System.out.println("ZoneId: " + id); ZoneId current = ZoneId.systemDefault(); System.out.println("Current Zone: " + current);
- Chrono Units
in
java.time.temporal.ChronoUnit
, enum to replace integer values used for Date/TimeLocalDate current = LocalDate.now(); System.out.println("Current date: " + current); LocalDate nextWeek = current.plus(1, ChronoUnit.WEEKS); System.out.println("Next week: " + nextWeek); LocalDate nextMonth = current.plus(1, ChronoUnit.MONTHS); System.out.println("Next month: " + nextMonth); LocalDate nextYear = current.plus(1, ChronoUnit.YEARS); System.out.println("Next year: " + nextYear);
- Period & Duration
period: date based amount of time
duration: time based amount of time
LocalDate current = LocalDate.now(); System.out.println("Current date: " + current); LocalDate nextWeek = current.plus(1, ChronoUnit.WEEKS); System.out.println("Next week: " + nextWeek); Period p = Period.between(nextWeek, current); System.out.println("Period: " + p); LocalTime t1 = LocalTime.now(); Duration oneHour = Duration.ofHours(1); LocalTime t2 = t1.plus(oneHour); Duration d = Duration.between(t1, t2); System.out.println("Duration: " + d);
- Temporal Adjusters
to perform date mathematics
LocalDate current = LocalDate.now(); System.out.println("Current date: " + current); LocalDate nextTuesday = current.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)); System.out.println("Next Tuesday: " + nextTuesday); LocalDate firstInYear = LocalDate.of(current.getYear(), current.getMonth(), 1); LocalDate secondSaturday = firstInYear.with(TemporalAdjusters.nextOrSame( DayOfWeek.SATURDAY)).with(TemporalAdjusters.next(DayOfWeek.SATURDAY)); System.out.println("Second Saturday: " + secondSaturday);can use
toInstant()
to convert original Date and Calendar objectsDate current = new Date(); System.out.println("Current: " + current); Instant now = current.toInstant(); ZoneId currentZone = ZoneId.systemDefault(); LocalDateTime localDateTime = LocalDateTime.ofInstant(now, currentZone); System.out.println("Local date: " + localDateTime); ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, currentZone); System.out.println("Zoned date: " + zonedDateTime);
Base64#
simple: output mapped to a set of characters, A-Za-z0-9+/, encoder does not add any line feed and decoder rejects characters that are not in the set
url: output mapped to a set of characters, A-Za-z0-9+_, output is URL and filename safe
MIME: output mapped to MIME friendly format and represented in lines <= 76 characters each, and uses carriage return ‘r’ followed by linefeed ‘n’ as separator, no line separator at the end of encoded output
Base64.Decoder
andBase64.Encoder
classes implement encoder and decoder for byte data, inheriting methods from java.lang.Object
getDecoder()
: returnBase64.Decoder
that decode using basic type decoding scheme
getEncoder()
: returnBase64.Encoder
that encode using basic type encoding scheme
getMimeDecoder()
: returnBase64.Decoder
that decode using MIME type
getMimeEncoder()
: returnBase64.Encoder
that encode using MIME type
getMimeEncoder(int, byte[])
: returnBase64.Encoder
that encode using MIME type with given line length and line separator
getUrlDecoder()
: returnBase64.Decoder
that decode using URL and Filename safe type
getUrlEncoder()
: returnBase64.Decoder
that encode using URL and Filename safe typetry { String base64EncodedString = Base64.getEncoder().encodeToString( "HelloWorld".getBytes("utf-8")); System.out.println("Encoded with basic: " + base64EncodedString); byte[] base64DecodedBytes = Base64.getDecoder().decode(base64EncodedString); System.out.println("Decoded string: " + new String(base64DecodedBytes)); base64EncodedString = Base64.getUrlEncoder().encodeToString( "HelloWorld".getBytes("utf-8")); System.out.println("Encoded with url: " + base64EncodedString); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 10; ++i) { stringBuilder.append(UUID.randomUUID().toString()); } byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8"); String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes); System.out.println("Encoded with MIME: " + mimeEncodedString); } catch (UnsupportedEncodingException e) { System.out.println(e.getMessage()); }
JVM#
Life Cycle, Availability, Preparation for Threads, JVM Data Types, Class File, Bytecode
Execution Engine, JIT Compilation, Class Loading, Memory Management, Garbage Collection
base of the entire Java platform’s independence from specific hardware and OS, operating at the OS layer
JVM does not know about the specifics of Java programming language
the class files encapsulate JVM instructions or bytecodes, along with symbol table and supplementary information
any programming language that can be expressed in a valid class file can leverage JVM
JVM acts as interpreter for Java bytecode, managing memroy, multithreading, and various runtime services
even though Java programming language is platform independent, JVM is platform-specific and designed to adapt the host system
Life Cycle#
one application per JVM instance, ensuring independence and security of each Java application
- Instance Birth
JVM runtime instance is created when a Java application is launched
the instance is responsible for executing bytecode and managing runtime environment
- Execution
JVM instance runs the application by invoking
main()
, which should be public, static, returnvoid
, and acceptString[]
any class with proper
main()
can be the starting point for a Java application
- Application Execution
JVM executes the application, managing necessary resources
- Application Completion
JVM instance terminates once the application is executed
JVM can use native methods written and C or C++ and dynamically linked to interact and leverage platform-specific features
native methods provide access to OS and system resources that are not easily accessible through pure Java code
Availability#
concurrent processes ensure JVM continuous availability
- Timers
organise periodic events such as interrupts and repetitive processes
crucial in maintaining the synchrony of JVM operations
- Garbage Collector Processes
ensure efficient memory utilisation
manage memory by cleaning up objects that are no longer in use
- Compilers
convert bytecode into native code, also known as just-in-time (JIT) compilation
enhances the performance of Java applications
- Listeners
receive signals and information, and relay it to appropriate processes within JVM
Preparation for Threads#
- Memory Allocation
JVM allocates memory to the thread, includes dedicated portion of the heap
each thread has its own memory space, ensuring isolation
- Object Synchronisation
mechanisms, such as locks and monitors, are set up to access shared resources
helps prevent data corruption in multi-threaded applications
- Specific Registers
specific registers are part of the thread’s execution context
registers hold data and execution state information
- Allocate Native Thread
a native thread managed by the OS is allocated to support Java thread’s execution
native thread is responsible for executing the Java code
JVM is responsible to handle exception, ensure thread safety, and close it if necessary
every resources must be released when a thread completes execution
JVM Data Types#
- Primitives
JVM do not perform extensive type checking or runtime verification
boolean, number,
returnAddress
- Reference Values
objects, such as complex data structures, are type checked and verified at runtime
Class: to point to instances of classes
Array: to reference arrays, can be of primitive or objects
Interface: to point to objects that implement interfaces
reference values are always set to
null
initially, and can be set to it to release resources
- returnAddress
internal to JVM only to manage method invocations and returns, such as recursive method calls
method call stack or execution stack maintains a stack of
returnAddress
- boolean
managed using
int
type, 8 bitsno specific bytecode instruction for boolean operations
boolean arrays are treated as byte arrays
- null
a special reference value to indicate no valid object is associated with a reference
operations on it can lead to
NullPointerException
setting references to
null
is not a guarantee method for resource management
Class File#
vital relationship between compiled Java code and the JVM
adhere to a structured format curcial for the JVM to interpret and execute programs
Java Source -> Java Compiler -> Class File
use
javac File.java
to compile, andjavap -c File.class
to disassemble bytecodeClass File Structure
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }
- Headers
contain metadata such as Java version compatibility and constant pool
Magic Number: identifies a file as a Java class file,
0xCAFEBABE
Java Version Compatibility: indicates language features and specifications of the class file with
minor_version
andmajor_version
Constant Pool Reference: contains symbolic references for the interpretation and execution of Java programs, such as strings, class names, method signatures, and other constants
Access Flags: a set of binary values for access control of a Java class, e.g
ACC_PUBLIC
,ACC_FINAL
,ACC_SUPER
This & Super Class: point to constant pool entries of current class and its superclass
Interfaces, Fields & Methods Count: provide class structure blueprint for efficient memory allocation and execution planning
Attributes: additional information about the class
- Fields
field declaration includes data type, unique id, optional modifiers for visibility, accessibility, and behaviour
Instance Variables: related to a class instance and have unique set of values for each object
Class Variables: shared among all class instances, denoted with
static
when a field is declared, name and type are stored as entries in the constant pool
- Methods
encapsulate behaviour within classes
return type is key to know the nature of the data generated during execution
Bytecode#
platform-independent low-level representation of Java code, comprehensible by JVM
bridge between high-level Java code and machine-specific language
bytecode enables Java programs to run on any device equipped with a JVM
bytecode instructions operate on specific types of values
generated bytecode can vary depending on the JVM version and vendor
- Integer Operations
i
: e.g.iload
(load int),iadd
(add int), operate as 32-bit signed integersas Boolean values are integers, integer arithmetic and logical instructions are used
- Long Operations
l
: e.g.lload
(load long),lmul
(multiply long), operate as 64-bit signed long integers
- Short Operations
s
: e.g.sload
(load short), operate on 16-bit signed short integers
- Byte Operations
b
: e.g.bload
(load byte), operate on 8-bit signed byte integers
- Char Operations
c
: e.g.caload
(load array of char), operate on 16-bit Unicode characters
- Float Operations
f
: e.g.fload
(load float), operate on 32-bit single-precision floating point
- Double Operations
d
: e.g.dload
(load double), operate on 64-bit double-precision floating point
- Reference Operations
a
: e.g.aload
(load reference),aload_0
(loadthis
onto stack), operate on object references
- Arithmetic Operations
operate on the first two values on the
operand
stackAddition:
iadd
,ladd
,fadd
,dadd
Subtraction:
isub
,lsub
,fsub
,dsub
, subtract the second from the firstMultiplication:
imul
,lmul
,fmul
,dmul
Division:
idiv
,ldiv
,fdiv
,ddiv
, divide the first by the secondRemainder:
irem
,lrem
,frem
,drem
, remainder of dividing the first by the secondNegation:
neg
,lneg
,fneg
,dneg
, changes the signLocal Variable Increment:
iinc
, increment by a constant value
- Bitwise Operations
Shift:
ishl
,ishr
,iushr
,lshl
,lshr
,lushr
, can shift right with or without sign extensionBitwise:
ior
,lor
,iand
,land
,ixor
,lxor
- Comparison Operations
1 = greater, -1 = less, 0 = equal
dcmpg
,dcmpl
,fcmpg
,fcmpl
,lcmp
- Constant Operations
ldc
: push value from constant pool onto the operand stack
- Value Conversions
transforming data between different types, from integers to longs, floats and doubles, and vice versa, while maintaining precision
i2l
,i2f
,i2d
,l2f
,l2d
,f2d
,i2b
,i2s
,i2c
,l2i
,f2i
,f2l
,d2i
,d2l
,d2f
when converting values, always consider for precision loss and overflow risks
- Object Mainpulation
new
creates an object by allocating memory and invoking its constructorthe reference to the newly created object is placed on the stack
newarray
: create primitive type array
anewarray
: create object references array
multianewarray
: create multi-dimensional array
getfield
: get instance field value from object
putfield
: set instance field value in object
getstatic
: get static field value from class
putstatic
: set static field value in classGetting Values from Respective Array:
baload
(byte),caload
(char),saload
(short),iaload
(int),laload
(long),faload
(float),daload
(double),aaload
(reference)Storing Values into Respective Array:
bastore
,castore
,sastore
,iastore
,lastore
,dastore
arraylength
: get array length and pushes it onto the stack
instanceof
: check if object is an instance of particular class
checkcast
: check and cast object to a class, ensure type compatibility
- Method Calls & Returns
invokevirtual
: call method from instance, foundation of dynamic and polymorphic behaviour in Java
invokeinterface
: call method from interface
invokespecial
: call private or superclass method
invokestatic
: call static method, which does not rely on instance
invokedynamic
: create object dynamicallyReturn:
ireturn
,lreturn
,freturn
,dreturn
,areturn
athrow
: throw an exception or error
ACC_SYNCHRONIZED
: method is declared withsynchronized
keyword
monitorenter
: method enters the monitor, ensuring exclusive execution
monitorexit
: exit the monitor (intrinsic lock)
- Branching
branch by comparing values on the stack
Top value with 0:
ifeq
,ifne
,iflt
,ifle
,ifgt
,ifge
Top value with null:
ifnull
,ifnonnull
Two ints:
if_icmpeq
,if_icmpne
Second int to First:
if_icmplt
,if_icmple
,if_icmpgt
,if_icmpge
Two Object References:
if_acmpeq
,if_acmpne
Switch with consecutive integer cases:
tableswitch
lookupswitch
: similar totableswitch
, but support sparse case valuesUnconditional branch to target:
goto
,goto_w
(wide index)Jump to Subroutine and save Return Address on stack:
jsr
,jsr_w
(wide index)Return from Subroutine:
ret
(using address saved by previousjsr
)
- Type Descriptor
each parameter and method is assigned to indicate data type
B (byte): signed 8-bit int
C (char): Unicode character
D (double): double precision
F (float): single precision
I (int): 32-bit int
J (long): 64-bit int
L Classname (reference): instance of a class
S (short): 16-bit short int
Z (boolean): true or false
[ (array reference): array with element type
e.g. [L Classname (array of objects of a class), [ [B (2D array of bytes)
- Method Descriptor
locals
: number of local variables
stack
: maximum stack size during method execution
args_size
: number of arguments passed to the method
this
is passed as argument for instance method, but not for static method as it belongs to the class itself
Execution Engine#
dynamically translates bytecode into native machine code during program execution
- Stack-based Execution Model
employed by JVM that manipulates an operand stack
operands are pushed and popped as bytecode instructions are interpreted
although platform independent, bytecode interpretation cannot consistently deliver peak performance due to additional abstraction layer
- Execution Steps
Loading: the class loader locates and loads the compiled class files, including the core libraries and user-defined classes
Verification: loaded bytecode is verified for language specifications to prevent harmful code execution
Preparation: memory is allocated for class variables and static fields with default values
Resolution: symbolic references are resolved to concrete references so that classes and methods can be linked correctly
Initialisation: the class is initialised by executing static blocks and variables
Execution:
main()
or entry point is invoked and the program begins execution
- Optmisations
JVM optimises performance within each runtime using various techniques
e.g. JIT, and caching of frequently accessed classes and resources
optimisations are lost when the application stops
projects such as Project Leyden and CRaC (used by AWS Lambda SnapStart) aim to improve startup time and overall footprint of Java programs
- System Operation Layers
Hardware: physical components, foundation of all higher layers
ISA: define the language between software and hardware, JVM interacts indirectly through OS
OS: manage resources and mediator between application software and hardware, JVM utilises its services for platform-independent environment
Application: interact with OS to execute tasks
JIT Compilation#
- Interpreter Level
real-time translation of individual bytecode to machine code during execution
gives quick startup and platform independence, but interpretation process can impact execution speed
balance between agility and adaptability
- Baseline JIT Compilation
also called the C1 compiler
identifies frequently executed code segments or hotspots, and dynamically compiles them into machine code at runtime
compiled code is stored in memory within the same runtime, reducing repeated interpretation and improving execution speed
- Dynamic Adaptation
compiler continuously monitor, identifies and selectively compile hotspots, based on the evolving runtime behaviour
baseline JIT compiler can optimise most impactful areas for immediate performance gains
- Code Cache
dynamically stores optimised compiled code snippets ready to be executed
provides swift access during subsequent invocations
Class Loading#
allow to adapt and extend functionality during runtime
N
: binary representation for a class or interface
L
: class loader
C
: specified class or interface associated withN
when JVM asks
L
to locateN
,L
loadsC
- Direct Loading
L
acquire the binary representation and instruct JVM to instantiateC
from it
- Indirect Loading
L
give the loading task to another class loaderthe delegated class loader may load
C
directly, or use further delegations untilC
is loaded
- Bootstrap Class Loader
necessary part of the JVM
JVM checks if the loader is recorded as the initiator for a given class or interface, and the class or interface is present
if not recorded, the bootstrap class loader finds a representation, asks JVM to derive the class from it, and creates it
- User-defined Class Loader
subclass of
ClassLoader
abstract classallows apps to customise how the JVM dynamically generates classes
JVM checks if the loader is recorded as the initiator for a given class or interface, and the class or interface is present
if not recorded, JVM invokes
loadClass()
, asking to directly load and create, or delegate the loading process to another class loader
Memory Management#
JVM auto manages memory assignment and cleanup
- Program Counter Register
area within a thread’s memory storing address of currently executing instruction
critical for sequential order of program execution within a thread
Pointer: directs the thread to the next instruction
Return Address: ensures return to the previous execution point after method completion
PC value for non-native methods is the instruction address
PC value for native methods is the pointer
- Stack
for method calls and local variables, separate stack for each thread
Frame: store information related to method execution, such as parameters and local variables
a new frame is inserted when a thread calls a method, and discarded when concludes
Local Variables: specific to currently executing method for intermediate results and relevant parameters, accessed through indexing
Operand Stack: LIFO, dynamically stores constants, local variables, field values, and method results
Frame Data: information about method’s execution context, including references to current class and method
a frame also handles partial results, dynamic linking, returning values for methods, and managing exceptions
implementation-specific details, such as debugging information, can be added to a frame
StackWalker API allows access to stack frame information
sizes of the local variable array and operand stack are determined at compile-time
single local variable can hold Boolean, byte, char, short, int, float, reference, or returnaddress
pairs of local variables can hold long or double
- Native Method Stack
dedicated memory for native methods written in languages such as C or C++
operate separately from Java stacks, and handle execution of native code
JVM may allocate stacks per thread in fixed or dynamic sizes
programmers may be able to control the initial, maximum and minimum stack sizes
can have
StackOverflowError
, also affecting the Java stackdynamic expansion can cause
OutOfMemoryError
- Method Area
stores class level data that contains method code, static variables, and constant pool
dedicated space for each loaded class, and shared among all threads
logically part of the heap, but may differ in garbage collection and compaction policies
programmers may be able to control its fixed or dynamic size
can have
OutOfMemoryError
- Heap
dynamic and shared memory to store objects during runtime
unused memory is reclaimed by garbage collector
programmers may be able to control the initial, maximum and minimum sizes, being fixed or dynamic non-contiguous
can have
OutOfMemoryError
reference objects within have two pointers, one towards Object Pool and other towards constant pool
vectors contain size and reference list fields
Garbage Collection#
regularly scans, identifies, and reclaims memory of unreferenced objects to prevent memory leaks
multiple garbage collectors exist within the JVM
can check GC with
java -Xlog:gc* -version
- Object Generations
heap is divided into different generations, and different collection algorithms are applied to each
younger objects are collected more frequently, as they are more likely to become unreachable
older objects undergo less frequent and more comprehensive garbage collection
short-lived objects are quickly identified and collected
modern GCs use algorithms and heuristics to optimise memory management based on application’s behaviour
- Mark and Sweep Algorithm
Mark: objects are marked reachable or unreachable
Sweep: unreachable objects are reclaimed
can increase CPU usage and object cleanup scheduling overhead
memory will be fragmented, impacting efficiency of the app
- Serial GC
simple single-threaded collector, integrates with Mark and Sweep
halts the app execution during collection
straightforward sequential memory management, but can impact user experience
suitable for apps with limited resources that accept brief pauses
-XX:+UserSerialGC
,-XX:-UseSerialGC
: enable and disable Serial GC
-XX:NewRatio=<value>
: set ratio of young generation to old
-XX:NewSize=<size>
: set initial size of young generation
-XX:MaxNewSize=<size>
: set max size of young generation
-XX:SurvivorRatio=<value>
: set ratio of Eden space to survivor space
-XX:MaxTenuringThreshold=<value>
: set max tenuring threshold for objects in young
-XX:TargetSurvivorRatio=<value>
: set survivor space size as a percentage of young generation size
-XX:PretenureSizeThreshold=<size>
: objects larger than the value go to the old
-XX:MaxHeapSize=<size>
: set max heap size
- Parallel GC
use multiple threads for collection, suitable for large-scale, data-intensive apps
heap is divided into sections, and parallel collection is performed
CPU usage increases, and can impact app’s responsiveness
-XX:+UseParallelGC
,-XX:-UseParallelGC
: enable and disable Parallel GC
-XX:ParallelGCThreads=<value>
: set number of threads for collection
-XX:MaxGCPauseMillis=<value>
: set max pause-time for collection
-XX:GCTimeRatio=<value>
: set ratio of collection time to app time
-XX:UseAdaptiveSizePolicy
: enable adaptive sizing policies for heap and survivor spaces
-XX:AdaptiveSizeThroughPutPolicy
: configure adaptive sizing policy for throughput-oriented collection
-XX:AdaptiveSizePolicyOutputInterval=<n>
: set interval in number of collections for adaptive sizing policy output
-XX:ParallelGCVerbose
: enable verbose output
- Garbage-First GC
also called G1, default collector since Java 9, balance between low latency and high throughput with predictable pause times
suitable for interactive and real-time applications
heap is divided into uniformly sized regions, and categorised as Eden, survivor, and old
Liveness Space: regions with live objects, which are actively referenced by the app
identifies and prioritises regions with the least live data for collection, thus reducing frequency and duration of garbage collection pauses
uses adaptive collection strategies based on the app’s dynamic behaviour
-XX:+UseG1GC
,-XX:-UseG1GC
: enable and disable G1 GC
-XX:G1HeapRegionSize=<value>
set size of collection regions
-XX:MaxGCPauseMillis=<value>
: set max pause-time for collection
-XX:InitiatingHeapOccupancyPercent=<value>
: set heap occupancy percentage to start collection cycle
-XX:G1NewSizePercent=<value>
: set heap size percentage to use as minimum for young
-XX:G1MaxNewSizePercent=<value>
: set max heap size percentage to use as max for young
-XX:ParallelGCThreads=<value>
: set number of threads for collection
-XX:ConcGCThreads=<value>
: set number of parallel collection threads for concurrent phase
-XX:G1ReservePercent=<value>
: set target percentage of heap to reserve as space for future collection cycles
-XX:G1TargetSurvivorOccupancy=<value>
: set target occupancy of survivor space within each region
-XX:G1HeapWastePercent=<value>
: set target percentage of wasted space within a region before it is considered for reclamation
- Z GC
introduced in Java 11, minimal pause times with low latency and responsiveness
suitable for real-time systems, and interactive apps
perform major collection tasks concurrently with the application threads
Multi-mapping: multiple virtual addresses point to the same physical location
may not have same throughput as other collectors, and not be optimal for apps with large heaps
can resize the heap dynamically, adapting to the app’s demand
-XX:+UseZGC
,-XX:-UseZGC
: enable and disable Z GC
-XX:MaxGCPauseMillis=<value>
: set max pause-time for collection
-XX:GCPauseIntervalMillis=<value>
: set max interval between pauses
-XX:ConcGCThreads=<value>
: set number of parallel collection threads for concurrent phase
-XX:ParallelGCThreads=<value>
: set number of parallel collection threads for parallel phase
-XX:ZUncommitDelay=<value>
: set delay for uncommitting memory after a region is no longer needed
-XX:ZUncommitDelayMax=<value>
: set max delay for uncommitting memory after a region is no longer needed
-XX:ZUncommitDelayPolicy=<adaptive\|fixed>
: set uncommit delay policy
-XX:SoftMaxHeap=<value>
: set soft max heap size
-XX:ZHeapSize=<value>
: set heap size
Tuning & Ergonomics#
Profiling: to gain insights into the runtime behaviour of applications
- Ergonomics
adaptive tuning capabilities embedded within the JVM to auto adjust configuration based on hardware and app behaviour
to enhance performance and responsiveness of Java apps without manual intervention
e.g. adjusting garbage collection algorithms, heap sizes and thread counts
Premature Optimisation: JVM making assumptions about app behaviour without sufficient runtime information
default configuration set by ergonomics is often considered premature optimisation
Serial GC: usually default for single-processor and memory limited system
G1 GC: more than two processors, and sufficient amount of memory (1792 MB or more)
default max heap size: 50%, 25% or 1/64 of available memory
automated ergonomics may not always produce optimal performance
manually configuring GC parameters gives more control and predictability over JVM
Alternative JVMs#
SDKMAN#
allows to manage and switch between multiple SDKs and versions
can choose different JVM vendors, without the trouble of manual installations
GraalVM#
support multiple languages with ahead-of-time (AOT) compilation, removing JIT compilation during runtime
allow to integrate different languages within a single application
slight memory usage increase, initial compilation may be longer than other JVMs, and certain language features or libraries may not be fully compatible
suitable for microservices and serverless architecture, high-performance computing, and resource-intensive apps
always consider trade-offs and project constraints to get performance gains
- Native Image
compile apps into standalone executables, removing the need for a VM
increase startup time and runtime performance, and reduce memory footprint
suitable for short-lived apps or responsive microservices
since JIT is removed, runtime optimisation is no longer available
need to manage native libraries, reflective access, and other factors to get optimal results
need to handle dynamically loaded or generated code
may not capture the complete runtime profile for apps with complex runtime behaviour
can have challenges for cross-platform compatibility
image size can increase due to dependencies
Creating Native Image
javac -d build src/main/java/expert/os/App.java jar --create --file App.jar --main-class expert.os.App -C build . native-image -jar App.jar
OpenJ9#
scalability, resource efficiency and swift startup times
suitable for cloud-based apps, microservices, and responsive environments
combine advanced JIT compilation techniques and aggressive memory management
Corretto#
Amazon Corretto provides LTS, and integration with AWS
built on the foundation of OpenJDK and compatible with it
secure, stable, and improve responsiveness and throughput
Azul#
built on OpenJDK
- Zulu
open-sourced and has a 3-month release cycle
include CraC feature for fast startup, and can integrate with JavaFX
- Zing
has C4 (Continuously Concurrent Compacting Collector) garbage collector, and Falcon JIT compiler for low latency and pauseless collection
enhanced runtime performance, suitable for high-throughput and low-latency apps
scalable for both small-scale and enterprise-level systems
offer LTS and stability
Semeru#
based on OpenJ9, resource efficiency and rapid startup time
container friendly, suitable for cloud-native apps and microservices
contain AOT compilation
Temurin#
formerly AdoptOpenJDK, production-ready builds of OpenJDK
provide timely updates and security patches
transparent and collaborative from Eclipse community
Dragonwell#
in-house OpenJDK implementation at Alibaba
optimised for scaling demands of eCommerce and financial and logistics apps
Kona#
Tencent default JDK for cloud computing, big data and other Java apps
production-ready distribution of OpenJDK with LTS
Liberica#
open source implementation built from OpenJDK from BellSoft
pass Java Compatibility Kit (JCK), and support JavaFX across all versions
Mandrel#
specialised distribution of GraalVM from Red Hat
make native image generation for Quarkus apps easier
Microsoft OpenJDK#
provide LTS binaries for Java 11 and 17 on x64 server, macOS, Linux, and Windows
contain backported fixes and enhancements
SapMachine#
downstream version of OpenJDK
to support customers and partners for Java apps within SAP ecosystem
References & External Resources#
Santana, Otavio. (2023). Mastering the Java Virtual Machine. Birmingham, UK: Packt Publishing