Back to cheat sheets

Languages

Java

A to-the-point review of the Java an SDET gets asked about: data types, modifiers, OOP, strings, collections, exceptions, generics & streams — plus the JUnit/TestNG ecosystem. Short explanations, short examples.

01Data Types & Variables

Java has two kinds of types: primitives and reference types.

  • Primitives hold the value directly and are copied on assignment: int, long, double, float, boolean, char, byte, short.
  • Reference types (objects, arrays, String) hold a reference to data on the heap; assigning copies the reference, so both variables point at the same object.
  • Each primitive has a wrapper class (intInteger, booleanBoolean) for use in generics/collections. Converting between them is autoboxing / unboxing.
int a = 1; int b = a; b = 9;            // a stays 1 (value copied)

int[] x = { 1 }; int[] y = x; y[0] = 9; // x[0] is now 9 (same array)

List<Integer> nums = new ArrayList<>();
nums.add(5);                            // autobox: int -> Integer
Pass-by-value: Java always passes by value. For objects the reference is copied, so a method can change the object's fields but cannot reseat the caller's variable. var (Java 10+) infers a local variable's type.

02Modifiers & Keywords

Access modifiers — who can see a member:

  • public — anywhere.
  • private — only inside the same class.
  • protected — same package + subclasses.
  • (no modifier)package-private: visible only within the same package. This is the default.

Other key modifiers:

  • static — belongs to the class, not an instance.
  • final — can't change: a final variable is a constant, a final method can't be overridden, a final class can't be subclassed.
  • abstract — no body; a subclass must implement it.

03OOP Pillars

  • Encapsulationprivate fields + public getters/setters.
  • Inheritanceextends one class; prefer composition over deep hierarchies.
  • Polymorphism — overriding (runtime) vs overloading (compile-time).
  • Abstraction — abstract classes and interfaces hide detail behind a contract.
Abstract class vs Interface — when & how

Abstract class = a partial base with shared code/state; a class extends one of them and overrides its abstract methods. Use it for an "is-a" relationship.
Interface = a contract / capability; a class implements many and provides the methods. Since Java 8 it can have default methods. Use it for a "can-do" capability.

abstract class Shape {                  // shared base
    abstract double area();             // no body -> subclass must implement
}
interface Drawable {                    // capability
    void draw();
}

// extend ONE class, implement MANY interfaces
class Circle extends Shape implements Drawable {
    final double r;
    Circle(double r) { this.r = r; }
    @Override double area() { return Math.PI * r * r; }
    @Override public void draw() { System.out.println("O r=" + r); }
}
Overloading vs Overriding

Overloading = same method name, different parameters, chosen at compile time. Overriding = a subclass redefines a method with the same signature, chosen at runtime.

04Strings & Immutability

String is a reference type but immutable — once created it never changes. Every "modification" returns a new String and leaves the original alone.

String s = "hello";
s.toUpperCase();      // returns "HELLO" but s is STILL "hello"
s = s.toUpperCase();  // reassign to keep the result

Why immutable? Thread-safety, safe use as HashMap keys (a stable hashCode), and the String pool — identical literals are interned and shared.

String a = "hi", b = "hi";
a == b;                 // true  -> same pooled object
new String("hi") == a;  // false -> a brand-new object
a.equals(b);            // true  -> ALWAYS compare strings with equals()

Building a string in a loop creates a new object each pass. Use StringBuilder (or StringBuffer when thread-safe).

StringBuilder sb = new StringBuilder();
for (String row : rows) sb.append(row).append('\n');
String out = sb.toString();   // one final allocation
== vs equals(): == compares references (same object); equals() compares value. For Strings and objects, always use equals().

05Collections Framework

Everything sits under List, Set, Map, or Queue:

  • List = ordered, duplicates allowed.
  • Set = no duplicates.
  • Map = key/value pairs.
TypeNotes
ArrayListResizable array; fast index access — the default List.
LinkedListDoubly-linked; fast add/remove at the ends.
HashMapKey→value, O(1) average, no ordering — the default Map.
LinkedHashMapHashMap that keeps insertion order.
TreeMapKeys sorted, O(log n).
HashSetUnique elements, backed by a HashMap.

Used most in practice: ArrayList for ordered lists and HashMap for key lookups.

List<String> names = new ArrayList<>();
names.add("Ada");
boolean has = names.contains("Ada");

Map<String, Integer> ages = new HashMap<>();
ages.put("Ada", 36);
ages.getOrDefault("Bob", 0);          // safe read, no null
ages.merge("Ada", 1, Integer::sum);   // count / increment
equals() & hashCode()

Hash collections (HashMap, HashSet) find an object by its hash first, then check equals. The rule: if two objects are equal they must return the same hashCode. Override both together (using the same fields) or hash lookups break — entries go missing and duplicates slip in. A record (Java 16+) generates both for you.

06Exceptions

Two kinds:

  • Checked — must be handled or declared with throws (e.g. IOException); enforced at compile time.
  • UncheckedRuntimeExceptions like NullPointerException, IllegalArgumentException; not enforced.
  • try / catch / finallyfinally always runs.
  • try-with-resources auto-closes anything AutoCloseable, even on exception.
  • throw raises an exception; throws declares one a method may propagate.
// try / catch / finally
try {
    int x = Integer.parseInt(input);
} catch (NumberFormatException e) {
    System.out.println("not a number");
} finally {
    System.out.println("always runs");
}

// try-with-resources: reader closed automatically
try (BufferedReader r = new BufferedReader(new FileReader("data.csv"))) {
    return r.readLine();
} catch (IOException e) {
    throw new RuntimeException("read failed", e);  // wrap, keep the cause
}
Good hygiene: catch the narrowest exception, never leave an empty catch, and keep the original cause when you wrap. In tests, let unexpected errors fail loudly.

07Generics & Streams

Generics add compile-time type safety and remove casts: a List<String> can only hold Strings.

Streams (Java 8+) process a collection as a pipeline: source → intermediate ops (lazy) → one terminal op. Nothing runs until the terminal op.

List<String> names = people.stream()
    .filter(p -> p.getAge() > 18)   // keep adults
    .map(Person::getName)           // transform
    .sorted()
    .collect(Collectors.toList());  // terminal op

long adults = people.stream().filter(p -> p.getAge() > 18).count();

Map<String, List<Person>> byDept = people.stream()
    .collect(Collectors.groupingBy(Person::getDept));

Optional<T> models "maybe a value" and avoids null checks: opt.map(...).orElse(default).

Streams vs loops: streams read well for filter/map/collect pipelines; a plain for loop is fine (and faster) for simple iteration or when you need to break early. Don't force everything into a stream.

08Test Ecosystem

  • JUnit 5@Test, @BeforeEach/@AfterEach, @BeforeAll/@AfterAll, @ParameterizedTest, @DisplayName, assertions, and a flexible extensions model.
  • TestNG — groups, data providers, dependencies, flexible parallel config, suite XML; common in Selenium frameworks.
  • Mockito — mock/stub dependencies; verify interactions.
  • AssertJ / Hamcrest — fluent, readable assertions.
  • Maven / Gradle — build & dependency management; Surefire/Failsafe run tests in CI.
@ParameterizedTest
@CsvSource({ "2,4", "3,9", "5,25" })
void squares(int in, int expected) {
    assertEquals(expected, in * in);
}

@Test
void usesMock() {
    UserRepo repo = mock(UserRepo.class);
    when(repo.find(1)).thenReturn(new User(1, "Ada"));
    assertEquals("Ada", new UserService(repo).nameOf(1));
    verify(repo).find(1);
}
JUnit vs TestNG: TestNG has richer built-in suite/parallel/group features and data providers; JUnit 5 is modular, extensible, and the default in modern Java. Both are fine — know why a framework picked one.

09Rapid-Fire Q&A

Reveal each answer to self-check, then test yourself with the quiz.

Primitive vs reference type?

Primitives hold the value and copy on assignment (int, boolean); reference types hold a reference to a heap object (objects, arrays, String).

Is Java pass-by-value or by-reference?

Always pass-by-value; for objects the reference is copied, so you can change the object but not reseat the caller's variable.

Default (package-private) vs protected?

No modifier = visible only within the same package; protected = same package plus subclasses.

What does final do?

final variable = constant; final method = can't be overridden; final class = can't be subclassed.

Abstract class vs interface?

Abstract class = shared code/state, extend ONE ("is-a"); interface = contract, implement MANY ("can-do"), with default methods since Java 8.

Overloading vs overriding?

Overloading = same name, different params, compile-time; overriding = same signature in a subclass, runtime.

== vs equals()?

== compares references; equals() compares value — always use equals() for Strings and objects.

Why is String immutable?

Thread-safety, safe HashMap keys, and string-pool sharing; use StringBuilder for heavy concatenation.

Why override hashCode with equals?

Hash collections find objects by hash first; equal objects must share a hashCode or HashMap/HashSet lookups break.

Checked vs unchecked exception?

Checked must be handled or declared (compile-time enforced); unchecked (RuntimeException) need not be.

ArrayList vs LinkedList?

ArrayList = fast index access (the usual choice); LinkedList = fast add/remove at the ends.

What is a Stream?

A Java 8 pipeline over a collection — filter / map / collect — that stays lazy until a terminal operation runs.