Top

Java Programming

41.

What is the purpose of `serialVersionUID`?

Ans:

The serialVersionUID is a unique ID used during the serialization and deserialization process. It is a private static final long field in a class that implements `Serializable`.

Its purpose is to provide a version number for the class to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization.

  • During serialization, the JVM records the `serialVersionUID` of the class in the byte stream.
  • During deserialization, the JVM compares the `serialVersionUID` from the byte stream with the `serialVersionUID` of the local class.

If the UIDs match, the object is deserialized. If they do not match, it indicates that the local class version is incompatible with the serialized object's version, and an `InvalidClassException` is thrown. This prevents unexpected errors that could arise from deserializing an object into an incompatible class structure.

If you don't declare a `serialVersionUID`, the JVM will generate one automatically based on the class details (fields, methods, etc.). However, it is strongly recommended to explicitly declare one, because even minor compiler differences or class modifications can cause the auto-generated UID to change, breaking deserialization.

42.

Explain the full lifecycle of a thread.

Ans:

A thread in Java goes through several states during its lifetime. These states are defined in the `Thread.State` enum.

  1. NEW: A thread that has been created but has not yet started. The `start()` method has not been called on it.
  2. RUNNABLE: A thread that is ready to be executed by the thread scheduler. It might be currently running or just waiting for its turn to be picked by the scheduler. This state includes both 'ready' and 'running'.
  3. BLOCKED: A thread that is waiting to acquire a monitor lock to enter a `synchronized` block or method. It is blocked because another thread currently holds that lock.
  4. WAITING: A thread that is waiting indefinitely for another thread to perform a particular action. A thread enters this state by calling `Object.wait()`, `Thread.join()`, or `LockSupport.park()`. It can only leave this state if another thread calls `notify()` or `notifyAll()` on the object it's waiting on, or if it is interrupted.
  5. TIMED_WAITING: A thread that is waiting for a specified period of time. A thread enters this state by calling methods with a timeout, such as `Thread.sleep(long millis)`, `Object.wait(long timeout)`, or `Thread.join(long millis)`. It will return to the RUNNABLE state when the timeout expires or it receives a notification.
  6. TERMINATED: A thread that has completed its execution. Its `run()` method has finished. The thread is dead and cannot be started again.

43.

What is a `ThreadPoolExecutor` and what are its core parameters?

Ans:

A `ThreadPoolExecutor` is a powerful and flexible class from the `java.util.concurrent` package that creates and manages a pool of worker threads. It separates the task submission from the task execution, allowing for better resource management by reusing existing threads instead of creating a new one for every task.

The main constructor has several key parameters:

  • corePoolSize: The number of threads to keep in the pool, even if they are idle.
  • maximumPoolSize: The maximum number of threads allowed in the pool.
  • keepAliveTime: When the number of threads is greater than the core size, this is the maximum time that excess idle threads will wait for new tasks before terminating.
  • unit: The time unit for the `keepAliveTime` argument (e.g., `TimeUnit.SECONDS`).
  • workQueue: The queue used to hold tasks before they are executed. If all core threads are busy, new tasks are placed in this queue. Examples include `LinkedBlockingQueue`, `ArrayBlockingQueue`, and `SynchronousQueue`.
  • threadFactory: A factory used to create new threads when needed.
  • handler: The policy to use when a task is rejected because the thread pool is saturated (both the maximum pool size and the queue are full). Examples include `AbortPolicy` (throws an exception) and `CallerRunsPolicy` (the submitting thread runs the task itself).

44.

What is `Callable` and how is it different from `Runnable`?

Ans:

Both `Callable` and `Runnable` are interfaces representing tasks that can be executed by a thread. However, `Callable` is a more powerful version introduced in Java 5.

FeatureRunnableCallable<V>
Method SignatureIt has a single abstract method: void run().It has a single abstract method: V call().
Return ValueThe run() method does not return any value (its return type is `void`).The call() method can return a result of type `V`.
Exception HandlingThe run() method cannot throw a checked exception. You must handle it inside the method.The call() method can throw a checked exception.
SubmissionSubmitted to an `ExecutorService` using the execute(Runnable) method.Submitted to an `ExecutorService` using the submit(Callable) method. This method returns a `Future` object.

A Future object represents the result of an asynchronous computation. You can use it to check if the computation is complete, wait for its completion, and retrieve the result from the `call()` method.

45.

What is a `CompletableFuture`?

Ans:

CompletableFuture, introduced in Java 8, is an evolution of the `Future` interface. It represents a future result of an asynchronous computation but provides much more powerful capabilities for composing, combining, and handling these computations.

Key advantages over a simple `Future`:

  • Non-Blocking Operations: You can attach callback functions that will be executed automatically when the future completes, without blocking a thread to wait for the result. This is done using methods like thenApply(), thenAccept(), and thenRun().
  • Composable: You can chain multiple `CompletableFuture`s together to create a pipeline of asynchronous operations. Methods like thenCompose() are used for this.
  • Combinable: You can combine the results of two or more independent `CompletableFuture`s using methods like thenCombine(), allOf(), and _anyOf().
  • Exception Handling: It provides explicit methods for handling exceptions that occur during the asynchronous computation, such as exceptionally() and handle().

It is a cornerstone of modern asynchronous programming in Java.

46.

Explain the Java Memory Model (JMM).

Ans:

The Java Memory Model (JMM) defines the rules for how threads in a multi-threaded Java application interact through memory. It specifies the conditions under which a write to a variable by one thread is guaranteed to be visible to a read of that same variable by another thread.

Key concepts of the JMM include:

  • Main Memory and Working Memory: Conceptually, each thread has its own private 'working memory' (which can be thought of as CPU caches and registers) where it keeps copies of variables from the shared 'main memory' (RAM).
  • Visibility: The JMM addresses when changes made in one thread's working memory are flushed to main memory, and when other threads update their working memory from main memory. Keywords like volatile and `synchronized` are used to enforce these visibility guarantees.
  • Atomicity: An operation is atomic if it appears to the rest of the system to occur instantaneously. The JMM defines which basic operations are atomic (e.g., reads and writes to most primitive types).
  • Ordering and Happens-Before Relationship: The JMM defines a partial ordering on all actions within a program, called the 'happens-before' relationship. If one action happens-before another, then the results of the first action are guaranteed to be visible to the second. Synchronization actions, like locking/unlocking a monitor or writing/reading a volatile variable, create happens-before relationships.

Without the JMM, it would be impossible to write correct and predictable concurrent programs in Java.

47.

What is the difference between Heap and Stack memory?

Ans:

Heap and Stack are two different memory areas used by the JVM.

Stack Memory:

  • Each thread has its own private Stack.
  • It is used for static memory allocation and the execution of a thread (method calls).
  • When a method is invoked, a new 'stack frame' is created on the thread's stack. This frame holds local variables, parameters, and references to objects.
  • Memory is allocated and deallocated automatically in a LIFO (Last-In, First-Out) manner as methods are called and returned.
  • Memory is short-lived.
  • Access is fast.
  • If this memory is filled, Java throws a StackOverflowError.

Heap Memory:

  • There is one Heap for the entire application, shared by all threads.
  • It is used for dynamic memory allocation. All objects and their instance variables are created in the heap.
  • Memory is managed by the Garbage Collector.
  • Memory is long-lived (persists as long as the object is referenced).
  • Access is slower compared to the stack.
  • If this memory is filled, Java throws an OutOfMemoryError.

48.

What is Garbage Collection and how does it work?

Ans:

Garbage Collection (GC) is the process by which the Java Virtual Machine (JVM) automatically manages memory. Its purpose is to identify and discard objects that are no longer in use by the application, thereby freeing up heap memory.

The basic process is often called Mark and Sweep:

  1. Mark Phase: The garbage collector starts from a set of 'GC Roots' (which include active threads, static variables, etc.) and traverses the entire object graph. Every object it can reach from a GC Root is marked as 'alive' or 'referenced'.
  2. Sweep Phase: After marking, the garbage collector scans the entire heap. Any object that was not marked as alive is considered garbage because it is unreachable. The memory occupied by these garbage objects is reclaimed.

Modern JVMs use a Generational Garbage Collection strategy. The heap is divided into generations:

  • Young Generation: Where all new objects are allocated. It is further divided into an 'Eden' space and two 'Survivor' spaces. Most objects die young, so minor GC runs frequently here.
  • Old Generation (Tenured Generation): Objects that survive multiple GC cycles in the Young Generation are promoted to the Old Generation. Major GC runs less frequently here to clean up long-lived objects.

This strategy is efficient because it makes the assumption that most objects are short-lived and can be collected quickly.

49.

Can you force garbage collection in Java?

Ans:

No, you cannot force garbage collection in Java. You can only request or suggest it.

The `System.gc()` method can be called to suggest that the JVM perform garbage collection. However, the Java Language Specification states:

"Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects... When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects."

This means the JVM is not obligated to run the garbage collector immediately or at all. The decision is up to the JVM's internal algorithms.

In practice, calling `System.gc()` is highly discouraged. It can interfere with the JVM's own sophisticated GC scheduling and potentially cause performance issues rather than solve them. It's almost always better to let the JVM manage memory on its own.

50.

What are Generics in Java?

Ans:

Generics, introduced in Java 5, are a feature that provides type safety for collections and methods by allowing you to create classes, interfaces, and methods that operate on types as parameters.

Before generics, you would store objects in a collection like `ArrayList` and would have to cast them back to their specific type when retrieving them. This was error-prone, as you could accidentally add an object of the wrong type, leading to a `ClassCastException` at runtime.

Benefits of Generics:

  • Stronger Type Checks at Compile Time: The compiler enforces type correctness, catching invalid types at compile time instead of runtime.
  • Elimination of Casts: It removes the need for explicit casting, making the code cleaner and more readable.
  • Enabling Generic Algorithms: It allows developers to implement generic algorithms that work on collections of different types in a type-safe manner.
// Without Generics (unsafe and requires casting)
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

// With Generics (type-safe and no casting needed)
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);

Loading…
Tags: Java Programming Interview Questions and Answers || Java Programming Sort Questions and Answers || Java Programming Detailed Questions and Answers || Java Programming Tutorial