Top

Java Programming

21.

What are fail-fast and fail-safe iterators?

Ans:

  • Fail-Fast Iterators: These iterators immediately throw a ConcurrentModificationException if the underlying collection is modified structurally (elements are added or removed) by any means other than the iterator's own remove() method while the iterator is traversing the collection. They make a best-effort attempt to detect concurrent modification but do not offer a guarantee. The iterators for most non-concurrent collections like ArrayList and HashMap are fail-fast.
  • Fail-Safe Iterators (or Weakly Consistent Iterators): These iterators do not throw any exception if the collection is modified while iterating. They operate on a clone or a snapshot of the collection, so they are not affected by modifications made to the original collection after the iterator has been created. The iterators for collections in the java.util.concurrent package, like ConcurrentHashMap and CopyOnWriteArrayList, are fail-safe.

22.

How do you create a thread in Java?

Ans:

There are two primary ways to create a thread in Java:

1. By Extending the `Thread` Class:

  1. Create a new class that extends the java.lang.Thread class.
  2. Override the run() method in your class. This method contains the code that will be executed by the new thread.
  3. Create an instance of your new class and call its start() method. The start() method will in turn call the run() method in the new thread of execution.
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running...");
    }
}

public class Main {
    public static void main(String args[]) {
        MyThread t1 = new MyThread();
        t1.start();
    }
}

2. By Implementing the `Runnable` Interface:

  1. Create a new class that implements the java.lang.Runnable interface.
  2. Implement the run() method.
  3. Create an instance of your class, create a new Thread object passing your runnable object to its constructor, and then call the thread's start() method.
class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Thread is running...");
    }
}

public class Main {
    public static void main(String args[]) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        t1.start();
    }
}

Implementing `Runnable` is generally preferred because it allows your class to extend another class (as Java does not support multiple inheritance of classes) and it promotes a better separation of concerns (the task to be run is separate from the thread mechanism itself).

23.

What is synchronization in Java?

Ans:

Synchronization is a mechanism in Java that controls the access of multiple threads to any shared resource. It is a tool for preventing race conditions and ensuring data consistency in a multi-threaded environment.

When a thread wants to execute a synchronized block of code, it must first acquire the intrinsic lock (or 'monitor') of the object associated with that code. As long as the thread holds the lock, no other thread can enter any synchronized block on the same object. When the thread exits the synchronized block, it automatically releases the lock, allowing another waiting thread to acquire it.

Synchronization can be achieved in two ways:

  1. Synchronized Method: By applying the synchronized keyword to a method signature. This locks the entire method, using the `this` object's lock for instance methods, or the `Class` object's lock for static methods.
public synchronized void incrementCounter() {
    // ... code to modify shared state
}
  1. Synchronized Block: By using the synchronized keyword on a block of code. This allows you to synchronize only the critical section of a method rather than the entire method, which can improve performance. It also allows you to specify which object to lock on.
public void myMethod() {
    // ... non-critical code
    synchronized(this) {
        // ... code to modify shared state
    }
    // ... other non-critical code
}

24.

What is the difference between `sleep()` and `wait()`?

Ans:

Both methods are used to pause the execution of a thread, but they are used for different purposes and behave differently.

Featuresleep()wait()
ClassIt is a static method of the Thread class.It is an instance method of the Object class.
Lock ReleaseThe thread does not release the lock (monitor) it holds while it is sleeping.The thread releases the lock it holds so that another thread can acquire it and enter a synchronized block.
ContextCan be called from any context.Must be called from within a synchronized block or method, otherwise it throws an IllegalMonitorStateException.
Waking UpWakes up automatically after the specified time has elapsed, or if it is interrupted.Wakes up only when another thread calls notify() or notifyAll() on the same object, or if it is interrupted, or if it times out (if a timeout was specified).
PurposeUsed to pause the current thread for a specified period without any specific condition.Used for inter-thread communication and conditional waiting. A thread waits for a certain condition to become true.

25.

What is a deadlock?

Ans:

A deadlock is a situation in a multi-threaded environment where two or more threads are blocked forever, each waiting for a resource that is held by another thread in the same set.

For a deadlock to occur, four conditions (known as the Coffman conditions) must be met simultaneously:

  1. Mutual Exclusion: At least one resource must be held in a non-sharable mode; that is, only one thread can use the resource at any given time.
  2. Hold and Wait: A thread must be holding at least one resource and waiting to acquire additional resources that are currently being held by other threads.
  3. No Preemption: A resource cannot be forcibly taken from a thread; it can only be released voluntarily by the thread holding it.
  4. Circular Wait: A set of waiting threads {T0, T1, ..., Tn} must exist such that T0 is waiting for a resource held by T1, T1 is waiting for a resource held by T2, ..., and Tn is waiting for a resource held by T0.

The most common way to prevent deadlock is to break the circular wait condition by ensuring that all threads acquire locks in a fixed, predetermined order.

26.

What is the `volatile` keyword?

Ans:

The volatile keyword in Java is a field modifier that provides a guarantee of visibility and ordering for shared variables in a multi-threaded context.

  1. Visibility: When a variable is declared as volatile, any write to that variable by one thread is guaranteed to be immediately visible to any other thread that reads the variable. It ensures that reads and writes happen directly from/to main memory, bypassing any thread-local caches where the value might otherwise become stale.
  2. Ordering: It prevents certain types of instruction reordering by the compiler and JVM, ensuring that reads and writes of the volatile variable happen in the program order relative to other memory operations.

However, volatile does not guarantee atomicity. For example, an operation like count++ (which is actually three separate operations: read, increment, write) is not atomic and is not made thread-safe just by making `count` volatile. For atomic compound actions, you still need to use `synchronized` blocks or classes from the `java.util.concurrent.atomic` package (like `AtomicInteger`).

27.

What are Lambda Expressions in Java 8?

Ans:

A lambda expression is a short block of code which takes in parameters and returns a value. They are a key feature of Java 8, enabling a more functional style of programming.

Lambda expressions are essentially anonymous functions. They allow you to treat functionality as a method argument, or code as data. They are primarily used to provide an implementation for a functional interface (an interface with a single abstract method).

Syntax:

(parameter1, parameter2, ...) -> { code block }

Example (without Lambda):

List<String> names = Arrays.asList("peter", "anna", "mike");
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

Example (with Lambda):

List<String> names = Arrays.asList("peter", "anna", "mike");
Collections.sort(names, (a, b) -> a.compareTo(b));

As you can see, lambdas make the code much more concise and readable.

28.

What is the Stream API in Java 8?

Ans:

The Stream API, introduced in Java 8, is used to process sequences of elements from a source, such as a collection. It provides a declarative, functional-style way to perform operations on data.

A stream is not a data structure itself; it takes input from Collections, Arrays, or I/O channels. Stream operations are divided into two categories:

  1. Intermediate Operations: These are operations that return another stream, allowing them to be chained together to form a pipeline. They are lazy, meaning they are not executed until a terminal operation is invoked. Examples include:
    • filter(predicate): Filters elements based on a condition.
    • map(function): Transforms each element into another object.
    • sorted(): Sorts the stream.
  2. Terminal Operations: These operations produce a result or a side-effect. They trigger the execution of the entire pipeline of intermediate operations. Examples include:
    • forEach(action): Performs an action for each element.
    • collect(collector): Transforms the stream into a different data structure, like a `List` or `Set`.
    • reduce(identity, accumulator): Reduces the stream to a single summary value.

Example:

List<String> names = Arrays.asList("apple", "banana", "apricot", "cherry");

List<String> filteredAndUppercased = names.stream()   // Create a stream
    .filter(s -> s.startsWith("a"))        // Intermediate operation
    .map(String::toUpperCase)              // Intermediate operation
    .sorted()                              // Intermediate operation
    .collect(Collectors.toList());         // Terminal operation

// Result: ["APPLE", "APRICOT"]

29.

What is the `Optional` class in Java 8?

Ans:

The Optional class, introduced in Java 8, is a container object which may or may not contain a non-null value. Its primary purpose is to provide a better way to handle `null` values and avoid `NullPointerException`.

Instead of a method returning `null` when a value is not present, it can return an empty `Optional`. This forces the caller to explicitly handle the case where a value is absent, leading to more robust code.

Key Methods:

  • Optional.of(value): Creates an Optional. Throws `NullPointerException` if the value is null.
  • Optional.ofNullable(value): Creates an Optional. Allows the value to be null.
  • Optional.empty(): Creates an empty Optional.
  • isPresent(): Returns `true` if a value is present, otherwise `false`.
  • get(): Returns the value if present, otherwise throws `NoSuchElementException`.
  • orElse(defaultValue): Returns the value if present, otherwise returns a default value.
  • orElseThrow(exceptionSupplier): Returns the value if present, otherwise throws an exception created by the provided supplier.

Example:

Optional<String> foundName = findNameById(123);

// Provides a default value if the name is not found
String name = foundName.orElse("Unknown");

System.out.println(name);

30.

What are default methods in interfaces?

Ans:

Default methods were introduced in Java 8. They are methods in an interface that have an implementation. A class that implements the interface inherits the default implementation.

The primary motivation for default methods was to allow the Java Collections API to be evolved without breaking backward compatibility. For example, the forEach method was added to the Iterable interface as a default method. If it had been added as a regular abstract method, every class in existence that implemented Iterable would have been broken and forced to implement it.

A class can choose to:

  • Use the default implementation from the interface.
  • Override the default method with its own implementation.
  • Re-declare the method as abstract, forcing its subclasses to provide an implementation.
interface MyInterface {
    void regularMethod();

    default void newDefaultMethod() {
        System.out.println("This is a default implementation.");
    }
}

class MyClass implements MyInterface {
    @Override
    public void regularMethod() {
        // ... implementation
    }

    // MyClass now automatically has newDefaultMethod()
}

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