more text
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
# File 3: Core Java APIs
|
||||
|
||||
The Java language by itself is only part of the story. What makes Java productive in real work is the standard library. Once you move beyond syntax, you spend large amounts of time with `String`, collections, generics, exceptions, file and stream APIs, time APIs, and utility classes.
|
||||
|
||||
This file focuses on the APIs that show up constantly in real applications. These topics are foundational because they influence how data moves through the system, how failures are handled, and how code remains readable and type-safe at scale.
|
||||
|
||||
## Strings and Immutability
|
||||
|
||||
### Intuition
|
||||
|
||||
A `String` is one of the most frequently used objects in Java. It represents text such as user names, JSON fields, URLs, tokens, file paths, SQL fragments, log messages, and HTTP headers.
|
||||
|
||||
Java makes `String` immutable, which means once a string object is created, its contents do not change.
|
||||
|
||||
### Why Immutability Matters
|
||||
|
||||
Immutability makes strings:
|
||||
|
||||
- safer to share across threads
|
||||
- easier to reason about
|
||||
- more predictable as map keys and cache keys
|
||||
- less error-prone in APIs that pass text through multiple layers
|
||||
|
||||
### Example
|
||||
|
||||
```java
|
||||
String original = "order";
|
||||
String upper = original.toUpperCase();
|
||||
|
||||
System.out.println(original); // order
|
||||
System.out.println(upper); // ORDER
|
||||
```
|
||||
|
||||
`original` is unchanged. `toUpperCase()` returns a new `String`.
|
||||
|
||||
### Internal Detail That Matters
|
||||
|
||||
Java often interns string literals, meaning identical literals may refer to the same pooled object. That is an optimization detail, not something business logic should rely on.
|
||||
|
||||
This is why using `==` for strings is dangerous.
|
||||
|
||||
```java
|
||||
String a = new String("paid");
|
||||
String b = new String("paid");
|
||||
|
||||
System.out.println(a == b); // false
|
||||
System.out.println(a.equals(b)); // true
|
||||
```
|
||||
|
||||
### Performance Concern
|
||||
|
||||
Repeated string concatenation inside loops can create many temporary objects.
|
||||
|
||||
```java
|
||||
String result = "";
|
||||
for (String item : items) {
|
||||
result += item;
|
||||
}
|
||||
```
|
||||
|
||||
Prefer `StringBuilder` for heavy concatenation.
|
||||
|
||||
```java
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String item : items) {
|
||||
builder.append(item);
|
||||
}
|
||||
String result = builder.toString();
|
||||
```
|
||||
|
||||
### Real-World Use Cases
|
||||
|
||||
- building log lines or audit entries
|
||||
- parsing and validating incoming request fields
|
||||
- constructing file paths, cache keys, and search queries
|
||||
- formatting API responses or templated messages
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
- comparing strings with `==`
|
||||
- forgetting that strings are immutable and assuming a method call mutates them
|
||||
- building large strings inefficiently in loops
|
||||
|
||||
## Collections Framework
|
||||
|
||||
Collections are how Java code represents groups of related elements. In real systems, almost every workflow involves a collection somewhere: request headers, order items, search results, cache entries, event batches, deduplicated IDs, grouped metrics, or lookup tables.
|
||||
|
||||
### Collections Structure Diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Collection Framework] --> B[List]
|
||||
A --> C[Set]
|
||||
A --> D[Map]
|
||||
B --> E[ArrayList]
|
||||
B --> F[LinkedList]
|
||||
C --> G[HashSet]
|
||||
C --> H[TreeSet]
|
||||
D --> I[HashMap]
|
||||
D --> J[TreeMap]
|
||||
```
|
||||
|
||||
### `List`
|
||||
|
||||
A `List` is ordered and allows duplicates.
|
||||
|
||||
```java
|
||||
List<String> steps = new ArrayList<>();
|
||||
steps.add("validate");
|
||||
steps.add("charge");
|
||||
steps.add("notify");
|
||||
```
|
||||
|
||||
Use a list when order matters or when repeated values are acceptable.
|
||||
|
||||
#### `ArrayList`
|
||||
|
||||
This is the default `List` choice in most business applications. It provides fast indexed access and good general-purpose performance.
|
||||
|
||||
Typical use cases:
|
||||
|
||||
- ordered API results
|
||||
- batched work items
|
||||
- DTO lists in controller responses
|
||||
|
||||
#### `LinkedList`
|
||||
|
||||
Used far less often in typical backend applications than beginners expect. It can be helpful in queue-like patterns, but `ArrayList` and dedicated queue implementations are usually more practical.
|
||||
|
||||
### `Set`
|
||||
|
||||
A `Set` stores unique elements.
|
||||
|
||||
```java
|
||||
Set<String> roles = new HashSet<>();
|
||||
roles.add("ADMIN");
|
||||
roles.add("USER");
|
||||
roles.add("ADMIN");
|
||||
```
|
||||
|
||||
The duplicate `ADMIN` is ignored.
|
||||
|
||||
Use a set when uniqueness matters.
|
||||
|
||||
Real-world examples:
|
||||
|
||||
- deduplicating email addresses
|
||||
- tracking processed event IDs for idempotency checks
|
||||
- storing permission names
|
||||
|
||||
### `Map`
|
||||
|
||||
A `Map` stores key-value pairs.
|
||||
|
||||
```java
|
||||
Map<String, Integer> inventory = new HashMap<>();
|
||||
inventory.put("laptop", 15);
|
||||
inventory.put("mouse", 48);
|
||||
```
|
||||
|
||||
Maps are everywhere in backend systems.
|
||||
|
||||
Typical examples:
|
||||
|
||||
- ID to entity lookup
|
||||
- configuration name to value
|
||||
- region to tax rule
|
||||
- user ID to session metadata
|
||||
|
||||
### Choosing Common Implementations
|
||||
|
||||
| Type | Default Choice | Why |
|
||||
| --- | --- | --- |
|
||||
| `List` | `ArrayList` | Fast iteration and index-based reads |
|
||||
| `Set` | `HashSet` | Fast uniqueness checks |
|
||||
| `Map` | `HashMap` | Fast key-based lookup |
|
||||
|
||||
Choose tree-based variants like `TreeSet` or `TreeMap` when you explicitly need sorted ordering.
|
||||
|
||||
### Practical Usage Pattern
|
||||
|
||||
Suppose you receive a batch of orders and want to group them by customer.
|
||||
|
||||
```java
|
||||
Map<String, List<Order>> ordersByCustomer = new HashMap<>();
|
||||
|
||||
for (Order order : orders) {
|
||||
ordersByCustomer
|
||||
.computeIfAbsent(order.getCustomerId(), key -> new ArrayList<>())
|
||||
.add(order);
|
||||
}
|
||||
```
|
||||
|
||||
This is a very common pattern in reporting, aggregation, and batch-processing pipelines.
|
||||
|
||||
### Pitfalls
|
||||
|
||||
- assuming `HashMap` preserves insertion order; it does not
|
||||
- forgetting that mutable keys can break map behavior
|
||||
- choosing a list when you really need uniqueness or fast lookup
|
||||
- modifying a collection while iterating in unsupported ways, causing `ConcurrentModificationException`
|
||||
|
||||
## Generics
|
||||
|
||||
Generics allow Java to express type-safe reusable data structures and APIs.
|
||||
|
||||
### Intuition
|
||||
|
||||
Without generics, collections would hold generic `Object` values, forcing you to cast manually and discover mistakes late. Generics move those checks to compile time.
|
||||
|
||||
```java
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add("Anita");
|
||||
```
|
||||
|
||||
Now the compiler knows this list is supposed to contain strings.
|
||||
|
||||
### Why This Matters in Real Systems
|
||||
|
||||
Generics make API contracts clearer.
|
||||
|
||||
Examples:
|
||||
|
||||
- `List<Order>` means a list of orders, not arbitrary objects
|
||||
- `Map<String, UserSession>` means string keys mapped to session objects
|
||||
- `ResponseEntity<CustomerDto>` clearly communicates response payload type in a web application
|
||||
|
||||
### Generic Methods
|
||||
|
||||
```java
|
||||
public static <T> T first(List<T> items) {
|
||||
if (items.isEmpty()) {
|
||||
throw new IllegalArgumentException("List cannot be empty");
|
||||
}
|
||||
return items.get(0);
|
||||
}
|
||||
```
|
||||
|
||||
### Wildcards at a High Level
|
||||
|
||||
You will eventually see forms such as `List<? extends Number>` or `List<? super Integer>`. The full details take practice, but the intuition is:
|
||||
|
||||
- `extends` is for reading from a producer
|
||||
- `super` is for writing to a consumer
|
||||
|
||||
This is often summarized as PECS: producer extends, consumer super.
|
||||
|
||||
### Internal Note
|
||||
|
||||
Java generics use type erasure, meaning generic type information is mostly removed at runtime. This is why you cannot do everything with generics that reified generic systems in some other languages allow.
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
- using raw types like `List` instead of `List<String>`
|
||||
- overcomplicating code with advanced wildcards when a simpler API would do
|
||||
- expecting generic type arguments to remain fully available at runtime
|
||||
|
||||
## Exception Handling
|
||||
|
||||
Failure handling is a core part of Java engineering. Good code does not just describe the happy path. It makes failure modes visible and deliberate.
|
||||
|
||||
### The Idea Behind Exceptions
|
||||
|
||||
An exception represents a failure or abnormal condition that interrupts normal execution.
|
||||
|
||||
Some failures are expected operationally, such as missing files or invalid user input. Others represent programming bugs, such as null dereferences or invalid assumptions.
|
||||
|
||||
### Checked vs Unchecked Exceptions
|
||||
|
||||
Java has two broad exception categories.
|
||||
|
||||
#### Checked exceptions
|
||||
|
||||
These are enforced by the compiler. You must catch them or declare them.
|
||||
|
||||
Examples include many file and I/O related exceptions.
|
||||
|
||||
#### Unchecked exceptions
|
||||
|
||||
These extend `RuntimeException`. The compiler does not force handling.
|
||||
|
||||
Examples include:
|
||||
|
||||
- `NullPointerException`
|
||||
- `IllegalArgumentException`
|
||||
- `IllegalStateException`
|
||||
|
||||
### Why the Distinction Exists
|
||||
|
||||
Checked exceptions signal recoverable or expected external failure modes.
|
||||
|
||||
Unchecked exceptions often represent programming mistakes or invalid internal states.
|
||||
|
||||
In real engineering, teams vary on how much they like checked exceptions, but understanding the model is still important.
|
||||
|
||||
### Example
|
||||
|
||||
```java
|
||||
public String readFile(Path path) throws IOException {
|
||||
return Files.readString(path);
|
||||
}
|
||||
```
|
||||
|
||||
The caller must decide whether to handle `IOException` or propagate it.
|
||||
|
||||
### Practical Guidance
|
||||
|
||||
- throw exceptions that communicate intent clearly
|
||||
- do not catch exceptions just to ignore them
|
||||
- wrap low-level exceptions when exposing a cleaner domain-level abstraction
|
||||
- include useful context in logs and messages
|
||||
|
||||
### Real-World Pattern
|
||||
|
||||
Suppose a service calls a third-party payment gateway. The low-level HTTP client might throw transport exceptions, but your business layer may convert those into a domain-specific exception like `PaymentUnavailableException`.
|
||||
|
||||
That makes the rest of the system easier to understand.
|
||||
|
||||
### `try`, `catch`, `finally`, and try-with-resources
|
||||
|
||||
```java
|
||||
try (BufferedReader reader = Files.newBufferedReader(path)) {
|
||||
return reader.readLine();
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Failed to read config", exception);
|
||||
}
|
||||
```
|
||||
|
||||
The try-with-resources form is important because it closes resources automatically.
|
||||
|
||||
### Pitfalls
|
||||
|
||||
- catching `Exception` too broadly and hiding real issues
|
||||
- using exceptions for normal control flow
|
||||
- logging and rethrowing the same exception repeatedly, creating noisy duplicate logs
|
||||
- losing the original cause when wrapping exceptions badly
|
||||
|
||||
## Java I/O Basics
|
||||
|
||||
I/O means interacting with systems outside your program's in-memory state.
|
||||
|
||||
Examples include:
|
||||
|
||||
- reading files
|
||||
- writing logs
|
||||
- handling network sockets
|
||||
- streaming data to cloud storage
|
||||
- loading configuration from disk
|
||||
|
||||
### Intuition
|
||||
|
||||
I/O is slower and less predictable than pure in-memory operations because it depends on disks, networks, operating system buffers, remote services, and external state.
|
||||
|
||||
That is why I/O-heavy code needs stronger error handling and performance awareness.
|
||||
|
||||
### Core Modern APIs
|
||||
|
||||
The `java.nio.file` package is the common modern entry point for file work.
|
||||
|
||||
```java
|
||||
Path path = Path.of("config/app.properties");
|
||||
String content = Files.readString(path);
|
||||
Files.writeString(path, content + "\nmode=prod");
|
||||
```
|
||||
|
||||
### Streams of Data
|
||||
|
||||
Buffered APIs are often used for efficient reading and writing.
|
||||
|
||||
```java
|
||||
try (BufferedReader reader = Files.newBufferedReader(path)) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Production Use Cases
|
||||
|
||||
- loading service configuration or templates at startup
|
||||
- ingesting CSV or log files in batch jobs
|
||||
- exporting reports to disk or object storage
|
||||
- reading secrets or certificates mounted into a container
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
- reading huge files into memory when streaming would be safer
|
||||
- forgetting character encoding concerns
|
||||
- failing to close resources properly
|
||||
- performing blocking I/O on critical request threads without understanding throughput impact
|
||||
|
||||
## Practical Usage Patterns That Show Up Everywhere
|
||||
|
||||
### Pattern 1: Deduplication
|
||||
|
||||
Use a `Set` when idempotency or uniqueness matters.
|
||||
|
||||
```java
|
||||
Set<String> processedEventIds = new HashSet<>();
|
||||
```
|
||||
|
||||
### Pattern 2: Lookup by Key
|
||||
|
||||
Use a `Map` for fast retrieval.
|
||||
|
||||
```java
|
||||
Map<Long, Customer> customersById = new HashMap<>();
|
||||
```
|
||||
|
||||
### Pattern 3: Ordered Results
|
||||
|
||||
Use a `List` when sequence matters.
|
||||
|
||||
```java
|
||||
List<Transaction> transactions = new ArrayList<>();
|
||||
```
|
||||
|
||||
### Pattern 4: Safe Text Handling
|
||||
|
||||
Use immutable strings for identifiers, payload fragments, and logs, but switch to `StringBuilder` for heavy concatenation.
|
||||
|
||||
### Pattern 5: Resource Safety
|
||||
|
||||
Use try-with-resources for anything that must be closed, such as readers, streams, sockets, and many database-facing abstractions.
|
||||
|
||||
## How These APIs Show Up in Production Systems
|
||||
|
||||
Consider a batch job that processes order exports:
|
||||
|
||||
1. it reads a file using Java I/O APIs
|
||||
2. it parses each line into objects and stores them in collections
|
||||
3. it uses maps for lookups and sets for deduplication
|
||||
4. it uses strings to validate and normalize text fields
|
||||
5. it throws or wraps exceptions when bad data or file issues occur
|
||||
6. it emits a summary report built safely and efficiently
|
||||
|
||||
This is ordinary Java engineering. It is not glamorous, but a large amount of production reliability depends on doing these basics well.
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- `String` is immutable, which improves safety and predictability, but you should still be mindful of comparison and concatenation costs.
|
||||
- The collections framework gives you distinct tools for ordering, uniqueness, and lookup, and choosing the right one affects both clarity and performance.
|
||||
- Generics move many type errors to compile time and make library and application code much easier to reason about.
|
||||
- Exception handling is about making failure explicit and meaningful, not just preventing crashes.
|
||||
- Java I/O interacts with slow and failure-prone external systems, so resource handling and error handling matter a lot.
|
||||
- These APIs form the everyday vocabulary of production Java code, especially in backend services, batch jobs, and integration-heavy systems.
|
||||
Reference in New Issue
Block a user