What is a Singleton class?
The Singleton pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to it.
To implement the Singleton pattern, we need to:
Example (Eager Initialization):
public class MySingleton {
// Eagerly create the single instance
private static final MySingleton instance = new MySingleton();
// Make the constructor private
private MySingleton() {}
// Provide a global point of access
public static MySingleton getInstance() {
return instance;
}
}
To make a Singleton thread-safe during lazy initialization, the `getInstance()` method needs to be synchronized or a technique like double-checked locking must be used.
What are marker interfaces?
A marker interface is an interface with no fields or methods. It is an empty interface.
Its purpose is to 'mark' a class as having a certain capability or property. The JVM or other frameworks can then check if a class implements this interface (using the `instanceof` operator) and perform special processing for it.
Classic examples from the JDK include:
java.io.Serializable
: This interface marks a class as being serializable. The JVM's serialization mechanism checks for this interface before attempting to serialize an object.java.lang.Cloneable
: This interface indicates that the `Object.clone()` method can make a legal field-for-field copy of instances of that class. If a class doesn't implement `Cloneable`, calling `clone()` on it will throw a `CloneNotSupportedException`.java.rmi.Remote
: This interface marks an object as being accessible remotely.What is the difference between pass-by-value and pass-by-reference?
This is a tricky question. The official answer is that Java is always strictly pass-by-value.
The confusion in Java arises when passing objects. When you pass an object to a method, you are passing a copy of the reference value, not a copy of the object itself. This reference value points to the same object in memory.
// Demo
class Dog {
String name;
Dog(String name) { this.name = name; }
}
// Main method
Dog myDog = new Dog("Max"); // myDog holds a reference value, say 'xyz123'
changeName(myDog);
System.out.println(myDog.name); // Prints "Fido"
void changeName(Dog dog) {
// 'dog' receives a copy of the reference value 'xyz123'
dog.name = "Fido"; // Modifies the 'name' field of the original object
dog = new Dog("Buddy"); // 'dog' now points to a NEW object. This does NOT affect 'myDog'.
}
Because the method can change the *state* of the original object, it looks like pass-by-reference. However, because you cannot change the original reference itself (as shown by `dog = new Dog("Buddy")`), Java is technically pass-by-value.
What is constructor chaining?
Constructor chaining is the process of calling one constructor from another constructor within the same class or from a subclass.
1. Chaining within the same class (using `this()`):
The `this()` keyword is used to call another overloaded constructor in the same class. The `this()` call must be the very first statement in the constructor.
public class Employee {
private String name;
private int id;
// Default constructor
public Employee() {
// Calls the parameterized constructor
this("Default Name", 0);
}
// Parameterized constructor
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
}
2. Chaining to a superclass (using `super()`):
The `super()` keyword is used to call a constructor from the parent class. The `super()` call must also be the very first statement in the subclass's constructor. If you don't explicitly call `super()`, the compiler will automatically insert a call to the no-argument constructor of the superclass.
class Person {
Person() {
System.out.println("Person constructor called");
}
}
class Student extends Person {
Student() {
super(); // Calls the Person constructor
System.out.println("Student constructor called");
}
}
What is the difference between `Hashtable` and `HashMap`?
Though both store data in key-value pairs, `Hashtable` is a legacy class that has been retrofitted into the Collections Framework, while `HashMap` is the standard choice.
Feature | Hashtable | HashMap |
---|---|---|
Thread Safety | Synchronized. It is thread-safe. All its methods are synchronized. | Not synchronized. It is not thread-safe. For a thread-safe alternative, you should use `ConcurrentHashMap` or `Collections.synchronizedMap()`. |
Performance | Slower due to the synchronization overhead on every operation. | Faster because it is not synchronized. |
Null Values | Does not allow null keys or null values. Will throw a `NullPointerException`. | Allows one null key and multiple null values. |
Inheritance | It inherits from the `Dictionary` class (an obsolete class). | It inherits from the `AbstractMap` class. |
Iterator | Uses `Enumerator` to iterate over values, which is considered legacy. It also supports `Iterator`. | Uses `Iterator`, which is the modern standard and supports the `remove()` method. |
Conclusion: You should almost always use `HashMap` in single-threaded applications and `ConcurrentHashMap` in multi-threaded applications. `Hashtable` should be avoided in new code.
Explain the internal working of `ConcurrentHashMap`.
ConcurrentHashMap
is the preferred choice for thread-safe map operations in Java. It provides much better performance than a synchronized `HashMap` or a `Hashtable` by using a more granular locking mechanism.
In short, `ConcurrentHashMap` avoids locking the entire map for write operations, which is why it offers high concurrency and scalability.
What is the `CopyOnWriteArrayList`?
CopyOnWriteArrayList
is a thread-safe variant of `ArrayList` in which all mutative operations (like `add`, `set`, etc.) are implemented by creating a fresh copy of the underlying array.
How it works:
Key Properties:
When to use it: It is best suited for applications where reads are far more frequent than writes. For example, a list of listeners in an event-based system where listeners are rarely added or removed but are frequently iterated over to dispatch events.
What are static and instance initializer blocks?
Initializer blocks are used to initialize instance or static variables.
Instance Initializer Block:
class MyClass {
{
// This is an instance initializer block
System.out.println("Instance initializer block executed.");
}
MyClass() {
System.out.println("Constructor executed.");
}
}
// new MyClass() will print "Instance initializer block executed." then "Constructor executed."
Static Initializer Block:
static
keyword.class MyStaticClass {
static {
// This is a static initializer block
System.out.println("Static initializer block executed.");
}
}
// The message will be printed only the first time MyStaticClass is referenced.
What is object cloning and what are its types?
Object cloning is the process of creating an exact copy of an object. The clone()
method of the `Object` class is used for this. A class must implement the `Cloneable` marker interface to indicate that its objects can be legally cloned.
There are two types of cloning:
1. Shallow Copy (Shallow Clone):
2. Deep Copy (Deep Clone):
What is serialization and the `transient` keyword?
Serialization is the process of converting a Java object's state into a byte stream. This byte stream can then be stored in a file, kept in memory, or transmitted across a network.
Deserialization is the reverse process: reconstructing the object from the byte stream.
To make a class serializable, it must implement the `java.io.Serializable` marker interface.
The transient
keyword is a variable modifier used in serialization. If you define any data member as `transient`, it will not be serialized. When the object is deserialized, the value of the transient field will be its default value (e.g., 0 for `int`, `null` for objects).
This is useful for fields that you do not want to persist, such as:
public class User implements Serializable {
private String username;
private transient String password; // Password will not be saved during serialization
//...
}