Advanced class design:

Anonymous classes are implicitly final and it can never be static.

static method > default method

default method > abstract method

default method > default method (re-declared with different implementation)

default method > static method

static method > non-static method

non-static method > static method

Static methods can never be abstract.

Static interface methods must be invoked using the interface name instead of reference variable – otherwise an error will be raised.

When overriding you set of exceptions may be smaller (none is ok) or the same but no bigger; although you can add as many unchecked exceptions as you need.

Differences between static and default methods in Java 8

  1. Default methods can be overriden in implementing class, while static cannot.
  2. Static method belongs only to Interface class, so you can only invoke static method on Interface class, not on class implementing this Interface
  3. Both class and interface can have static methods with same names, and neither overrides other!

Lambda Expressions and Functional Interfaces:

A local variable has to be final or effectively final to be accessed from an inner class or lambda expression.

All of the java.lang package wrapper classes are immutable: Boolean, Byte, Character, Double, Float, Integer, Long, Short, String.

The parameter names in the lambda expression exist in the same scope as the method or the block in which the lambda expression is created.


  • Enum constructor is always private.
  • Enums are implicitly final.
  • Enums cannot extend anything (they already extend java.lang.Enum) but can implement some interface.
  • You cannot clone enums.
  • Implement Comparable (can be added to sorted collections)
    • important note here: elements are compared by the order they were defined! not alphabetically or some other way!
  • these methods are important:
    • values() – an array of all constants
    • valueOf() – return a constant from Enum
    • ordinal() – index of constant starting with 0
    • name() – name of the constant 

Access modifier of constructors match that of the class it belongs to.

Outer classes have access to private inner members of inner classes.

Generic methods:

Generic methods are methods that take generic parameter types – that is they may be used on different types (classes, interfaces) not just one (as in case of simple methods).

Difference between simple and generic method:

  • Simple method:
    public static void methodName(){}
  • Generic method:
    public static <T> void methodName(){} 


Subtyping does not work for generic parameters.

List<Number> list = new ArrayList<Integer>(); // this is wrong


Wildcard parameter types allow for subtyping.

List<Number> list = new ArrayList<Number>();


List<Number> list = new ArrayList<>();

Wildcard parameters

Wildcard parameters can be used when you want just to use methods that access objects.

List<?> list = new ArrayList<Integer>();

Wildcard parameters cannot be used when you plan to use methods that modify objects.


List<?> list = new ArrayList<Integer>();


Collection nameMay add null keysMay add null values
HashSetNot applicableYes
HashMap YesYes 


TreeMap methods to be learned:

  • pollFirstEntry() – removes first map entry and returns removed Map.Entry, null if map empty
  • pollLastEntry() – removes last map entry and returns removed Map.Entry, null if map empty
  • tailMap(K fromKey, boolean inclusive) – returns a view of the map starting with fromKey and may include that element or not depending on inclusiveView here is an important term – if you modify the map that is a view of another map the source map will get modified.


  • can act as Queue and as Stack – so it has methods that reproduce behavior of:
    • Queue (FirstInFirstOut), has methods:
      • add(e) / offer(e) 
        • add(e) – returns true if element added or throws IllegalStateException if queue already has maximum capacity
        • offer(e) – returns  true if ok or false if not – no exceptions
      • remove() / poll()
        • remove() – removes and returns head of queue or throws NoSuchElementException if queue is empty
        • poll() – removes and returns head or null if queue is empty
      • element() / peek()
        • element() – returns head of queue or throws NoSuchElementException if queue is empty
        • peek() – returns head or null
    • Stack (LastInFirstOut), has methods:
      • push(e) – equal to addElement(e); pushes an item to top of stack. Returns the item pushed.
      • pop() – removes top element of stack and returns it (just like remove() in queue) or throws EmptyStackException if stack was empty.

Nota bene: In Stack peek() may throw an EmptyStackException whilst in Queue peek() would just return null.

⇐ Out (Queue)  Dequeue queue behaviour ⇐ In
  • removeFirst
  • pollFirst
  • getFirst
  • peekFirst
  • addLast
  • offerLast
⇒ In (Stack)
⇐ Out (Stack)
 Dequeue stack behaviour  

  • addFirst


Collections.sort(someCollection, null) – will just use the default sorting for someCollection – no exception will be thrown.

Sorting strings rule:
spaces < numbers < uppercase < lowercase

From java.util only 2 collections are thread-safe:

  1. Vector
  2. Hashtable

You can turn a simple collection in a thread-safe one using java.util.Collections.synchronizedCollectionName() like:

Set s = Collections.synchronizedSet(new HashSet());

Since Java 5 there is a special package with thread-safe collections – java.util.concurrent.

Keys of a TreeMap have to be comparable between each other.

Integer class hashCode() returns the int value.

Exceptions and Assertions

Any exception that is thrown while closing a resource is added to the list of suppressed exceptions of the exception thrown while opening a resource (or thrown from the try block.)

The exception parameter in a multi-catch clause is implicitly final. Thus, it cannot be reassigned. If there is only one exception in the catch clause exception parameter is not final.

Resources are closed automatically at the end of the try block in reverse order of their creation. The close method is called on all the resources one by one even if any resource throws an exception in its close method.

An overriding method must not throw any new or broader checked exceptions than the ones declared in the overridden method. This means, the overriding method can only throw the exceptions or the subclasses of the exceptions declared in the overridden method. It can throw any subclass of Error or RuntimeException as well because it is not mandatory to declare Errors and RuntimeExceptions in the throws clause. An overriding method may also choose not to throw any exception at all.

When an assertion fails, a programmer should always throw the exception.

AutoCloseable‘s close method throws Exception while Closeable‘s close method throws IOException.

In try-with-resources resources are closed at the end of the try block and before any catch or finally block.

Using the Java SE 8 Date/Time API

Important classes in java.time

java.time contains following subpackages:

  1. java.time.temporal – access date/time fields and units
  2. java.time.format – format input/output of date/time objects
  3. – handle timezones
  4. java.time.chrono – support foreign calendar systems


java.time.LocalDate – date without time or timezone. Represented in ISO-8601 – YYYY-MM-DD – 2018-10-26.

  • – current date in YYYY-MM-DD formate ex. 2018-05-07. System time, default timezone.
  • LocalDate.of(2018, 1, 5) – not 01

To avoid ambiguity with months – java.time.Month enum may be used.

ex.: LocalDate.of(2018, Month.MAY, 05) 


Represents time without dates or time zones. Time is in ISO-8601 calendar system format: HH:MM:SS.nanosecond.


Represents both date and time – without time zones.

You can extract LocalDate and LocalTime from LocalDateTime using .toLocalDate() and .toLocalTime().

When printing LocalDateTime you get something like 2018-10-03T16:14:11.344. T is a separator between date and time.


Instant is used for timestamps. It stores seconds since the beginning of Unix epoch (January 1, 1970 at 00:00:00). Negative values are stored for any time before that moment.

Instant stores seconds in long types variables and nanoseconds in int type.

Nota bene:’s time zone is Greenwich not system default time zone.


Used for measuring an amount of time in yearsmonthsdays.

Important methods:

  • Period.between(LocalDate a, LocalDate b)
  • Period.of(int years, int months, int days)
  • Period.ofWeeks(int a)
  • Period.ofDays(int a)
  • Period.ofMonths(int a)
  • Period.ofYears(int a)
  • Period.parse(CharSequence string) – ex. Period.parse(“P2Y1M3D”)

Printing returns something like: P3Y2M4D.

P – period


Duration is similar to Period but it works with hoursminutes, seconds, milliseconds (millis) and nanoseconds (nanos).

When printed you obtain: PT4H3M46S.

Note the smallest unit being printed is S. Millis and nanos are printed as decimal formatted units of seconds. 

If a section has zero value it is omitted.

Duration duration = Duration.ofMillis(2);

Prints: PT0.002S

Duration duration = Duration.ofNanos(3);

Prints: PT0.000000003S

Enum ChronoUnit (implementation of TemporalUnit – the only one as of version 8) is very handy when you need to work with time-related constructos and so on.

Duration duration = Duration.of(2, ChronoUnit.NANOS);

Working with time zones and daylight savings

Time zones are identified by the offset from GMT(Greenwich Mean Time – GMT aka UTC/Greenwich).


Zone ids are held in a Set<String>. Like: “Asia/Kolkata”



ZoneId zoneId = ZoneId.of("America/Marigot");
System.out.println(zoneId.getDisplayName(TextStyle.FULL, Locale.US));
System.out.println(zoneId.getDisplayName(TextStyle.SHORT, Locale.US));

Atlantic Time


ZoneOffset represents zone offset from GMT (UTC/Greenwich).




LocalDate + LocalTime + ZoneId = ZonedDateTime

LocalDate localDate =;
LocalTime localTime =;
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDate, localTime, zoneId);


LocalDateTime + ZoneId = ZonedDateTime

LocalDateTime localDateTime =;
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId); // or localDateTime.atZone(zoneId)


I/O Fundamentals

Console is used for interaction with user – usually through command-line and keyboard. 

Console is in package.

User typed info (character format) is read using:

  • readLine()
  • readPassword()

Console has a Reader and a PrintWriter associated with it which can be received using:

  • reader()
  • writer()

For Console to be available – JVM must be started from command-line and standard input & output streams should not be redirected. As opposite to JVM being started by some background job scheduler. If Console is there it is acquired by System.console(); if not – this method returns null.

Return type of readPassword() is char[] and not a String.

Serializable is a “marker” interface – meaning it does not declare any methods.

During deserialization no-args constructor of the first non-serializable super class is invoked.

“Because readObject() can read any serializable object, a cast to the correct type is required. With that in mind, the class file must be accessible from the system in which the restoration occurs. In other words, the object’s class file and methods are not saved; only the object’s state is saved.”

Non-serializable fields of a serializable object must be marked as transient. Transient and static fields are never serialized.

Although Serializable interface has no methods defined it IS possible to change the default behavior of the JVM for serialization deserialization. For that we have to define 2 methods in our class:

  • private void writeObject(ObjectOutputStream out) throws IOException;
  • private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

JVM looks for these methods and in case they are there uses them for serializing/deserializing purposes. Both ObjectOutputStream & ObjectInputStream have methods that guarantee the default serialization/deserialization functionality:

  • out.defaultWriteObject()
  • in.defaultReadObject()

These methods are added to the body of writeObject/readObject and are surrounded with custom logic. All of these manipulations allow for customization of serialization/deserialization.

The order of values to be read explicitly in readObject must be exactly the same as the order they were written in writeObject.

File class:

  • mkdir() – will create last directory in path
  • mkdirs() – will create directories recursively – all missing ones in the supplied path
  • getParentFile() – returns a File object
  • getParent() – returns a String

FileOutputStream has a constructor with append as 2nd argument – setting it to true will make FileOutputStream append characters to the end of the file instead of beginning.

OutputStream doesn’t provide methods for writing primitives – writes bytes only.

DataInput/DataOutputStream have specialized methods for writing primitives – ex. writeInt(), writeChar() etc.

Data streams support binary I/O of primitive data type values (booleancharbyteshortintlongfloat, and double) as well as String values.

DataOutputStream can only be created as a wrapper for an existing byte stream object.

ObjectInputStream and ObjectOutputStream are used with serializable objects + primitives (covers DataInput/DataOutputStream functionality).

A stream can only contain one copy of an object, though it can contain any number of references to it. However, if a single object is written to two different streams, it is effectively duplicated — a single program reading both streams back will see two distinct objects.

PrintWriter has overloaded print methods for writing various primitives. None of the PrintWriter methods throw any I/O exceptions because they suppress the errors in writing and set an internal flag for error status instead.
The checkError method returns true if there has been a problem in writing.

There is no way you can chain a Reader and a Writer together.

Buffered streams are used to wrap unbuffered ones.

There are 4 different buffered stream types:

  1. BufferedInputStream
  2. BufferedOutputStream
  3. BufferedReader
  4. BufferedWriter

For wrapping you just pass the unbuffered stream to the buffered stream’s constructor:

BufferedReader br = new BufferedReader(new FileReader("filename.txt"));

BufferedReader – java.nio.file.NoSuchFileException

FileInputStream, FileOutputStream, RandomAccessFile –


Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.