Learn Java

Java cheat sheets

The syntax and library calls you reach for most, on one page. Each sheet is a reminder, not a lesson: when something here is new, follow the link to the lesson that explains it.

Program basics

The shape of a program, printing, and the comments you can leave in it.

Explained in 1. Your first Java program and 2. Variables, types, and input/output

The smallest program (Java 25)

A compact source file: no class line needed. Run it with java Main.java.

void main() {
IO.println("Hello");
}

The classic program

The file must be called Main.java, after the public class.

public class Main {
public static void main(String[] args) {
System.out.println("Hello");
}
}

Print with and without a new line

System.out.println("one line");
System.out.print("no new line");
System.out.printf("%d apples%n", 3);

Comments

// to the end of the line
/* across
several lines */

Variables and types

Every variable has a type that never changes. The common ones, and moving between them.

Explained in 2. Variables, types, and input/output, 3. Operators, expressions, and casting and 32. Wrapper classes and autoboxing

Declare and give a value

A char uses single quotes, a String double quotes.

int age = 30;
double price = 9.99;
boolean done = false;
char grade = 'A';
String name = "Ada";

Let Java work out the type

var only works for local variables that get a value straight away. The type is still fixed.

var count = 10; // an int
var names = new ArrayList<String>();

A value that never changes

final double TAX_RATE = 0.2;
static final int MAX_USERS = 100; // inside a class

Whole-number division and remainder

Dividing two ints drops the fraction. Make one side a double to keep it.

7 / 2 // 3
7 % 2 // 1
7 / 2.0 // 3.5
-7 % 3 // -1

Convert between number types

A cast to int cuts the fraction off. It does not round.

(int) 3.99 // 3
(double) 7 / 2 // 3.5
(char) ('a' + 1) // 'b'
Math.round(2.5) // 3

Limits and overflow

An int that goes past its limit wraps around without an error. Use long for big counts.

Integer.MAX_VALUE // 2147483647
Integer.MAX_VALUE + 1 // -2147483648
long big = 3_000_000_000L;

Text to number and back

parseInt throws a NumberFormatException for text such as "4o".

Integer.parseInt("42") // 42
Double.parseDouble("2.5") // 2.5
String.valueOf(42) // "42"
"" + 3.0 // "3.0"

Decisions and loops

Choosing what runs, and running it again.

Explained in 4. Making decisions: if, else and switch and 5. Loops: for, while, do-while

if, else if, else

if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else {
grade = 'C';
}

Pick a value with a condition

String label = age >= 18 ? "adult" : "child";

switch with arrows

The arrow form never falls through to the next case, so it needs no break.

switch (day) {
case "SAT", "SUN" -> System.out.println("weekend");
default -> System.out.println("weekday");
}

switch that produces a value

Inside a block, yield gives the switch its value.

int days = switch (month) {
case 2 -> 28;
case 4, 6, 9, 11 -> 30;
default -> {
System.out.println("long month");
yield 31;
}
};

Count with a for loop

for (int i = 0; i < 5; i++) {
System.out.println(i); // 0 to 4
}

Visit every element

The enhanced for loop works on arrays and on every collection.

for (String name : names) {
System.out.println(name);
}

Repeat while a condition holds

A do loop always runs its body at least once.

while (balance < goal) {
balance *= 1.05;
}
do {
input = scanner.nextLine();
} while (input.isBlank());

Leave a loop early or skip a turn

outer:
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r][c] < 0) continue; // next c
if (grid[r][c] == target) break outer;
}
}

Arrays

A fixed number of values of one type, found by position.

Explained in 6. Arrays, one dimension and 8. Arrays, two dimensions, and practice

Make an array

int[] scores = new int[5]; // [0, 0, 0, 0, 0]
int[] primes = {2, 3, 5, 7};
String[] names = new String[3]; // [null, null, null]

Length, first and last

primes[4] throws an ArrayIndexOutOfBoundsException: positions run from 0 to length minus 1.

primes.length // 4
primes[0] // 2
primes[primes.length - 1] // 7

Print an array

Printing the array itself shows something like [I@1b6d3586.

Arrays.toString(primes) // "[2, 3, 5, 7]"

Sort, fill and copy

Arrays.sort(scores);
Arrays.fill(scores, -1);
int[] longer = Arrays.copyOf(primes, 6); // [2, 3, 5, 7, 0, 0]

Compare two arrays

a == b is only true when both names point at the same array.

Arrays.equals(a, b) // same length and same values

A grid of rows and columns

int[][] grid = new int[3][4]; // 3 rows of 4
grid[1][2] = 9; // row 1, column 2
grid.length // 3
grid[0].length // 4
Arrays.deepToString(grid)

Strings

Common String methods. A String never changes: each method returns a new one.

Explained in 20. Strings, StringBuilder, StringBuffer and 21. String practice problems

Length and one character

Positions start at 0, so the last one is length() - 1.

"hello".length() // 5
"hello".charAt(1) // 'e'

Compare the text

== asks whether two variables point at the same object, not whether the text matches.

a.equals(b)
a.equalsIgnoreCase(b)
a.compareTo(b) // negative, 0 or positive

Cut and search

substring(1, 3) includes position 1 and stops before 3.

"hello".substring(1, 3) // "el"
"hello".indexOf('l') // 2
"hello".contains("ell") // true
"hello".startsWith("he") // true

Change case, trim and repeat

"Ada".toUpperCase() // "ADA"
" hi ".strip() // "hi"
"ab".repeat(3) // "ababab"
"a-b".replace('-', '+') // "a+b"

Empty and blank

"".isEmpty() // true
" ".isEmpty() // false
" ".isBlank() // true

Split and join

split takes a regular expression: split on a dot with split("\\.").

String[] parts = "a,b,c".split(",");
String joined = String.join("-", parts); // "a-b-c"

Build a string with values in it

String.format("%s is %d", "Ada", 36) // "Ada is 36"
"%.2f".formatted(3.14159) // "3.14"

Build a long string in a loop

Adding to a String in a loop makes a new string every time; a StringBuilder grows one buffer.

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++) {
sb.append(i).append(' ');
}
String result = sb.toString(); // "0 1 2 "

Text over several lines

A text block removes the indentation its lines and its closing quotes all share.

String poem = """
Roses are red,
violets are blue.
""";

Methods and classes

Naming a piece of work, and designing your own types.

Explained in 12. Methods: parameters and return values, 11. Creating classes and objects, 13. Constructors, 14. Access modifiers and encapsulation and 26. The static and final keywords

A method that returns a value

static int square(int n) {
return n * n;
}
square(4) // 16

Any number of arguments

static int sum(int... nums) {
int total = 0;
for (int n : nums) total += n;
return total;
}
sum(1, 2, 3) // 6

A class with a constructor

this.name is the field; name alone is the parameter.

public class Dog {
private final String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
@Override
public String toString() { return name + " (" + age + ")"; }
}

Make objects and use them

Dog rex = new Dog("Rex", 3);
rex.getName() // "Rex"
System.out.println(rex); // Rex (3)

One value shared by the whole class

private static int created = 0; // one copy, not one per object
static int created() { return created; }

Equal by content

Override both or neither: a HashSet uses hashCode first, then equals.

@Override
public boolean equals(Object o) {
return o instanceof Dog d && d.name.equals(name) && d.age == age;
}
@Override
public int hashCode() { return Objects.hash(name, age); }

Inheritance and interfaces

Sharing code between classes, and promising what a class can do.

Explained in 16. Inheritance, 17. Method overriding and super, 18. Polymorphism, 23. Abstract classes and 24. Interfaces

Extend a class

super(...) must be the first line of the constructor.

class Cat extends Animal {
Cat(String name) { super(name); }
@Override
String sound() { return "Meow"; }
}

The object decides which method runs

Animal pet = new Cat("Tom");
pet.sound() // "Meow", from Cat

A class that cannot be made, only extended

abstract class Shape {
abstract double area();
String describe() { return "area " + area(); }
}

An interface with a default method

interface Greeter {
String name();
default String greet() { return "Hello, " + name(); }
}
record Person(String name) implements Greeter {}

Check the type and use it

The pattern names the cat c, so no separate cast is needed.

if (pet instanceof Cat c) {
c.purr();
}

Records, enums and sealed types

Data classes in one line, fixed sets of values, and closed families of types.

Explained in 29. Records: data classes in one line, 28. Enums, lambdas, and functional interfaces and 30. Sealed types and pattern matching

A record

Records come with a constructor, accessors, equals, hashCode and toString.

record Point(int x, int y) {}
Point p = new Point(1, 2);
p.x() // 1
p // Point[x=1, y=2]

Check a record's values

record Range(int lo, int hi) {
Range {
if (lo > hi) throw new IllegalArgumentException("lo > hi");
}
}

An enum with a field

enum Size {
SMALL(250), LARGE(500);
final int millilitres;
Size(int ml) { this.millilitres = ml; }
}
Size.LARGE.millilitres // 500
Size.valueOf("SMALL") // SMALL

A sealed family and a switch over it

No default needed: the compiler knows every kind of Shape.

sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
double area = switch (shape) {
case Circle c -> Math.PI * c.r() * c.r();
case Square s -> s.side() * s.side();
};

Lists, maps and sets

Collections that grow, look things up by key, or keep one of each.

Explained in 7. ArrayList: a list that can grow, 34. The Collections Framework, 35. HashMap and HashSet: finding things fast, 37. Comparable, Comparator and sorting and 38. Stacks, queues and deques

A list that grows

List<String> names = new ArrayList<>();
names.add("Ada");
names.add(0, "Grace"); // insert at the front
names.get(1) // "Ada"
names.set(1, "Alan");
names.remove("Grace");
names.size() // 1

A fixed list

List.of cannot change: add throws an UnsupportedOperationException.

List<Integer> nums = List.of(3, 1, 2);

Look things up by key

Map<String, Integer> stock = new HashMap<>();
stock.put("kiwi", 3);
stock.get("kiwi") // 3
stock.get("fig") // null
stock.getOrDefault("fig", 0) // 0
stock.merge("kiwi", 1, Integer::sum); // now 4

Walk through a map

A HashMap keeps no order. Use a TreeMap for sorted keys or a LinkedHashMap for insertion order.

for (Map.Entry<String, Integer> e : stock.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue());
}

One of each

Set<String> seen = new HashSet<>();
seen.add("x") // true
seen.add("x") // false, already there

Sort a list

Collections.sort(names);
people.sort(Comparator.comparing(Person::age));
people.sort(Comparator.comparing(Person::age).reversed()
.thenComparing(Person::name));

Remove while looping

Calling names.remove inside a for-each loop throws a ConcurrentModificationException.

names.removeIf(n -> n.isBlank());

Stacks, queues and priority

Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); stack.pop();
Queue<String> line = new ArrayDeque<>();
line.offer("a"); line.poll();
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(5); pq.add(1); pq.poll(); // 1, the smallest

Lambdas and streams

Passing behaviour around, and describing what to do with a collection.

Explained in 28. Enums, lambdas, and functional interfaces, 53. Streams: processing collections step by step, 54. Collectors and stream recipes and 55. Optional: a value that might be missing

Lambda shapes

x -> x * 2
(a, b) -> a + b
() -> "hello"
s -> { System.out.println(s); }

Method references

String::length // s -> s.length()
System.out::println // x -> System.out.println(x)
Integer::compare // (a, b) -> Integer.compare(a, b)

Filter, change and collect

Nothing runs until the last call: streams are lazy.

List<String> loud = names.stream()
.filter(n -> n.length() > 3)
.map(String::toUpperCase)
.toList();

Add up and count

int total = nums.stream().mapToInt(Integer::intValue).sum();
long big = nums.stream().filter(n -> n > 10).count();
IntStream.rangeClosed(1, 5).sum() // 15

Group and count

Map<Integer, Long> byLength = words.stream()
.collect(Collectors.groupingBy(String::length, Collectors.counting()));

A value that may be missing

Optional<String> first = names.stream().findFirst();
first.orElse("nobody")
first.map(String::length).orElse(0)

Exceptions

Handling what goes wrong, and saying so yourself.

Explained in 48. Exception handling and 9. Reading errors and debugging

Catch an exception

try {
int n = Integer.parseInt(text);
} catch (NumberFormatException e) {
System.out.println("Not a number: " + e.getMessage());
} finally {
System.out.println("always runs");
}

Several kinds in one catch

catch (IOException | IllegalStateException e) { ... }

Close a resource for you

Anything opened in the parentheses is closed when the block ends, even after an exception.

try (BufferedReader in = Files.newBufferedReader(path)) {
System.out.println(in.readLine());
}

Throw your own

if (amount <= 0) {
throw new IllegalArgumentException("amount must be positive");
}

A custom exception

Extend Exception for a checked exception callers must handle, RuntimeException for one they need not.

class OutOfStockException extends Exception {
OutOfStockException(String item) { super(item + " is out of stock"); }
}
void order(String item) throws OutOfStockException { ... }

Keyboard and files

Reading what the user types, and reading and writing files.

Explained in 45. Scanner and console applications, 46. Reading files, 47. Writing files and 49. Path and Files: modern file handling

Read from the keyboard

nextInt leaves the Enter behind, so the next nextLine would read an empty line without the extra call.

Scanner in = new Scanner(System.in);
int age = in.nextInt();
in.nextLine(); // finish the line
String name = in.nextLine();

Read a whole file

List<String> lines = Files.readAllLines(Path.of("notes.txt"));
String text = Files.readString(Path.of("notes.txt"));

Write a file

Without APPEND, writing replaces what the file held.

Files.writeString(Path.of("out.txt"), "Hello\n");
Files.writeString(Path.of("log.txt"), "more\n", StandardOpenOption.APPEND);

Read line by line

try (BufferedReader in = Files.newBufferedReader(Path.of("data.csv"))) {
String line;
while ((line = in.readLine()) != null) {
String[] fields = line.split(",");
}
}

Check before reading

Reading a missing file throws a NoSuchFileException.

Files.exists(Path.of("notes.txt")) // true or false

Dates, money and text patterns

java.time for dates, BigDecimal for exact amounts, regular expressions for text.

Explained in 50. Dates and times with java.time, 51. Money and exact numbers: BigDecimal and BigInteger and 52. Regular expressions: patterns in text

Dates

Dates never change: plusDays returns a new date, so keep the result.

LocalDate d = LocalDate.of(2025, 3, 14);
d.plusDays(30) // 2025-04-13
d.getDayOfWeek() // FRIDAY
LocalDate.parse("2025-12-25")

Time between two dates

ChronoUnit.DAYS.between(start, end) // total days
Period.between(start, end) // like P2M14D: 2 months, 14 days

Format a date for people

d.format(DateTimeFormatter.ofPattern("d MMM yyyy")) // "14 Mar 2025"

Exact money

Make a BigDecimal from a string: new BigDecimal(0.1) keeps the double's tiny error.

BigDecimal price = new BigDecimal("19.99");
BigDecimal total = price.multiply(new BigDecimal("3")); // 59.97
total.setScale(1, RoundingMode.HALF_EVEN) // 60.0
total.compareTo(new BigDecimal("59.970")) == 0 // true

Check and change text with a pattern

In a Java string every backslash of the pattern is written twice.

"2025-03-14".matches("\\d{4}-\\d{2}-\\d{2}") // true
"a1b22c333".replaceAll("\\d+", "#") // "a#b#c#"

Find every match

Matcher m = Pattern.compile("\\d+").matcher("7 cats, 12 dogs");
while (m.find()) {
System.out.println(m.group()); // 7, then 12
}

Testing with JUnit

Small programs that check your code gives the right answers.

Explained in 22. Testing your code with JUnit

A test

Expected value first, then the actual one: the failure message reads them that way round.

class CalculatorTest {
@Test
void addsTwoNumbers() {
assertEquals(5, Calculator.add(2, 3));
}
}

Compare doubles

assertEquals(0.3, 0.1 + 0.2, 1e-9); // within 0.000000001

Expect an exception

assertThrows(IllegalArgumentException.class, () -> account.deposit(-5));

A fresh object for every test

private Account account;
@BeforeEach
void setUp() { account = new Account(100); }

One test, many inputs

@ParameterizedTest
@ValueSource(ints = {0, -5, 1_000})
void neverNegative(int n) {
assertTrue(Math.abs(n) >= 0);
}