wait(), notify(), getClass(), clone() methods


wait() and notify() methods

wait() and notify() work in tandem – when one thread calls wait() on an object, that thread will block until another thread calls notify() or notifyAll() on that same object.

(See Also: wait()/notify())

package com.example.examples.object;
 
import java.util.concurrent.atomic.AtomicBoolean;
 
public class WaitAndNotifyCustom {
    public static void main(String[] args) throws InterruptedException {
        final Object lock = new Object();
        AtomicBoolean isWaiting = new AtomicBoolean(false);
 
        Thread threadX = new Thread("Thread X") {
            public void run() {
                System.out.println("X1: This can print before or after Y1");
                System.out.println("X2: Thread X is about to start waiting...");
                try {
                    synchronized (lock) {
                        lock.wait();
                    }
                    System.out.println("X3: Thread X has finished waiting. It happens after Y3.");
                } catch (InterruptedException e) {
                    System.out.println("Thread X was interrupted while waiting");
                } finally {
                    isWaiting.set(true);
                }
            }
        };
 
        Thread threadY = new Thread("Thread Y") {
            public void run() {
                System.out.println("Y1: This can print before or after X1");
                System.out.println("Y2: Thread Y will wait for 5 seconds");
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        System.err.println("Thread Y was interrupted from waiting");
                    }
                }
                System.out.println("Y3: Will ALWAYS print before X3 since X3 happens after lock.notify()");
 
                while (!isWaiting.get()) {
                    synchronized (lock) {
                        lock.notify();
                    }
                }
            }
        };
 
        threadX.start();
        threadY.start();
 
        threadX.join();
        threadY.join();
 
        System.out.println("Execution finished!");
    }
}

Some example output:

Y1: This can print before or after X1
Y2: Thread Y will wait for 5 seconds
X1: This can print before or after Y1
X2: Thread X is about to start waiting...
Y3: Will ALWAYS print before X3 since X3 happens after lock.notify()
X3: Thread X has finished waiting. It happens after Y3.
Execution finished!

getClass() method

The getClass() method can be used to find the runtime class type of an object. See the example below:

public class Employee {
 
    private long employeeID;
    private String fullName;
 
    public Employee(long employeeID, String fullName) {
        this.employeeID = employeeID;
        this.fullName = fullName;
    }
}
 
public class Engineer extends Employee {
 
    private String engineerID;
 
    public Engineer(String engineerID, long employeeID, String fullName) {
        super(employeeID, fullName);
        this.engineerID = engineerID;
    }
}
 
public static void main(String[] args) {
    Employee employee = new Employee(1001, "Alice Smith");
    Engineer specificEngineer = new Engineer("A123", 1002, "Bob Johnson");
    Employee anotherSpecificEngineer = new Engineer("B456", 1003, "Charlie Brown");
 
    System.out.println(employee.getClass()); // Prints "class Employee"
    System.out.println(specificEngineer.getClass()); // Prints "class Engineer"
    System.out.println(anotherSpecificEngineer.getClass()); // Prints "class Engineer"
}

The getClass() method will return the most specific class type, which is why when getClass() is called on anotherSpecificUser, the return value is class SpecificUser because that is lower down the inheritance tree than User.

It is noteworthy that, while the getClass method is declared as:

public final native Class<?> getClass();

The actual static type returned by a call to getClass is Class<? extends T> where T is the static type of the object on which getClass is called.

i.e. the following will compile:

Class<? extends String> cls = "".getClass();

clone() methods

The clone() method is used to create and return a copy of an object. This method arguable should be avoided as it is problematic and a copy constructor or some other approach for copying should be used in favour of clone().

For the method to be used all classes calling the method must implement the Cloneable interface.

The Cloneable interface itself is just a tag interface used to change the behaviour of the native clone() method which checks if the calling objects class implements Cloneable. If the caller does not implement this interface a CloneNotSupportedException will be thrown.

The Object class itself does not implement this interface so a CloneNotSupportedException will be thrown if the calling object is of class Object.

For a clone to be correct it should be independent of the object it is being cloned from, therefore it may be necessary to modify the object before it gets returned. This means to essentially create a "deep copy" by also copying any of the mutable objects that make up the internal structure of the object being cloned. If this is not implemented correctly the cloned object will not be independent and have the same references to the mutable objects as the object that it was cloned from. This would result in inconsistent behaviour as any changes to those in one would affect the other.

class Sample implements Cloneable {
	int w;
	String x;
	float[] y;
	Date z;
 
	public Sample clone() {
		try {
			Sample result = new Sample();
			// copy primitives by value
			result.w = this.w;
			// immutable objects like String can be copied by reference
			result.x = this.x;
 
			// The fields y and z refer to a mutable objects; clone them recursively.
			if (this.y != null) {
				result.y = this.y.clone();
			}
			if (this.z != null) {
				result.z = this.z.clone();
			}
 
			// Done, return the new object
			return result;
 
		} catch (CloneNotSupportedException e) {
			// in case any of the cloned mutable fields do not implement Cloneable
			throw new AssertionError(e);
		}
	}
}

Basic Programs