A serialized instance of the class is downloaded and deserialized. That the class itself is also downloaded (based on server-provided auxiliary download locations) is a secondary feature. The downloaded instance is then deserialized, which may run a custom deserialization method of the class, which in turn would contain the malicious code.
The serialized object doesn’t contain any code. But the client needs the code of object’s class to deserialize it.
Instead of providing the object directly, the server can provide a JNDI object reference, which (among other things) contains a `classFactoryLocation` attribute specifying an URL where the bytecode of the class of the serialized object can be downloaded. If the class isn’t already present in the client’s codebase, the client proceeds to download it, in order to then be able to deserialize the actual object of interest.
This only works over LDAP, with the intended use-case that enterprises store and organize their code repository as part of their LDAP directory.