It is better to deliver your classes in a ZIP or JAR file than to deliver them one class at a time over the network or load them individually from separate files in the filesystem. This packaged delivery provides some of the benefits of clustering[13] (see Section 14.1.2). The benefits gained from packaging class files come from reducing I/O overhead such as repeated file opening and closing, and possibly improving seek times.[14] Within the ZIP or JAR file, the classes should not be compressed unless network download time is a factor for the application. The best way to deliver local classes for performance reasons is in an uncompressed ZIP or JAR file. Coincidentally, that's how they're delivered with the SDK.
[13] "Clustering" is an unfortunately overloaded word, and is often used to refer to closely linked groups of server machines. In the context here, I use "clustering" to mean the close grouping of files.
[14] With operating system-monitoring tools, you can see the system temporarily stalling when the operating system issues a disk-cache flush if lots of files are closed around the same time. If you use a single packed file for all classes (and resources), you avoid this potential performance hit.
It is possible to further improve the classloading times by packing the classes into the ZIP/JAR file in the order in which they are loaded by the application. You can determine the loading order by running the application with the -verbose option, but note that this ordering is fragile: slight changes in the application can easily alter the loading order of classes. A further extension to this idea is to include your own classloader that opens the ZIP/JAR file itself and reads in all files sequentially, loading them into memory immediately. Perhaps the final version of this performance-improvement route is to dispense with the ZIP/JAR filesystem: it is quicker to load the files if they are concatenated together in one big file, with a header at the start of the file giving the offsets and names of the contained files. This is similar to the ZIP filesystem, but it is better if you read the header in one block, and read in and load the files directly rather than going through the java.util.zip classes.
One further optimization to this classloading tactic is to start the classloader running in a separate (low-priority) thread immediately after VM startup.