Chapter 1. An Introduction to Java
Prior to launching into the main topics in this book, let’s step back and think about some of the core assumptions of a work that calls itself Java: The Good Parts. As a colleague of mine was wont to say, “good” is such a value-laden term that it is proper to first think about what we mean when we say that a programming language, or part of a programming language, is good.
What makes a programming language good, and what parts of a language contribute to the goodness of the language, is generally a debate best undertaken late at night with the aid of considerable chemistry. Such debates—and they are always debates, never simple discussions—have a certain nonterminating nature to them, much like debates over the best editor, the proper way to format code, or open source licenses. In an attempt to keep my mailbox from overflowing on the date of publication of this book (or, perhaps, on the date that the book is first read), it may be worthwhile to look for a moment at the notion of “best” or “good” with regard to a programming language.
The most famous (or infamous) seed for such discussions is Richard Gabriel’s essay The Rise of “Worse is Better.”[2] In this essay, Gabriel gives a convincing argument that Lisp was (and is) a better language than C, but that C won anyway because of all sorts of factors that had nothing to do with the goodness of the languages. In fact, according to the essay, it was because C and Unix were worse than their alternatives (Lisp and Multix) that those languages were able to come to dominate programming.
The main problem with the argument (which I’ve pointed out before)[3] is that it makes the mistake of thinking that “worse” and “better” are predicates denoting properties that adhere to entities directly.[4] Put more simply, these discussions assume that you can talk about a language being good or bad in a vacuum, or in some absolute fashion. But that’s the wrong way to think about these notions, especially when applying them to things that are means to some other end. One shouldn’t argue or even assert that programming language x is better than programming language y, because “better than” requires a third term. We need to know what you are trying to do with the programming languages before you can talk about which one is better than the other. Likewise, if you want to talk about a language being worse or better, you have to say what you are using them for. A language is worse or better at doing some things, and for different things, you may get a different answer on which language is worse or better.
To see why this matters, let’s go back to Gabriel’s Worse is Better argument. What Gabriel showed was that Lisp was simpler, allowed the programmer more flexibility, was surrounded by a better set of programming tools, and was better for rapid development of code. What he didn’t point out is that C, at the time, was available on much cheaper hardware, and code written in the language ran much faster (on the cheap hardware) than code written in Lisp. The fact that C won the language battle didn’t so much show that something worse was valued more than something that was better, but that the people making the decisions valued the places where C was better more than they valued the places that Lisp was better.
What Gabriel presupposed in his argument was that the factor that made a programming language good was the productivity of the individual programmer using that language. Even if we buy all of his arguments, the best we could conclude is that Lisp was a better programming language for an individual programmer. But that aspect of the language had to be balanced against the cost of the environment, where the C language was better. It wasn’t that C was worse; it was that it was worse for some things and better in others. The reason that C won the programming language war was that running fast on cheap hardware was more important to those making the decisions (who, generally, were not programmers) than increased individual programmer productivity.
The relevance of all this to the current book is that, before I can launch into talking about the good parts of Java, I need to spend a little time talking about what Java is good for. I’m not going to claim that the parts of Java I’m going to talk about in what follows are always the good parts, no matter what you are trying to do with the language. Nor am I going to try to claim that Java itself is the language you should use for any and all of your programming needs. Java is a tool, and any craftsman[5] who relies on a single tool is going to have a hard time with some jobs. Perhaps everything you do is the kind of thing where Java is the appropriate tool. But if you have a programming task where some other language is more appropriate than Java (and there are lots of such tasks), then citing that task as something that Java does badly does not mean that Java is not a good language. It just means that Java isn’t good for that task.
What Is Java Good For?
So what is the Java language good for? Or, more precisely, what are the situations in which I find myself reaching for the tool that is Java?
First, I find Java a useful tool when I’m working on a project that is either so large, so complex, or on such a short schedule that I need to be working with other programmers to get it done. Java has a number of characteristics that allow you to break the work up into smaller, independent pieces, and then ensure that those pieces really stay independent. There are also features of the language that make it easier to explain to others what you are doing and how the system fits together, which also aid in this sort of multiperson effort.
I find the Java language and environment to be a useful tool when the code that I am writing needs to run on multiple platforms. The claim that you can write your code once and run it anywhere with the Java language and the Java virtual machine is, to a large extent, true. It has been bashed for a number of reasons, some of them absolutely true, but most of which have to do with the graphics libraries or the look-and-feel of the user interface. These are hard problems, and it isn’t even clear what the right answer is with respect to some of them. I remember debates in the Java organization about whether “write once, run anywhere” meant that the user interface would look the same on all platforms, or meant that the user interface would look like the native interface on the platform, a problem especially vexing if you support both Windows and Mac. It is also true that running everywhere does not always mean running optimally everywhere. For real performance-sensitive applications, some tuning might be needed.
But for the most part, Java bytecodes will run the same on the Java virtual machine no matter what the platform. And this is certainly a far cry from the kind of “portable” C or C++ code that we’ve had in the past, where “portability” meant that you could compile most or all of the code on any machine (after running the appropriate configuration files), and then start debugging once the compile completed successfully. Java’s notion of portability has to do with the object code, and if there is any debugging to be done, it is the responsibility of the virtual machine vendor, not the application writer. Having a portable binary format is a different sort of thing, and a real advantage if you are going to run on multiple machines.
Java is a useful tool when the code you are writing is going to be used for a long time, either by itself or as a component or library that is designed to have lots of different users. There are complexities with getting a Java program started, and there are complexities that make Java an inconvenient choice for a one-off, quick hack, or experimental program. In fact, I would venture to say that Java is best used on systems that are designed before they are written (at least to some level of exactitude) rather than those that grow as the code is produced.
Finally, I would say that Java is a useful tool if the code you are writing needs to be reliable. Of course, all of us write programs that are meant to be reliable. But there is a trade-off between the kinds of freedoms we can have in writing our code and the reliability that the language and environment force on us. The Java language and environment have made a number of design decisions that are meant to make the code written more reliable, even if it makes the programmer’s experience somewhat more difficult.
So when, in this book, I talk about the good parts of Java, I’m going to be talking about the parts that aid in writing programs (or parts of programs) in environments in which Java itself is the appropriate choice of programming language and environment. I’m assuming that you are using Java for a large, multiperson project that is producing long-lived programs or components that need to be highly reliable, and that those programs or components will be used and modified by even more people. This is not all of programming, and for other kinds of programming activities in other kinds of environments, what I will be talking about might not make Java a good language, and in fact Java itself might be the wrong tool for the job. For clarity of exposition, in what follows, I will talk about things that are good parts of Java. When I do, you should understand that I mean “good part of Java, for doing the things that Java is good for doing.”
When I first started using Java, one of the real attractions of the language and the environment was the simplicity of it all. There was a language that generated bytecodes that would run on any Java virtual machine, no matter what the underlying operating system. There were a small number of libraries that provided basic functionality, all written by the same small group of engineers and all sharing a similar design philosophy. If you knew C or C++, Java was easy to learn. Although some libraries may have been better than others, they were all reasonable.
Since then, Java and the associated environment have grown tremendously, both in popularity and in size. Java started out as a language for writing web applications that would run in a browser, and is now a language that is used for core system programming in the enterprise. The language itself has seen a number of major and minor additions, ranging from generic types (major) to autoboxing (minor). The platform has grown from a single entity to multiple editions, from Java Enterprise Edition and Standard Edition to all of the small Java environments for phones and smart cards. The set of libraries has grown at an even faster rate.
The end result is an overall system that has grown in complexity and can be daunting to those trying to master it. But within this large and complex system, there is still the small and simple core system, if you just know where to find it. The goal of this book is to show that small, simple system that every Java programmer who is using Java for what the language and environment are good for should know. This is not meant as a tutorial in the language; there are many of those, so I’m going to assume that you are familiar with Java and can read the examples without a lot of explanation. Nor is this an attempt to show you the clever tricks and techniques that will allow you go get the most out of your Java program—others have done that job far better than I can do it.
Instead, this is a reminder of which parts of the language are useful in all situations, and at times, a reflection on why particular design choices were made or not made. My hope is that you will come away from this book with a reinforced view of the use of certain parts of the language, and a better understanding of why those parts of the language are useful, when to use them, and when to avoid other solutions. Along the way, there will be some digressions on the history of the language and environment, along with unjustified (but not, I hope, unjustifiable) opinions on the art of programming, system building, and software design. There will be times, no doubt, that the text veers from description of the language and becomes unintentionally confessional, in that the viewpoints might tell the reader more about the author than about the right way to do things in Java. When that happens, all I can say is that there is no right way to do things in Java (or any other language), but these are ways that have worked for me. And they may help you avoid the many wrong ways of doing things, which is no small thing in itself.
If nothing else, I can say that what follows are some discussions of the features of Java that I have found useful in my work over the past 15+ years. I do mostly system (more precisely, distributed systems) programming, so there is very little about building graphical user interfaces in what follows. I don’t do beans, or enterprise applications, so most of Java Enterprise Edition is ignored. This is more a reflection of my experience than a reflection of those parts of the Java environment. The fact that something isn’t discussed in this book may not mean that it isn’t a good part of Java, but rather that it is a part of Java that I haven’t had to use.
This is not to say that all of Java is good. There will be times in what follows that I will talk about things that should be avoided, or that seemed like a good idea at the time but have since turned out to be, well…less than good ideas. Some of these are ideas that I helped bring into the language, so I’m not going to try to assign praise or blame. But one can’t talk about the good without contrasting it with the bad. At least I can’t, and I haven’t tried here.
[2] There are a number of versions of this essay, but the one I usually refer to is http://www.jwz.org/doc/worse-is-better.html.
[4] In rereading this, I realize that the time I misspent as a youth studying philosophy is showing itself. Please bear with me; this won’t last long.
[5] Yes, this assumes that programming is a craft. I’ve argued this elsewhere (see http://research.sun.com/techrep/Perspectives/PS-2006-6.pdf) and so won’t rehash the argument here.
Get Java: The Good Parts now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.