Ways to create objects
- Using
new
keyword - Using
Class.newInstance()
method - Using
clone()
method - Using
deserialization
- Using
newInstance()
method of Constructor class
Using new keyword
It is the most common way to create object. It allocates memory for a new object and invokes the constructor to initialize object.
Syntax
ClassName objectName = new ClassName(parameters);
public class Car {
String model;
int year;
// Constructor
public Car(String model, int year) {
this.model = model;
this.year = year;
}
public void displayInfo() {
System.out.println("Model: " + model + ", Year: " + year);
}
public static void main(String[] args) {
Car car1 = new Car("Toyota", 2021); // Creating an object
car1.displayInfo(); // Output: Model: Toyota, Year: 2021
}
}
Using Class.newInstace() method
In Java, the Class
class provides a method called newInstance()
that can be used to create new instances of classes dynamically at runtime.
The newInstance()
method creates a new instance of the class represented by the Class
object on which it is called. It invokes the class’s no-argument constructor to initialize the object.
The class for which you want to create an instance using newInstance()
must have a public, no-argument constructor. If the class does not have such a constructor, InstantiationException
is thrown.
public class MyClass {
private String name;
// Default constructor
public MyClass() {
this.name = "Default";
}
// Parameterized constructor
public MyClass(String name) {
this.name = name;
}
// Getter method
public String getName() {
return name;
}
public static void main(String[] args) {
try {
// Obtain the Class object for MyClass
Class<MyClass> clazz = MyClass.class;
// Create a new instance of MyClass using newInstance() method
MyClass obj1 = clazz.newInstance();
MyClass obj2 = clazz.newInstance();
// Display the names of the created objects
System.out.println("Name of obj1: " + obj1.getName());
System.out.println("Name of obj2: " + obj2.getName());
// Creating object with parameterized constructor using newInstance() method
MyClass obj3 = clazz.getDeclaredConstructor(String.class).newInstance("Custom Name");
System.out.println("Name of obj3: " + obj3.getName());
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
Using clone() keyword
In Java, cloning is a process of creating an exact copy of an existing object. The clone()
method in Java is used to create a duplicate of an object, which is a field-by-field copy of the original object. This method is part of the Cloneable
interface and is defined in the Object
class.
The Cloneable
interface is a marker interface, which means it does not contain any methods. It indicates that a class is eligible for cloning using the clone()
method. If a class does not implement Cloneable
and its clone()
method is called, a CloneNotSupportedException
is thrown.
public class Employee implements Cloneable {
String name;
int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) {
try {
Employee emp1 = new Employee("John", 25);
Employee emp2 = (Employee) emp1.clone(); // Creating a clone
System.out.println(emp1.name + ", " + emp1.age); // Output: John, 25
System.out.println(emp2.name + ", " + emp2.age); // Output: John, 25
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
Types of Cloning
- Shallow Cloning: Above code is an example of shallow cloning. It means,
clone()
will create a new instance of the object but for fields that are objects (references) only the reference is copies not the new object is created. Use shallow cloning when the object contains primitive fields or immutable objects. - Deep Cloning: Creates a new instance of the object and recursively copies all objects referenced by the fields. For fields that are objects, new instances of those objects are created, ensuring that changes to the clone do not affect the original. It is used when the object contains mutable fields or complex objects. This is achieved by adding the code in the overriding
clone()
method.
public class Address implements Cloneable {
private String city;
public Address(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Address{city='" + city + "'}";
}
}
public class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone(); // Deep clone the address
return cloned;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "', address=" + address + '}';
}
public static void main(String[] args) {
try {
Address address = new Address("New York");
Person original = new Person("John Doe", 30, address);
Person clone = (Person) original.clone();
System.out.println("Original: " + original);
System.out.println("Clone: " + clone);
// Modifying the clone's address
clone.address.city = "San Francisco";
System.out.println("After modifying clone's address:");
System.out.println("Original: " + original);
System.out.println("Clone: " + clone);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
Using deserialization
Deserialization is the process of converting a byte stream back into a copy of an object. This is typically used in conjunction with serialization, where the object’s state is saved to a byte stream and later restored. Serialization converts an object into a sequence of bytes that can be easily persisted to a file or sent over a network.
To make a class serializable, it must implement the Serializable
interface. This is a marker interface (an interface with no methods) that enables the serialization mechanism to identify objects of this class as serializable.
By implementing the Serializable
interface and using ObjectOutputStream
and ObjectInputStream
, you can serialize and deserialize objects efficiently.
import java.io.*;
public class Person implements Serializable {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
try {
// Serialization
Person person = new Person("Alice", 30);
FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(person);
out.close();
fileOut.close();
// Deserialization
FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
Person deserializedPerson = (Person) in.readObject();
in.close();
fileIn.close();
System.out.println("Deserialized Person: " + deserializedPerson.name + ", " + deserializedPerson.age);
// Output: Deserialized Person: Alice, 30
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Using newInstance method of Constructor class
In Java, reflection provides the ability to inspect and manipulate classes, methods, and fields at runtime. One powerful feature of reflection is the ability to create objects dynamically. The Constructor
class, part of the java.lang.reflect
package, allows you to create instances of classes using the newInstance()
method.
The Constructor
class provides information about, and access to, a constructor for a class. It has methods to access the constructor’s metadata, parameters, and to create new instances.
To obtain a Constructor
object, you use the getConstructor()
or getDeclaredConstructor()
methods from the Class
object. The getConstructor()
method retrieves public constructors, while getDeclaredConstructor()
retrieves all constructors regardless of their access modifier.
The newInstance()
method of the Constructor
class creates a new instance of the class, initializing it with the parameters provided.
import java.lang.reflect.Constructor;
public class Example {
private String message;
public Example(String message) {
this.message = message;
}
public void showMessage() {
System.out.println("Message: " + message);
}
public static void main(String[] args) {
try {
// Get the Class object associated with the Example class
Class<Example> clazz = Example.class;
// Get the Constructor object for the constructor that takes a String parameter
Constructor<Example> constructor = clazz.getConstructor(String.class);
// Use the Constructor object to create a new instance
Example example = constructor.newInstance("Hello, Reflection!");
// Call the showMessage method on the new instance
example.showMessage(); // Output: Message: Hello, Reflection!
} catch (Exception e) {
e.printStackTrace();
}
}
}
Uses
- Dynamic Object Creation: Useful in frameworks and libraries where objects need to be created dynamically at runtime based on configuration or user input.
- Dependency Injection: Reflection can be used to create instances and inject dependencies dynamically in IoC (Inversion of Control) containers.
- Serialization/Deserialization: Custom serialization frameworks can use reflection to instantiate objects during the deserialization process.
- Testing: Creating instances of classes with private constructors for unit testing