DevTools

Cheatsheet Java

Linguagem de programação orientada a objetos

Back to languages
Java
126 cards found
Categories:
Versions:

Basic Syntax


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

Every Java program starts in a main method inside a class. System.out.println() prints a line to the console. The file name must match the public class name.

Constants (final)
final int MAX = 100;
final String APP = "MyApp";

// class constant
public static final double PI = 3.14159;

The final keyword makes the variable immutable after AS first assignment. By convention, constants use UPPERCASE. Combine with static for constants shared by the class.

Compare Strings
String a = "Anna";
String b = new String("Anna");

a.equals(b);          // true (content)
a == b;               // false (reference)
a.equalsIgnoreCase("ana"); // true

To compare the content of strings use equals(), never == (which compares references). equalsIgnoreCase() ignores case. It is one of the most common mistakes in Java.

Multidimensional arrays
int[][] matrix = {
    { 1, 2, 3 },
    { 4, 5, 6 }
};
int val = matrix[1][2]; // 6

An array of arrays creates a matrix. Declare with type[][] and access with two indices [row][column]. Rows can have different sizes, since each row is an independent array.

Variables
int age = 30;
String name = "Anna";
double price = 9.99;
boolean active = true;
char letter = 'A';

Declare variables by indicating the type followed by the name and the value. Java is strongly typed, so each variable has a fixed type. Use String for text and char for a single character in single quotes.

Arithmetic operators
int a = 10, b = 3;
int sum = a + b;   // 13
int remainder = a % b;  // 1
a++;                // increments
b--;                // decrements
int div = a / b;    // 3 (integer)

The operators + - * / do the basic math and % returns the remainder of the division. Since both are int, the division truncates the result. Use ++ and -- to increment or decrement.

Casting
int n = (int) 3.99;     // 3 (truncates)
double d = (double) 5;  // 5.0
long l = 100;           // automatic widening
int x = (int) l;        // manual narrowing

Casting converts between types. Widening (small to large) is automatic; narrowing (large to small) requires an explicit (type) and may lose data.

Structure and comments
// line comment
/* block
   comment */
/** Javadoc: documentation */

package com.example;
import java.util.List;

Java has line comments (//), block comments (/* */) and documentation comments (/** */). package organizes the classes and import brings in classes from other packages.

Primitive types
byte  b = 127;      // 8 bits
short s = 32000;    // 16 bits
int   i = 2_000_000; // 32 bits
long  l = 9_000_000_000L;
float f = 3.14f;
double d = 3.14159;
boolean ok = true;
char c = '€';

Primitive types are not objects and store the value directly. int is the default integer and double the default decimal. The suffix L marks a long and f a float.

Comparison and logical
boolean iguais = (a == b);
boolean largest  = (a > b);
boolean both  = (a > 0 && b > 0);
boolean some  = (a > 0 || b > 0);
boolean negado = !ativos;

Comparison operators (==, !=, >, <=) return boolean. The logical ones && (and), || (or) and ! (negation) combine conditions. Use == only for primitives.

Text conversions
int n = Integer.parseInt("42");
double d = Double.parseDouble("3.14");
String s = String.valueOf(42);
String t = 42 + "";      // shortcut
String bin = Integer.toBinaryString(10);

Convert text to a number with Integer.parseInt() and Double.parseDouble(). For the reverse direction use String.valueOf(). These methods throw NumberFormatException if the text is invalid.

Wrappers
Integer n = 10;        // autoboxing
int x = n;             // unboxing
int v = Integer.parseInt("123");
double d = Double.parseDouble("3.14");
int max = Integer.MAX_VALUE;

Each primitive type has a wrapper class (Integer, Double, Boolean...). Autoboxing automatically converts between primitive and object. Wrappers offer useful methods like parseInt().

Strings
String s = "Hello " + name;
String f = String.format("%d years", age);
int tam = s.length();
String maius = s.toUpperCase();
boolean tem = s.contains("Hello");

String is immutable in Java. Concatenate with + or format with String.format(). Useful methods: length(), toUpperCase(), contains(), substring() and trim().

Arrays
int[] nums = { 1, 2, 3 };
int[] out = new int[5];
out[0] = 10;
int tam = nums.length;
int first = nums[0];

An array has a fixed size defined at creation. Declare with type[] and access by index (base 0). The length property gives the size. Use new int[n] to create an empty array.

Flow Control


11 cards
if / else
if (age >= 18) {
    System.out.println("adult");
} else if (age > 12) {
    System.out.println("young");
} else {
    System.out.println("child");
}

if runs a block when the condition is true. Chain alternatives with else if and use else for the remaining case. Braces are optional for a single statement, but recommended.

for
for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

for (int i = 10; i > 0; i--) {
    System.out.println(i);
}

The for loop has three parts: initialization, condition and increment. It repeats while the condition is true. It is ideal when you know how many iterations you want.

Ternary operator
String r = age >= 18 ? "adult" : "minor";

int max = (a > b) ? a : b;

The operator condition ? valueIfTrue : valueIfFalse is a one-line conditional. It returns one of two values depending on the condition. It is handy for simple assignments.

switch
switch (day) {
    case 1:
        System.out.println("Seg");
        break;
    case 2:
        System.out.println("Ter");
        break;
    default:
        System.out.println("?");
}

switch compares a value against several case. Each case needs break to exit, otherwise it continues to the next one (fall-through). default handles unforeseen values.

for-each
int[] nums = { 1, 2, 3 };
for (int x : nums) {
    System.out.println(x);
}

for (String s : list) {
    System.out.println(s);
}

for-each goes through each element of an array or collection without managing indices. The syntax is for (type item : collection). It is more readable, but does not give direct access to the index.

break and continue
for (int i = 0; i < 10; i++) {
    if (i == 3) continue; // skips
    if (i == 7) break;    // ends
    System.out.println(i);
}

continue skips to the next iteration and break ends the loop entirely. Both help control the flow inside loops without complex conditions.

switch expression (14+)
String r = switch (day) {
    case 1 -> "Seg";
    case 2 -> "Ter";
    case 6, 7 -> "Weekend";
    default -> "?";
};

The modern syntax with -> returns a value directly and does not need break. You can group several cases with commas (case 6, 7). It is safer and more readable than the classic switch.

while
int x = 10;
while (x > 0) {
    System.out.println(x);
    x--;
}

while repeats while the condition is true, testing before each iteration. If the condition starts false, the body never runs. Do not forget to update the control variable.

Labeled loops
external:
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i * j > 6) break external;
    }
}

A label identifies an outer loop. break label exits directly from the labeled loop, useful in nested loops where a simple break would only exit the innermost one.

switch with yield
int letters = switch (day) {
    case 1 -> 3;
    case 2 -> {
        System.out.println("calculating");
        yield 3;
    }
    default -> 0;
};

When a case needs several statements, use a block with yield to return the value. yield ends the case and provides the result of the switch expression.

do-while
int x = 0;
do {
    System.out.println(x);
    x++;
} while (x < 10);

do-while runs the body at least once, since it tests the condition at the end. It is useful for menus or validations where you want to run the block before checking.

Methods and Lambdas


11 cards
Simple method
public int add(int a, int b) {
    return a + b;
}

int r = add(2, 3); // 5

A method is declared with modifier returnType name(parameters). Use return to give back the result. The return type must match the returned value.

Overloading
int area(int l) { return l * l; }
int area(int a, int b) { return a * b; }
double area(double r) { return Math.PI * r * r; }

Overloading allows several methods with the same name, the long the the parameters differ in number or type. The compiler picks the right version based on the arguments passed.

Method reference
list.forEach(System.out::println);
list.sort(String::compareTo);

Supplier<List<String>> s = ArrayList::new;

The method reference Class::method is a shortcut for lambdas that only call a method. Common forms: object::method, Class::method and Class::new for constructors.

Void method
public void print(String msg) {
    System.out.println(msg);
}

print("Hello"); // without return

A void method returns no value. It is used for actions with side effects, such the printing or changing state. It does not need return (or use return; to exit early).

Constructor
public class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}
Person p = new Person("Anna");

The constructor has the same name the the class and has no return type. It is called with new to initialize the object. this distinguishes the field from the parameter with the same name.

Recursion
int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
factorial(5); // 120

A recursive method calls itself, solving the problem in smaller cases. It always needs a base case to stop, otherwise it causes a StackOverflowError.

Static method
public static int double(int x) {
    return x * 2;
}

int r = Math.max(3, 5); // chama without instance

A static method belongs to the class rather than an instance, so it can be called without creating an object. It cannot access instance fields (this). It is common in utilities like Math.

Constructor chaining
public class Point {
    int x, y;
    Point() { this(0, 0); }
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

A constructor can call another one of the same class with this(...), avoiding repeated code. The call must be the first statement. It is useful for setting default values.

Functional interfaces
Predicate<Integer> largest = n -> n > 10;
Function<Integer, Integer> double = n -> n * 2;
Supplier<String> hello = () -> "hello";
Consumer<String> prints = System.out::println;

The java.util.function package brings ready-made functional interfaces: Predicate (test), Function (transform), Supplier (provide) and Consumer (consume). They are the foundation of lambdas.

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

The type... parameter accepts a variable number of arguments, handled internally the an array. It must be the last method parameter. Handy for functions like String.format().

Lambda
Runnable r = () -> System.out.println("Hello");

list.forEach(x -> System.out.println(x));

Comparator<Integer> c = (a, b) -> a - b;

A lambda is an anonymous function with the syntax (params) -> body. It implements functional interfaces concisely. It is widely used with forEach(), sort() and streams.

Classes and Objects


12 cards
Class with getter/setter
public class Person {
    private String name;

    public String getName() { return name; }
    public void setName(String n) { name = n; }
}

Encapsulation hides the fields with private and exposes them through getters and setters. This way you control how the data is read and changed.

Enum with fields
enum Planet {
    TERRA(5.97e24), MARTE(6.39e23);

    final double mass;
    Planet(double mass) { this.mass = mass; }
}

An enum can have fields, a constructor and methods like a class. Each constant calls the constructor with its arguments. Useful for associating data with each value.

Anonymous class
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

An anonymous class defines and instantiates an implementation without naming it. It is used to implement interfaces or extend classes on the spot. Today it is often replaced by lambdas.

Access modifiers
public    // all
protected // subclasses + package
(default) // only the package
private   // only a own class

Java has four access levels. public is visible everywhere, private only inside the class, protected for subclasses, and the default (no modifier) restricts it to the package.

this and super
this.name = name;   // field of the instance
this.method();      // method current

super();            // constructor of the superclass
super.method();     // method of the parent

this refers to the current instance and super to the superclass. this() calls another constructor of the same class and super() the parent constructor.

Immutable class
public final class Currency {
    private final double value;
    public Currency(double value) { this.value = value; }
    public double getValue() { return value; }
}

An immutable class has final fields, a constructor that sets them and only getters (no setters). Marking the class the final prevents inheritance. Immutable objects are safe under concurrency.

Record (16+)
public record Point(int x, int y) {}

Point p = new Point(1, 2);
p.x();        // 1 (not is getX)
p.toString(); // Point[x=1, y=2]

A record is an automatically generated immutable data class. It creates a constructor, getters (named after the field), equals(), hashCode() and toString(). Ideal for carrying data.

static
public class Counter {
    static int total = 0;
    static void inc() { total++; }
}
Counter.inc(); // shared by all

static members belong to the class and are shared by all instances. They are accessed with Class.member. A static { } block runs once when the class is loaded.

toString()
@Override
public String toString() {
    return "Person[name=" + name + "]";
}
System.out.println(person); // chama toString()

The toString() method returns a textual representation of the object. It is called automatically when printing or concatenating. Override it to get readable output instead of Class@hash.

Enum
enum State { ACTIVE, INACTIVE, SUSPENDED }

State e = State.ACTIVE;
State[] all = State.values();
String name = e.name();

An enum defines a fixed set of named constants. It is safer than using integers or strings. The values() and name() methods list and get the values.

Nested classes
class Externa {
    class Interna { }          // inner
    static class Estatica { }  // nested
}
Externa.Interna i = new Externa().new Interna();

An inner class has access to the outer class fields and needs an instance of it. A static nested class is independent. They serve to group related logic.

equals() and hashCode()
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Person p)) return false;
    return name.equals(p.name);
}
@Override
public int hashCode() { return name.hashCode(); }

Override equals() to compare by content and hashCode() to use the object in a HashMap/HashSet. They should always be overridden together: equal objects have the same hashCode.

Inheritance and Interfaces


11 cards
Inheritance (extends)
public class Animal {
    public void speak() { }
}
public class Dog extends Animal {
    @Override
    public void speak() { }
}

Inheritance uses extends for a child class to inherit fields and methods from the parent. Java only allows inheriting from one class. @Override indicates that the method replaces the parent's.

Polymorphism
Animal a = new Dog();
a.speak(); // Dog version

List<String> l = new ArrayList<>();

Polymorphism lets you treat an object by its superclass or interface, running the method of the real class. The Animal variable holds a Dog, but calls the concrete implementation at runtime.

Sealed classes (17+)
public sealed interface Shape
    permits Circle, Square { }

public final class Circle implements Shape { }

A sealed class controls which classes may extend or implement it, through the permits clause. The children must be final, sealed or non-sealed. It makes switch by patterns safe.

Abstract class
public abstract class Shape {
    public abstract double area();
    public void describe() { }
}

An abstract class cannot be instantiated and may have abstract methods (without a body) that subclasses are required to implement. It serves the a base template with shared logic.

final
public final class Immutable { }
// class Child extends Immutable { } // ERROR

public final int x = 5; // constant
public final void method() { } // cannot be overridden

final applied to a class prevents inheritance, to a method prevents overriding, and to a variable makes it a constant. The String class is final.

Multiple interfaces
public class Dog extends Animal
    implements Swimmer, Playful {
    public void swim() { }
    public void play() { }
}

Java does not allow multiple class inheritance, but a class can implement several interfaces, separated by commas. This is how you get multiple behavior without the diamond problem.

Interface
public interface Animal {
    void speak();
}
public class Dog implements Animal {
    public void speak() { }
}

An interface defines a contract of methods that classes implement with implements. A class can implement several interfaces. It is the basis of polymorphism in Java.

instanceof
if (obj instanceof Dog) {
    Dog c = (Dog) obj;
    c.speak();
}

The instanceof operator checks whether an object is of a given type before casting. It avoids ClassCastException. It returns true or false.

Object: the root
Object o = "text"; // everything is Object

o.toString();
o.equals(other);
o.hashCode();

All classes implicitly inherit from Object. That is why any object has toString(), equals() and hashCode(). An Object variable accepts any type.

Default methods
public interface Animal {
    void speak();
    default void sleep() {
        System.out.println("zzz");
    }
}

A default method has an implementation in the interface itself, avoiding breaking existing classes when adding methods. Classes can use it directly or override it.

instanceof pattern (16+)
if (obj instanceof Dog c) {
    c.speak(); // c already is available
}

Pattern matching for instanceof combines the check and the casting into a single statement. The variable c becomes available inside the block if the test passes, eliminating the manual cast.

Collections


13 cards
List (ArrayList)
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.get(0);     // 1
list.remove(0);
list.size();     // 1

A List is an ordered collection that accepts duplicates. The ArrayList is the most common implementation, based on a dynamic array. Access by index with get(i) and add with add().

Set (HashSet)
Set<String> set = new HashSet<>();
set.add("a");
set.add("a");      // ignorado
set.contains("a"); // true
set.size();        // 1

A Set is a collection without duplicates. The HashSet is the fastest, with no guaranteed order. Calling add() on an existing element has no effect. Useful for removing repeats.

Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.isEmpty()) it.remove();
}

An Iterator traverses a collection with hasNext() and next(). It is the only safe way to remove elements with remove() during iteration without throwing ConcurrentModificationException.

Immutable collections
List<String> l = List.of("a", "b", "c");
Set<Integer> s = Set.of(1, 2, 3);
Map<String, Integer> m = Map.of("a", 1, "b", 2);
// l.add("d"); // UnsupportedOperationException

The factory methods List.of(), Set.of() and Map.of() create immutable and compact collections. Any attempt to modify them throws UnsupportedOperationException.

LinkedList
List<String> list = new LinkedList<>();
list.add("a");
list.addFirst("b");
list.addLast("c");
String p = list.get(0);

The LinkedList stores elements in linked nodes. It is efficient at inserting/removing at the ends with addFirst() and addLast(), but slower at accessing by index than the ArrayList.

TreeSet
Set<Integer> nums = new TreeSet<>(List.of(3, 1, 2));
nums.first(); // 1
nums.last();  // 3

The TreeSet keeps the elements sorted. It offers methods like first() and last() to access the extremes. The elements must be comparable to each other.

Comparable
public class Person implements Comparable<Person> {
    @Override
    public int compareTo(Person o) {
        return name.compareTo(o.name);
    }
}

The Comparable interface defines the natural ordering of a class through the compareTo() method. It returns negative, zero or positive. It allows using Collections.sort() directly.

Map (HashMap)
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.get("a");          // 1
map.containsKey("a");  // true
map.getOrDefault("z", 0);

A Map associates keys with values. The HashMap is the default implementation, with no guaranteed order. Use put() to insert and get() to read. getOrDefault() avoids null.

Queue and Deque
Queue<String> queue = new LinkedList<>();
queue.offer("a");   // adds
queue.poll();       // removes and returns
queue.peek();       // see without remove

Deque<String> stack = new ArrayDeque<>();
stack.push("x");   // LIFO
stack.pop();

The Queue is a FIFO queue with offer()/poll(). The Deque allows inserting/removing at both ends and works the a LIFO stack with push()/pop().

Comparator
list.sort(Comparator.comparing(Person::getName));
list.sort(Comparator.comparingInt(Person::getAge).reversed());

A Comparator defines an external ordering without changing the class. It is created with Comparator.comparing() and chained with reversed() or thenComparing(). Ideal for multiple orderings.

TreeMap and LinkedHashMap
Map<String, Integer> ord = new TreeMap<>();   // keys ordenadas
Map<String, Integer> ins = new LinkedHashMap<>(); // order de insertion

The TreeMap keeps the keys naturally sorted and the LinkedHashMap preserves insertion order. Choose based on whether you need sorting or keeping the sequence in which you added.

Arrays (utility)
int[] nums = { 3, 1, 2 };
Arrays.sort(nums);
Arrays.toString(nums);     // [1, 2, 3]
List<Integer> l = Arrays.asList(1, 2, 3);
int[] copy = Arrays.copyOf(nums, 5);

The Arrays class brings utilities for arrays: sort() sorts, toString() prints, asList() converts to a list and copyOf() copies. Not to be confused with the array type.

Collections
Collections.sort(list);
Collections.reverse(list);
Collections.shuffle(list);
int max = Collections.max(nums);
List<String> sync = Collections.synchronizedList(list);

The Collections class offers static somethingrithms: sort(), reverse(), shuffle(), max()/min(). It also creates synchronized or immutable views of collections.

Exceptions


10 cards
try / catch / finally
try {
    int r = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());
} finally {
    System.out.println("always executa");
}

The try block contains risky code and catch handles the exception if it occurs. finally always runs, whether or not there is an error, making it ideal for cleanup.

Checked vs Unchecked
// checked: forces handling (IOException)
// unchecked: RuntimeException
NullPointerException
IllegalArgumentException
IndexOutOfBoundsException

Checked exceptions (inheriting from Exception) force handling with try or throws. Unchecked ones (inheriting from RuntimeException) indicate programming errors and do not force it.

Common exceptions
NullPointerException     // usar null
ArrayIndexOutOfBoundsException // index invalid
ClassCastException       // cast wrong
NumberFormatException    // parse invalid
IllegalArgumentException // argument mau

The most frequent unchecked exceptions: NullPointerException when using null, IndexOutOfBounds on invalid indexes and NumberFormatException when converting text. Prevention is worth more than catching.

throw
if (value < 0) {
    throw new IllegalArgumentException("Value invalid");
}

The throw statement throws an exception manually, interrupting the flow. You create an instance of the exception with a message. It is used to validate arguments and invalid states.

multi-catch
try {
    // code
} catch (IOException | SQLException e) {
    System.out.println("Error: " + e);
}

multi-catch handles several exceptions in the same block, separated by |. It reduces duplicated code when the handling is the same. The exceptions cannot be in the same hierarchy.

Best practices with exceptions
// specific before de generic
try { } catch (IOException e) { }
      catch (Exception e) { }

// not usar exceptions to controlo de fluxo

Catch specific exceptions before generic ones and never ignore an empty catch. Use exceptions for exceptional errors, not for normal flow control. Prefer Optional to returning null.

throws (declare)
public void read() throws IOException {
    Files.readString(Path.of("f.txt"));
}

The throws clause in the signature declares that the method may throw a checked exception, transferring the responsibility to the caller. The caller must handle or re-declare it.

Custom exception
public class SaldoException extends Exception {
    public SaldoException(String msg) {
        super(msg);
    }
}
throw new SaldoException("Insufficient balance");

Create your own exception by extending Exception (checked) or RuntimeException (unchecked). Pass the message to the constructor with super(msg). Give names ending in Exception.

try-with-resources
try (var br = Files.newBufferedReader(p)) {
    String line = br.readLine();
} // fecha automatically

try-with-resources declares resources in parentheses and closes them automatically at the end, even on error. It requires the resource to implement AutoCloseable. It prevents resource leaks.

Exception information
catch (Exception e) {
    String msg = e.getMessage();
    e.printStackTrace();
    Throwable cause = e.getCause();
}

An exception object offers getMessage() (message), printStackTrace() (call stack) and getCause() (original exception). Useful for logging and debugging errors.

Modern Features


11 cards
var (inference)
var list = new ArrayList<String>();
var n = 10;
var s = "text"; // String

for (var i = 0; i < 5; i++) { }

var (Java 10+) lets the compiler infer the local variable type from the value. It only works on initialized local variables. It does not change the type: it remains strongly typed.

Simplified main (21+)
void main() {
    System.out.println("Hello");
}

With the preview of instance main methods, the entry point can be a simple void main() without public static or String[] args. It reduces the ceremony for small programs.

Stream.iterate and generate
Stream.iterate(1, n -> n * 2)
    .limit(5)
    .forEach(System.out::println);

Stream.generate(Math::random).limit(3);

Stream.iterate() generates an infinite sequence applying a function to the previous value. Stream.generate() creates values with a Supplier. Use limit() to end them.

Text blocks (15+)
String json = """
        {
            "name": "Anna",
            "age": 30
        }
        """;

A text block uses three quotes """ for multi-line strings without escapes. The common indentation is removed automatically. Ideal for readable JSON, SQL and HTML.

String: new methods
"  x  ".strip();      // "x" (Unicode)
"  x  ".trim();       // "x"
"".isBlank();         // true
"ab".repeat(3);       // "ababab"
"a\nb".lines();       // Stream<String>

Modern String methods: strip() removes spaces (Unicode-aware), isBlank() tests whether it is empty/spaces, repeat(n) repeats and lines() returns a stream of lines.

var in lambdas
BiFunction<Integer, Integer, Integer> sum =
    (var a, var b) -> a + b;

list.forEach((var x) -> System.out.println(x));

You can use var in the parameters of a lambda to keep the syntax consistent or to add annotations to them. All parameters must use var or none.

Switch pattern matching (21+)
String r = switch (obj) {
    case Integer i -> "integer " + i;
    case String s  -> "text " + s;
    case null      -> "null";
    default        -> "other";
};

The modern switch accepts type patterns instead of only constants. Each case Type var tests and extracts the value. With sealed classes, the default can be omitted.

Compact constructor (record)
public record Age(int value) {
    public Age {
        if (value < 0)
            throw new IllegalArgumentException();
    }
}

A compact constructor in a record omits the parameters and validates the fields before the automatic assignment. It does not need this.value = value. Ideal for validating invariants in records.

Unnamed variables (22+)
try {
    // ...
} catch (Exception _) {
    System.out.println("error");
}
int total = 0;
for (var _ : list) total++;

The unnamed variable _ marks values you will not use, such the exceptions or iteration elements. It makes the intent clear and avoids throwaway names. It can be repeated in the same scope.

Pattern with guard (when)
String r = switch (value) {
    case Integer i when i < 0 -> "negative";
    case Integer i            -> "positive";
    default                   -> "other";
};

The when clause adds an extra condition (guard) to a pattern. It allows refining cases without an inner if. The more specific cases must come before the generic ones.

Sequenced Collections (21+)
SequencedCollection<String> s = new LinkedHashSet<>();
s.addFirst("a");
s.addLast("z");
s.getFirst();
s.reversed();

The SequencedCollection interface standardizes ordered collections, exposing addFirst(), addLast(), getFirst() and reversed(). There is also SequencedMap.

Tips and Good Practices


12 cards
StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String s = sb.toString();

The StringBuilder is efficient for concatenating strings in loops, since it is mutable and avoids creating objects at each +. Use append() and convert at the end with toString().

Naming conventions
class MyClass { }       // PascalCase
int myVariable;          // camelCase
void myMethod() { }        // camelCase
final int MAX_VALUE = 100;  // UPPER_SNAKE
package com.company.app;    // lowercase

By convention: classes in PascalCase, variables and methods in camelCase, constants in UPPER_SNAKE_CASE and packages in lowercase. Following the standard makes the code idiomatic.

printf and formatting
System.out.printf("Name: %s, Age: %d%n", name, age);
System.out.printf("Price: %.2f%n", 9.99);
String s = String.format("%05d", 42); // "00042"

printf() formats the output with markers: %s (string), %d (integer), %f (decimal, %.2f with 2 places) and %n (newline). String.format() returns the formatted string.

Check null
if (obj != null) { }
Objects.requireNonNull(obj, "not can be null");
boolean eq = Objects.equals(a, b);
String h = Objects.toString(obj, "default");

The Objects class helps deal with null: requireNonNull() throws an exception if it is null, equals() compares safely and toString() gives a default value.

Math
Math.max(3, 5);     // 5
Math.min(3, 5);     // 3
Math.abs(-7);       // 7
Math.pow(2, 3);     // 8.0
Math.sqrt(16);      // 4.0
Math.round(3.6);    // 4

The Math class offers static mathematical functions: max()/min(), abs() (absolute value), pow() (power), sqrt() (root) and round() (round).

Random
Random r = new Random();
int n = r.nextInt(100);     // 0 a 99
double d = r.nextDouble();  // 0.0 a 1.0

int dado = ThreadLocalRandom.current().nextInt(1, 7);

The Random class generates random numbers: nextInt(limit) for integers and nextDouble() for decimals. ThreadLocalRandom is preferable under concurrency and accepts ranges.

Import
import java.util.List;
import java.util.*;
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;

import brings in classes from other packages. import java.util.* imports all classes of the package and import static allows using static members (like PI) without the class name.

Dates (java.time)
LocalDate today = LocalDate.now();
LocalDate data = LocalDate.of(2024, 1, 15);
LocalDate tomorrow = today.plusDays(1);
int year = today.getYear();

The java.time package (Java 8+) handles dates immutably. LocalDate is a date without time, LocalTime a time and LocalDateTime both. Use plusDays() to add.

Files
Path p = Path.of("data.txt");
String text = Files.readString(p);
List<String> lines = Files.readAllLines(p);
Files.writeString(p, "Hello");

The Files class (with Path) simplifies reading and writing files. readString() reads everything, readAllLines() returns a list of lines and writeString() writes. It throws IOException.

Compile and run
javac App.java      // compila -> App.class
java App            // executa

java App.java       // executa direct (11+)
java -jar app.jar   // executa one JAR

javac compiles the source code to bytecode (.class) and java runs it on the JVM. Since Java 11 you can run a file directly with java App.java.

Read input (Scanner)
Scanner sc = new Scanner(System.in);
String name = sc.nextLine();
int age = sc.nextInt();
sc.close();

The Scanner reads data from the console. nextLine() reads a line of text and nextInt() an integer. Close it with close() when you are done. For performance use BufferedReader.

Best practices
// prefer interfaces for types
List<String> l = new ArrayList<>();

// use try-with-resources
// avoids null: returns Optional or empty lists
// short and cohesive methods

Declare variables by the interface type (List instead of ArrayList) for flexibility. Return empty collections instead of null, use try-with-resources and keep methods small.

Streams and Functional


12 cards
Stream: filter and map
List<Integer> r = list.stream()
    .filter(x -> x > 5)
    .map(x -> x * 2)
    .collect(Collectors.toList());

A stream processes collections in a functional and declarative way. filter() selects elements and map() transforms them. The stream does not change the original collection.

Stream: groupingBy
Map<String, List<Person>> byCity = people.stream()
    .collect(Collectors.groupingBy(Person::getCity));

Collectors.groupingBy() groups elements by a key, returning a Map of lists. It is the equivalent of an SQL "group by". Combine it with counting() to count per group.

Optional: create
Optional<String> a = Optional.of("Anna");
Optional<String> b = Optional.ofNullable(name);
Optional<String> v = Optional.empty();

The Optional is a container that may or may not hold a value, avoiding null. of() requires a present value, ofNullable() accepts null and empty() creates an empty Optional.

Stream: aggregation
long n = list.stream().count();
boolean some = list.stream().anyMatch(x -> x > 10);
boolean all = list.stream().allMatch(x -> x > 0);
Optional<Integer> prim = list.stream().findFirst();

Terminal aggregation operations summarize the stream: count() counts, anyMatch()/allMatch() test conditions and findFirst() returns the first element in an Optional.

Stream: flatMap
List<String> all = lists.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());

flatMap() transforms each element into a stream and flattens everything into a single one. It is useful when each element generates several results (e.g. lists of lists) and you want a single, flat stream.

Optional: get value
String r = opt.orElse("Anonymous");
String l = opt.orElseGet(() -> calculate());
opt.ifPresent(System.out::println);
String v = opt.orElseThrow();

To get the value safely use orElse() (default value), orElseGet() (computed), ifPresent() (only if it exists) or orElseThrow(). Avoid calling get() directly.

Stream: reduce
int sum = list.stream()
    .reduce(0, (acc, x) -> acc + x);

int product = list.stream()
    .reduce(1, (a, b) -> a * b);

reduce() combines all elements into a single value, starting from an initial accumulator. The first argument is the initial value and the second the function that accumulates. It replaces sum/product loops.

Stream: distinct/sorted/limit
List<Integer> r = list.stream()
    .distinct()
    .sorted()
    .limit(5)
    .collect(Collectors.toList());

Common intermediate operations: distinct() removes duplicates, sorted() sorts and limit(n) restricts to the first n elements. They are chained before the terminal operation.

Optional: map and filter
String r = opt
    .map(String::toUpperCase)
    .filter(s -> s.length() > 2)
    .orElse("empty");

The Optional supports functional operations: map() transforms the value if present and filter() keeps it only if it passes the condition. If it becomes empty at any step, orElse() returns the default.

Stream: collect
List<String> names = people.stream()
    .map(Person::getName)
    .collect(Collectors.toList());

String csv = people.stream()
    .map(Person::getName)
    .collect(Collectors.joining(", "));

collect() accumulates the results into a structure. Collectors.toList() creates a list and Collectors.joining() joins strings with a separator. It is the most versatile terminal operation.

Numeric streams
int sum = list.stream().mapToInt(x -> x).sum();
double average = list.stream().mapToInt(x -> x).average().orElse(0);
int max = list.stream().mapToInt(x -> x).max().orElse(0);

Numeric streams (IntStream, DoubleStream) avoid boxing and offer sum(), average() and max(). Convert with mapToInt() to access these operations.

Complete pipeline
List<String> top = people.stream()
    .filter(p -> p.getAge() >= 18)
    .sorted(Comparator.comparing(Person::getName))
    .map(Person::getName)
    .limit(3)
    .toList();

A typical pipeline chains several operations: filter, sort, transform and limit. toList() (Java 16+) is a shortcut for collect(Collectors.toList()). The operations only run at the terminal.

Generics


9 cards
Generic class
public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}
Box<String> c = new Box<>();

A generic class uses a type parameter <T> that is replaced when instantiating. It allows writing reusable and type-safe code. T is a convention for "Type".

Wildcard (?)
void print(List<?> list) {
    for (Object o : list) System.out.println(o);
}

The wildcard ? represents an unknown type. List<?> accepts lists of any type, but you can only read the elements the Object. It gives flexibility to methods.

Restrictions
// NOT allowed:
// List<int> x;        // only objects
// new T();            // without instantiate
// new T[10];          // without arrays of T
// if (x instanceof List<String>) {}

Generics only accept reference types (not primitives — use wrappers). You cannot instantiate T, create arrays of T or use instanceof with generic types, due to type erasure.

Generic method
public <T> T first(List<T> list) {
    return list.get(0);
}
String s = first(List.of("a", "b"));

A generic method declares the type before the return: <T> T name(...). The type is inferred from the arguments. It works in any class, even a non-generic one.

Upper bounded (? extends)
double sum(List<? extends Number> nums) {
    double total = 0;
    for (Number n : nums) total += n.doubleValue();
    return total;
}

? extends Number accepts Number or subtypes (Integer, Double). It is covariant: you can read the Number, but not add elements. Ideal for data producers.

Diamond operator
List<String> a = new ArrayList<String>(); // old
List<String> b = new ArrayList<>();       // diamond

Map<String, List<Integer>> m = new HashMap<>();

The diamond operator <> lets the compiler infer the types from the declaration, avoiding repeating them. It makes the code cleaner without losing type safety.

Lower bounded (? super)
void add(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

? super Integer accepts Integer or supertypes (Number, Object). It is contravariant: you can add Integer safely. Ideal for data consumers (PECS rule).

Bounded types
public <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

The restriction <T extends Type> limits the generic to a type or its subtypes. This lets you call methods of that type (here compareTo()). It accepts classes and interfaces.

Type erasure
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
a.getClass() == b.getClass(); // true!

Type erasure removes generic types at runtime, replacing T with its bound. That is why List<String> and List<Integer> are the same class at runtime. The check happens only at compile time.