Java Core & Internals
The HashMap is a fundamental data structure that implements the Map interface using a hashing mechanism. When storing key-value pairs via put(key, value), the key's hash code is computed to determine the bucket index. Collisions ocurr when distinct keys generate the same hash code; these are resolved using a linked list (or a balanced tree in newer Java versions) within the bucket, storing entries as Map.Entry objects.
The default initial capacity of 16 and a load factor of 0.75 are chosen to balance memory usage and performance. The threshold for expansion is calculated as capacity * loadFactor. A capacity that is a power of two (2n) ensures that the index calculation hash & (length - 1) is equivalent to hash % length but computationally faster. This bitmask operation also guarantees a more uniform distribution of entries, reducing collisions. If the capacity were not a power of two (e.g., 15), the lower bits of the hash would always be zeroed out, leading to clustering and wasted space.
Regarding the String class, the equals() method is overridden to compare content rather than memory addresses. The comparison first checks reference equality, then type and length, finally iterating through the character array. In contrast, the == operator compares object references.
public boolean isContentEqual(Object anotherObj) {
if (this == anotherObj) {
return true;
}
if (anotherObj instanceof TextObject) {
TextObject other = (TextObject) anotherObj;
if (this.chars.length == other.chars.length) {
for (int i = 0; i < this.chars.length; i++) {
if (this.chars[i] != other.chars[i]) {
return false;
}
}
return true;
}
}
return false;
}
The String.intern() method manages the string pool. In JDK 7 and later, when invoked, it checks the runtime constant pool. If the string exists, the reference is returned; otherwise, the string is added to the pool and its reference returned. This ensures that references for identical string literals point to the same memory location.
Concurrency & Multithreading
Processes serve as the smallest unit of resource allocation, while threads are the smallest unit of CPU scheduling. Threads within the same process share memory resources but have independent execution stacks, making context switching between threads faster than between processes.
Thread pools are utilized to manage resources efficiently, avoiding the overhead of creating and destroying threads repeatedly. The ThreadPoolExecutor configuration involves several key parameters:
- Core Pool Size: The number of threads to keep alive even when idle.
- Maximum Pool Size: The upper bound on the number of threads.
- Keep Alive Time: The duration excess threads survive before termination.
- Work Queue: The buffer holding tasks before execution.
Synchronization mechanisms are categorized into pessimistic and optimistic locking. Pessimistic locking (e.g., synchronized or database row locks) assumes conflicts will occur and blocks access. Optimistic locking (e.g., CAS or version checks) assumes conflicts are rare and validates data integrity only during the update phase.
Networking & I/O Models
When a browser accesses a URL, the sequence begins with Domain Name System (DNS) resolution. The browser checks its cache, followed by the operating system cache and the hosts file. If unresolved, a recursive query is made to the local DNS server, which traverses the Root, TLD, and authoritative DNS servers to obtain the IP address.
Subsequently, a TCP connection is established via a three-way handshake (SYN, SYN-ACK, ACK) to ensure reliable transmission. The browser sends an HTTP request, the server processes it and returns HTML content, which the browser renders. Upon completion, the connection closes via a four-way handshake (FIN, ACK, FIN, ACK). The side initiating the closure enters a TIME_WAIT state for twice the Maximum Segment Lifetime (2MSL) to ensure the final ACK is received and to prevent delayed packets from affecting new connections.
Java providse several I/O models:
- BIO (Blocking I/O): Every connection requires a dedicated thread.
- NIO (Non-blocking I/O): Uses a
Selector(multiplexor) to monitor multiple channels. A single thread can manage numerous connections, polling for readiness events. - AIO (Asynchronous I/O): The operating system notifies the application upon completion of read/write operations via callbacks.
Linux systems utilize epoll for efficient I/O multiplexing. Unlike select, which scans all file descriptors linearly (O(n)), epoll uses a red-black tree to manage descriptors and a ready list to return only active connections (O(1)).
Framework Internals (Spring & MyBatis)
Spring Inversion of Control (IoC) manages object lifecycles and dependencies. The container initialization involves locating resources, loading Bean definitions, and registering them. Dependency Injection (DI) handles wiring these beans. Aspect-Oriented Programming (AOP) modularizes cross-cutting concerns like transactions. Spring AOP typically uses dynamic proxies (JDK Proxy or CGLIB) to intercept method calls, executing advice (logic) before, after, or around the target method.
// Conceptual Proxy Logic
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Pre-processing logic (e.g., transaction start)
Object result = method.invoke(target, args);
// Post-processing logic (e.g., transaction commit)
return result;
}
MyBatis execution flow begins with parsing configuration files (XML) into a Configuration object. An SqlSessionFactory is created from this configuration, which produces SqlSession instances. Mapper interfaces are implemented using dynamic proxies; calling a mapper method triggers the proxy to execute the corresponding SQL statement mapped in the configuration.
Database Systems
InnoDB is the default storage engine in modern MySQL for transactional applications. It supports ACID properties, row-level locking, and foreign keys. MyISAM, an older engine, offers table-level locking and faster read operations but lacks transaction support and crash recovery.
Indexing efficiency relies heavily on strategy. For a composite index on columns (A, B, C), the query can utilize the index if it filters by A, A and B, or A, B, and C (Leftmost Prefix Matching). Queries filtering solely by B or C will not use this index.
Redis is an in-memory data structure store supporting strings, hashes, lists, sets, and sorted sets. It persists data to disk via RDB (snapshots) or AOF (logging write commands). Redis uses a hash table (dictionary) internally for key storage, employing separate chaining for collision resolution.
Design Patterns
Common design patterns utilized in enterprise development include:
- Factory Pattern: Encapsulates object creation logic.
- Builder Pattern: Separates the construction of a complex object from its representation, allowing step-by-step creation.
- Strategy Pattern: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. The client chooses the specific strategy at runtime.
- Proxy Pattern: Provides a surrogate or placeholder to control access to an object, commonly used in AOP.
- Observer Pattern: Defines a subscription mechanism where multiple observers are notified of state changes in a subject.
System Design & Distributed Concepts
Remote Procedure Call (RPC) allows a client to invoke a function on a remote server as if it were local, abstracting the network communication details.
Microservices architecture structures an application as a collection of loosely coupled services. Each service runs in its own process and communicates via lightweight mechanisms (usually HTTP APIs). This contrasts with monolithic applications where all modules share the same memory space and deployment unit.