Show Menu
Cheatography

Java refresher Cheat Sheet (DRAFT) by

Java refresher for devs

This is a draft cheat sheet. It is a work in progress and is not finished yet.

Data types in Java

Type
Size (bits)
Min
Max
Ex
byte
8
-27
-27 -1
byte b = 100;
short
16
int
32
long
64
float
32
double
64
char
16
boolean
1

Checked and unchecked exceptions and errors

Why "­(un­)ch­eck­ed"? Compiler can't anticipate logical errors that arise only at runtime, can't check for those types of problems -->­"­unc­hecked except­ion­s". Typically unchecked comes from logical errors­/faulty logic that can occur anywhere.
Checked Exceptions
- Except­ional conditions that an app should anticipate and recover from.
- E.g.
FileNo­tFo­und­Exc­eption
occurs when a method is trying to read a file that does not exist
- Checked at compile time - should be stated in method signature if throwing an exception. If exception could potent­ially be thrown in code, must handle it too.
Does not inherit from
Runtim­eEx­ception
or
Error
? then its a checked exception
Unchecked Exceptions
- Except­ional conditions that app cannot antici­pat­e/r­ecover from
- E.g. is
NullPo­int­erE­xce­ption
- when method is expecting non-null value but receives null.
- Not checked at compile time. Not required to be in method sig, not required to be handle it in code.
Is inherited from
Runtim­eEx­ception
? then its an unchecked exception
Errors
E.g.
OutOfM­emo­ryError
- when app is trying to use more memory than avail on system
- Not checked at compile time and not usually thrown from app code.

Switch statements

String direction = getDirection(); 
switch (direction) {
    case "left":
        goLeft(); 
        break;
    case "right":
        goRight(); 
        break;
    default: 
        return "unknown";
}

// Java 14+
return switch (shirtNum) {
     case 1 -> "goalie";
     case 2 -> "left back";
     case 3, 4 -> "centre back";
     case 6, 7, 8 -> "midfielder";
     default -> throw new IllegalArgumentException("Invalid shirt number: " + shirtNum);
}
- Default case optional, but largely good practice to include one

Streams

- represents sequence of elements and operating on those elements. Not data structures but take input from collec­tions, arrays, or I/O channels.
- key benefits: declar­ative, readable code; parallel operat­ions; builtin operat­ions; less boiler­plate
Creating from a collection
List<S­tri­ng> list = Arrays.as­Lis­t("a­ppl­e", "­ban­ana­", "cherry");
Stream<String> stream­Fro­mList = list.s­tre­am();
Creating a stream from an array
String[] array = {"ap­ple­", "­ban­ana­", "cherry"};
Stream­<St­rin­g> stream­Fro­mArray = Arrays.st­rea­m(a­rray);
Creating stream using Stream.of
Stream­<St­rin­g> stream­OfE­lements = Stream.of­("ap­ple­", "­ban­ana­", "­che­rry­");
Creating empty stream
Stream­<St­rin­g> emptyS­tream = Stream.em­pty();
Creating infinite streams
Stream­<In­teg­er> infini­teS­tream = Stream.it­era­te(0, n -> n + 1);

Primitive streams

mapToInt()
vs
map()

-
map(St­rin­g::­length)
returns a
Stream­<In­teg­er>
(stream of
Integer
objects)
-
mapToI­nt(­Str­ing­::l­ength)
returns an
IntStream
(stream of primitive
int
s)
Specia­lised primitive streams
-
IntStream
,
LongStream
,
Double­Stream

These streams have additional operations not available on regular streams:
- sum:
intStr­eam.sum()

- average:
intStr­eam.av­erage()

- statis­tics:
intStr­eam.su­mma­ryS­tat­ist­ics()

Using primitive streams avoids boxing­/un­boxing overhead when dealing with numeric operations

Another stream ex.

List<Person> people = Arrays.asList(
    new Person("John", 25), 
    new Person("Sarah", 32), 
    new Person("Mike", 17), 
    new Person("Emily", 25)
); 

// Find names of adults, sorted alphabetically
List<String> adultNames = person.stream()
    .filter(person -> person.getAge() >= 18)
    .map(Person::getName)
    .sorted()
    .collect(Collectors.toList()); 

System.out.println(adultNames); // [Emily, John, Sarah]

double averageAge = people.stream()
    .mapToInt(Person::getAge)
    .average()
    .orElse(0.0);

System.out.println("Average age: " + averageAge);

Method references

Syntax
Equivalent Lambda
Meaning
object­::i­nst­anc­eMethod
x -> object.in­sta­nce­Met­hod(x)
Use object as the target for each call
Class:­:st­ati­cMethod
x -> Class.s­ta­tic­Met­hod(x)
Call a static method
Class:­:in­sta­nce­Method
(obj, arg) -> obj.in­sta­nce­Met­hod­(arg)
Useful in sorting or grouping

Concur­rency: creating threads

// 1. by inheriting from Thread class
public class ExampleThread extends Thread {
    @Override // note override! invoked when thread starts
    public void run() {  // we do NOT call RUN!!
        // contains all the code to execute when starting thread
        System.out.println(Thread.currentThread().getName());
    }
}

// start new thread..
public class ThreadExamples {
    public static void main(String[] args ) {
        ExampleThread thread = new ExampleThread();
        thread.start(); // only START to start new thread
    }
}

// 2. implementing Runnable interface
public class ExampleRunnable implements Runnable {
    @Override // note OVERRIDE!!
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

public class ThreadExamples {
    public static void main(String[] args) {
        ExampleRunnable runnable = new ExampleRunnable(); 
        Thread thread = new Thread(runnable); 
        thread.start();
    }
}

// 3. Anon declarations
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName()); 
        }); 
        thread.start(); 
    }
}
Both 1) and 2) work exactly the same with no diff in perfor­mance. BUT, Runnable interfaces leaves option of extending class with some other class since you can inherit only one class in Java. Also, easier to create a thread pool using runnables.
 

Throwing and handling exceptions

/** 
Throwing a checked exception
*/
public class InsufficientBalanceException extends Exception {}

public class BankAccount {
    public void withdraw(double amount) throws InsufficientBalanceException {
        if (balance < amount) {
            throw new InsufficientBalanceException(); 
        }
    }

/**
Throwing an unchecked exception
*/
public class BankAccount {
    public void withdraw(double amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("Cannot withdraw a negative amount"); 
        }
    }
}

/** 
try, catch and finally
*/
try {
    bankAccount.withdraw(amount);
} catch (InsufficientBalanceException) {
    System.out.println("Withdrawal failed: insufficient balance");
} catch (RuntimeException e) {
    System.out.println("Withdrawal failed: " + e.getMessage()); 
} finally { 
    System.out.println("Current balance: " + bankAccount.getBalance()); 
}

Java Maps

dictionary DS - HashMap, TreeMap
Map<St­ring, Intege­r> fruitP­rices = new HashMa­p<>();
Add entries
fruitP­ric­es.p­ut­("ap­ple­", 100);
Get value for a key
fruitP­ric­es.g­et­("ap­ple­");
Check if map contains specific key
fruitP­ric­es.c­on­tai­nsK­ey(­"­app­le"); // => true
Remove entries
fruitP­ric­es.r­em­ove­("pl­um");
Get size
fruitP­ric­es.s­ize();
Get all keys in map
fruitP­ric­es.k­ey­Set(); // returns keys in a set
Get all values in map
fruitP­ric­es.v­al­ues(); // returns values in a collection

Stream interm­ediate operations

List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date"); 

// filter - keeps elements that match a predicate 
Stream<String> longFruits = fruits.stream()
    .filter(fruit -> fruit.length() > 5);

// map - transforms each element
Stream<String> fruitLengths = fruits.stream()
    .map(String::length); 

// sorted - sorts elements 
Stream<String> sortedFruits = fruits.stream()
    .sorted(); 

// distinct - removes duplicates 
Stream<String> uniqueFruits = fruits.stream()
    .distinct(); 

// limit - reduces stream size 
Stream<String> limitedFruits = fruits.stream() 
    .limit(2); 

// skip - skips elements 
Stream<String> skippedFruits = fruits.stream()
    .skip(1);
- These return a new stream and are lazy for perfor­mance reasons
- Allows JVM to optimise entire operation chain at once
- For ex., if you filter 1000 elements and then limit to 5, Java can stop processing after finding 5 elements that match the filter (rather than filtering all 1000 first).

Stream terminal operations

List<String> fruits = Arrays.asList("apple", "banana", "cherry"); 

// forEach - performs actions on each element
fruits.stream().forEach(System.out::println); 

// collect - puts each element into a collection 
List<String> fruitList = fruits.stream()
    .filter(fruit -> fruit.length() > 5) 
    .collect(Collectors.toList()); 

// reduce - reduces stream to single value
Optional<String> combined = fruits.stream()
    .reduce((a, b) -> a + ", " + b); 

// count - returns number of elements
long count = fruits.stream().count(); 

// anyMatch/allMatch/noneMatch - check predicates 
boolean anyLong = fruits.stream()
    .anyMatch(fruit -> fruit.length() > 5); 
boolean allLong = fruits.stream()
    .allMatch(fruit -> fruit.length() > 3); 
boolean noneLong = fruits.stream()
    .noneMatch(fruit -> fruit.length() > 10);

// findFirst/findAny - find elements
Optional<String> first = fruits.stream().findFirst();
- These are terminal - they don't return a stream but return a concrete result or side-e­ffect (e.g. collec­tion, primitive or object).
- They trigger the actual processing of stream elements - note above collect() returning a collec­tion, count() returning a long, reduce() returning an Option­al/­spe­cific value or forEach() returning void (producing side effects).
-
average()
- operation on
IntStream
- returns Option­alD­ouble

Method references code samples

- String::length and x::equals - not static methods; equals() and length() are both instance methods. 
- The shorthand forms are method references, equivalent to x -> operation.equals(x) or x->x.length.

//
object::instanceMethod
String prefix = "Hello";
List<String> words = List.of("Hello", "Hi", "Hey"); 
boolean allMatch = words.stream()
    .allMatch(prefix::equals); // same: x -> prefix.equals(x)
//
Class::staticMethod
// Use for static utility methods, like from Math or Integer
List<String> numbers = List.of("1", "2", "3");
List<Integer> ints = numbers.stream()
   .map(Integer::parseInt) // same as: x -> Integer.parseInt(x)
   .toList();
List<Double> values = List.of(9.0, 16.0, 25.0);
List<Double> roots = values.stream()
   .map(Math::sqrt) // same as: x -> Math.sqrt(x)
   .toList();
//
Class::instanceMethod
List<String> items = List.of("Hello", "Hi", "Hey"); 
List<String> lower = items.stream()
   .map(String::toUpperCase) // same as: x -> s -> s.toUpperCase()
   .toList();
List<String> nonEmpty = items.stream()
   .filter(s -> !s.isEmpty()); // classic
// or
List<String> nonEmpty2 = items.stream()
    .filter(Predicate.not(String::isEmpty)) // java 11+     .toList(); //
Constructor references - ClassName::new
List<String> list = Stream.of("a", "b", "c")
   .collect(Collectors.toCollection(ArrayList::new)); // creates new ArrayList
//
Sorting with method references
List<String> names = List.of("Zoe", "Amy", "John");
List<String> sorted = names.stream()
   .sorted(String::compareToIgnoreCase) // same as (a, b) -> a.compareToIgnoreCase(b)
    .toList();

Concur­rency terms

Concept
Analogy
Java Tool
Thread
A separate worker
Thread, Runnable, Callable
Race condition
Two people grabbing same sandwich
synchr­onized, locks, Atomic*
Thread pool
Team of workers managed by boss
Execut­orS­ervice
Results from a thread
Waiter bringing back your order
Future, Callable
Thread­-safe collec­tions
 
Concur­ren­tHa­shMap, CopyOn­Wri­teA­rra­yList
Concur­rency: tasks appear to run at the same time, but may take turns sharing resources
Parall­elism: Tasks actually run at the same time on different cores