In Java, a class provides the blueprint for objects, and an object is created from a class. Though creating objects seems like a straightforward concept, many developers and interview candidates often overlook the different methods available for object creation. Knowing various ways to create objects is beneficial and can be a key topic during technical interviews.
Let's discuss various ways to create objects in Java, each with an example to illustrate how they work internally.
Using new keyword
Using newInstance() method of Class
Using clone() method
Using Deserialization
Using newInstance() method of Constructor
The new keyword is the most common way to create objects in Java. Nearly 99% of objects are created using this method. By using new, you can call any constructor (default or parameterized) to initialize the object.
Example:
// Java program to illustrate object creation using new keyword
class GFG {
String name = "GeeksForGeeks";
public static void main(String[] args) {
// Creating an object using new keyword
GFG obj = new GFG();
System.out.println(obj.name); // Output: GeeksForGeeks
}
}
Benefits:
Simple and direct.
Offers full control over object initialization.
Drawbacks:
Requires constructor knowledge.
Cannot create objects dynamically at runtime without the class name.
Use Case:
Standard object creation for most Java applications.
If you know the class name and it has a public default constructor, you can create an object using Class.forName() and newInstance().
Example:
// Java program to illustrate object creation using newInstance()
class GFG {
String name = "GeeksForGeeks";
public static void main(String[] args) {
try {
// Loading class dynamically and creating an instance
Class<?> cls = Class.forName("GFG");
GFG obj = (GFG) cls.newInstance();
System.out.println(obj.name); // Output: GeeksForGeeks
} catch (Exception e) {
e.printStackTrace();
}
}
}
Benefits:
Allows dynamic object creation at runtime.
Useful in frameworks and libraries.
Drawbacks:
Deprecated in newer versions of Java (Java 9+).
Requires proper exception handling (ClassNotFoundException, InstantiationException).
Use Case:
Useful in frameworks like Spring and Hibernate for dependency injection and dynamic object creation.
When clone() is called, the JVM creates a new object and copies the content of the original object. The constructor is not invoked during cloning. For cloning, the class must implement the Cloneable interface.
Example:
// Java program to illustrate object creation using clone() method
class GFG implements Cloneable {
String name = "GeeksForGeeks";
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) {
GFG obj1 = new GFG();
try {
GFG obj2 = (GFG) obj1.clone();
System.out.println(obj2.name); // Output: GeeksForGeeks
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
Benefits:
Creates a duplicate object without calling the constructor.
Drawbacks:
Only performs a shallow copy by default. A deep copy requires extra code.
Classes need to implement Cloneable interface; otherwise, it throws CloneNotSupportedException.
Use Case:
Useful when duplicating objects, such as in prototypes or simulations.
During deserialization, an object is recreated from its serialized form, and the constructor is not invoked. The class must implement Serializable.
Example (Serialization):
import java.io.*;
class GFG implements Serializable {
private String name;
GFG(String name) {
this.name = name;
}
public static void main(String[] args) {
try {
GFG obj = new GFG("GeeksForGeeks");
FileOutputStream fos = new FileOutputStream("file.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Example (Deserialization):
import java.io.*;
public class GFG {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("file.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
GFG obj = (GFG) ois.readObject();
System.out.println(obj.name); // Output: GeeksForGeeks
ois.close();
fis.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Benefits:
Allows persisting objects and reconstructing them across sessions.
Drawbacks:
I/O operations make it slower.
Requires class to implement Serializable.
Use Case:
Common in distributed systems, session management, and caching where objects need to be stored and retrieved across different sessions.
This method is similar to the newInstance() method of Class. However, it can also invoke parameterized and private constructors using the reflection API.
Example:
import java.lang.reflect.Constructor;
class GFG {
private String name;
GFG() {}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
try {
Constructor<GFG> constructor = GFG.class.getDeclaredConstructor();
GFG obj = constructor.newInstance();
obj.setName("GeeksForGeeks");
System.out.println(obj.name); // Output: GeeksForGeeks
} catch (Exception e) {
e.printStackTrace();
}
}
}
Benefits:
Supports invoking private and parameterized constructors.
Useful in more flexible object creation processes.
Drawbacks:
More complex and requires understanding of Java Reflection API.
Slower due to reflection overhead.
Use Case:
Common in frameworks that require fine control over object construction, such as dependency injection frameworks.