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., String s). | == 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. |