miscellaneous
Java Cheatsheet when transistioning from C++
Transitioning from C++ to Java often requires a solid reference for the syntactic differences and Java conventions.
☕ Core Java Syntax & Concepts
| Concept | C++ Syntax/Style | Java Equivalent/Convention | Notes |
|---|---|---|---|
| Basic Output | std::cout << "Hi"; | System.out.println("Hi"); | Use System.out.printf() for C-style formatted output. |
| Header/Include | #include <iostream> | import java.util.List; | Java uses import statements for classes/packages. |
| Primitive Types | int, double, bool | int, double, boolean | boolean (lower case) is a primitive. Java has no unsigned types. |
| Main Method | int main() { ... } | public static void main(String[] args) { ... } | Mandatory signature for the entry point. |
| Object Creation | MyClass* obj = new MyClass(); | MyClass obj = new MyClass(); | Java uses automatic memory management (Garbage Collection), so no manual delete is needed. |
| String Concatenation | "A" + std::to_string(i) | "A" + i | The + operator is overloaded for string concatenation. |
🧩 Variables & Data Structures
1. Primitives vs. Wrappers
Java distinguishes between primitive types (for performance) and wrapper classes (for use in Collections).
| Primitive Type | Wrapper Class | Size | Default Value |
|---|---|---|---|
int | Integer | 32-bit | 0 |
long | Long | 64-bit | 0L |
boolean | Boolean | 1-bit | false |
char | Character | 16-bit (Unicode) | \u0000 |
float | Float | 32-bit | 0.0f |
double | Double | 64-bit | 0.0d |
Autoboxing/Unboxing: Java automatically converts between primitives and their wrappers (e.g.,
int i = new Integer(5)works seamlessly).
2. Arrays, Strings, and Collections (The Indexing Reference)
| Data Structure | Creation / Declaration | Size Property | Element Access | Notes |
|---|---|---|---|---|
| Native Array | int[] arr = new int[5]; | arr.length (Field) | arr[i] (Square brackets) | Fixed size. |
| String | String s = "data"; | s.length() (Method) | s.charAt(i) (Returns char) | Immutable. |
List (ArrayList) | List<T> list = new ArrayList<>(); | list.size() (Method) | list.get(i) (Method) | Dynamic size. Requires wrapper classes (T). |
Map (HashMap) | Map<K, V> map = new HashMap<>(); | map.size() (Method) | map.get(key) (Method) | Key-value pairs. Use map.put(k, v) to insert. |
🧱 Object-Oriented Programming (OOP)
| Concept | Java Implementation Details | C++ Contrast |
|---|---|---|
| Inheritance | Uses extends keyword. Java has single inheritance for classes. | Uses : or virtual keywords. C++ supports multiple inheritance. |
| Interfaces | Use interface keyword and implement with implements. Can implement multiple interfaces. | C++ uses Abstract Base Classes with purely virtual methods to achieve a similar goal. |
| Encapsulation | Use Access Modifiers (public, private, protected, default). | Similar, but Java’s default (package-private) is distinct. |
| Constructors | Same name as the class. Use this() to call another constructor in the same class. | Similar, but Java strictly prohibits manual memory allocation/deallocation (new/delete). |
| Method Overriding | Use the @Override annotation (highly recommended). All non-static methods are virtual by default. | Must explicitly use the virtual keyword in the base class in C++. |
| Final Keyword | Used for classes (cannot be inherited), methods (cannot be overridden), or variables (constant). | Similar to const in C++. |
Access Modifiers
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes | No |
(default) | Yes | Yes | No | No |
private | Yes | No | No | No |
🌐 Modern Java Features (Since Java 8)
| Feature | Syntax Example | Use Case (Relevant to Backend) |
|---|---|---|
| Lambda Expressions | (a, b) -> a + b | Concise implementation of functional interfaces (e.g., Spring framework callbacks). |
| Streams | list.stream().filter(i -> i > 5).collect(Collectors.toList()); | Performing chained, efficient, and often parallel processing on collections. |
| Optionals | Optional<String> name = Optional.ofNullable(val); name.ifPresent(System.out::println); | Handling potential null values gracefully to prevent NullPointerException (a common Java error). |
| Records (Since Java 14) | public record User(String name, int id) {} | Creating concise, immutable data classes often used for DTOs (Data Transfer Objects) in microservices. |
var (Local Variable Type Inference) | var list = new ArrayList<String>(); | Reducing boilerplate code, especially with long generic type names. Only for local variables. |
🐛 Common C++ Java Pitfalls
| C++ Pitfall | Java Rule/Fix | Why it Happens |
|---|---|---|
Comparing Objects with == | ALWAYS use objectA.equals(objectB) to compare content (e.g., Strings). | == compares object references (memory addresses) in Java, not content. |
Missing delete keyword | Java is Garbage Collected. There is no delete. | Manual memory management is replaced by the JVM’s Garbage Collector. |
| Passing by Reference | Everything is Pass-by-Value in Java. For objects, the reference itself is passed by value (allowing modification of the original object’s state). | Java does not have explicit reference types like & in C++. |
NullPointerException (NPE) | Check for null or use Optional<T>. | Attempting to call a method on a variable that is currently pointing to null. |
🌟 Java Best Practices for Backend Development
These practices will help you write “Java-idiomatic” code that is easier for other Java developers (and the JVM) to understand and maintain.
1. Object Equality and Data Handling
| Practice | Details & Why | C++ Context |
|---|---|---|
Always Use .equals() | For comparing the content of objects (especially String, wrappers like Integer, or your custom classes), use a.equals(b). | Unlike C++ where == can be overloaded for deep comparison, == in Java only compares object references (memory addresses). |
Implement hashCode() and equals() Together | If you override equals() in a class, you must also override hashCode(). IDEs like IntelliJ or Eclipse can auto-generate these methods. | This is critical for correctness when using the object as a key in a HashMap or an element in a HashSet. |
Use final for Immutability | Make fields final and classes final (if not intended for inheritance) to ensure thread-safety and predictability. | Immutability is a cornerstone of thread-safe Microservices and concurrent programming. |
Use StringJoiner or String.join() | Avoid repetitive string concatenation loops using + or +=. | Prefer the efficient StringBuilder for building dynamic strings within loops. String is immutable, so + creates new objects every time. |
2. The Collections Framework
| Practice | Details & Why | Explaination |
|---|---|---|
| Program to Interfaces | Declare variables using interfaces, not implementation classes. | Good: List<String> list = new ArrayList<>(); Bad: ArrayList<String> list = new ArrayList<>(); This makes code more flexible and easier to swap implementations later. |
| Use Generics | Always specify the types in angle brackets (e.g., <String>). | Prevents runtime ClassCastException errors and provides compile-time type safety. This is similar to C++ templates but with different constraints. |
| Choose the Right List | For frequent random access by index, use ArrayList. For frequent insertions/deletions in the middle of the list, use LinkedList. | ArrayList is backed by an array; LinkedList uses nodes (better for queue-like operations). |
3. Exception Handling
| Practice | Details & Why | C++ Context |
|---|---|---|
| Distinguish Checked vs. Unchecked | Checked exceptions (e.g., IOException) must be declared (throws) or handled (try/catch). Unchecked (e.g., RuntimeException, NullPointerException) do not. | In modern backend development, favor Unchecked Exceptions (e.g., custom exceptions extending RuntimeException) to avoid verbose throws clauses and overly defensive code. |
Use try-with-resources | Use the try (Resource r = ...) syntax for anything that needs closing (e.g., streams, files, database connections). This automatically calls the close() method, preventing resource leaks. This is similar to C++‘s RAII (Resource Acquisition Is Initialization) principle. | |
| Don’t Swallow Exceptions | Never use an empty catch (Exception e) {} block. At a minimum, log the exception. | Swallowing exceptions makes debugging nearly impossible, especially in a distributed Microservices environment. |
4. Modern Java (Java 8+) and Functional Programming
| Practice | Details & Why | Notes |
|---|---|---|
| Prefer Streams Over Loops | Use Java Streams (.stream()) with map, filter, and reduce for collection processing. | Streams are more declarative, often lead to cleaner code, and can easily be executed in parallel (.parallelStream()). |
Use Optional for Return Values | Use Optional<T> as the return type for methods that might return “no result” instead of returning null. | This forces the caller to explicitly handle the “no result” case, virtually eliminating NullPointerException (NPEs). A common practice in Spring Data repositories. |
| Embrace Lambda Expressions | Use lambdas (params) -> { body } instead of anonymous inner classes for functional interfaces. | Leads to much more concise, readable, and functional code. |
5. OOP and Design
| Practice | Details & Why | Notes |
|---|---|---|
| Single Responsibility Principle (SRP) | Ensure every class and method has only one reason to change. | Leads to small, focused classes which are easier to test, maintain, and integrate into frameworks like Spring Boot. |
| Prefer Composition Over Inheritance | Where possible, use fields of other classes (composition) instead of the extends keyword (inheritance). | Inheritance creates tight coupling. Composition is more flexible and is key to the Dependency Injection patterns used by Spring. |
| Use Enums for Constants | Use the enum type instead of public static final int or String fields. | Provides type-safe constants, prevents incorrect values, and allows for method definitions within the constant type. |
🚀 Java 8 Specific Best Practices
These practices leverage the functional programming features introduced in Java 8 to write more concise, readable, and often more robust code.
1. Functional Interfaces & Lambdas
| Tip | Details & Why | Example |
|---|---|---|
| Use Method References | When a lambda simply calls an existing method, use a method reference (::) for ultimate conciseness. | Instead of: list.forEach(s -> System.out.println(s)); Use: list.forEach(System.out::println); |
| Favor Built-in Functional Interfaces | Stick to standard interfaces (Predicate, Function, Consumer, Supplier) instead of creating your own basic functional interfaces. | Predicate<T> for boolean tests, Function<T, R> for mapping, Consumer<T> for side effects (e.g., printing). |
Use @FunctionalInterface | Annotate your custom interfaces with @FunctionalInterface. | It ensures the interface has exactly one abstract method (the definition of a functional interface) and provides a compiler check. |
2. Streams API
| Tip | Details & Why | Example |
|---|---|---|
| Avoid Side Effects in Stream Operations | Keep operations like map() and filter() stateless and non-interfering. Don’t modify external data (or the source collection) inside a stream chain. | Stream operations are designed for functional purity. Side effects can lead to unpredictable behavior, especially with parallel streams. |
| Use Primitive Streams Where Possible | Use IntStream, LongStream, and DoubleStream instead of Stream<Integer>, Stream<Long>, etc., when dealing with collections of primitives. | Avoids the overhead of autoboxing/unboxing, improving performance and memory usage—crucial for high-throughput backend services. |
| Prefer Terminal Operations for Collection | Use the highly efficient methods in Collectors (e.g., Collectors.toList(), Collectors.toMap(), Collectors.joining()) to finalize your stream. | Avoids manual creation and population of collections, resulting in cleaner code. |
Use Collectors.groupingBy() | Use this powerful collector to group stream elements, often replacing several lines of manual Map population. | Example: people.stream().collect(groupingBy(Person::getCity)); |
3. Optional<T>
| Tip | Details & Why | Example |
|---|---|---|
Optional is for Return Types ONLY | Do NOT use Optional for method parameters or as fields in domain objects (DTOs/Entities). It defeats the purpose and clutters the code. | The purpose is to signal “no result” to the caller, forcing safe handling, not for general state management. |
Avoid Optional.get() | Calling .get() without first checking isPresent() defeats the entire safety purpose of Optional and can still throw a NoSuchElementException. | Instead, use safe alternatives like: .orElse(T), .orElseGet(Supplier), or .orElseThrow(). |
Use ifPresent() for Side Effects | If you only want to perform an action if the value is present, use optional.ifPresent(consumer). | Example: user.ifPresent(u -> log.info("User found: " + u.getId())); Avoids manual if (user.isPresent()) checks. |
4. Date and Time API (java.time)
| Tip | Details & Why | Example |
|---|---|---|
Use java.time (JSR-310) Exclusively | Do NOT use the old, flawed classes like java.util.Date, java.util.Calendar, or java.sql.Timestamp. | The old APIs were mutable (not thread-safe) and poorly designed. java.time objects are immutable and clearly separate date, time, and timezone. |
| Distinguish Timezone Needs | Use LocalDate (date only), LocalTime (time only), or LocalDateTime (date + time) when working with values independent of timezone. | Use ZonedDateTime or OffsetDateTime when dealing with external systems or users across different time zones. |
Use Duration and Period | Use Duration (time-based) and Period (date-based) for calculating time differences. | Example: Duration d = Duration.between(t1, t2); Avoids error-prone manual subtraction of milliseconds. |