When Java was introduced, the first thing that struck me was the garbage collection. Like many people who are now Java programmers, I was working extensively in C and C++ at the time. The garbage collection problem plagued us terribly. It's difficult to specify the contract for exactly when allocated memory is to be freed, and who's responsible for it. Often, memory would be freed too early, resulting in a system crash. Or the memory wouldn't be freed at all, which in some ways was worse. At least with a crash, you'd know that something was wrong. A memory leak might not be detected until much later, and it wouldn't be at all clear where among many changes the leak had been introduced.
The next thing that struck me about Java was its binary compatibility. At that point in time, even trivial changes to code in a C or C++ program could induce massive recompilations. The more developers, the more you had to recompile, which could put a serious crimp into the development cycle. You could try to bypass complete recompilations (although there would still often be lengthy link steps), but that could cause a different category of problem that wouldn't show up until late in the compile/link/test cycle. A one-line change to a .H file could induce a complete recompilation, and I've worked on projects where that was an overnight joband that's assuming that it worked the first time.
Things have changed in C and C++ since then, with improved incremental compilers and linkers, not to mention much faster computers. In fact, despite Java's binary compatibility features, I recommend frequent recompilations anyway, since other features of Java do a great job of letting the compiler spot bugs for you. Still, the fact that you don't have to recompile all the time, and the fact that Java binary compatibility puts limits on just how much you have to recompile, is a huge boost to programmer productivity.
What Is Java Binary Compatibility?
Now that I've spent most of a page praising the utility of Java binary compatibility, what is it, really? It's a set of rules about the relationships of Java source code files (.java files) to compiled Java files (.class files), which tell you what kinds of changes to the .java files require changes to the rest of the .class files in the project.
As with almost everything in Java, binary compatibility is platform-independent. It's not a feature of the javac compiler or of Sun's implementation of the Java Virtual Machine (although a few JVM implementations bend the binary compatibility rules to achieve a performance boost).
Binary compatibility is not the platform independence of Java (also known as "write once run anywhere"). Nor is it about independence between different versions of Java, although it does help guarantee that programs written against Java 1.4 will be compatible with version 1.5 when it's introduced.
Binary compatibility is about the compatibility of different versions of the same Java class after it has been compiled. You can change a class and not have to compile the other classes that use it, nor recompile the classes that it uses. This has a number of advantages. The binary compatibility rules allow you to distribute code patches and have them integrated into an existing installation very easily. All you have to do is replace the class files that have been changed.
Goals of Binary Compatibility in Java
Allow patching of existing code
Minimize size of patches
Decrease linking problems
Increase assurance of compatibility between files
The binary compatibility rules are about a lot more than just speeding developer compilation cycles. They're also about the ability to integrate code from disparate sources and have it work together. I like to think of the rules in terms of contract-based programming. A particular piece of source code promises certain things, and when that piece of code is compiled, those promises become set in stone. The Java compiler enforces some of those promises, and leaves them in the compiled code.
The promises take the form of classes, method signatures, and field types. As long as those don't change, you can swap out implementations; rearrange methods and fields; and add classes, methods, and fieldsall without breaking the contract. This applies not just to the Java sources but to the binary Java class files compiled from them, which is why we call it binary compatibility.