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 (
int↔Integer,boolean↔Boolean) 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 -> Integervar (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: afinalvariable is a constant, afinalmethod can't be overridden, afinalclass can't be subclassed.abstract— no body; a subclass must implement it.
03OOP Pillars
- Encapsulation —
privatefields + public getters/setters. - Inheritance —
extendsone 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 = 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 = 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 resultWhy 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== 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.
| Type | Notes |
|---|---|
| ArrayList | Resizable array; fast index access — the default List. |
| LinkedList | Doubly-linked; fast add/remove at the ends. |
| HashMap | Key→value, O(1) average, no ordering — the default Map. |
| LinkedHashMap | HashMap that keeps insertion order. |
| TreeMap | Keys sorted, O(log n). |
| HashSet | Unique 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 / incrementHash 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. - Unchecked —
RuntimeExceptions likeNullPointerException,IllegalArgumentException; not enforced.
try/catch/finally—finallyalways runs.- try-with-resources auto-closes anything
AutoCloseable, even on exception. throwraises an exception;throwsdeclares 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
}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).
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);
}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.