Java Cheat Sheet
Syntax, patterns, and idioms - searchable, filterable by version, and copy-ready.
What Is a Java Cheat Sheet?
A Java cheat sheet is a condensed, scannable reference covering Java syntax, data types, control flow, object-oriented programming patterns, and standard library methods.
It targets developers who know programming basics but need fast recall of Java-specific syntax without digging through the full Java Language Specification (JLS) or Oracle docs.
Java is not a language you learn once and never look up. Even senior developers regularly reference method signatures, type ranges, and collection syntax. Over 90% of Fortune 500 companies use Java for their software systems (Netguru, 2025), which means Java syntax is a long-term skill with real daily use.
Scope of this reference: Java SE (Standard Edition), which underpins Android development, back-end development, and most enterprise application stacks. It covers the core language, not Spring, Hibernate, or Android SDK specifics.
This cheat sheet pairs well with the python cheat sheet, javascript cheat sheet, and sql cheat sheet if you work across multiple languages.
What Are the Java Data Types and Variable Declarations?
Java data types define what kind of value a variable holds and how much memory the JVM allocates for it. Java provides 8 primitive types and a separate category of reference types including String, arrays, and objects.
Java 10 also introduced var for local type inference, so you can skip explicit declarations in many method bodies.
Primitive Types Table
|
Type |
Size |
Default |
Range |
|---|---|---|---|
|
|
1 byte |
0 |
-128 to 127 |
|
|
2 bytes |
0 |
-32,768 to 32,767 |
|
|
4 bytes |
0 |
-2,147,483,648 to 2,147,483,647 |
|
|
8 bytes |
0L |
-9.2 × 10^18 to 9.2 × 10^18 |
|
|
4 bytes |
0.0f |
~7 decimal digits precision |
|
|
8 bytes |
0.0d |
~15 decimal digits precision |
|
|
2 bytes |
|
0 to 65,535 (Unicode) |
|
|
1 byte |
false |
true / false |
Primitive types store values directly on the stack. A reference type (Integer, String, an object) stores a heap address instead.
Accessing a primitive int at 4 bytes is significantly faster than accessing its Integer wrapper at 16 bytes on a 64-bit JVM (codingpancake, 2026).
Reference Types and Null Handling
String is not a primitive. It is an immutable object in the java.lang package. String literals are stored in the JVM String pool; objects created with new String() are not.
String a = "hello"; // String pool
String b = new String("hello"); // heap object — avoid this
Null is the default value for any uninitialized reference type. Calling methods on a null reference throws NullPointerException. Java 17 and later provide improved NPE messages identifying which variable is null.
Arrays are reference types. int[] holds primitive values but the array itself lives on the heap.
int[] scores = new int[5]; // default values: 0
String[] names = {"Alice", "Bob"}; // inline init
var total = 100; // type inferred as int (Java 10+)
Type Casting Syntax
Widening (implicit): No data loss. Smaller type assigned to a larger type automatically.
int x = 42;
double d = x; // widening: int -> double, no cast needed
Narrowing (explicit): Possible data loss. Cast required.
double d = 9.99;
int x = (int) d; // x = 9, decimal truncated
final declares a constant. By convention, constants use UPPER_SNAKE_CASE.
final int MAX_RETRIES = 3;
What Are the Java Operators?
Java operators cover 6 categories: arithmetic, relational, logical, bitwise, assignment, and the ternary operator. Knowing operator precedence prevents logic bugs that are annoying to track down.
Arithmetic and Assignment
int a = 10, b = 3;
a + b // 13
a - b // 7
a * b // 30
a / b // 3 (integer division — no decimal)
a % b // 1 (modulus)
a++ // post-increment: use then add
++a // pre-increment: add then use
Compound assignment operators combine an operation with assignment:
a += 5; // a = a + 5
a -= 2; // a = a - 2
a *= 3; // a = a * 3
a /= 4; // a = a / 4
a %= 2; // a = a % 2
Relational and Logical
Relational operators return boolean:
a == b // equal
a != b // not equal
a > b // greater than
a < b // less than
a >= b // greater or equal
a <= b // less or equal
Logical operators:
&& // AND — both must be true
|| // OR — at least one true
! // NOT — inverts boolean
Short-circuit evaluation: && stops if the left side is false. || stops if the left side is true. This matters when the right side has side effects.
Bitwise and Ternary
Bitwise operators work at the binary bit level:
a & b // AND
a | b // OR
a ^ b // XOR
~a // bitwise complement
a << 2 // left shift (multiply by 4)
a >> 2 // right shift (divide by 4, signed)
a >>> 2 // unsigned right shift
Ternary operator: Compact conditional. One line, two outcomes.
String result = (a > b) ? "greater" : "not greater";
Precedence order (high to low, partial): ++/-- → *///% → +/- → <</>> → relational → ==/!= → & → ^ → | → && → || → ternary → assignment.
When in doubt, use parentheses. They override precedence and make intent explicit.
What Are the Java Control Flow Statements?
Control flow statements determine which code runs, how many times, and under what conditions. Java provides 3 branching constructs and 3 loop types, plus 3 control keywords.
Conditional Statements
if (score >= 90) {
System.out.println("A");
} else if (score >= 75) {
System.out.println("B");
} else {
System.out.println("C");
}
Classic switch statement:
switch (day) {
case "MON": System.out.println("Monday"); break;
case "FRI": System.out.println("Friday"); break;
default: System.out.println("Other");
}
Switch expression (Java 14+): No fall-through, no break needed. Returns a value directly.
String label = switch (day) {
case "MON", "TUE" -> "Weekday";
case "SAT", "SUN" -> "Weekend";
default -> "Other";
};
Switch expressions are cleaner. Use them over classic switch for new code on Java 14+.
Loop Types and Syntax
|
Loop |
Best For |
Key Trait |
|---|---|---|
|
|
Known iteration count |
Init, condition, update in one line |
|
|
Unknown count, check first |
May never execute |
|
|
Unknown count, run at least once |
Always executes body once |
|
|
Arrays, Iterable collections |
Cleaner, no index access |
// Standard for
for (int i = 0; i < 5; i++) { System.out.println(i); }
// While
int i = 0;
while (i < 5) { System.out.println(i); i++; }
// Do-while
do { System.out.println(i); i++; } while (i < 5);
// For-each
int[] nums = {1, 2, 3};
for (int n : nums) { System.out.println(n); }
Loop Control Keywords
break exits the loop immediately.
continue skips the current iteration and moves to the next.
Labeled break exits a specific outer loop when working with nested loops:
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) break outer; // exits both loops
}
}
return exits the entire method, not just the loop.
What Are Java Arrays and Their Key Methods?
A Java array is a fixed-size, ordered collection of elements with the same type. Once created, its length cannot change. For dynamic sizing, use ArrayList instead.
The java.util.Arrays class provides utility methods that cover most common array operations.
Array Declaration and Initialization
// Static init
int[] scores = {85, 90, 78, 92};
// Dynamic init (values default to 0)
int[] scores = new int[4];
// 2D array
int[][] matrix = new int[3][4]; // 3 rows, 4 cols
int[][] grid = {{1,2}, {3,4}, {5,6}}; // inline
Access any element with its zero-based index: scores[0] returns the first element. Accessing an out-of-range index throws ArrayIndexOutOfBoundsException.
Array length uses the .length property, not a method: scores.length. Note that String.length() uses parentheses. This trips people up constantly.
Arrays Utility Methods
import java.util.Arrays;
int[] arr = {5, 2, 8, 1};
Arrays.sort(arr); // sorts in-place: [1, 2, 5, 8]
Arrays.toString(arr) // "[1, 2, 5, 8]" — for printing
Arrays.copyOf(arr, 6) // copy, pad with 0s to length 6
Arrays.copyOfRange(arr, 1, 3) // elements at index 1 and 2
Arrays.fill(arr, 0) // set all elements to 0
Arrays.binarySearch(arr, 5) // index of 5 (array must be sorted)
Arrays.equals(arr, otherArr) // true if same length and values
Arrays.sort() runs in O(n log n) using dual-pivot Quicksort for primitives and TimSort for objects.
What Are the Java String Methods?
A Java String is an immutable sequence of characters backed by a char[] array internally. Every method that appears to "modify" a string actually creates and returns a new String object.
The java.lang.String class is automatically imported. No import statement needed.
Common String Methods Table
|
Method |
Returns |
Example |
|---|---|---|
|
|
int |
|
|
|
char |
|
|
|
String |
|
|
|
int |
|
|
|
boolean |
|
|
|
String |
|
|
|
String[] |
|
|
|
String |
|
|
|
String |
|
|
|
String |
|
|
|
boolean |
|
|
|
boolean |
|
String comparison: Always use .equals(), not ==. The == operator compares object references, not values. Two String objects with identical content can still fail == if they were not interned.
String a = new String("hello");
String b = new String("hello");
a == b // false — different heap objects
a.equals(b) // true — same characters
StringBuilder Syntax
Baeldung JMH benchmarks show StringBuilder is the fastest string-building method, outperforming String.format() and StringBuffer in single-threaded scenarios.
Use StringBuilder when concatenating strings inside loops. Using + inside a loop creates a new heap object on every iteration.
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World");
String result = sb.toString(); // "Hello, World"
// Chaining
String r = new StringBuilder()
.append("Java")
.append(" 21")
.toString();
StringBuffer is the thread-safe version. Use it only when multiple threads write to the same buffer. For single-threaded code, StringBuilder is always faster.
String.format() is readable but slower due to internal regex parsing. Fine for low-frequency formatting; avoid it inside tight loops.
String msg = String.format("User: %s, Score: %d", name, score);
// printf equivalent:
System.out.printf("Name: %s%n", name);
What Are Java Classes, Objects, and Constructors?
A class is a blueprint. An object is an instance of that blueprint created at runtime with new. Every Java program with a main() method is a class.
JetBrains reports that 72% of Java developers use IntelliJ IDEA as their primary IDE, and the IDE's code generation features handle constructor scaffolding and getter/setter generation automatically. Still worth knowing the syntax by hand.
Class Declaration Syntax
public class Car {
// Fields (instance variables)
private String brand;
private int year;
private static int count = 0; // shared across all instances
// Default constructor
public Car() {
count++;
}
// Parameterized constructor
public Car(String brand, int year) {
this.brand = brand; // 'this' disambiguates field from param
this.year = year;
count++;
}
// Copy constructor
public Car(Car other) {
this.brand = other.brand;
this.year = other.year;
}
// Static method
public static int getCount() { return count; }
// Instance method
public String getBrand() { return brand; }
public void setBrand(String brand) { this.brand = brand; }
}
Object Instantiation
Car c1 = new Car("Toyota", 2022);
Car c2 = new Car(c1); // copy constructor
Car c3 = new Car(); // default constructor
System.out.println(Car.getCount()); // 3 — static method on class
System.out.println(c1.getBrand()); // "Toyota" — instance method
Static vs Instance Members
|
Member Type |
Belongs To |
Access |
Use Case |
|---|---|---|---|
|
Instance field |
Each object |
|
Per-object state |
|
Static field |
The class |
|
Shared counters, constants |
|
Instance method |
Each object |
|
Object behavior |
|
Static method |
The class |
|
Utility, factory methods |
this refers to the current object instance. It resolves naming conflicts between constructor parameters and instance fields. It also passes the current object to another method.
If a constructor has no explicit call to another constructor or to super(), the compiler inserts a call to super() automatically.
What Are the Java OOP Concepts With Syntax Examples?
Java's object-oriented model rests on 4 pillars: inheritance, encapsulation, polymorphism, and abstraction. These are not theoretical concepts in Java. They map directly to keywords and syntax patterns you write every day.
Spring, the most widely used Java framework, is used by 65% of Java developers (JetBrains, 2025). Every Spring bean and service class relies on these OOP principles, especially encapsulation and interface-based abstraction.
Inheritance and Super Keyword
Inheritance uses extends. A subclass inherits all public and protected members from its parent.
class Animal {
String name;
void speak() { System.out.println("..."); }
}
class Dog extends Animal {
@Override
void speak() {
super.speak(); // calls parent version
System.out.println("Woof!");
}
}
Key rules:
-
Java supports single class inheritance only (one
extendsper class) -
super.methodName()calls the overridden parent version -
@Overrideannotation is not required but catches typos at compile time
Interfaces vs. Abstract Classes
Both define contracts. The choice matters for design.
|
Feature |
Abstract Class |
Interface |
|---|---|---|
|
Instantiation |
No |
No |
|
Constructor |
Yes |
No |
|
Fields |
Any type |
|
|
Default methods |
Yes |
Yes (Java 8+) |
|
Multiple inheritance |
No |
Yes ( |
|
When to use |
Shared state + behavior |
Pure contract / multiple types |
interface Drawable {
void draw(); // abstract by default
default void reset() { } // default implementation (Java 8+)
}
abstract class Shape {
String color;
abstract double area(); // subclasses must implement
}
class Circle extends Shape implements Drawable {
double radius;
@Override public double area() { return Math.PI * radius * radius; }
@Override public void draw() { System.out.println("Drawing circle"); }
}
Method Overloading vs. Overriding
Two concepts. Completely different mechanics.
Overloading (compile-time polymorphism): Same method name, different parameter list. Resolved at compile time.
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
String add(String a, String b) { return a + b; }
Overriding (runtime polymorphism): Subclass provides its own version of a parent method. Resolved at runtime based on the actual object type.
Animal a = new Dog(); // reference is Animal, object is Dog
a.speak(); // calls Dog's speak(), not Animal's
The actual object type, not the reference type, determines which overridden method runs. That is the core of runtime polymorphism in the Java virtual machine.
What Are the Java Access Modifiers?
Java has 4 access modifiers that control where classes, methods, and fields are visible. Getting this wrong breaks encapsulation and makes codebases hard to maintain.
JetBrains survey data shows 68% of Java developers prefer private access for fields to minimize unintended interactions.
Access Modifier Scope Table
|
Modifier |
Same Class |
Same Package |
Subclass |
Anywhere |
|---|---|---|---|---|
|
|
Yes |
No |
No |
No |
|
default (none) |
Yes |
Yes |
No |
No |
|
|
Yes |
Yes |
Yes |
No |
|
|
Yes |
Yes |
Yes |
Yes |
Design rule: start with private. Only widen visibility when there is a specific reason to do so.
Non-Access Modifiers
Non-access modifiers change class and member behavior without affecting visibility.
static: Belongs to the class, not an instance. No object needed to call it.
public static int count = 0; // class-level field
public static void printCount() { } // class-level method
final: 3 different behaviors depending on context:
final int MAX = 100; // constant — value cannot change
final class ImmutableClass { } // cannot be subclassed
final void criticalMethod() { } // cannot be overridden
abstract: Class cannot be instantiated. Method has no body and must be implemented by a concrete subclass.
synchronized: Method or block allows only one thread at a time. Used in multi-threaded programming.
What Are the Java Collections Framework Classes?
The Java Collections Framework (JCF) is part of java.util and provides interfaces (List, Set, Map, Queue) with concrete implementations. Research on real-world Java codebases shows ArrayList accounts for 47% of all collection instantiations, followed by HashMap at 23% (SMU empirical study).
List Implementations
Declare to the interface, not the implementation. List<String> list = new ArrayList<>() is correct. ArrayList<String> list = new ArrayList<>() is fine but couples your code to the implementation.
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.get(0); // "Alice"
names.remove("Bob");
names.size(); // 1
names.contains("Alice"); // true
Collections.sort(names);
ArrayList gives O(1) random access. LinkedList is faster for mid-list insertions but slower for indexed access due to cache misses. Use ArrayList by default.
Map Implementations
HashMap is the go-to for key-value storage. O(1) average for get and put.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 88);
scores.get("Alice"); // 95
scores.containsKey("Bob"); // true
scores.remove("Bob");
// Iterating entries
for (Map.Entry<String, Integer> e : scores.entrySet()) {
System.out.println(e.getKey() + ": " + e.getValue());
}
LinkedHashMap preserves insertion order. TreeMap sorts by key. Use TreeMap when you need ordered keys or range queries (subMap, headMap).
Set and Queue Types
HashSet — unique elements, no guaranteed order:
Set<String> ids = new HashSet<>();
ids.add("A1"); ids.add("A2"); ids.add("A1"); // duplicate ignored
ids.size(); // 2
Queue and Deque: ArrayDeque replaces both Stack and LinkedList as the preferred queue implementation.
Deque<String> queue = new ArrayDeque<>();
queue.offer("task1"); // add to tail
queue.poll(); // remove from head
queue.push("task2"); // add to head (stack behavior)
queue.pop(); // remove from head (stack behavior)
Avoid Stack (legacy, synchronized). Avoid Vector (legacy, synchronized). Both have better modern replacements.
What Are Java Exception Handling Keywords and Patterns?
Exception handling separates error-management code from business logic. Java uses a checked/unchecked model that forces you to handle recoverable errors at compile time.
try-catch-finally is the foundation. The finally block runs regardless of whether an exception was thrown. Good for cleanup.
try-catch-finally Block Structure
try {
int result = 10 / 0; // throws ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Math error: " + e.getMessage());
} catch (Exception e) {
System.out.println("Other error"); // more specific catches go first
} finally {
System.out.println("Always runs"); // cleanup here
}
Multi-catch (Java 7+): Handle multiple exception types in one block.
catch (IOException | SQLException e) {
System.out.println("IO or DB error: " + e.getMessage());
}
Checked vs. Unchecked Exceptions
|
Type |
Examples |
Must Handle? |
Cause |
|---|---|---|---|
|
Checked |
|
Yes |
External, recoverable |
|
Unchecked |
|
No |
Programming errors |
|
Error |
|
No |
JVM-level, unrecoverable |
Oracle's rule: if a caller can reasonably recover from it, make it checked. If not, use unchecked.
Try-With-Resources and Custom Exceptions
Try-with-resources (Java 7+): Auto-closes any AutoCloseable resource when the block exits.
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line = br.readLine();
} catch (IOException e) {
e.printStackTrace();
} // br.close() called automatically — no finally needed
Custom exception:
class InsufficientFundsException extends RuntimeException {
private double amount;
public InsufficientFundsException(double amount) {
super("Insufficient funds: " + amount);
this.amount = amount;
}
}
Extend Exception for checked, RuntimeException for unchecked. Never leave catch blocks empty. Always log or rethrow.
What Are Java Lambda Expressions and Functional Interfaces?
Lambda expressions, introduced in Java 8, allow passing behavior as data. They reduce boilerplate by replacing anonymous inner classes with concise inline functions.
Lambda syntax:
// No params
Runnable r = () -> System.out.println("Running");
// One param (parens optional)
Consumer<String> print = s -> System.out.println(s);
// Multiple params, multiple statements
Comparator<Integer> cmp = (a, b) -> {
int result = Integer.compare(a, b);
return result;
};
Core Functional Interfaces from java.util.function
Java 8 introduced 4 primary functional interface types in java.util.function:
Predicate<T>: Takes T, returns boolean. Used for filtering.
Predicate<String> isLong = s -> s.length() > 5;
isLong.test("Hello"); // false
isLong.test("HelloWorld"); // true
Function<T,R>: Takes T, returns R. Used for mapping/transformation.
Function<String, Integer> strLen = s -> s.length();
strLen.apply("Hello"); // 5
Consumer<T>: Takes T, returns nothing. Used for side effects.
Consumer<String> logger = s -> System.out.println("LOG: " + s);
logger.accept("Event fired");
Supplier<T>: Takes nothing, returns T. Used for lazy initialization.
Supplier<Double> random = () -> Math.random();
random.get(); // returns a random double
Method References and @FunctionalInterface
Method references are shorthand for lambdas that just call a method.
// static method reference
Function<String, Integer> parse = Integer::parseInt;
// instance method reference
Consumer<String> print = System.out::println;
// constructor reference
Supplier<ArrayList<String>> listFactory = ArrayList::new;
@FunctionalInterface annotation is optional but recommended. It causes a compile error if someone accidentally adds a second abstract method to the interface.
What Are Java Stream API Methods?
The Stream API, also from Java 8, processes collections declaratively. A stream is a pipeline: data flows through a sequence of operations. Streams do not modify the source collection.
64% of enterprises use Java in cloud environments (Second Talent, 2025). Stream pipelines appear throughout modern Java cloud service code for filtering, transforming, and aggregating data.
Creating and Chaining Streams
List<String> names = List.of("Alice", "Bob", "Charlie", "David");
// From a collection
names.stream()
// From values
Stream.of("a", "b", "c")
// From an array
Arrays.stream(new int[]{1, 2, 3})
Intermediate operations (lazy, return a new Stream):
.filter(s -> s.startsWith("A")) // keep matching elements
.map(String::toUpperCase) // transform each element
.flatMap(s -> Arrays.stream(s.split(""))) // flatten nested streams
.sorted() // natural sort
.sorted(Comparator.reverseOrder()) // custom sort
.distinct() // remove duplicates
.limit(3) // take first 3
.skip(2) // skip first 2
Terminal operations (eager, consume the stream):
.collect(Collectors.toList()) // to List
.collect(Collectors.toSet()) // to Set
.collect(Collectors.joining(", ")) // concatenate strings
.collect(Collectors.groupingBy(String::length)) // group by key
.forEach(System.out::println) // side effect
.count() // element count
.findFirst() // Optional<T>
.reduce(0, Integer::sum) // fold to single value
Full Pipeline Example
List<String> result = names.stream()
.filter(n -> n.length() > 3)
.map(String::toLowerCase)
.sorted()
.collect(Collectors.toList());
// ["alice", "charlie", "david"]
Parallel streams: Add .parallelStream() instead of .stream() to split work across CPU cores.
long count = names.parallelStream()
.filter(n -> n.contains("a"))
.count();
Parallel streams help with large datasets and CPU-bound operations. Avoid them for small collections or I/O-bound work. The overhead of splitting and merging outweighs the benefit for most small collections.
What Are Java File I/O and Key java.nio Methods?
Java provides 2 file I/O APIs: the legacy java.io (stream-based, blocking) and the modern java.nio.file (buffer-based, richer). For most file tasks today, java.nio.file.Files is the cleaner choice.
java.nio was introduced in Java 1.4. java.nio.file (NIO.2) arrived in Java 7, adding the Path, Files, and Paths classes used in all modern Java file code (Baeldung, 2024).
Reading Files with java.nio
import java.nio.file.*;
import java.io.IOException;
Path path = Path.of("data.txt"); // Java 11+
// Path path = Paths.get("data.txt"); // Java 7-10
// Read entire file as String (Java 11+)
String content = Files.readString(path);
// Read all lines into a List
List<String> lines = Files.readAllLines(path);
// Read lines as a Stream (memory-efficient for large files)
try (Stream<String> stream = Files.lines(path)) {
stream.forEach(System.out::println);
}
Files.lines() processes lazily. It does not load the entire file into memory at once. Correct for large log files or CSV imports.
Writing Files with java.nio
// Write a String (creates or overwrites)
Files.writeString(path, "Hello, Java!");
// Write lines
List<String> data = List.of("line1", "line2");
Files.write(path, data);
// Append to existing file
Files.writeString(path, "\nNew line",
StandardOpenOption.APPEND);
BufferedReader and BufferedWriter (java.io)
Use BufferedReader/BufferedWriter from java.io when working with legacy code or when you need line-by-line processing with more control.
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
try (BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt", true))) {
bw.write("Appended line");
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
FileWriter(path, true) sets append mode. Without true, the file is overwritten.
Always wrap file operations in try-with-resources. Every file handle is AutoCloseable. Leaving one open is a resource leak that causes production issues.
For more on related reference sheets, see the git cheat sheet, docker cheat sheet, bash cheat sheet, and regex cheat sheet if you work across command-line and scripting tools alongside Java.
If you are evaluating which IDE to use for Java development, comparisons like Kotlin or Java and tools like Android Studio or IntelliJ IDEA vs Android Studio provide context on the broader software development toolchain Java fits into.
FAQ on Java Cheat Sheet
What is the difference between == and .equals() in Java?
== compares object references, not values. Two String objects with identical content can return false with == if they live on different heap addresses. Always use .equals() for string comparison in Java.
What are the 8 primitive data types in Java?
They are byte, short, int, long, float, double, char, and boolean. Each has a fixed size. int takes 4 bytes, long takes 8. These are the only types that store values directly on the stack rather than as heap references.
What is the difference between ArrayList and LinkedList?
ArrayList uses a resizable array. Random access is O(1). LinkedList uses a doubly-linked structure with faster mid-list insertions but slower indexed access. In practice, ArrayList outperforms LinkedList in most real-world scenarios due to CPU cache efficiency.
When should I use StringBuilder instead of String concatenation?
Use StringBuilder inside loops. The + operator creates a new immutable String object on every iteration, producing unnecessary heap objects. For simple one-off concatenations outside loops, the + operator is fine and readable.
What is the difference between checked and unchecked exceptions?
Checked exceptions like IOException are verified at compile time and must be handled or declared. Unchecked exceptions like NullPointerException are runtime errors. If a caller can reasonably recover from a failure, use a checked exception. Otherwise, use unchecked.
What does the static keyword do in Java?
static binds a field or method to the class itself, not to any instance. You call it as ClassName.method() without creating an object. Static fields are shared across all instances, making them useful for counters, constants, and utility methods.
What is the difference between an interface and an abstract class?
An abstract class can hold state, constructors, and concrete methods. An interface defines a pure contract. A class can implement multiple interfaces but only extend one abstract class. Use interfaces when you need multiple inheritance of behavior across unrelated types.
How does the Java Stream API differ from a for loop?
Streams are declarative. You describe what to do, not how to iterate. A stream().filter().map().collect() pipeline is often more readable than a nested loop. Streams also support parallel execution via .parallelStream() with minimal code changes.
What is try-with-resources and when should I use it?
It automatically closes any AutoCloseable resource when the block exits, eliminating the need for a finally block. Use it whenever you open a file, database connection, or network socket. It prevents resource leaks that cause hard-to-trace production failures.
What is the difference between HashMap, LinkedHashMap, and TreeMap?
HashMap offers O(1) lookups with no ordering guarantee. LinkedHashMap preserves insertion order. TreeMap sorts keys by natural order or a custom Comparator, at the cost of O(log n) operations. Choose based on whether ordering matters for your use case.