This chapter introduces Java and points out some of its strengths. It describes how programming in Java is different from programming in any other language and how you can take advantage of these differences to create exciting new applications. Also included in this chapter are high-level overviews of the Java language and the Java runtime environment. By the time you complete this chapter, you will have a good introduction to what Java is and what it can do for you.
Java was developed at Sun Microsystems. Work on Java originally began with the goal of creating a platform-independent language and operating system for consumer electronics. The original intent was to use C++, but as work progressed in this direction, the Java developers realized that they would be better served by creating their own language rather than extending C++. The effort toward consumer electronics led the Java team, then known as FirstPerson, Inc., toward developing hardware and software for the delivery of video-on-demand with Time-Warner.
Unfortunately (or perhaps fortunately, depending on your perspective), Time-Warner selected Silicon Graphics as the vendor for its video-on-demand project. This setback left the FirstPerson team with an interesting piece of software (Java) but no market in which to place it. Eventually, the natural synergies of the Java language and the World Wide Web (WWW) were noticed, and Java found a market.
What we know today as Java is both a programming language and an environment for executing programs written in the Java language. Unlike traditional compilers, which convert source code into machine-level instructions, the Java compiler translates Java source code into instructions that are interpreted by the runtime Java Virtual Machine. So, unlike languages like C and C++, on which Java is based, Java is an interpreted language.
Although most of the current excitement and anticipated use of Java are related to the Internet, and the World Wide Web in particular, Java did not begin as an Internet project. Because of this, Java is suitable for many general-purpose programming tasks and, in fact, many of the Java tools themselves are written in Java. It is a compiler development custom that a language has come of age when its compiler can be written in the language. According to this custom, the Java language has come of age.
The excitement over Java as an Internet development language is related to its capability to solve two key problems with Internet content:
Although these two problems are related, each warrants independent consideration.
Current WWW pages are very good at conveying some types of information but are inadequate for conveying other types. In particular, WWW pages excel at conveying passive, static information. This type of information changes infrequently (static) and doesn't change in response to user interactions with it (passive).
For example, many Web pages enable you to enter a company's name or stock ticker symbol and receive current price quotes. One of the best is the APL Quote Server located at http://qs.secapl.com/cgi-bin/qs and shown in Figure 1.1. This page is static because it isn't automatically updated with new quotes while the page is being browsed. It is passive because the user cannot interact with the page, other than to request to see a similar page.
Figure 1.1 : Retrieving passive, static stock prices.
Current Web development technologies excel at displaying this type of page. Much of human communication is passive, static, or both. A highway billboard is a perfect example of a conventional means of communication that is both passive and static. Just as not all billboards will go the way of Burma Shave, not all passive, static WWW content needs to become active and dynamic (the opposites of passive and static).
However, Java is an enabling technology that allows for the creation of more powerful pages. Continuing with the example of a page that shows the price of a stock at a given point in time, you could use Java to create a page that shows a graph of a stock's price over time and have that graph continue to update in real time while you browse the page. This is where Java comes in-because Java is a full-featured programming language, Web pages like this become much more feasible. Sun Microsystems has created a page that does exactly this. It is located at
http://java.sun.com/java.sun.com/applets/applets/StockDemo/index.html
and is shown in Figure 1.2.
Figure 1.2 : A Java version of a stock price Web page.
On the Java WWW page shown in Figure 1.2, there is a line of stock ticker symbols and prices that scrolls across the top of the screen. Below that are graphs for three different companies' stocks. Each of these graphs is updated every five seconds based on the latest trades. (The page shown in Figure 1.2 is just an illustration and the prices of the NEATO company are randomly generated.)
In the pre-Java world of the Web, as a developer of Web content and pages, you could not count on your users having a specific browser configuration. You could create a Web page with leading edge graphics, sound, and real-time multimedia. Unfortunately, if any of the visitors to your Web site did not configure a browser add-on to handle the latest whiz-bang features, these users wouldn't get the full impact of your site.
Just as the prior section showed that Java enabled applications to go beyond passive, static content, Java again serves as an enabling technology. In this case, Java enables you, as a Web developer, to create Web pages that will be delivered consistently to all users with a Java-enabled browser.
Not only does Java free you from concerns about how users have configured their Web browsers, it also frees you from concerns about which hardware and operating system platform they are using. Because Java source code is interpreted, if a Java interpreter exists for a specific hardware and operating system platform, you can write programs with the knowledge that they will be usable on that platform.
Now that you've read about some of the benefits of using Java for World Wide Web applications, it's time to take a quick look behind the scenes at how this is accomplished. Figure 1.3 shows how Java source code is transferred over the Internet. This figure shows a host computer that is storing the source code to a Java program. When a user on a local computer uses the Internet to connect to this host with a Java-enabled browser, the source is transferred from the host computer to the local computer.
Figure 1.3 : Transfer of Java source code over the Internet.
Implemented within the Java-enabled browser is the Java Virtual Machine, which interprets the Java source code. The Java Virtual Machine acts on an input byte stream and converts it into the desired program actions. In the case of Java, the byte stream is a sequence of opcodes (operation codes) and operands. Each pair of opcode and operand is read by the interpreter, and the specified action is performed. This is illustrated in Figure 1.4.
Of course, the use of Java extends beyond the Web, and there is much to recommend Java as a general-purpose development language. You've already seen that Java is completely portable to a variety of hardware platforms and operating systems. In this section, you'll learn about some of Java's attributes that make it a desirable general-purpose language.
For example, because Java borrows much of its syntax and many of its concepts from C and C++, there is a preexisting pool of programmers who could quickly learn Java. However, Java goes far beyond being a mere derivative of C++. It adds to C++ in the areas of automatic memory management and language-level support for multithreaded applications. On the other hand, Java remains easier to learn and simpler to use than C++ because of those C++ features that were left out of Java: multiple inheritance, pointers, and the goto statement, among others.
Because implementations of the Java Virtual Machine can be very efficient, it is possible for Java programs to run almost as quickly as C++ programs. This is a key feature in convincing developers that Java is a viable language for non-Internet development. Because of Java's strengths as an Internet language, many of these same strengths apply when Java is used as a language for client-server development. It is very likely that as corporations do more and more Internet development in Java, they will begin to apply these same Java developers to their client-server projects. Java's strengths in terms of network awareness, security, portability, and performance make it ideally suited for corporate client-server development, as well as Internet development.
In discussing Java programs, it has become standard to refer to Java programs that are embedded in another language as applets and to stand-alone programs as applications. For example, when you use Java to augment a World Wide Web page, the Java code is embedded within HTML code. Therefore, this is referred to as an applet. On the other hand, a Java program that is not embedded within HTML or any other language and can stand on its own is referred to as an application.
Of course, there is a subtle implication here that applications are larger (and, therefore, presumably more complex) than applets. However, this is not necessarily true. Applications and applets alike can range from simple one-class programs to programs with hundreds of classes. The implication that an applet is somehow less than an application is unfortunately a connotation it is necessary to live with in an otherwise valid distinction.
Having seen that Java is equally suited as a language for development both on and off the Internet, it's time to look more closely at the Java language itself. The creators of Java at Sun Microsystems have defined the Java language as "a simple, object-oriented, distributed, interpreted, robust, secure, architecture-neutral, portable, high-performance, multithreaded, and dynamic language." Well, they managed to fit all of the important 1990s buzzwords into one sentence, but we need to look more closely at Java to see if they managed to fit all of these concepts into one language.
If you have experience with any object-oriented language, especially C++, you probably will find Java to be easier than your high school prom date. Because Java started out as C++ but has had certain features removed, it is certainly a simpler language than C++.
The simplicity of Java is enhanced by its similarities to C and C++. Because many of today's current programmers, especially those likely to consider using Java, are experienced in at least C and probably C++, Java is instantly familiar to these programmers.
Java has simplified C++ programming by both adding features beyond those found in C++ and by removing some of the features that make C++ a complicated and difficult language to master. Java is simple because it consists of only three primitive data types-numbers, Boolean types, and arrays. Everything else in Java is a class. For example, strings are true objects, not just arrays of characters. Similarly, arrays in the Java language are first-class objects, not just memory allocations and runtime representations.
Java offers additional simplifications over C++. The ubiquitous goto statement has been removed. Operator overloading, a frequent source of confusion during the maintenance of C++ programs, is not allowed in Java. Unlike C and C++, Java has no preprocessor. This means that the concepts behind #define and typedef are not necessary in Java. Java reduces the redundancy of C++ by removing structures and unions from the language. These are both just poor cousins of a full-fledged class and are superfluous in a cohesively designed language. Of course, they were necessary in C++ because it was important for early C++ translators and then compilers to be able to correctly interpret the existing C code that relied on these features.
The most important C++ feature left out of Java is the capability to directly manipulate memory addresses through the use of pointers. Pointers are one of the cornerstones of the C and C++ languages, and it would be difficult to write many programs in these languages without using pointers. However, as any C or C++ programmer will admit, pointers are also a significant source of problems and debugging time in C and C++ programs. Pointers can accidentally be set to point to the wrong thing, causing unexpected behavior including crashes. Pointers also can be used to store allocated memory. If the allocated memory isn't freed, or released back to the operating system, then the program will gradually leak memory, until it eventually runs out. An entire set of commercial products, such as the Bounds Checker products, has come into existence to help programmers identify these types of pointer-related problems. Java simplifies this by completely removing pointers from the language and using a handle-based solution instead.
Of course, if all Java did was remove syntax from C++, it would be a poor compiler instead of an exciting new language. Java goes well beyond C++ by adding some important features. One of the most important is automatic memory management, usually known as garbage collection. Garbage collection is really just a blue-collar term that means that you don't need to free memory that you allocate-the Java Virtual Machine takes care of doing this for you. If you're a C or C++ programmer, or have ever had to track down memory leaks in another language, just imagine how nice your life could be if you never have to do it again. You would have time for walks on the beach, barbecued turkey burgers on holiday weekends, and romantic evenings with your spouse.
Java goes beyond C++ in a variety of other ways, as well. For example, Java includes language-level support for writing multithreaded programs. A multithreaded program is one that is written such that it performs more than one task at a time. For example, consider the stock price Web page shown earlier in Figure 1.2. One thread in the program to create this page may be constantly retrieving quotes from the stock exchange while another thread searches various news databases for breaking stories about the stocks being monitored. Although you can definitely write this program in a traditional single-threaded manner, the ability to use multiple threads can make it simpler to write and maintain.
Of course, Java is object-oriented. In fact, in the mid-1990s, it's hard to imagine someone developing a new language and declaring it the greatest new thing without it being object-oriented. In its approach to object-orientation, Java follows more closely along the lines of languages such as SmallTalk than C++. Except for its primitive data types, everything in Java is an object. In contrast, C++ is much more lax in that you are entirely free to mix and match object-oriented code (classes) and procedural code (functions). In Java, this is not the case. There are no global functions in Java: all functions are invoked through an object.
Java's support for object-orientation does not include multiple inheritance. The designers of the language felt that the complexity introduced by multiple inheritance was not justified by its benefits.
Java classes are comprised of methods and variables. Class methods are the functions that an object of the class can respond to. Class variables are the data that define the state of an object. In Java, methods and variables can be declared as private, protected, or public. Private methods and variables are not accessible outside of the class. Protected members are accessible to subclasses of the class, but not to other classes. Finally, public methods and variables are accessible to any class.
Classes in Java can be defined as abstract. An abstract class is a class that collects generic state and behavioral information. More specific classes are defined as subclasses of the abstract class and are used to define actual, specific entities. For example, software in use at a pet store may have an abstract class named Pet. This class would store information that is common to all pets-birthdate, cost, sale price, date received, and so on. Derived from the abstract Pet class could be classes such as Dog, Cat, Bird, and Fish. Each of these classes can augment the abstract class as necessary. For example, a member variable called WaterType (salt or fresh) would be necessary in Fish. Because WaterType would be meaningless for Dogs, Cats, and Birds, it is not part of the abstract implementation of Pet.
Java facilitates the building of distributed applications by a collection of classes for use in networked applications. By using Java's URL (Uniform Resource Locator) class, an application can easily access a remote server. Classes also are provided for establishing socket-level connections.
Because Java is interpreted, once the Java interpreter has been ported to a specific machine, that machine can instantly run the growing body of Java applications. As an example of the usefulness of this, imagine a hypothetical chip manufacturer, Outtel, that has just finished its newest CPU chip. This new chip, named the Zentium, serves as the foundation of a new line of computers being marketed toward Zen Buddhist monasteries. Once Outtel ports the Java interpreter to work on the Zentium, the new machine will be able to run all of the Java development utilities-the compiler, the debugger, and so on. Contrast this with a traditional language. If Outtel wants to release a C++ compiler with its new computer it must port, or create from scratch, the compiler, the debugger, the runtime library, and so on.
Also, when using an interpreter, programmers are freed from some of the concerns of intermodule dependencies. You no longer have to maintain a "make" file that is sometimes as complicated as the hardest part of your program.
Another advantage is that the time-consuming edit-compile-link-test cycle is broken. Without the compile and link steps, working in an interpreted environment is a much simpler edit-test cycle. Even with today's quick C++ compilers, it is not uncommon for a complete recompile and relink of a large program to be measured in hours and take the better part of a day. Without having to wait for lengthy compiles and links, Java promotes prototyping and easier debugging.
The designers of Java anticipated that it would be used to solve some very complex programming problems. Writing a distributed, multithreaded program that can run on a variety of operating systems with a variety of processors is not a simple task. To do it successfully, you need all the help your programming language can offer you. With this in mind, Java was created as a strongly typed language. Data type issues and problems are resolved at compile-time, and implicit casts of a variable from one type to another are not allowed.
Memory management has been simplified in Java in two ways. First, Java does not support direct pointer manipulation or arithmetic. This makes it impossible for a Java program to overwrite memory or corrupt data. Second, Java uses runtime garbage collection instead of explicit freeing of memory. In languages like C++, it is necessary to delete or free memory once the program has finished with it. Java follows the lead of languages such as LISP and SmallTalk by providing automatic support for freeing memory that has been allocated but is no longer used.
Closely related to Java's robustness is its focus on security. Because Java does not use pointers to directly reference memory locations, as is prevalent in C and C++, Java has a great deal of control over the code that exists within the Java environment.
It was anticipated that Java applications would run on the Internet and that they could dynamically incorporate or execute code found at remote locations on the Internet. Because of this, the developers of Java hypothesized the existence of a hostile Java compiler that would generate Java byte codes with the intent of bypassing Java's runtime security. This led to the concept of a byte-code verifier. The byte-code verifier examines all incoming code to ensure that the code plays by the rules and is safe to execute. In addition to other properties, the byte code verifier ensures the following:
Back in the dark ages of the early 1980s, there was tremendous variety in desktop personal computers. You could buy computers from Apple, Commodore, Radio Shack, Atari, and eventually even from IBM. Additionally, every machine came with its own very different operating system. Because developing software is such a time-consuming task, very little of the software developed for use on one machine was ever ported and then released for use on a different machine.
In many regards, this situation has improved with the acceptance of Windows, the Apple Macintosh, and UNIX variations as the only valid personal computer options. However, it is still not easy to write an application that can be used on Windows NT, UNIX, and a Macintosh. And it's getting more complicated with the move of Windows NT to non-Intel CPU architectures.
A number of commercially available source code libraries (for example, Zinc, ZApp, and XVT) attempt to achieve application portability. These libraries attempt this by focusing on either a lowest common denominator among the operating systems or by creating a common core API (Application Programming Interface).
Java takes a different approach. Because the Java compiler creates byte code instructions that are subsequently interpreted by the Java interpreter, architecture neutrality is achieved in the implementation of the Java interpreter for each new architecture.
In addition to being architecture-neutral, Java code is also portable. It was an important design goal of Java that it be portable so that as new architectures (due to hardware, operating system, or both) are developed, the Java environment could be ported to them.
In Java, all primitive types (integers, longs, floats, doubles, and so on) are of defined sizes, regardless of the machine or operating system on which the program is run. This is in direct contrast to languages like C and C++ that leave the sizes of primitive types up to the compiler and developer.
Additionally, Java is portable because the compiler itself is written in Java and the runtime environment is written in POSIX-compliant C.
For all but the simplest or most infrequently used applications, performance is always a consideration. It is no surprise, then, to discover that achieving high performance was one of the initial design goals of the Java developers. A Java application will not achieve the performance of a fully compiled language such as C or C++. However, for most applications, including graphics-intensive ones such as are commonly found on the World Wide Web, the performance of Java is more than adequate. For some applications, there may be no discernible difference in performance between C++ and Java.
Many of the early adopters of C++ were concerned about the possibility of performance degradation as they converted their programs from C to C++. However, many C++ early adopters discovered that, although a C program will outperform a C++ program in many cases, the additional development time and effort don't justify the minimal performance gains. Of course, because we're not all programming in assembly language, there must be some amount of performance we're willing to trade for faster development.
It is very likely that early experiences with Java will follow these same lines. Although a Java application may not be able to keep up with a C++ application, it will normally be fast enough, and Java may enable you to do things you couldn't do with C++.
Writing a computer program that only does a single thing at a time is an artificial constraint that we've lived with in most programming languages. With Java, we no longer have to live with this limitation. Support for multiple, synchronized threads is built directly into the Java language and runtime environment.
Synchronized threads are extremely useful in creating distributed, network-aware applications. Such an application may be communicating with a remote server in one thread while interacting with a user in a different thread.
Because it is interpreted, Java is an extremely dynamic language. At runtime, the Java environment can extend itself by linking in classes that may be located on remote servers on a network (for example, the Internet). This is a tremendous advantage over a language like C++ that links classes in prior to runtime.
In C++, every time member variables or functions are added to a class, it is necessary to recompile that class and then all additional code that references that class. Of course, the problem is exacerbated by the fact that you need to remember to recompile the files that reference the changed class. Using make files reduces the problem, but for large, complex systems, it doesn't eliminate it.
Java addresses this problem by deferring it to runtime. At runtime, the Java interpreter performs name resolution while linking in the necessary classes. The Java interpreter is also responsible for determining the placement of objects in memory. These two features of the Java interpreter solve the problem of changing the definition of a class used by other classes. Because name lookup and resolution are performed only the first time a name is encountered, only minimal performance overhead is added.
Of course, in order to write Java applications or applets, you need more than a language-you need the tools that let you write, test, and debug your programs. This section gives a very high-level overview of the various Java tools that come with the Java Developer's Kit. (For instructions on downloading the Java Developer's Kit from Sun Microsystems, see Chapter 2, "Installing Java.")
There is, of course, a Java compiler, named javac. The Java compiler takes input source code files (these files typically have the extension .java) and converts them into compiled byte code files. The Java compiler is discussed in more detail in Chapter 9, "javac: The Java Compiler."
The Java interpreter, known eponymously as java, can be used to execute Java applications. The interpreter translates byte codes directly into program actions.
The Java debugger, jdb, enables you to debug your Java classes. Unfortunately, the Java debugger is a throwback to the pre-GUI debugger dark ages of programming. The Java debugger is a command-line debugger that is enough to make you wish for even the 1988 version of CodeView. However, you can use the jdb to set breakpoints, inspect objects and variables, and monitor threads.
The Java debugger is discussed in more detail in Chapter 15, "jdb: The Java Debugger."
One of the basic tenets of object-oriented programming is that programmers unfamiliar with a class need only concern themselves with the public interface of that class. If you want to use a Queue or Stack class, you shouldn't be concerned with (or need to be concerned with) how this class has been written. Whether it uses a linked list, a statically sized array, or a dynamic array shouldn't influence whether and how you use the class.
Because you should be interested only in the public interface of a class, the Java Developer's Kit includes a disassembler, javap, that can be used to display the public interface, both methods and variables, of a class. Additionally, the Java disassembler includes options to display private members or to display the actual byte codes for the class's methods. This last option can be particularly useful if you want to achieve a greater understanding of the byte codes used by the Java interpreter.
Because Java is a new language and must fit in a world dominated by C and C++, included in Java is the capability to use native C code within a Java class. One of the steps in doing this is using the Java header file generator, javah. The process for doing this is fully described in Chapter 19, "Extending Your Programs with Native Methods."
As programmers, we've fought it in every way possible. Unfortunately, there is no longer any excuse for not documenting our source code. Using the JavaDoc utility provided with the Java Developers Kit, you can easily generate documentation in the form of HTML files. To do this, you embed special comments and tags in your source code and then process your code through JavaDoc. All of the on-line Java API documentation was created with JavaDoc.
JavaDoc is described in detail in Chapter 16, "Using JavaDoc to Document Your Program."
If you will be writing Java applets, you will definitely want to become familiar with the Applet Viewer. This small program provides a real Java environment for testing applets. It loads the HTML file in which the applet has been embedded and displays the application in a browser-like window, as shown in Figure 1.5.
Figure 1.5 : A bar chart applet being run in the Applet Vicwer.
In Figure 1.5, the Java class Chart.class has been loaded by the Applet Viewer and is displayed on the screen. This is a simple applet for displaying a four-line bar chart. One nice feature of the Applet Viewer is that you can easily view the HTML tags or parameters that control the applet. By selecting Tag from the Applet menu, you can see all of the tags, as shown in Figure 1.6.
Figure 1.6 : HTML tags used to create the bar chart applet.
In this illustration, you can see that the c1 value was set to 10 and the c2 value was set to 20. By looking back at Figure 1.5, you can see that these were the values used to draw the top two bars. The Applet Viewer is described in more detail in Chapter 11, "Using the Applet Viewer."
Of course, if you plan to use Java to develop Internet content, you need to have a Java-enabled browser. Not only do you need a Java-enabled browser, so will your prospective users if your project is to be a success. Fortunately, you (and your prospective users!) are in luck. Java-enabled browsers are available from both Sun Microsystems and Netscape.
HotJava is the name given by Sun Microsystems to its Java World Wide Web browser. HotJava is written entirely in Java. Although HotJava's purpose is to view applets embedded in HTML documents, it serves as the best example to date of what can be done in a Java application. The main screen for HotJava appears in Figure 1.7.
Figure 1.7 : The main screen of the HotJava browser.
HotJava presents the user with a very clean and simple interface. It has a series of menus across the top and a single set of six buttons that can be used for navigation. In order, these buttons will move to the prior page, move to the next page, move to the home page, reload the current page, stop loading a page, and view HTML errors.
As an example of the type of Web content that Java enables, consider Figure 1.8. This figure shows a Web page with two adjacent illustrations. The illustration on the left is an x-ray with a horizontal indicator through it. The illustration on the right is a cross-section through the left illustration at the position indicated by the horizontal line. The 14 enclosed in a circle and connected to the line indicates that the line is at position 14.
Figure 1.8 : Sample Java Web content viewed through HotJava.
This page is both active and dynamic because a user can select the line and move it to a different location on the left illustration. This will cause a different cross-section to be displayed, as shown in Figure 1.9.
Figure 1.9 : Moving the selection line to view a different crossection in Hotjava.
Because HotJava includes a full Java Virtual Machine, it is possible to create Web pages with active, dynamic content as shown in this example.
Of course, HotJava is not your only choice for a Java-enabled Web browser. Beginning with version 2.0 of its Navigator browser, Netscape includes Java compatibility. The inclusion of Java support in a Netscape browser is significant because Netscape is the dominant browser vendor. With Java support included in Navigator, Java applets are within reach of most World Wide Web users. An example of using Netscape Navigator to view a Java Web page is shown in Figure 1.10.
Figure 1.10 : A java-enabled stock quote page viewed with Netscape Navigator.
For comparison purposes, Figure 1.10 is the same page that was
displayed by HotJava in
Figure 1.2.
By now, you've gotten a good overview of what Java is and what you can do with it. You've learned how it can be used to create Web-based applets that move beyond the passive, static content of current Web pages. You've also learned that Java is a general-purpose language that can be used for stand-alone applications that have nothing to do with the Internet.
In this chapter, you also examined Sun Microsystems's claim that Java is "a simple, object-oriented, distributed, interpreted, robust, secure, architecture-neutral, portable, high-performance, multithreaded, and dynamic language." Each of these characteristics was examined and found to be more than just marketing hype. Finally, this chapter gave you a high-level overview of the tools that are included with the Java Developer's Kit and of two Java-enabled Web browsers.
In the next chapter, you will learn how to install Java, including how to download the latest version of the Java Developer's Kit from the World Wide Web.