Java Fundamentals — Cheat Sheet
Java A–Z · 10 topics. Download the PDF or the Instagram carousel and share it.
Cheat Sheet · AiCanCode.org
Java Fundamentals
Java A–Z10 topicsQuick revision reference
1
JVM, JDK & JRE
- ✓JDK ⊃ JRE ⊃ JVM — JDK for developing, JRE for running, JVM for executing bytecode
- ✓Java bytecode is platform-neutral; the JVM translates it to native code per platform
- ✓JIT compilation converts frequently-executed bytecode to native code for peak performance
- ✓Heap is shared across threads; each thread has its own Stack, PC Register, and Native Method Stack
- ✓Class loading uses parent delegation: Bootstrap → Platform → Application ClassLoader
- ✓From Java 9+, use jlink to create lean custom runtimes instead of shipping a full JRE
HelloWorld.java
// Step 1: Compile source to bytecode
// javac HelloWorld.java → produces HelloWorld.class
// Step 2: JVM executes the bytecode
// java HelloWorld
public class HelloWorld {
public static void main(String[] args) {
// Print JVM details at runtime
System.out.println("Java version : " + System.getProperty("java.version"));
System.out.println("JVM name : " + System.getProperty("java.vm.name"));
System.out.println("Running on : " + System.getProperty("os.name"));
}
}2
Variables & Data Types
- ✓Java has exactly 8 primitive types: byte, short, int, long, float, double, char, boolean
- ✓Instance/static fields default to 0/0.0/false/null; local variables must be explicitly initialised
- ✓Always use L suffix for long literals > Integer.MAX_VALUE, and f for float literals
- ✓Reference variables store memory addresses — two references to the same object share mutations
- ✓Integer caches −128 to 127: use .equals() for wrapper comparisons, never ==
- ✓var (Java 10+) enables local type inference but the variable remains statically typed
PrimitiveDemo.java
public class PrimitiveDemo {
// Instance fields → get default values
int defaultInt; // 0
double defaultDouble; // 0.0
boolean defaultBool; // false
public static void main(String[] args) {
byte b = 127;
short s = 32_767; // underscore separator (Java 7+)
int i = 2_147_483_647;
long l = 9_223_372_036_854_775_807L; // L suffix required
float f = 3.14f; // f suffix required
double d = 3.141592653589793;
char c = 'A'; // or 'A'
boolean flag = true;
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Long.MIN_VALUE); // -9223372036854775808
System.out.println((int) c); // 65 — char is numeric!
}
}3
Operators & Expressions
- ✓Integer division truncates toward zero: 7/2 = 3; cast to double for real division
- ✓Use && and || (short-circuit) for conditions — prevents NPE on chained null checks
- ✓For object comparison, == tests reference equality; use .equals() for content equality
- ✓Integer overflow wraps silently; use Math.addExact() to throw ArithmeticException
- ✓<<n multiplies by 2ⁿ; >>n divides by 2ⁿ; >>>n is unsigned (fills with 0)
- ✓n & 1 checks odd/even; n & (n-1) clears the lowest set bit — staples of bit manipulation
ArithmeticDemo.java
public class ArithmeticDemo {
public static void main(String[] args) {
int a = 7, b = 2;
System.out.println(a + b); // 9
System.out.println(a - b); // 5
System.out.println(a * b); // 14
System.out.println(a / b); // 3 (integer division — truncates)
System.out.println(a % b); // 1 (remainder)
// Cast to double for real division
System.out.println((double) a / b); // 3.5
// Negative modulo: sign follows dividend
System.out.println(-7 % 3); // -1
System.out.println(7 % -3); // 1
// Pre vs post increment
int x = 5;
System.out.println(x++); // 5 (post: use then increment)
System.out.println(++x); // 7 (pre: increment then use)
// Overflow wraps silently for int
System.out.println(Integer.MAX_VALUE + 1); // -2147483648
// Use Math.addExact() to throw on overflow
// Math.addExact(Integer.MAX_VALUE, 1); // throws ArithmeticException
}
}4
Control Flow
- ✓Classic switch falls through by default — always add break or migrate to switch expressions
- ✓Switch expressions (Java 14+) with -> eliminate fall-through and can return values
- ✓Pattern matching in switch (Java 21) replaces instanceof chains with guard-aware type matching
- ✓do-while guarantees at least one execution; while may execute zero times
- ✓Enhanced for-each is cleaner but gives no index; use classic for when you need the index
- ✓Labeled break outer exits a named outer loop — useful in matrix traversal problems
IfElseDemo.java
public class IfElseDemo {
public static String classify(Object obj) {
// Pattern matching instanceof (Java 16+)
if (obj instanceof String s) {
return "String of length " + s.length();
} else if (obj instanceof Integer i && i > 0) {
return "Positive integer: " + i;
} else if (obj == null) {
return "null";
} else {
return "Other: " + obj.getClass().getSimpleName();
}
}
public static void main(String[] args) {
System.out.println(classify("hello")); // String of length 5
System.out.println(classify(42)); // Positive integer: 42
System.out.println(classify(-5)); // Other: Integer
System.out.println(classify(null)); // null
// Ternary
int score = 75;
String result = score >= 60 ? "Pass" : "Fail";
System.out.println(result); // Pass
}
}5
Arrays in Java
- ✓Array length is fixed at creation; use ArrayList for dynamic sizing
- ✓Elements default to 0/false (primitives) or null (objects) — no explicit init needed
- ✓Arrays.sort() uses dual-pivot quicksort for primitives (O(n log n)) and TimSort for objects
- ✓Arrays.binarySearch() only works correctly on a pre-sorted array
- ✓System.arraycopy() is the fastest bulk-copy — it calls native code
- ✓Prefix sum enables O(1) range queries; two-pointer reduces pair-sum from O(n²) to O(n)
ArrayBasics.java
import java.util.Arrays;
public class ArrayBasics {
public static void main(String[] args) {
// Declaration and allocation
int[] scores = new int[5]; // [0, 0, 0, 0, 0]
scores[0] = 95;
scores[4] = 87;
System.out.println(scores.length); // 5
// Array initialiser — size inferred
String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri"};
// Enhanced for-each
for (String day : days) System.out.print(day + " ");
System.out.println();
// Arrays utility methods
int[] nums = {5, 2, 8, 1, 9, 3};
Arrays.sort(nums); // in-place sort
System.out.println(Arrays.toString(nums)); // [1, 2, 3, 5, 8, 9]
int idx = Arrays.binarySearch(nums, 5); // works only on sorted array
System.out.println("Index of 5: " + idx);
int[] copy = Arrays.copyOf(nums, 4); // first 4 elements
System.out.println(Arrays.toString(copy)); // [1, 2, 3, 5]
int[] range = Arrays.copyOfRange(nums, 2, 5); // indices 2..4
System.out.println(Arrays.toString(range)); // [3, 5, 8]
int[] filled = new int[4];
Arrays.fill(filled, 7);
System.out.println(Arrays.toString(filled)); // [7, 7, 7, 7]
}
}6
Strings & StringBuilder
- ✓Strings are immutable — every modification creates a new String object; the original is unchanged
- ✓String literals share pooled instances; new String() bypasses the pool — always compare with .equals()
- ✓Use StringBuilder (not +) in loops to avoid O(n²) concatenation cost
- ✓strip() (Java 11+) is Unicode-aware; prefer it over trim() for modern code
- ✓isBlank() (Java 11+) returns true for empty strings AND strings containing only whitespace
- ✓substring(start, end) — end index is exclusive; substring(7, 11) gives 4 characters
StringPool.java
public class StringPool {
public static void main(String[] args) {
// Literals go to the string pool
String a = "hello";
String b = "hello";
System.out.println(a == b); // true — same pooled object
System.out.println(a.equals(b)); // true
// new String() bypasses the pool
String c = new String("hello");
System.out.println(a == c); // false — different heap objects
System.out.println(a.equals(c)); // true — same content
// intern() returns the pooled reference
String d = c.intern();
System.out.println(a == d); // true — now pointing to pool
// Strings are immutable — every "change" creates a new object
String s = "Java";
s = s + " 21"; // s now points to a NEW String "Java 21"
System.out.println(s); // Java 21
// The original "Java" object is unchanged (and eventually GC'd)
}
}7
Methods in Java
- ✓Java is strictly pass-by-value — for objects, the reference is copied, not the object
- ✓Method overloading is resolved at compile time (static polymorphism); overriding is runtime
- ✓Varargs (int... nums) must be the last parameter; internally treated as an array
- ✓Every recursive call consumes a stack frame — deep recursion without a base case causes StackOverflowError
- ✓Java does not optimise tail recursion — for large inputs, prefer iterative solutions
- ✓Use lo + (hi - lo) / 2 instead of (lo + hi) / 2 in binary search to avoid integer overflow
PassByValue.java
public class PassByValue {
static void tryChangeInt(int x) {
x = 99; // changes local copy only
}
static void tryChangeRef(StringBuilder sb) {
sb.append(" World"); // mutates the OBJECT — caller sees this
}
static void tryReplaceRef(StringBuilder sb) {
sb = new StringBuilder("Replaced"); // changes LOCAL copy of reference only
}
public static void main(String[] args) {
// Primitive — original unchanged
int n = 5;
tryChangeInt(n);
System.out.println(n); // 5
// Object — mutation IS visible (both refs point to same object)
StringBuilder s = new StringBuilder("Hello");
tryChangeRef(s);
System.out.println(s); // Hello World
// Reassigning ref inside method — caller NOT affected
tryReplaceRef(s);
System.out.println(s); // Hello World (unchanged)
}
}8
Classes & Objects
- ✓If you define any constructor, the compiler no longer generates a default no-arg constructor
- ✓this() to chain constructors must be the first statement in the constructor body
- ✓Make fields private; expose state through public getters and validated setters
- ✓All classes extend Object implicitly — override toString(), equals(), hashCode() for proper behaviour
- ✓equals() and hashCode() must be overridden together — inconsistency breaks HashMap/HashSet
- ✓Objects become eligible for GC when all references to them go out of scope
BankAccount.java
public class BankAccount {
// Fields (state)
private String owner;
private double balance;
private static int totalAccounts = 0; // shared across all instances
// No-arg constructor
public BankAccount() {
this("Unknown", 0.0); // delegates to 2-arg constructor
}
// Parameterised constructor
public BankAccount(String owner, double initialBalance) {
if (initialBalance < 0) throw new IllegalArgumentException("Balance cannot be negative");
this.owner = owner; // 'this' disambiguates field from param
this.balance = initialBalance;
totalAccounts++;
}
// Copy constructor
public BankAccount(BankAccount other) {
this(other.owner, other.balance);
}
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
balance += amount;
}
public boolean withdraw(double amount) {
if (amount > balance) return false;
balance -= amount;
return true;
}
// Getters
public String getOwner() { return owner; }
public double getBalance() { return balance; }
public static int getTotalAccounts() { return totalAccounts; }
@Override
public String toString() {
return "BankAccount[owner=" + owner + ", balance=" + balance + "]";
}
public static void main(String[] args) {
BankAccount acc1 = new BankAccount("Alice", 1000.0);
BankAccount acc2 = new BankAccount("Bob", 500.0);
BankAccount acc3 = new BankAccount(acc1); // copy constructor
acc1.deposit(200);
acc1.withdraw(50);
System.out.println(acc1); // BankAccount[owner=Alice, balance=1150.0]
System.out.println(BankAccount.getTotalAccounts()); // 3
}
}9
Inheritance
- ✓Java supports single inheritance for classes; use interfaces for multiple type inheritance
- ✓@Override makes the compiler verify the signature — always use it when overriding
- ✓super() to call parent constructor must be the first line; compiler inserts it if missing
- ✓Method dispatch is decided at runtime based on the actual object type (dynamic dispatch)
- ✓Covariant return types let overriding methods return a more specific type
- ✓Prefer composition over inheritance when the IS-A relationship is not genuine
Animal.java / Dog.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public String speak() {
return name + " makes a sound";
}
public String describe() {
return "I am " + name;
}
}
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // must call parent constructor first
this.breed = breed;
}
@Override // compiler verifies signature matches
public String speak() {
return name + " barks!";
}
@Override
public String describe() {
return super.describe() + ", a " + breed; // reuse + extend
}
public static void main(String[] args) {
Dog dog = new Dog("Rex", "Labrador");
System.out.println(dog.speak()); // Rex barks!
System.out.println(dog.describe()); // I am Rex, a Labrador
// Polymorphic reference — Animal variable holds a Dog
Animal a = new Dog("Buddy", "Poodle");
System.out.println(a.speak()); // Buddy barks! (runtime dispatch)
System.out.println(a instanceof Dog); // true
}
}10
Interfaces
- ✓Interface methods are public+abstract by default; fields are public+static+final
- ✓A class can implement multiple interfaces — Java's answer to multiple inheritance
- ✓Default methods (Java 8+) add behaviour to interfaces without breaking existing implementations
- ✓If two interfaces have the same default method, the implementing class must override it
- ✓A functional interface has exactly one abstract method — use @FunctionalInterface to enforce this
- ✓Prefer interfaces for defining contracts and capabilities; use abstract classes when sharing code
Circle.java
public interface Drawable {
// Abstract method — must be implemented
void draw();
// Constant (public static final implicitly)
int MAX_SIZE = 1000;
}
public interface Resizable {
void resize(double factor);
}
// Implementing multiple interfaces
public class Circle implements Drawable, Resizable {
private double radius;
public Circle(double radius) { this.radius = radius; }
@Override
public void draw() {
System.out.println("Drawing circle with radius " + radius);
}
@Override
public void resize(double factor) {
radius *= factor;
System.out.println("Resized to radius " + radius);
}
public static void main(String[] args) {
Circle c = new Circle(5.0);
c.draw(); // Drawing circle with radius 5.0
c.resize(2.0); // Resized to radius 10.0
// Polymorphic — interface reference
Drawable d = new Circle(3.0);
d.draw();
// d.resize(2.0); // compile error — Drawable doesn't have resize
}
}Learn this free with Aria, your AI tutor → AiCanCode.org/learn/java