Java draws on many years of programming experience with other languages in its choice of features. It is worth taking a moment to compare Java at a high level with some other languages, both for the benefit of those of you with other programming experience and for the newcomers who need to put things in context. We do not expect you to have a knowledge of any particular programming language in this book and when we refer to other languages by way of comparison, we hope that the comments are self-explanatory.
At least three pillars are necessary to support a universal programming language today: portability, speed, and security. Figure 1-2 shows how Java compares to a a few of the languages that were popular when it was created.
You may have heard that Java is a lot like C or C++, but that’s really not true except at a superficial level. When you first look at Java code, you’ll see that the basic syntax looks like C or C++. But that’s where the similarities end. Java is by no means a direct descendant of C or a next-generation C++. If you compare language features, you’ll see that Java actually has more in common with highly dynamic languages such as Smalltalk and Lisp. In fact, Java’s implementation is about as far from native C as you can imagine.
If you are familiar with the current language landscape, you will notice that C#, a popular language, is missing from this comparison. C# is largely Microsoft’s answer to Java, admittedly with a number of niceties layered on top. Given their common design goals and approach (e.g., use of a virtual machine, bytecode, sandbox, etc.), the platforms don’t differ substantially in terms of their speed or security characteristics. C# is theoretically as portable as Java, but to date it is supported on far fewer platforms. Like Java, C# borrows heavily from C syntax but is really a closer relative of the dynamic languages. Most Java developers find it relatively easy to pick up C# and vice versa. The majority of time spent moving from one to the other is learning the standard library.
The surface-level similarities to these languages are worth noting, however. Java borrows heavily from C and C++ syntax, so you’ll see terse language constructs, including an abundance of curly braces and semicolons. Java subscribes to the C philosophy that a good language should be compact; in other words, it should be sufficiently small and regular so a programmer can hold all the language’s capabilities in his or her head at once. Just as C is extensible with libraries, packages of Java classes can be added to the core language components to extend its vocabulary.
C has been successful because it provides a reasonably feature-packed programming environment, with high performance and an acceptable degree of portability. Java also tries to balance functionality, speed, and portability, but it does so in a very different way. C trades functionality for portability; Java initially traded speed for portability. Java also addresses security issues that C does not (although in modern systems many of those concerns are now addressed in the operating system and hardware).
In the early days before JIT and adaptive compilation, Java was slower than statically compiled languages and there was a constant refrain from detractors that it would never catch up. But as we described in the previous section, Java’s performance is now comparable to C or C++ for equivalent tasks and those criticisms have generally fallen quiet. ID Software’s open source Quake2 video game engine has been ported to Java. If Java is fast enough for first-person combat video games, it’s certainly fast enough for business applications.
Scripting languages such as Perl, Python, and Ruby are very popular. There’s no reason a scripting language can’t be suitable for safe, networked applications. But most scripting languages are not well suited for serious, large-scale programming. The attraction to scripting languages is that they are dynamic; they are powerful tools for rapid development. Some scripting languages such as Perl also provide powerful tools for text-processing tasks that more general-purpose languages find unwieldy. Scripting languages are also highly portable, albeit at the source code level.
The problem with scripting languages is that they are rather casual about program structure and data typing. Most scripting languages (with a hesitant exception for Python and later versions of Perl) are not object-oriented. They also have simplified type systems and generally don’t provide for sophisticated scoping of variables and functions. These characteristics make them less suitable for building large, modular applications. Speed is another problem with scripting languages; the high-level, usually source-interpreted nature of these languages often makes them quite slow.
Java offers some of the essential advantages of a scripting language: it is highly dynamic, along with the added benefits of a lower-level language. Java has a powerful Regular Expression API that competes with Perl for working with text and language features that streamline coding with collections, variable argument lists, static imports of methods, and other syntactic sugar that make it more concise.
Incremental development with object-oriented components, combined with Java’s simplicity, make it possible to develop applications rapidly and change them easily. Studies have found that development in Java is faster than in C or C++, strictly based on language features. Java also comes with a large base of standard core classes for common tasks such as building GUIs and handling network communications. But along with these features, Java has the scalability and software-engineering advantages of more static languages. It provides a safe structure on which to build higher-level frameworks (and even other languages).
As we’ve already said, Java is similar in design to languages such as Smalltalk and Lisp. However, these languages were used mostly as research vehicles rather than for development of large-scale systems. One reason is that these languages never developed a standard portable binding to operating system services, such as the C standard library or the Java core classes. Smalltalk is compiled to an interpreted bytecode format, and it can be dynamically compiled to native code on the fly, just like Java. But Java improves on the design by using a bytecode verifier to ensure the correctness of compiled Java code. This verifier gives Java a performance advantage over Smalltalk because Java code requires fewer runtime checks. Java’s bytecode verifier also helps with security issues, something that Smalltalk doesn’t address.
 See, for example, G. Phipps, “Comparing Observed Bug and Productivity Rates for Java and C++,”Software—Practice & Experience, volume 29, 1999.