Core Java in One Shot: The Ultimate Core Java Cheat Sheet

    Core Java in One Shot: The Ultimate Core Java Cheat Sheet

    A simple and structured revision guide covering all the essential Core Java concepts in one place. From OOP principles, collections, exception handling, multithreading, and file handling to JVM basics — this cheat sheet helps you quickly revise what truly matters.

    default profile

    Shreyash Gurav

    March 02, 2026

    34 min read

    Core Java in One Shot – The Ultimate Cheat Sheet

    Java remains one of the most powerful and widely-used programming languages in the enterprise world. Whether you are preparing for an interview, revisiting concepts, or learning it for the first time, having a structured cheat sheet can save you hours of sifting through documentation. This guide provides a fast, practical reference to the most important Core Java concepts, combining essential theory with code examples you can use immediately.

    1. Java Introduction#

    History of Java#

    Java was created by James Gosling and his team at Sun Microsystems in the early 1990s. Originally named "Oak," it was designed for interactive television, but the project was ahead of its time. The team pivoted, and in 1995, Java 1.0 was officially released. Its timing coincided perfectly with the explosive growth of the World Wide Web, as Java's ability to run small programs (applets) in browsers made web pages dynamic for the first time. Sun Microsystems was later acquired by Oracle Corporation in 2010, which now maintains and develops the language.

    Features of Java#

    Java's enduring popularity stems from its robust design principles. It was built from the ground up to be reliable, secure, and portable.

    • Platform Independent: Java code does not run directly on the operating system. It compiles to an intermediate form called bytecode, which runs on a virtual machine.
    • Object-Oriented: Java embraces the object-oriented paradigm, allowing developers to model real-world entities using classes and objects. Everything except the primitive data types is an object.
    • Robust: The language is designed for reliability. It has strong memory management, a strict type system, and comprehensive exception handling to catch and manage errors.
    • Secure: Java runs inside a virtual machine sandbox, which isolates it from the underlying system. It also lacks explicit pointers, preventing unauthorized memory access.
    • Multithreaded: Java has built-in support for concurrent programming, allowing developers to write applications that can perform multiple tasks simultaneously, improving performance and responsiveness.

    WORA (Write Once, Run Anywhere)#

    This is the cornerstone philosophy of Java. When you write and compile Java code, it generates bytecode (.class files). This bytecode is not specific to any one operating system. To run it on a particular machine, you need a Java Virtual Machine (JVM) built for that specific operating system (Windows, Linux, macOS). The JVM acts as an intermediary, interpreting the bytecode into native machine code. This means you can compile your code on a Windows machine and run it without changes on a Linux server, provided a JVM is installed there.

    JDK, JRE, JVM#

    Understanding the Java ecosystem starts with these three acronyms. They are nested layers of the Java platform.

    ComponentFull FormWhat it ContainsPurpose
    JVMJava Virtual MachineRuntime engine, Interpreter, JIT CompilerExecutes the bytecode. It is the runtime environment's heart.
    JREJava Runtime EnvironmentJVM + Java standard librariesProvides the libraries and JVM needed to run Java applications. It is for end-users.
    JDKJava Development KitJRE + Development Tools (javac, jar, javadoc)Provides everything needed to develop and run Java applications. It is for developers.

    Java Program Structure#

    Every Java application has a defined structure. The JVM looks for a specific entry point to start execution.

    // 1. Package declaration (optional, but good practice for organizing classes) package com.example.myapp; // 2. Import statements (optional, for using classes from other packages) import java.util.Scanner; // 3. Class declaration (mandatory - the filename must match this class name) public class Main { // 4. Main method (the mandatory entry point for any Java application) public static void main(String[] args) { // 5. Statements (the actual code to be executed) System.out.println("Hello, World!"); } }

    2. Java Fundamentals#

    Data Types#

    Java is a statically-typed language, meaning every variable must have a declared type. Data types are divided into two categories.

    CategoryTypeSize/RangeDescriptionExample
    Primitivebyte1 byte (-128 to 127)Useful for saving memory in large arrays.byte b = 100;
     short2 bytes (-32,768 to 32,767)Rarely used, mainly for specific memory constraints.short s = 10000;
     int4 bytesThe default integer type.int i = 100000;
     long8 bytesUsed when a wider range than int is needed. Suffix with L.long l = 100000L;
     float4 bytes (single precision)For fractional numbers, less precise than double. Suffix with f.float f = 5.75f;
     double8 bytes (double precision)The default floating-point type.double d = 19.99;
     char2 bytes (Unicode)Stores a single character, using single quotes.char c = 'A';
     boolean

    JVM dependent

    (True / False)

    Represents logical true/false values.boolean flag = true;
    Non-PrimitiveString, Arrays, Classes, Interfaces, etc.VariesThese are reference types that refer to objects stored in heap memory.String str = "Hello";

    Variables#

    Variables are containers for storing data values. In Java, every variable has a type, a name, and a scope.

    // Declaration - telling the compiler about the variable's name and type int number; // Initialization - assigning a value for the first time number = 10; // Declaration + Initialization (combining both steps) String message = "Hello, World!";

    Types of Variables:

    • Local Variable: Declared inside a method, constructor, or block. It is created when the method is called and destroyed when it exits. It must be initialized before use.
    • Instance Variable (Non-static): Declared inside a class but outside any method. It belongs to a specific instance (object) of the class.
    • Static Variable (Class Variable): Declared with the static keyword. There is only one copy of this variable, shared by all objects of the class.

    Operators#

    Operators are special symbols that perform specific operations on one, two, or three operands and return a result.

    • Arithmetic: + (addition), - (subtraction), *(multiplication), / (division), % (modulo/remainder)
    • Relational: == (equal to), != (not equal to), > (greater than), < (less than), >= (greater than or equal to), <= (less than or equal to). They return a boolean result.
    • Logical: && (logical AND), || (logical OR), ! (logical NOT). Used to combine conditional statements.
    • Assignment: = (assign), += (add and assign), -= (subtract and assign), *= (multiply and assign), /= (divide and assign)
    • Unary: ++ (increment by 1), - (decrement by 1), ! (logical complement)
    • Ternary: A shorthand for an if-then-else statement. variable = (condition) ? expressionIfTrue : expressionIfFalse;

    Type Casting#

    Type casting is converting a value from one data type to another. Java supports two types of casting.

    Widening (Implicit/Automatic): Converting a smaller type to a larger type. This is safe and happens automatically because there is no risk of data loss.

    int myInt = 9; double myDouble = myInt; // Automatic casting: int to double (9.0)

    Narrowing (Explicit/Manual): Converting a larger type to a smaller type. This can lead to data loss and must be done manually by placing the target type in parentheses.

    double myDouble = 9.78; int myInt = (int) myDouble; // Manual casting: double to int (9, data lost)

    Input / Output (Scanner)#

    For basic user input from the console, the Scanner class (from java.util package) is the standard tool.

    import java.util.Scanner; // 1. Import the Scanner class public class Main { public static void main(String[] args) { // 2. Create a Scanner object to read from System.in (keyboard) Scanner scanner = new Scanner(System.in); // 3. Output a prompt to the user System.out.print("Enter your name: "); // 4. Read the input as a String String name = scanner.nextLine(); System.out.print("Enter your age: "); // 5. Read the input as an integer int age = scanner.nextInt(); System.out.println("Hello, " + name + ". You are " + age + " years old."); // 6. Good practice to close the scanner to release resources scanner.close(); } }

    3. Control Flow#

    Control flow statements determine the order in which code is executed, allowing for decision-making and repetition.

    if / else#

    The most basic form of decision-making. The code inside a block executes only if the specified boolean condition is true.

    int score = 85; if (score >= 90) { System.out.println("Grade: A"); } else if (score >= 80) { System.out.println("Grade: B"); // This will execute } else if (score >= 70) { System.out.println("Grade: C"); } else { System.out.println("Grade: F"); // Default case }

    switch#

    The switch statement is useful for selecting one of many code blocks to execute based on the value of a sin gle variable (which can be int, char, String, or enum). It is often more readable than a long chain of if-else if statements.

    int dayNumber = 3; String dayName; switch (dayNumber) { case 1: dayName = "Monday"; break; // break is crucial to prevent fall-through case 2: dayName = "Tuesday"; break; case 3: dayName = "Wednesday"; // This will be assigned break; case 4: dayName = "Thursday"; break; // ... cases for 5, 6, 7 default: dayName = "Invalid day"; // break is optional here, as it's the last case } System.out.println(dayName); // Output: Wednesday

    Loops#

    Loops are used to execute a block of code repeatedly.

    for loop: Best used when the number of iterations is known beforehand.

    // Loop from i=0 to i=4, printing each value for (int i = 0; i < 5; i++) { System.out.println("Iteration: " + i); }

    Enhanced for-each loop: A simplified syntax for iterating over arrays and collections. You don't have access to the index.

    int[] numbers = {10, 20, 30, 40}; for (int num : numbers) { System.out.println(num); }

    while loop: Best used when the number of iterations is unknown, and the loop continues as long as a condition is true. The condition is checked before the loop body executes.

    int i = 0; while (i < 5) { System.out.println(i); i++; }

    do-while loop: Similar to a while loop, but the condition is checked after the loop body executes. This guarantees that the loop body will run at least once.

    int i = 0; do { System.out.println(i); i++; } while (i < 5);

    break / continue#

    These statements are used to alter the normal flow of a loop.

    • break: Immediately terminates the innermost loop or switch statement.
    • continue: Skips the remaining code in the current iteration and jumps to the next iteration of the loop.

    4. Methods#

    Methods are reusable blocks of code that perform a specific task. They are fundamental to structured and modular programming.

    Method Declaration#

    A method declaration provides all the information needed to call the method.

    // AccessModifier Optional(static) ReturnType methodName(ParameterList) { // // method body // // return statement (if return type is not void) // } public static int addNumbers(int a, int b) { int sum = a + b; return sum; // The value 'sum' is sent back to the caller }

    Parameters and Return Types#

    • Parameters: The variables listed in the method declaration inside the parentheses. They act as placeholders for the data the method will receive (a and b in the example).
    • Arguments: The actual values passed to the method when it is called (e.g., addNumbers(5, 3) passes 5 and 3 as arguments).
    • Return Type: The data type of the value the method returns. If a method does not return a value, its return type must be declared as void.

    Method Overloading#

    Method overloading allows a class to have multiple methods with the same name, as long as their parameter lists are different (different number, type, or order of parameters). This is a form of compile-time polymorphism.

    public class MathUtils { // Method to add two integers public int add(int a, int b) { return a + b; } // Overloaded method to add three integers (different number of parameters) public int add(int a, int b, int c) { return a + b + c; } // Overloaded method to add two doubles (different parameter type) public double add(double a, double b) { return a + b; } }

    Recursion#

    Recursion occurs when a method calls itself. Every recursive method must have a base condition that stops the recursion; otherwise, it will lead to a StackOverflowError. It is often used for problems that can be broken down into smaller, similar sub-problems, like traversing tree structures or calculating factorials.

    public static int factorial(int n) { // Base case: if n is 0, factorial is 1 if (n == 0) { return 1; } // Recursive case: n! = n * (n-1)! else { return n * factorial(n - 1); } } // factorial(4) returns 4 * factorial(3) -> 4 * 3 * factorial(2) -> ... -> 4*3*2*1 = 24

    5. Object Oriented Programming (OOP)#

    OOP is a programming paradigm based on the concept of "objects," which contain data (fields) and code (methods). Java is fundamentally built around these four pillars.

    Pillars of OOP

    Class and Object#

    • Class: A class is a blueprint or template from which individual objects are created. It defines the state (variables) and behavior (methods) that the objects of the class will have.
    • Object: An object is an instance of a class. When you create an object, you allocate memory to hold its state, as defined by its class.
    // Class definition class Car { // State (instance variables) String model; int year; // Behavior (methods) void startEngine() { System.out.println("The " + model + " engine is starting."); } } // Creating objects (instances of the Car class) Car myFirstCar = new Car(); myFirstCar.model = "Tesla Model 3"; myFirstCar.year = 2023; Car mySecondCar = new Car(); mySecondCar.model = "Ford Mustang"; mySecondCar.year = 1967; myFirstCar.startEngine(); // Output: The Tesla Model 3 engine is starting.

    Constructors#

    A constructor is a special method that is automatically called when an object is instantiated using the new keyword. It is used to initialize the new object's state. Constructors have the same name as the class and no return type.

    class Car { String model; int year; // Constructor public Car(String modelName, int manufactureYear) { model = modelName; year = manufactureYear; } } // When creating the object, we must provide the constructor arguments Car myCar = new Car("Tesla Model 3", 2023);

    this Keyword#

    Inside a class, the this keyword is a reference to the current object. It is most commonly used to distinguish between instance variables and parameters with the same name.

    public class Person { private String name; public Person(String name) { // Parameter 'name' shadows the instance variable this.name = name; // 'this.name' refers to the instance variable, 'name' refers to the parameter } }

    Encapsulation#

    Encapsulation is the mechanism of wrapping data (variables) and code acting on the data (methods) together as a single unit. It also involves hiding the internal state of an object from the outside world and providing controlled access through public methods (getters and setters). This protects data from unauthorized access and modification.

    public class BankAccount { private double balance; // Private variable - cannot be accessed directly // Public getter to read the balance (read-only access) public double getBalance() { return balance; } // Public method to deposit money (controlled modification) public void deposit(double amount) { if (amount > 0) { balance += amount; } } }

    Inheritance#

    Inheritance is a mechanism where one class (child/subclass) acquires the properties and methods of another class (parent/superclass). It represents an "is-a" relationship. In Java, inheritance is achieved using the extends keyword. A class can only extend one parent class.

    class Vehicle { // Parent class String brand = "Generic"; public void honk() { System.out.println("Beep beep!"); } } class Car extends Vehicle { // Child class inherits from Vehicle String model = "Model 3"; public void printDetails() { System.out.println(brand + " " + model); // Can access 'brand' inherited from Vehicle } }

    Polymorphism#

    Polymorphism means "many forms." It allows objects of different classes to be treated as objects of a common superclass, and the correct method for that specific object is called at runtime. This is achieved through method overriding.

    Method Overriding#

    Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its superclass. This is how runtime polymorphism is achieved. The method in the child class must have the same name, return type, and parameters as the parent method. It's good practice to use the @Override annotation.

    class Animal { public void makeSound() { System.out.println("Some generic animal sound"); } } class Dog extends Animal { @Override // This annotation tells the compiler we are overriding a method public void makeSound() { System.out.println("Woof woof!"); // Specific implementation for Dog } } // Polymorphism in action Animal myAnimal = new Dog(); // Reference of type Animal, object of type Dog myAnimal.makeSound(); // Output: Woof woof! (Dog's version is called)

    Abstraction#

    Abstraction is the concept of hiding complex implementation details and showing only the essential features of an object. It helps in managing complexity by focusing on what an object does rather than how it does it. In Java, abstraction is achieved through abstract classes and interfaces.

    Abstract Classes#

    An abstract class is a class that cannot be instantiated. It may contain abstract methods (methods without a body) and concrete methods (with a body). It serves as a partial blueprint for its subclasses, forcing them to implement the abstract methods.

    abstract class Shape { // Abstract method - no body, must be implemented by subclasses public abstract double calculateArea(); // Concrete method - can be used directly by subclasses public void display() { System.out.println("Displaying shape."); } } class Circle extends Shape { double radius; Circle(double r) { radius = r; } @Override public double calculateArea() { // Must implement this method return Math.PI * radius * radius; } }

    Interfaces#

    An interface is a completely abstract blueprint of a class. It only declares methods (and sometimes constants) but provides no implementation. A class can implement multiple interfaces, providing a way to achieve multiple inheritance in Java. Since Java 8, interfaces can also have default and static methods with implementations.

    interface Drawable { void draw(); // public and abstract by default } interface Colorable { void setColor(String color); } // A class can implement multiple interfaces class ColoredCircle implements Drawable, Colorable { private String color; @Override public void draw() { System.out.println("Drawing a circle of color: " + color); } @Override public void setColor(String color) { this.color = color; } }

    6. Arrays#

    Arrays are fundamental data structures that store a fixed-size sequential collection of elements of the same type.

    One-Dimensional Arrays#

    An array is declared with its type, followed by square brackets. Memory is allocated using the new keyword, and the size is fixed at creation.

    // Declaration and memory allocation int[] numbers = new int[5]; // Creates an array of 5 integers, all initialized to 0 // Declaration, allocation, and initialization in one line String[] names = {"Alice", "Bob", "Charlie"}; // Assigning values using index (zero-based) numbers[0] = 10; numbers[1] = 20; // numbers[5] = 60; // This would throw an ArrayIndexOutOfBoundsException

    Multi-Dimensional Arrays#

    Multi-dimensional arrays are arrays of arrays. The most common is the two-dimensional array, which can be visualized as a table with rows and columns.

    // 2D Array: 3 rows, 2 columns int[][] matrix = new int[3][2]; // Declaration and initialization int[][] predefinedMatrix = { {1, 2}, {3, 4}, {5, 6} }; int value = predefinedMatrix[1][0]; // value is 3 (second row, first column)

    Array Traversal#

    To access each element of an array, you typically use a loop.

    int[] scores = {85, 92, 78, 95}; // Using a standard for loop (useful when you need the index) for (int i = 0; i < scores.length; i++) { System.out.println("Element at index " + i + ": " + scores[i]); } // Using an enhanced for-each loop (simpler, no index) for (int score : scores) { System.out.println("Score: " + score); }

    Sorting and Searching#

    The java.util.Arrays class provides useful utility methods for working with arrays.

    import java.util.Arrays; int[] numbers = {42, 17, 8, 99, 23}; // Sorting the array in ascending order (modifies the original array) Arrays.sort(numbers); // numbers becomes [8, 17, 23, 42, 99] // Searching for a value using binary search (array MUST be sorted first) int index = Arrays.binarySearch(numbers, 23); // Returns the index of 23 (which is 2) System.out.println("Found 23 at index: " + index); // If not found, binarySearch returns a negative insertion point. int notFoundIndex = Arrays.binarySearch(numbers, 50); // Returns -5

    7. Strings#

    String Class#

    In Java, String is not a primitive type but a class (java.lang.String). It represents a sequence of characters. String objects are created either using literals or the new keyword.

    String str1 = "Hello"; // String literal - stored in the String Pool for efficiency String str2 = new String("World"); // Using new - creates a new object in the heap

    String Immutability#

    Once a String object is created, its value cannot be changed. Any operation that appears to modify a string, such as concat() or toUpperCase(), actually creates and returns a new String object. The original string remains unchanged. This immutability is crucial for security, caching (String Pool), and thread-safety.

    String s = "Hello"; s.concat(" World"); // This creates a new string "Hello World", but does not change 's' System.out.println(s); // Output: Hello (s is unchanged) String s2 = s.concat(" World"); // You must assign the result to a new variable System.out.println(s2); // Output: Hello World

    Important String Methods#

    String s = " Java Programming "; s.length(); // Returns 20 s.charAt(2); // Returns 'J' (index 2 is the third character) s.substring(5); // Returns "Programming" (from index 5 to end) s.substring(5, 9); // Returns "Prog" (from index 5 inclusive to 9 exclusive) s.contains("Java"); // Returns true s.trim(); // Returns "Java Programming" (removes leading/trailing spaces) s.toUpperCase(); // Returns " JAVA PROGRAMMING " (returns new string) s.toLowerCase(); // Returns " java programming " s.replace('a', 'x'); // Returns " Jxvx Progrmming " s.split(" "); // Returns an array: ["", "", "Java", "Programming", "", ""]

    StringBuilder vs StringBuffer#

    For scenarios where you need to perform many modifications (append, insert, delete) to a character sequence, using String would be inefficient due to the constant creation of new objects. StringBuilder and StringBuffer are mutable alternatives.

    FeatureStringBufferStringBuilder
    Thread-SafetySynchronized, meaning it is thread-safe for use in multi-threaded environments.Not synchronized, meaning it is not thread-safe.
    PerformanceSlower due to the overhead of synchronization.Faster, as it has no synchronization overhead.
    Use CaseUse when you need to modify strings from multiple threads concurrently.Use in single-threaded scenarios or where thread-safety is handled externally. This is the most common choice.
    // Example with StringBuilder StringBuilder sb = new StringBuilder("Start"); sb.append("Middle"); // sb now contains "StartMiddle" sb.insert(5, "End"); // sb now contains "StartEndMiddle" (inserts "End" at index 5) sb.delete(5, 8); // sb now contains "StartMiddle" (removes "End") String finalString = sb.toString(); // Convert back to an immutable String

    8. Exception Handling#

    Exception handling is a mechanism to handle runtime errors, allowing the normal flow of the application to continue. An exception is an event that disrupts the normal execution of a program.

    try / catch / finally#

    This is the primary block for handling exceptions.

    • try: Contains the code that might throw an exception.
    • catch: Handles the exception. You can have multiple catch blocks for different exception types.
    • finally: (Optional) Contains code that will always execute, regardless of whether an exception occurred or not. It is typically used for cleanup activities like closing files or database connections.
    try { int[] numbers = {1, 2, 3}; System.out.println(numbers[5]); // This will throw ArrayIndexOutOfBoundsException int result = 10 / 0; // This line will not execute because the exception above jumps to catch } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Error: Array index is out of bounds!"); } catch (ArithmeticException e) { System.out.println("Error: Cannot divide by zero!"); } finally { System.out.println("This block always runs, performing cleanup."); }

    throw and throws#

    throw: This keyword is used to explicitly throw a single exception from a method or a block of code. You can throw a newly created exception or one that was just caught.

    public void checkAge(int age) { if (age < 18) { // Explicitly throwing an exception throw new IllegalArgumentException("Age must be 18 or older."); } }

    throws: This keyword is used in a method signature to declare that the method might throw one or more exceptions. It informs the caller that they need to handle these exceptions.

    // This method declares that it might throw an IOException public void readFile(String path) throws IOException { // Code that might throw an IOException, like creating a FileReader FileReader file = new FileReader(path); }

    Custom Exceptions#

    While Java provides many built-in exceptions, you can create your own for application-specific error conditions. You create a class that extends Exception (to create a checked exception) or RuntimeException (to create an unchecked exception).

    // Custom checked exception class InsufficientFundsException extends Exception { public InsufficientFundsException(String message) { super(message); // Pass the message to the parent Exception class } } public class BankAccount { private double balance = 100; public void withdraw(double amount) throws InsufficientFundsException { if (amount > balance) { throw new InsufficientFundsException("You do not have sufficient funds."); } balance -= amount; } }

    9. Collections Framework#

    Collections Framework

    The Collections Framework provides a well-designed set of interfaces and classes for storing and processing groups of data as a single unit. It replaces arrays with more flexible and powerful data structures.

    List (Ordered, Allows Duplicates)#

    A List is an ordered collection (also known as a sequence). The user has precise control over where in the list each element is inserted and can access them by their integer index. Lists can contain duplicate elements.

    • ArrayList: A resizable-array implementation. It offers fast random access (O(1)) but is slower for inserting or deleting elements in the middle (O(n)) as it may require shifting elements.
    • LinkedList: A doubly-linked list implementation. It offers better performance for inserting and deleting elements in the middle (O(1) for some operations) but is slower for random access (O(n)). It can also be used as a queue or deque.
    List<String> arrayList = new ArrayList<>(); arrayList.add("Apple"); arrayList.add("Banana"); arrayList.add(0, "Orange"); // Insert at specific index String fruit = arrayList.get(1); // Fast access

    Set (Unordered or Sorted, No Duplicates)#

    A Set is a collection that cannot contain duplicate elements. It models the mathematical set abstraction.

    • HashSet: Uses a hash table for storage. It offers constant-time performance (O(1)) for basic operations like add, remove, and contains, but it makes no guarantees about the order of elements.
    • LinkedHashSet: Extends HashSet and maintains a doubly-linked list of its entries. This ensures that the iteration order is predictable—the order in which elements were inserted.
    • TreeSet: Implements the SortedSet interface using a tree structure (red-black tree). It stores elements in a sorted (ascending) order. Its operations are slower (O(log n)) than HashSet but guarantee sorted order.
    Set<Integer> hashSet = new HashSet<>(); hashSet.add(20); hashSet.add(10); hashSet.add(30); // Order is not guaranteed; might print [20, 10, 30] or similar Set<Integer> treeSet = new TreeSet<>(); treeSet.add(20); treeSet.add(10); treeSet.add(30); // Iteration will always be in sorted order: [10, 20, 30]

    Map (Key-Value Pairs)#

    A Map is an object that maps keys to values. It cannot contain duplicate keys; each key can map to at most one value.

    • HashMap: Implements the Map interface using a hash table. It offers constant-time performance for get and put. It does not guarantee the order of its entries.
    • LinkedHashMap: Extends HashMap and maintains a doubly-linked list of its entries, ensuring predictable iteration order—usually the order in which keys were inserted.
    • TreeMap: Implements the SortedMap interface. It stores its entries in a sorted order based on the keys (natural ordering or a provided comparator). Its operations are O(log n).
    Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 95); scores.put("Bob", 87); scores.put("Charlie", 92); int aliceScore = scores.get("Alice"); // 95 // Iterating over entries for (Map.Entry<String, Integer> entry : scores.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); }

    Iterator#

    An Iterator is an object that enables you to traverse through a collection and to remove elements selectively during the traversal. It provides a uniform way to access elements of any Collection.

    List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C")); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { // Check if there is another element String item = iterator.next(); // Get the next element System.out.println(item); if (item.equals("B")) { iterator.remove(); // Safely remove the current element from the list } }

    Comparable vs Comparator#

    Both interfaces are used to define the order of objects, especially for sorting.

    FeatureComparableComparator
    PurposeDefines the natural ordering of objects. For example, Integer's natural order is numeric ascending.Defines a custom ordering. For example, sorting Person objects by name length or age.
    Packagejava.langjava.util
    Methodint compareTo(T o)int compare(T o1, T o2)
    Class ModificationRequires the class whose objects are being compared to implement Comparable.A separate class or lambda expression implements Comparator. The original class remains unchanged.
    Sorting MethodCollections.sort(list)Collections.sort(list, comparator)
    // Comparable: Inside the class itself class Student implements Comparable<Student> { int age; String name; Student(int age, String name) { this.age = age; this.name = name; } @Override public int compareTo(Student s) { return this.age - s.age; // Sort by age (natural order) } } // Comparator: External, using a lambda for custom sorting List<Student> students = new ArrayList<>(); // ... add students // Sort by name using a Comparator (no change to Student class) Collections.sort(students, (s1, s2) -> s1.name.compareTo(s2.name));

    10. File Handling#

    File handling in Java is done through classes in the java.io package. It involves reading data from and writing data to files.

    File Class#

    The File class is an abstract representation of file and directory pathnames. It is used to get information about a file (like existence, size, permissions) but not for reading/writing the file content itself.

    import java.io.File; File myFile = new File("example.txt"); if (myFile.exists()) { System.out.println("File name: " + myFile.getName()); System.out.println("Absolute path: " + myFile.getAbsolutePath()); System.out.println("Writable: " + myFile.canWrite()); System.out.println("File size in bytes: " + myFile.length()); } else { System.out.println("File does not exist."); }

    FileReader / FileWriter#

    These classes are used for reading and writing character files (text files). They handle the conversion between bytes and characters using the platform's default character encoding.

    // Writing to a file using FileWriter FileWriter writer = new FileWriter("output.txt"); writer.write("Hello, this is the first line.\n"); writer.write("This is the second line."); writer.close(); // Always close to flush and release resources // Reading from a file using FileReader FileReader reader = new FileReader("output.txt"); int character; while ((character = reader.read()) != -1) { // read() returns -1 at end of file System.out.print((char) character); } reader.close();

    BufferedReader / BufferedWriter#

    For better performance, especially when dealing with large files or reading line by line, it is highly recommended to wrap FileReader and FileWriter with BufferedReader and BufferedWriter. These classes use an internal buffer to reduce the number of direct read/write operations to the underlying file system.

    // Efficient writing BufferedWriter bw = new BufferedWriter(new FileWriter("data.txt")); bw.write("Buffered Line 1"); bw.newLine(); // Writes a platform-specific line separator bw.write("Buffered Line 2"); bw.close(); // Efficient reading line by line BufferedReader br = new BufferedReader(new FileReader("data.txt")); String line; while ((line = br.readLine()) != null) { // readLine() returns null at EOF System.out.println(line); } br.close();

    Serialization#

    Serialization is the process of converting an object into a byte stream, which can then be saved to a file, sent over a network, or stored in a database. Deserialization is the reverse process. For a class to be serializable, it must implement the Serializable marker interface (an interface with no methods).

    import java.io.*; // The class must implement Serializable class Person implements Serializable { private static final long serialVersionUID = 1L; // Recommended for version control String name; int age; Person(String name, int age) { this.name = name; this.age = age; } } public class SerializationExample { public static void main(String[] args) { Person person = new Person("Alice", 30); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) { oos.writeObject(person); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) { Person deserializedPerson = (Person) ois.readObject(); System.out.println(deserializedPerson.name + ", " + deserializedPerson.age); // Output: Alice, 30 } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }

    11. Multithreading#

    Multithreading is a Java feature that allows concurrent execution of two or more parts of a program to maximize CPU utilization. Each part is called a thread.

    Creating Threads#

    There are two primary ways to create a thread in Java.

    Extending the Thread Class: Create a subclass of Thread and override its run() method. Then, create an instance of this subclass and call its start() method.

    class MyThread extends Thread { public void run() { System.out.println("Thread running: " + Thread.currentThread().getName()); } } // Usage MyThread t1 = new MyThread(); t1.start(); // start() automatically calls run() in a new thread

    Implementing the Runnable Interface (Preferred): Create a class that implements the Runnable interface and its run() method. Then, pass an instance of this class to a Thread object's constructor and call start(). This is preferred because Java supports single inheritance, so implementing Runnable allows your class to extend another class if needed.

    class MyRunnable implements Runnable { public void run() { System.out.println("Runnable thread running: " + Thread.currentThread().getName()); } } // Usage Thread t2 = new Thread(new MyRunnable()); t2.start();

    Thread Lifecycle#

    A thread can be in one of several states during its lifetime.

    Thread Life Cycle
    1. New: A thread is created but not yet started (new Thread()).
    2. Runnable: After calling start(), the thread is ready to run and waiting for CPU time.
    3. Blocked/Waiting: The thread is alive but not eligible to run. It is waiting for a monitor lock (blocked) or for another thread to perform an action (waiting).
    4. Timed Waiting: A version of waiting with a specified waiting time (e.g., Thread.sleep(1000), wait(5000)).
    5. Terminated: The thread's run() method has completed, and it is no longer alive.

    Synchronization#

    When multiple threads access shared resources (like an object's variable), it can lead to data inconsistency. Synchronization is the mechanism that ensures that only one thread can access the resource at a time. This is achieved using the synchronized keyword.

    public class Counter { private int count = 0; // Synchronized method - only one thread can execute this method at a time per object public synchronized void increment() { count++; } public int getCount() { return count; } } // In a multi-threaded scenario, this prevents race conditions

    Executor Framework#

    Introduced in Java 5, the Executor Framework provides a higher-level replacement for working with threads directly. It manages a pool of threads, handles their lifecycle, and allows you to submit tasks for asynchronous execution. This is more efficient and manageable than creating new threads for every task.

    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorExample { public static void main(String[] args) { // Create a fixed thread pool with 5 threads ExecutorService executor = Executors.newFixedThreadPool(5); // Submit tasks (Runnable) for execution for (int i = 0; i < 10; i++) { Runnable task = () -> { System.out.println("Executing task in " + Thread.currentThread().getName()); }; executor.submit(task); } // Important: Shut down the executor so the application can terminate executor.shutdown(); } }

    12. Java 8 Features#

    Java 8 was a landmark release, introducing significant features that brought functional programming concepts to the language.

    Lambda Expressions#

    A lambda expression is a short block of code that takes in parameters and returns a value. It provides a clear and concise way to represent an instance of a functional interface. It enables you to treat functionality as a method argument.

    // Old way using an anonymous inner class Runnable r1 = new Runnable() { @Override public void run() { System.out.println("Old way"); } }; // New way using a lambda expression Runnable r2 = () -> System.out.println("Lambda way");

    Functional Interfaces#

    A functional interface is an interface that has exactly one abstract method. They can have multiple default or static methods, but only one abstract method. Lambda expressions rely on these interfaces. Common examples include Runnable, Callable, Comparator, and those in the java.util.function package like Predicate, Function, and Consumer. The @FunctionalInterface annotation is used to declare an interface as functional.

    @FunctionalInterface interface MathOperation { int operate(int a, int b); // Single abstract method } // Lambda implementing the interface MathOperation addition = (a, b) -> a + b; int result = addition.operate(5, 3); // 8

    Streams API#

    The Stream API is used to process collections of objects. A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result. The key idea is to perform operations on data in a declarative way, similar to SQL queries. Streams do not store data; they operate on the source data structure (like a collection) and may also produce a result.

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Alex"); List<String> result = names.stream() // Create a stream from the list .filter(name -> name.startsWith("A")) // Intermediate operation: keep names starting with 'A' .map(String::toUpperCase) // Intermediate operation: convert to uppercase .sorted() // Intermediate operation: sort .collect(Collectors.toList()); // Terminal operation: collect results into a list System.out.println(result); // Output: [ALEX, ALICE]

    Method References#

    Method references are shorthand for lambda expressions when you are simply calling an existing method. They make the code even more concise. They use the :: operator.

    • Static Method: ClassName::staticMethodName (e.g., Math::max)
    • Instance Method of a particular object: instance::instanceMethodName (e.g., list::add)
    • Instance Method of an arbitrary object of a particular type: ClassName::instanceMethodName (e.g., String::toUpperCase used in the Streams example)
    • Constructor: ClassName::new

    Optional#

    Optional is a container object used to represent a value that may or may not be present. It helps in avoiding NullPointerException by forcing the programmer to think about and handle the case of a missing value. Instead of returning null, a method can return an Optional.

    public Optional<String> findUserNameById(int id) { // ... logic that might find a user if (found) { return Optional.of(userName); // Wrap the found value } else { return Optional.empty(); // Return an empty Optional } } // Using the Optional Optional<String> result = findUserNameById(123); String name = result.orElse("Default User"); // Provide a default if not found result.ifPresent(System.out::println); // Only print if present

    Date and Time API (java.time)#

    The new Date and Time API (JSR 310) in java.time package is a massive improvement over the old, flawed java.util.Date and java.util.Calendar. The new classes are immutable and thread-safe.

    import java.time.*; import java.time.format.DateTimeFormatter; // Getting current date and time LocalDate today = LocalDate.now(); // 2024-05-21 (example) LocalTime now = LocalTime.now(); // 10:15:30.123 LocalDateTime currentDateTime = LocalDateTime.now(); // Creating specific dates LocalDate specificDate = LocalDate.of(2025, Month.DECEMBER, 25); LocalTime specificTime = LocalTime.of(14, 30); // Formatting and Parsing DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); String formattedDate = today.format(formatter); // "21/05/2024" LocalDate parsedDate = LocalDate.parse("25/12/2024", formatter); // Date arithmetic LocalDate nextWeek = today.plusWeeks(1); LocalDate lastMonth = today.minusMonths(1);

    13. JVM Internals#

    Understanding the internals of the JVM helps in writing better-performing code and debugging issues like memory leaks and performance bottlenecks.

    ClassLoader#

    The ClassLoader is a part of the JVM that loads .class files from the file system, network, or other sources into memory. It works on three core principles: delegation, visibility, and uniqueness. The main class loaders are:

    1. Bootstrap ClassLoader: The root class loader. It loads core Java API classes from the core Java modulesand other core jars present in the JAVA_HOME/jre/lib directory.
    2. Extension ClassLoader: It loads classes from the jre/lib/ext directory or any other directory specified by the java.ext.dirs system property.
    3. System/Application ClassLoader: It loads classes from the application classpath, which includes your project's classes and any JAR files you have included.

    Heap vs Stack#

    Memory in the JVM is divided into several areas, with the two most important being the Heap and the Stack.

    FeatureStackHeap
    What it storesPrimitive local variables, object references (pointers), and method calls (stack frames).All objects and their instance variables.
    Memory TypeMemory per thread. Each thread has its own private stack.Memory shared across all threads of an application.
    Access SpeedFast access (LIFO structure).Slower access compared to the stack.
    LifecycleA stack frame is created when a method is called and destroyed when the method completes.Objects live until they are no longer referenced and are garbage collected.
    Size & ErrorSize is typically fixed per thread. A deep recursion can cause StackOverflowError.Size can grow. If the heap is full, you may get OutOfMemoryError.

    Garbage Collection (GC)#

    Garbage Collection is the automatic process of identifying and discarding objects that are no longer needed by a program (objects with no live references) to reclaim memory. This frees the developer from manual memory management.

    • How it works (basic idea): GC roots (like local variables, static variables) are identified. The garbage collector then traverses all references from these roots. Any objects reachable from a root are marked as "live." The rest are considered garbage.
    • Generational GC: Most JVMs use a generational approach because it has been observed that most objects die young. The heap is divided into generations:
      • Young Generation: Where new objects are allocated. GC here (Minor GC) is fast and frequent.
      • Old Generation: Objects that have survived several GC cycles in the young generation are promoted here. GC here (Major/Full GC) is slower and less frequent.
      • Metaspace (replacing PermGen): Stores class metadata.
    Garbage Collection

    JIT (Just-In-Time) Compiler#

    The JVM initially interprets the bytecode, which can be slow. To improve performance, the JIT compiler comes into play. It monitors the code being executed and identifies "hot spots"—sections of code that are run very frequently. The JIT compiler then compiles these hot spots into native machine code. This native code is cached and executed directly by the processor, bypassing the interpreter, leading to massive performance gains. This is why a Java program can become faster as it runs.

    Conclusion#

    This Core Java cheat sheet provides a structured overview of the most important concepts every Java developer should understand. From the fundamentals of the language and object-oriented programming to collections, multithreading, and JVM internals, it serves as a quick reference for both beginners and experienced developers. Use it to quickly revise key topics and strengthen your Java foundation. If you found this cheat sheet helpful, consider sharing it with your friends or fellow developers who might benefit from it as well.

    Want to Master Spring Boot and Land Your Dream Job?

    Struggling with coding interviews? Learn Data Structures & Algorithms (DSA) with our expert-led course. Build strong problem-solving skills, write optimized code, and crack top tech interviews with ease

    Learn more
    Java
    OOP
    Collections Framework
    Exception Handling
    Multithreading
    Was it helpful?

    Subscribe to our newsletter

    Read articles from Coding Shuttle directly inside your inbox. Subscribe to the newsletter, and don't miss out.

    More articles