HSBC Java Developer Interview Guide
HSBC Java Developer Interview Guide
Kafka is used as a distributed messaging system where data is published and consumed in a reliable, scalable manner. It uses a topic-based structure rather than queues, where data is written to categories called topics . Each topic can have multiple partitions, allowing for parallel processing and redundancy. Unlike traditional queue systems, Kafka’s topics do not delete messages upon consumption, enabling multiple consumers to read the same data independently at different times. This design supports real-time data processing and high-throughput event streaming for applications requiring distributed data storage and retrieval.
The Factory design pattern simplifies class instantiation by delegating the responsibility of creating instances to a factory class. This allows for loose coupling between the client code and the concrete classes used to instantiate new objects. For example, the `NumberFormat` in Java is an inbuilt class that uses the factory pattern to provide instances for number parsing and formatting . This design pattern helps manage future complexity, making it easier to introduce new classes adhering to the same interface without changing client code.
The `volatile` keyword in Java is used to indicate that a variable's value may be changed by different threads. It ensures that the value of the volatile variable is always read from the main memory, not from a thread's local cache. This guarantees visibility of changes to variables across threads, thus maintaining consistency in the Java Memory Model . Without `volatile`, a thread may not see the most recent update in another thread’s variable, leading to incorrect behavior in concurrent environments.
The `Future` interface in Java provides a way to retrieve the result of an asynchronous computation, helping manage multi-threading complexities. It allows checking if a task is complete, canceling a task, and retrieving a task's result with `get()`, which blocks until the computation is finished . However, the drawback is that blocking can lead to thread waiting, reducing efficiency if not managed correctly. Also, interruption handling must be considered when canceling tasks to avoid issues with task consistency and resource release.
In Spring Framework, transactions can be managed using the @Transactional annotation, which allows declarative transaction management without embedding transaction logic in code. Spring supports programmatic transaction management for finer control and declarative transaction management for simplicity and scalability . Spring transactions handle rollback based on specific exceptions, ensuring data consistency through various propagation behaviors and isolation levels. Choosing the right propagation and isolation levels helps prevent issues like dirty reads and phantom reads, ensuring that microservice components interact with databases consistently.
Java's `Optional` helps avoid null pointer exceptions by encapsulating optional values. Streams provide a powerful way to process sequences of data. By combining both, you can elegantly handle data presence and transform collections. For example, using streams, you can filter, map, and collect data; with `Optional`, you can provide alternate values or actions if data is absent . An example is `Optional.ofNullable(list).map(List::stream).orElseGet(Stream::empty).forEach(System.out::println);`, which safely processes a list that may be null in a stream pipeline.
The PUT method is idempotent, meaning multiple identical requests should result in the same resource state. It's used to update or create a resource at a specific URI. In contrast, the POST method is not idempotent and is often used to create resources where the URI is not predetermined, allowing the server to determine URI . PUT should be used when the client knows the complete resource to replace and can avoid side effects from repeated updates. POST is optimal for form submissions and uploading data to processes that require user-generated content without a set resource identifier.
Thread safety in a singleton pattern can be ensured using synchronization. By synchronizing the method that returns the singleton instance, it ensures that only one thread can execute the method at a time, preventing multiple instances from being created. For example, in the class `A`, the method `createInstance()` can be synchronized to ensure that only one instance of `A` is created even if multiple threads access it concurrently .
Garbage collection in Java automates memory management by reclaiming memory used by objects no longer needed by the application. This reduces the risk of memory leaks. Java’s Garbage Collector optimizes memory use by identifying objects that are unreachable and deallocating their memory . Java 8 introduced improvements such as string deduplication, which optimizes memory by storing only one copy of identical strings across the memory heap. Tuning and profiling the garbage collector’s behavior can be done to improve performance, especially in applications with large memory requirements.
The CQRS (Command Query Responsibility Segregation) pattern is applied in microservices to separate the command (write) and query (read) models, allowing for optimized database interactions and separation of concerns . This pattern allows scaling of read and write workloads independently, improving performance and maintainability by decoupling the data retrieval paths and state manipulation processes. CQRS benefits include enhanced performance due to specialized read/write paths, improved system scalability, and easier adoption of event sourcing to capture system state changes. However, it increases complexity by requiring separate models and potentially inconsistent models temporarily.


