Share E-Book

AuthorMarc Loy, Patrick Niemeyer, Daniel Leuck

Java continues to be one of the world's most widely used programming languages, making it a skill developers rely on to build cross-platform applications and keep pace with modern software development. Ideal for working programmers who are new to Java, this best-selling guide introduces the language's core features, APIs, programming techniques, and idioms through fun, realistic examples that focus on building real applications. In this updated seventh edition, you'll learn the latest Java features through Java 27, along with expanded coverage of file and network I/O, functional programming, and threads. You'll also discover how to use generative AI coding models effectively as part of your Java development workflow, while building a strong foundation in Java that you can apply to projects right away.

AI Reading Assistant

Whole-book reading guide from stratified index samples; jump to passages in the text

Passage locations
Tags
No tags
Publish Year: 2024
Language: 英文
File Format: PDF
File Size: 3.5 MB
Support Statistics
¥.00 · 0times
Text Preview (First 20 pages)
Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

(This page has no text content)
Learning Java SEVENTH EDITION Real-World Programming with Java With Early Release ebooks, you get books in their earliest form—the authors’ raw and unedited content as they write—so you can take advantage of these technologies long before the official release of these titles. Marc Loy, Patrick Niemeyer, and Daniel Leuck
Learning Java by Marc Loy, Patrick Niemeyer, and Daniel Leuck Copyright © 2027 Marc Loy. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Sarah Grey Development Editor: Sarah Grey Production Editor: Ashley Stussy Interior Designer: David Futato Interior Illustrator: Kate Dullea May 2000: First Edition July 2002: Second Edition May 2005: Third Edition June 2013: Fourth Edition March 2020: Fifth Edition August 2023: Sixth Edition April 2027: Seventh Edition
Revision History for the Early Release 2026-07-21: First Release See http://oreilly.com/catalog/errata.csp?isbn=9798341674981 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Learning Java, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. 979-8-341-67494-3 [LSI]
Brief Table of Contents (Not Yet Final) Chapter 1: A Modern Language (available) Chapter 2: A First Application (available) Chapter 3: Tools of the Trade (available) Chapter 4: The Java Language (available) Chapter 5: Objects in Java (available) Chapter 6: Error Handling and Logging (available) Chapter 7: Collections and Generics (unavailable) Chapter 8: Text and Core Utilities (unavailable) Chapter 9: Threads (unavailable) Chapter 10: File I/O (unavailable) Chapter 11: Network I/O (unavailable) Chapter 12: Functional Approaches (unavailable) Chapter 13: Expanding Java (unavailable)
Chapter 1. A Modern Language A NOTE FOR EARLY RELEASE READERS With Early Release ebooks, you get books in their earliest form—the author’s raw and unedited content as they write—so you can take advantage of these technologies long before the official release of these titles. This will be the 1st chapter of the final book. Please note that the GitHub repo will be made active later on. If you’d like to be actively involved in reviewing and commenting on this draft, please reach out to the editor at sgrey@oreilly.com. Java broke onto the scene in 1995, when the Internet was still new to most people— and to most businesses. Even then, a keen observer could see the impact a global information network would have. Some 30 years on, it is almost impossible to overstate the depth and breadth of that impact. Java played a critical role in that impact and continues to power enterprise services, even if its visibility and mindshare have waned some. The popularity and sheer ubiquity of web services initially powered by Java means newer languages and development tools have cropped up to fill the ever widening array of technical needs. Java is no longer the only option. Developers can tackle interesting, useful, and popular projects without needing Java at all. But Java is still there. Java is still popular. And Java still powers many backends. If you started out in one of those other languages and are looking to move to backend, enterprise environments, learning Java is a great choice to expand your skills. And Learning Java is a great choice to get you started! The greatest challenges and most exciting opportunities for software developers today lie in harnessing the power of networks. Applications
created today, whatever their intended scope or audience, will almost certainly run on machines linked by a global network of computing resources. The increasing importance of networks is placing new demands on existing tools and fueling the demand for a rapidly growing list of completely new kinds of applications. As users, we want software that works—consistently, anywhere, on any platform—and that plays well with other applications. We want dynamic applications that take advantage of a connected world, capable of accessing disparate and distributed information sources. We want truly distributed software that can be extended and upgraded seamlessly. We want intelligent applications that can roam the cloud for us, ferreting out information and serving as electronic emissaries. We have known for some time what kind of software we want, but it is really only in the past few years that we have begun to get it. The problem, historically, has been that the tools for building these applications have fallen short. The requirements of speed and portability have been, for the most part, mutually exclusive, and security has been largely ignored or misunderstood. In the past, truly portable languages were bulky, interpreted, and slow. These languages were popular as much for their high-level functionality as for their portability. Fast languages usually provided speed by binding themselves to particular platforms, so they met the portability requirement only halfway. There were even a few languages that nudged programmers to write better, more secure code, but they were primarily offshoots of the portable languages and suffered from the same problems. Java is a modern language that addresses all three of these fronts: portability, speed, and security. This is why it remains a dominant language in the world of programming almost three decades after its introduction. Some Background The Java programming language was designed to be a machine-independent programming language that is both safe enough to traverse networks and powerful enough to replace native executable code. Java addresses the
issues raised here and played a starring role in the growth of the internet, leading to where we are today. Java has become the premier platform for web-based applications and web services. These applications use technologies such as the Java Servlet API, Java Web Services, and many popular open source and commercial Java application servers and frameworks. Java’s portability and speed make it the platform of choice for modern business applications. Java servers running on open source Linux platforms are at the heart of the business and financial world today. Initially, most of the enthusiasm for Java centered on its capabilities for building embedded applications for the web, called applets. But in the early days, applets and other client-side graphical user interfaces (GUIs) written in Java were limited. Today, Java has Swing, a sophisticated toolkit for building GUIs. This development allowed Java to become a viable platform for developing traditional client-side application software. A few high- profile projects including IDEs and data visualization tools still use Java, although many other contenders have entered this crowded field. JavaScript (“no relation”) and its vast ecosystem have replaced applets outright. The applet API was deprecated fairly early and was removed in Java 26. This book will show you how to use Java to accomplish real-world programming tasks. In the coming chapters we’ll introduce you to a wide selection of Java features, including text processing, networking, file handling, and several of Java’s functional programming features. Java’s Origins The seeds of Java were planted in 1990 by Sun Microsystems patriarch and chief researcher Bill Joy. At the time, Sun was competing in a relatively small workstation market, while Microsoft was beginning its domination of the more mainstream, Intel-based PC world. When Sun missed the boat on the PC revolution, Joy retreated to Aspen, Colorado, to work on advanced research. He was committed to the idea of accomplishing complex tasks with simple software and founded the aptly named Sun Aspen Smallworks.
Of the original members of the small team of programmers Joy assembled in Aspen, James Gosling will be remembered as the father of Java. Gosling first made a name for himself in the early 1980s as the author of Gosling Emacs, the first version of the popular Emacs editor that was written in C and ran under Unix. Gosling Emacs was soon eclipsed by a free version, GNU Emacs, written by Emacs’s original designer. By that time, Gosling had moved on to design Sun’s Network extensible Window System (NeWS), which briefly contended with the X Window System for control of the Unix GUI desktop in 1987. Although some people would argue that NeWS was superior to X, NeWS lost because Sun kept it proprietary and didn’t publish source code, while the primary developers of X formed the X Consortium and took the opposite approach. Designing NeWS taught Gosling the power of integrating an expressive language with a network-aware windowing GUI. It also taught Sun that the internet programming community will ultimately refuse to accept proprietary standards, no matter how good they may be. NeWS’s failure sowed the seeds of Java’s licensing scheme and open (if not quite “open source”) code. Gosling brought what he had learned to Bill Joy’s nascent Aspen project. In 1992, work on the project led to the founding of the Sun subsidiary FirstPerson, Inc. Its mission was to lead Sun into the world of consumer electronics. The FirstPerson team worked on developing software for information appliances, such as cellular phones and personal digital assistants (PDAs). The goal was to enable the transfer of information and real-time applications over cheap infrared and traditional packet-based networks. Memory and bandwidth limitations dictated small, efficient code. The nature of the applications also demanded they be safe and robust. Gosling and his teammates began programming in C++, but they soon found themselves confounded by a language that was too complex, unwieldy, and insecure for the task. They decided to start from scratch, and Gosling began working on something he dubbed “C++ minus minus.” With the foundering of the Apple Newton (Apple’s earliest handheld computer), it became apparent that the PDA’s ship had not yet come in, so
Sun shifted FirstPerson’s efforts to interactive TV (ITV). The programming language of choice for ITV set-top boxes was to be the near ancestor of Java, a language called Oak. Even with its elegance and ability to provide safe interactivity, Oak could not salvage the lost cause of ITV. Customers didn’t want it, and Sun soon abandoned the concept. At that time, Joy and Gosling got together to decide on a new strategy for their innovative language. It was 1993, and the explosion of interest in the web presented a new opportunity. Oak was small, safe, architecture independent, and object-oriented. As it happens, these are also some of the requirements for a universal, internet-savvy programming language. Sun quickly changed focus, and, with a little retooling, Oak became Java. Growing Up It wouldn’t be an overstatement to say that Java (and its developer-focused bundle, the Java Development Kit, or JDK) caught on like wildfire. Even before its first official release, when Java was still a nonproduct, nearly every major industry player jumped on the Java bandwagon. Java licensees included Microsoft, Intel, IBM, and virtually all major hardware and software vendors. However, even with all this support, Java took a lot of knocks and experienced some growing pains during its first few years. A series of breach of contract and antitrust lawsuits between Sun and Microsoft over the distribution of Java and its use in Internet Explorer hampered its deployment on the world’s most common desktop operating system— Windows. Microsoft’s involvement with Java also became one focus of a larger federal lawsuit over serious anticompetitive practices at the company. Court testimony revealed the software giant had attempted to undermine Java by introducing incompatibilities in its version of the language. Meanwhile, Microsoft introduced its own Java-derived language called C# (C-sharp) as part of its .NET initiative and dropped Java from inclusion in Windows. C# has gone on to become a very good language in its own right, enjoying more innovation in recent years than has Java.
But Java continues to spread on a wide variety of platforms. As we begin looking at the Java architecture, you’ll see that much of what is exciting about Java comes from the self-contained virtual machine environment in which Java applications run. Java was carefully designed so that this supporting architecture can be implemented either in software, for existing computer platforms, or in customized hardware. Hardware implementations of Java are used in some smart cards and other embedded systems. You can even buy “wearable” devices, such as rings and dog tags, that have Java interpreters embedded in them. Software implementations of Java are available for all modern computer platforms, right down to portable computing devices. Today, an offshoot of the Java platform is the basis for Google’s Android operating system, which powers billions of phones and other mobile devices. In 2010, Oracle Corporation bought Sun Microsystems and became the steward of the Java language. In a somewhat rocky start to its tenure, Oracle sued Google over its use of the Java language in Android and lost. In July 2011, Oracle released Java Standard Edition 7,1 a significant Java release that included a new I/O package. In 2017, Java 9 introduced modules to address some long-standing issues with the way Java applications were compiled, distributed, and executed. Java 9 also kicked off a rapid update process leading to some Java versions being designated “long-term support” and the rest as standard, short-term versions. (More on these and other versions in “A Java Road Map”.) Oracle continues to lead Java development; however, it has also bifurcated the Java world by moving the main Java deployment environment to a costly commercial license, while offering a free subsidiary OpenJDK option that retains the accessibility many developers love and expect. A Virtual Machine Before we get much farther, it’s useful to know a bit more about the environment Java needs to do its magic. It’s OK if you don’t understand everything we touch on in these next sections. Any unfamiliar term you
might see will get its due in later chapters. We just want to provide you with an overview of Java’s ecosystem. At the core of that ecosystem is the Java Virtual Machine (JVM). Java is both a compiled and an interpreted language. Java source code is turned into simple binary instructions, much like ordinary microprocessor machine code. However, whereas C or C++ source is reduced to native instructions for a particular model of processor, Java source is compiled into a universal format—instructions for the virtual machine known as bytecode. Java bytecode is executed by a Java runtime interpreter. The runtime system performs all the normal activities of a hardware processor, but it does so in a safe, virtual environment. It executes a stack-based instruction set and manages memory like an operating system. It creates and manipulates primitive data types and loads and invokes newly referenced blocks of code. Most importantly, it does all this in accordance with a strictly defined open specification that can be implemented by anyone who wants to produce a Java-compliant virtual machine. Together, the virtual machine and language definition provide a complete specification. There are no features of the base Java language left undefined or implementation dependent. For example, Java specifies the sizes and mathematical properties of all its primitive data types rather than leaving it up to the platform implementation. The Java interpreter is relatively lightweight and small; it can be implemented in whatever form is desirable for a particular platform. The interpreter may be run as a separate application or it can be embedded in another piece of software, such as a web browser. Put together, this means that Java code is implicitly portable. The same Java application bytecode can run on any platform that provides a Java runtime environment, as shown in Figure 1-1. You don’t have to produce alternative versions of your application for different platforms, and you don’t have to distribute source code to end users.
Figure 1-1. The Java runtime environment The fundamental unit of Java code is the class. As in other object-oriented languages, classes are small, modular application components that hold executable code and data. Compiled Java classes are distributed in a universal binary format that contains Java bytecode and other class information. Classes can be maintained discretely and stored in files or archives locally or on a network server. The virtual machine locates and loads classes at runtime as they are needed by an application. In addition to the platform-specific runtime system, Java has a number of fundamental classes that contain architecture-dependent methods. These
native methods serve as the gateway between the Java virtual machine and the real world. They are implemented in a natively compiled language on the host platform and provide low-level access to resources such as the network, the windowing system, and the host filesystem. The vast majority of Java, however, is written in Java itself—bootstrapped from these basic parts—and is therefore portable. This includes important Java tools such as the Java compiler also written in Java and therefore available on all Java platforms in exactly the same way without porting. Historically, interpreters have been considered slow, but Java is not a traditional interpreted language. In addition to compiling source code down to portable bytecode, Java has also been carefully designed so that software implementations of the runtime system can further optimize their performance by compiling bytecode to native machine code on the fly. This is called dynamic or just-in-time (JIT) compilation. With JIT compilation, Java code can execute as fast as native code and maintain its transportability and security. This JIT feature is an often misunderstood point among those who want to compare language performance. There is only one intrinsic performance penalty that compiled Java code suffers at runtime for the sake of security and virtual machine design— array bounds checking. Everything else can be optimized to native code just as it can with a statically compiled language. Going beyond that, the Java language includes more structural information than many other languages, providing for more types of optimizations. Also remember that these optimizations can be made at runtime, taking into account the actual application behavior and characteristics. What can be done at compile time that can’t be done better at runtime? Well, there is a trade-off: time. The problem with a traditional JIT compilation is that optimizing code takes time. While a JIT compiler can produce decent results, it may suffer significant latency when the application starts up. This is generally not a problem for long-running server-side applications, but it is a serious problem for client-side software and applications that run on smaller devices with limited capabilities. To address this, Java’s compiler
technology, called HotSpot, uses a trick called adaptive compilation. If you look at what programs actually spend their time doing, it turns out that they spend almost all their time executing a relatively small part of the code again and again. The chunk of code that is executed repeatedly may be only a small fraction of the total program, but its behavior determines the program’s overall performance. Adaptive compilation allows the Java runtime to take advantage of new kinds of optimizations that simply can’t be done in a statically compiled language, hence the claim that Java code can run faster than C/C++ in some cases. To take advantage of this adaptive ability, HotSpot starts out as a normal Java bytecode interpreter, but with a difference: it measures (profiles) the code as it is executing to see what parts are being executed repeatedly. Once it knows which parts of the code are crucial to performance, HotSpot compiles those sections into optimal native machine code. Since it compiles only a small portion of the program into machine code, it can afford to take the time necessary to optimize those portions. The rest of the program may not need to be compiled at all—just interpreted—saving memory and time. In fact, the Java VM can run in one of two modes: client and server, which determine whether it emphasizes quick startup time and memory conservation or flat-out performance. As of Java 9, you can also put ahead- of-time (AOT) compilation to use if minimizing your application startup time is really important. A natural question to ask at this point is, why throw away all this good profiling information each time an application shuts down? Well, Sun partially broached this topic with the release of Java 5.0 through the use of shared read-only classes that are stored persistently in an optimized form. This significantly reduced both the startup time and overhead of running many Java applications on a given machine. The technology for doing this is complex, but the idea is simple: optimize the parts of the program that need to go fast, and don’t worry about the rest. Of course, “the rest” does contain code that could be further optimized. In 2022, OpenJDK’s Project Leyden kicked off with the intention of further reducing the startup time, minimizing the large size of Java applications,
and reducing the time it takes for all of the previously mentioned optimizations to take full effect. The mechanisms proposed by Project Leyden are fairly complex, so we won’t be discussing them in this book. But we wanted to highlight the constant work going into developing and improving Java and its ecosystem. More than 30 years after its debut, Java remains a modern language. Java Compared with Other Languages Java’s developers drew on many years of programming experience with other languages in their choice of features. It is worth taking a moment to compare Java at a high level with some of those languages, both for those of you with other programming experience and for the newcomers who need to put things in context. This book does expect you to have some programming experience,2 but that experience can come from any programming language. You may have heard that Java is a lot like C or C++, but that’s really not true except at a superficial level. 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 (such as a virtual machine, bytecode, and a sandbox), the platforms don’t differ substantially in terms of their speed or security characteristics. C# is more or less as portable as Java. 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. If you move from one to the other, the majority of your time will go towards learning their respective 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 that a programmer can hold all of its capabilities in their 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 portability for functionality; Java, in turn, initially traded speed for portability. Java addresses security issues that C does not, although in modern systems, many of those concerns are now addressed in the operating system and hardware. Scripting languages such as Perl, Python, and Ruby remain 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 Tcl (popular back when Java was being developed) also help programmers accomplish specific tasks, such as quickly creating graphical interfaces, that more general-purpose languages find unwieldy. Scripting languages are also highly portable, albeit at the source code level. Not to be confused with Java, JavaScript is an object-oriented scripting language originally developed by Netscape for the web browser. It serves as a web browser resident language for dynamic, interactive, web-based applications. JavaScript takes its name from its integration with and similarities to Java, but the comparison really ends there. There are, however, significant applications of JavaScript outside of the browser, such as Node.js,3 and it continues to rise in popularity for developers in a variety
of fields. For more information on JavaScript, check out JavaScript: The Definitive Guide by David Flanagan (O’Reilly). The problem with scripting languages is that they are often casual about program structure and data typing. They usually 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 slow compared to compiled applications. Advocates of individual scripting languages would take issue with some of these generalizations, and no doubt they’d be right in some cases! Scripting languages have improved in recent years—especially JavaScript and Python, which both have an enormous amount of ongoing research poured into their performance and security. But the fundamental trade-off is undeniable: scripting languages were born as loose, less-structured alternatives to systems programming languages, and they are generally not ideal for large or complex projects. Java offers some of the essential advantages of a scripting language, yet has many added benefits of a lower-level language. Java has a powerful regular expression package that competes with Perl for working with text. It also has 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 developing 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. Maven Central is an external resource with an enormous range of libraries and packages that can be quickly bundled into your environment to help you tackle all manner of new programming problems. 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 developing 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. Throughout the rest of this chapter, we’ll present a bird’s-eye view of the Java language. We’ll explain what’s new and what’s not-so-new about Java and why. Safety of Design Java is designed to be a “safe” language. But what do we mean by safe? Safe from what or whom? The Java security features that attract the most attention are those that make possible new types of dynamically portable software. Java provides several layers of protection from dangerously flawed code as well as more mischievous things such as viruses and Trojan horses. In the next section, we’ll take a look at how the Java virtual machine architecture assesses the safety of code before it’s run and how the Java class loader (the bytecode loading mechanism of the Java interpreter) builds a wall around untrusted classes. These features provide the foundation for high-level security policies that can allow or disallow various kinds of activities on an application-by-application basis.
In this section, though, we’ll look at some general features of the Java programming language. Perhaps more important than the specific security features, although often overlooked in the security din, is the safety that Java provides by addressing common design and programming problems. Java is intended to be as safe as possible from the simple mistakes programmers make ourselves, as well as those we inherit from legacy software. The goal with Java has been to keep the language simple, provide tools that have demonstrated their usefulness, and let users build more complicated facilities on top of the language when needed. Simplify, Simplify, Simplify… With Java, simplicity rules. Since Java started with a clean slate, it avoided features that have proved messy or controversial in other languages. For example, Java doesn’t allow programmer-defined operator overloading (which, in some languages, allows programmers to redefine the meanings of basic symbols like + and –). Java doesn’t have a source code preprocessor, so it doesn’t have things like macros, #define statements, or conditional source compilation. These constructs exist in other languages primarily to support platform dependencies, so in that sense, they should not be needed in Java. Conditional compilation is also commonly used for debugging, but Java’s sophisticated runtime optimizations and features such as assertions solve the problem more elegantly.4 Java provides a well-defined package structure for organizing class files. The package system allows the compiler to handle some of the functionality of the traditional make utility (a tool for building executables from source code). The compiler can also work with compiled Java classes directly because all type information is preserved; there is no need for extraneous source “header” files, as in C/C++. All this means that Java code requires less context to read. Indeed, you may sometimes find it faster to look at the Java source code than to refer to class documentation. Java also takes a different approach to some structural features that have been troublesome in other languages. For example, Java supports only a