The classloading process starts with a loading a byte array. This is usually read in
from a filesystem, but can be read from a URL or other location (often represented as
a Path object).
The Classloader::defineClass() method is responsible for turning a class file
(represented as a byte array) into a class object. It is a protected method and so is
not accessible without subclassing.
The first job of defineClass() is loading. This produces the skeleton of a class
object, corresponding to the class you're attempting to load. By this stage, some
basic checks have been performed on the class (e.g., the constants in the constant
pool have been checked to ensure that they're self-consistent).
However, loading doesn't produce a complete class object by itself, and the class isn't
yet usable. Instead, after loading, the class must be linked. This step breaks down
into separate subphases:
• Preparation and resolution
Verification confirms that the class file conforms to expectations, and that it doesn't
try to violate the JVM's security model (see “Secure Programming and Classload‐
ing” on page 315 for details).
JVM bytecode is designed so that it can be (mostly) checked statically. This has the
effect of slowing down the classloading process but speeding up runtime (as checks
can be omitted).
The verification step is designed to prevent the JVM from executing bytecodes that
might crash it or put it into an undefined and untested state where it might be vul‐
nerable to other attacks by malicious code. Bytecode verification is a defense against
malicious hand-crafted Java bytecodes and untrusted Java compilers that might out‐
put invalid bytecodes.
The default methods mechanism works via classloading.
When an implementation of an interface is being loaded, the
class file is examined to see if implementations for default
methods are present. If they are, classloading continues nor‐
mally. If some are missing, the implementation is patched to
add in the default implementation of the missing methods.