Because Java is multithreaded, initialization of a class or interface requires careful
synchronization, since some other thread may be trying to initialize the same class
or interface at the same time. There is also the possibility that initialization of a
class or interface may be requested recursively as part of the initialization of that
class or interface; for example, a variable initializer in class A might invoke a
method of an unrelated class B, which might in turn invoke a method of class A.
The implementation of the Java Virtual Machine is responsible for taking care of
synchronization and recursive initialization by using the following procedure. It
assumes that the Class
object has already been verified and prepared, and that the
Class
object contains state that indicates one of four situations:
Class
object is verified and prepared but not initialized.
Class
object is being initialized by some particular thread T.
Class
object is fully initialized and ready for use.
Class
object is in an erroneous state, perhaps because the verification or preparation step failed, or because initialization was attempted and failed.
The procedure for initializing a class or interface is then as follows:
Class
object that represents the class or interface to be initialized. This involves waiting until the current thread can obtain the lock for that object (§17.13).
wait
(§20.1.6) on this Class
object (which temporarily releases the lock). When the current thread awakens from the wait
, repeat this step.
Class
object and complete normally.
Class
object and complete normally.
Class
object is in an erroneous state, then initialization is not possible. Release the lock on the Class
object and throw a NoClassDefFoundError
.
Class
object is now in progress by the current thread and release the lock on the Class
object.
Class
object represents a class rather than an interface, and the superclass of this class has not yet been initialized, then recursively perform this entire procedure for the superclass. If necessary, verify and prepare the superclass first. If the initialization of the superclass completes abruptly because of a thrown exception, then lock this Class
object, label it erroneous, notify all waiting threads (§20.1.10), release the lock, and complete abruptly, throwing the same exception that resulted from initializing the superclass.
final
class variables and fields of interfaces whose values are compile-time constants are initialized first (§8.3.2.1, §9.3.1, §13.4.8).
Class
object, label it fully initialized, notify all waiting threads (§20.1.10), release the lock, and complete this procedure normally.
Error
or one of its subclasses, then create a new instance of the class ExceptionInInitializerError
, with E as the argument, and use this object in place of E in the following step. But if a new instance of ExceptionInInitializerError
cannot be created because an OutOfMemoryError
occurs, then instead use an OutOfMemoryError
object in place of E in the following step.
Class
object, label it erroneous, notify all waiting threads (§20.1.10), release the lock, and complete this procedure abruptly with reason E or its replacement as determined in the previous step.
ExceptionInInitializerError
as described here.)