One thing that helps me work with code that is new to me -- this is much less necessary for well-written code -- is to massively refactor it for a day or two and then to discard all of my changes. This process helps me understand what the code does; working with code helps me understand it. It also begins to teach me what parts of the code are fragile.
If you have the opportunity of migrating to a newer release of Java, then genericizing all collections will help understand what data types are passed around.
Of course, I do this after installing the software in a test lab and playing with it a little bit to understand what it does.
Edit: Thinking on my answer, also useful is to enable all diagnostic tracing and logging, to use the system, and then to study the logs. If communication protocol tracing exists, then looking at this trace will help understand the communication protocol used by the code, perhaps with a Wireshark trace of the same testing.
Another useful migration is to migrate from the old Concurrency library to the new Java 5 (and 6) concurrency library. This will help you understand where the threads are and when they are started and when they will shut down.
Of course, with any code changes on an unfamiliar code base, I assume that proper testing is done to ensure nothing is broken! However, to balance this, I've learned that after refactoring badly-written code, the bugs newly introduced are often far easier to find than the bugs that existed prior to the refactoring.