Java A Beginner's Guide, 6th Edition (PDF)

Java_A_Beginners_Guide_6th_Edi

User Manual: Pdf

Open the PDF directly: View PDF PDF.
Page Count: 1153

DownloadJava A Beginner's Guide, 6th Edition (PDF)
Open PDF In BrowserView PDF
®

The
Complete
Reference

™

Java
Eighth Edition

About the Author
Herbert Schildt is a leading authority on the Java, C++, C, and C# languages.
His programming books have sold millions of copies worldwide and have been
translated into all major foreign languages. He is the author of numerous
books on Java, including Java: A Beginner’s Guide, Herb Schildt’s Java Programming
Cookbook, Swing: A Beginner’s Guide, and The Art of Java. Among his other
bestsellers are C++: The Complete Reference™, C#: The Complete Reference™, and
C: The Complete Reference™. Although interested in all facets of computing, his
primary focus is computer languages, including compilers, interpreters, and
robotic control languages. He also has an active interest in the standardization
of languages. Schildt holds both graduate and undergraduate degrees from
the University of Illinois. He can be reached at his consulting office at (217)
586-4683. His web site is www.HerbSchildt.com.

About the Technical Editor
Dr. Danny Coward has been a contributor to the Java Platforms since 1997. He
was a founding member of the Java EE group while at Sun. He has served as a
member of the Java Community Process Executive Committee and has been a
leading contributor to all editions of the Java Platform—Java SE, Java ME, and
Java EE—and established the original JavaFX team.

®

The
Complete
Reference™

Java
Eighth Edition

Herbert Schildt

New York Chicago San Francisco
Lisbon London Madrid Mexico City
Milan New Delhi San Juan
Seoul Singapore Sydney Toronto

Copyright © 2011 by The McGraw-Hill Companies. All rights reserved. All rights reserved. Except as permitted under the United States
Copyright Act of 1976, no part of this publication may be reproduced or distributed in any form or by any means, or stored in a database
or retrieval system, without the prior written permission of the publisher.
ISBN: 978-0-07-160631-8
MHID: 0-07-160631-9
The material in this eBook also appears in the print version of this title: ISBN: 978-0-07-160630-1,
MHID: 0-07-160630-0.
All trademarks are trademarks of their respective owners. Rather than put a trademark symbol after every occurrence of a trademarked
name, we use names in an editorial fashion only, and to the benefit of the trademark owner, with no intention of infringement of the
trademark. Where such designations appear in this book, they have been printed with initial caps.
McGraw-Hill eBooks are available at special quantity discounts to use as premiums and sales promotions, or for use in corporate training
programs. To contact a representative please e-mail us at bulksales@mcgraw-hill.com.
Trademarks: McGraw-Hill, the McGraw-Hill Publishing logo, Complete Reference™, and related trade dress are trademarks or
registered trademarks of The McGraw-Hill Companies and/or its affiliates in the United States and other countries and may not be used
without written permission. All other trademarks are the property of their respective owners. The McGraw-Hill Companies is not
associated with any product or vendor mentioned in this book.
Information has been obtained by Publisher from sources believed to be reliable. However, because of the possibility of human or
mechanical error by our sources, Publisher, or others, Publisher does not guarantee to the accuracy, adequacy, or completeness of
any information included in this work and is not responsible for any errors or omissions or the results obtained from the use of such
information.
Oracle Corporation does not make any representations or warranties as to the accuracy, adequacy, or completeness of any
information contained in this Work, and is not responsible for any errors or omissions.
TERMS OF USE
This is a copyrighted work and The McGraw-Hill Companies, Inc. (“McGrawHill”) and its licensors reserve all rights in and to the
work. Use of this work is subject to these terms. Except as permitted under the Copyright Act of 1976 and the right to store and retrieve
one copy of the work, you may not decompile, disassemble, reverse engineer, reproduce, modify, create derivative works based upon,
transmit, distribute, disseminate, sell, publish or sublicense the work or any part of it without McGraw-Hill’s prior consent. You may use
the work for your own noncommercial and personal use; any other use of the work is strictly prohibited. Your right to use the work may
be terminated if you fail to comply with these terms.
THE WORK IS PROVIDED “AS IS.” McGRAW-HILL AND ITS LICENSORS MAKE NO GUARANTEES OR WARRANTIES AS
TO THE ACCURACY, ADEQUACY OR COMPLETENESS OF OR RESULTS TO BE OBTAINED FROM USING THE WORK,
INCLUDING ANY INFORMATION THAT CAN BE ACCESSED THROUGH THE WORK VIA HYPERLINK OR OTHERWISE,
AND EXPRESSLY DISCLAIM ANY WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. McGraw-Hill and its licensors do not
warrant or guarantee that the functions contained in the work will meet your requirements or that its operation will be uninterrupted or
error free. Neither McGraw-Hill nor its licensors shall be liable to you or anyone else for any inaccuracy, error or omission, regardless
of cause, in the work or for any damages resulting therefrom. McGraw-Hill has no responsibility for the content of any information
accessed through the work. Under no circumstances shall McGraw-Hill and/or its licensors be liable for any indirect, incidental, special,
punitive, consequential or similar damages that result from the use of or inability to use the work, even if any of them has been advised
of the possibility of such damages. This limitation of liability shall apply to any claim or cause whatsoever whether such claim or cause
arises in contract, tort or otherwise.

FREE SUBSCRIPTION
TO ORACLE MAGAZINE

GET
YOUR

Oracle Magazine is essential gear for today’s information technology professionals.
Stay informed and increase your productivity with every issue of Oracle Magazine.
Inside each free bimonthly issue you’ll get:

t 6
 QUPEBUFJOGPSNBUJPOPO0SBDMF%BUBCBTF 0SBDMF"QQMJDBUJPO4FSWFS 
8FCEFWFMPQNFOU FOUFSQSJTFHSJEDPNQVUJOH EBUBCBTFUFDIOPMPHZ 
BOECVTJOFTTUSFOET
t 5IJSEQBSUZOFXTBOEBOOPVODFNFOUT
t 5FDIOJDBMBSUJDMFTPO0SBDMFBOEQBSUOFSQSPEVDUT UFDIOPMPHJFT 
BOEPQFSBUJOHFOWJSPONFOUT
t %FWFMPQNFOUBOEBENJOJTUSBUJPOUJQT
t 3FBMXPSMEDVTUPNFSTUPSJFT

If there are other Oracle users at
your location who would like to
receive their own subscription to
Oracle Magazine, please photocopy this form and pass it along.

Three easy ways to subscribe:
1 Web

7JTJUPVS8FCTJUFBU oracle.com/oraclemagazine
:PVMMGJOEBTVCTDSJQUJPOGPSNUIFSF QMVTNVDINPSF
2 Fax

$PNQMFUFUIFRVFTUJPOOBJSFPOUIFCBDLPGUIJTDBSE
BOEGBYUIFRVFTUJPOOBJSFTJEFPOMZUP+1.847.763.9638
3 Mail

$PNQMFUFUIFRVFTUJPOOBJSFPOUIFCBDLPGUIJTDBSE
BOENBJMJUUP P.O. Box 1263, Skokie, IL 60076-8263

Copyright © 2008, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.

Want your own FREE subscription?
To receive a free subscription to Oracle Magazine, you must fill out the entire card, sign it, and date
it (incomplete cards cannot be processed or acknowledged). You can also fax your application to
+1.847.763.9638. Or subscribe at our Web site at oracle.com/oraclemagazine
Yes, please send me a FREE subscription Oracle Magazine.
From time to time, Oracle Publishing allows our partners
exclusive access to our e-mail addresses for special promotions and announcements. To be included in this program,
please check this circle. If you do not wish to be included, you
will only receive notices about your subscription via e-mail.
Oracle Publishing allows sharing of our postal mailing list with
selected third parties. If you prefer your mailing address not to
be included in this program, please check this circle.
If at any time you would like to be removed from either mailing list, please contact
Customer Service at +1.847.763.9635 or send an e-mail to oracle@halldata.com.
If you opt in to the sharing of information, Oracle may also provide you with
e-mail related to Oracle products, services, and events. If you want to completely
unsubscribe from any e-mail communication from Oracle, please send an e-mail to:
unsubscribe@oracle-mail.com with the following in the subject line: REMOVE [your
e-mail address]. For complete information on Oracle Publishing’s privacy practices,
please visit oracle.com/html/privacy/html

No.

x
signature (required)

date

name

title

company

e-mail address

street/p.o. box
city/state/zip or postal code

telephone

country

fax

Would you like to receive your free subscription in digital format instead of print if it becomes available?

Yes

No

YOU MUST ANSWER ALL 10 QUESTIONS BELOW.
1

08014004

2

WHAT IS THE PRIMARY BUSINESS ACTIVITY
OF YOUR FIRM AT THIS LOCATION? (check
one only)
o
o
o
o
o
o
o

01
02
03
04
05
06
07

o
o
o
o
o
o
o
o
o
o
o
o
o
o
o
o
o
o

08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
98

Aerospace and Defense Manufacturing
Application Service Provider
Automotive Manufacturing
Chemicals
Media and Entertainment
Construction/Engineering
Consumer Sector/Consumer Packaged
Goods
Education
Financial Services/Insurance
Health Care
High Technology Manufacturing, OEM
Industrial Manufacturing
Independent Software Vendor
Life Sciences (biotech, pharmaceuticals)
Natural Resources
Oil and Gas
Professional Services
Public Sector (government)
Research
Retail/Wholesale/Distribution
Systems Integrator, VAR/VAD
Telecommunications
Travel and Transportation
Utilities (electric, gas, sanitation, water)
Other Business and Services _________

3

4

WHICH OF THE FOLLOWING BEST DESCRIBES
YOUR PRIMARY JOB FUNCTION?
(check one only)
CORPORATE MANAGEMENT/STAFF
o 01 Executive Management (President, Chair,
CEO, CFO, Owner, Partner, Principal)
o 02 Finance/Administrative Management
(VP/Director/ Manager/Controller,
Purchasing, Administration)
o 03 Sales/Marketing Management
(VP/Director/Manager)
o 04 Computer Systems/Operations
Management
(CIO/VP/Director/Manager MIS/IS/IT, Ops)
IS/IT STAFF
o 05 Application Development/Programming
Management
o 06 Application Development/Programming
Staff
o 07 Consulting
o 08 DBA/Systems Administrator
o 09 Education/Training
o 10 Technical Support Director/Manager
o 11 Other Technical Management/Staff
o 98 Other

5

WHAT IS YOUR CURRENT PRIMARY OPERATING
PLATFORM (check all that apply)
o
o
o
o
o
o
o
o
o
o
o
o
o
o
o
o
o

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
98

99

o

Digital Equipment Corp UNIX/VAX/VMS
HP UNIX
IBM AIX
IBM UNIX
Linux (Red Hat)
Linux (SUSE)
Linux (Oracle Enterprise)
Linux (other)
Macintosh
MVS
Netware
Network Computing
SCO UNIX
Sun Solaris/SunOS
Windows
Other UNIX
Other
None of the Above

6

01
02
03
04
05
06
07

99

o

Hardware
Business Applications (ERP, CRM, etc.)
Application Development Tools
Database Products
Internet or Intranet Products
Other Software
Middleware Products
None of the Above

7

HARDWARE
o 15 Macintosh
o 16 Mainframe
o 17 Massively Parallel Processing

SERVICES
o 24 Consulting
o 25 Education/Training
o 26 Maintenance
o 27 Online Database
o 28 Support
o 29 Technology-Based Training
o 30 Other
99 o None of the Above

o
o

8

01
02
03
04
05
06

More than 25,000 Employees
10,001 to 25,000 Employees
5,001 to 10,000 Employees
1,001 to 5,000 Employees
101 to 1,000 Employees
Fewer than 100 Employees

DURING THE NEXT 12 MONTHS, HOW MUCH
DO YOU ANTICIPATE YOUR ORGANIZATION
WILL SPEND ON COMPUTER HARDWARE,
SOFTWARE, PERIPHERALS, AND SERVICES FOR
YOUR LOCATION? (check one only)
01
02
03
04
05
06

Less than $10,000
$10,000 to $49,999
$50,000 to $99,999
$100,000 to $499,999
$500,000 to $999,999
$1,000,000 and Over

WHAT IS YOUR COMPANY’S YEARLY SALES
REVENUE? (check one only)
o
o
o
o
o

9

18
19
20
21
22
23

WHAT IS YOUR COMPANY’S SIZE?
(check one only)

o
o
o
o
o
o

IN YOUR JOB, DO YOU USE OR PLAN TO PURCHASE ANY OF THE FOLLOWING PRODUCTS?
(check all that apply)
SOFTWARE
o 01 CAD/CAE/CAM
o 02 Collaboration Software
o 03 Communications
o 04 Database Management
o 05 File Management
o 06 Finance
o 07 Java
o 08 Multimedia Authoring
o 09 Networking
o 10 Programming
o 11 Project Management
o 12 Scientific and Engineering
o 13 Systems Management
o 14 Workflow

o
o
o
o
o
o

o
o
o
o
o
o

DO YOU EVALUATE, SPECIFY, RECOMMEND,
OR AUTHORIZE THE PURCHASE OF ANY OF
THE FOLLOWING? (check all that apply)
o
o
o
o
o
o
o

Minicomputer
Intel x86(32)
Intel x86(64)
Network Computer
Symmetric Multiprocessing
Workstation Services

o
o
o
o
o
o

01
02
03
04
05

$500, 000, 000 and above
$100, 000, 000 to $500, 000, 000
$50, 000, 000 to $100, 000, 000
$5, 000, 000 to $50, 000, 000
$1, 000, 000 to $5, 000, 000

WHAT LANGUAGES AND FRAMEWORKS DO
YOU USE? (check all that apply)
o
o
o
o

01
02
03
04

Ajax
C
C++
C#

o
o
o
o

13
14
15
16

Python
Ruby/Rails
Spring
Struts

10

05 Hibernate
06 J++/J#
07 Java
08 JSP
09 .NET
10 Perl
11 PHP
12 PL/SQL

o 17 SQL
o 18 Visual Basic
o 98 Other

WHAT ORACLE PRODUCTS ARE IN USE AT YOUR
SITE? (check all that apply)
ORACLE DATABASE
o 01 Oracle Database 11g
o 02 Oracle Database 10 g
o 03 Oracle9 i Database
o 04 Oracle Embedded Database
(Oracle Lite, Times Ten, Berkeley DB)
o 05 Other Oracle Database Release
ORACLE FUSION MIDDLEWARE
o 06 Oracle Application Server
o 07 Oracle Portal
o 08 Oracle Enterprise Manager
o 09 Oracle BPEL Process Manager
o 10 Oracle Identity Management
o 11 Oracle SOA Suite
o 12 Oracle Data Hubs
ORACLE DEVELOPMENT TOOLS
o 13 Oracle JDeveloper
o 14 Oracle Forms
o 15 Oracle Reports
o 16 Oracle Designer
o 17 Oracle Discoverer
o 18 Oracle BI Beans
o 19 Oracle Warehouse Builder
o 20 Oracle WebCenter
o 21 Oracle Application Express
ORACLE APPLICATIONS
o 22 Oracle E-Business Suite
o 23 PeopleSoft Enterprise
o 24 JD Edwards EnterpriseOne
o 25 JD Edwards World
o 26 Oracle Fusion
o 27 Hyperion
o 28 Siebel CRM
ORACLE SERVICES
o 28 Oracle E-Business Suite On Demand
o 29 Oracle Technology On Demand
o 30 Siebel CRM On Demand
o 31 Oracle Consulting
o 32 Oracle Education
o 33 Oracle Support
o 98 Other
99 o None of the Above

Contents at a Glance
Part I
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Part II
15
16
17
18
19
20
21
22
23
24
25
26
27
28

The Java Language
The History and Evolution of Java
An Overview of Java
Data Types, Variables, and Arrays
Operators
Control Statements
Introducing Classes
A Closer Look at Methods and Classes
Inheritance
Packages and Interfaces
Exception Handling
Multithreaded Programming
Enumerations, Autoboxing, and Annotations
(Metadata)
I/O, Applets, and Other Topics
Generics

3
17
35
61
81
109
129
161
187
207
227
259
289
325

The Java Library
String Handling
Exploring java.lang
java.util Part 1: The Collections Framework
java.util Part 2: More Utility Classes
Input/Output: Exploring java.io
Exploring NIO
Networking
The Applet Class
Event Handling
Introducing the AWT: Working with Windows,
Graphics, and Text
Using AWT Controls, Layout Managers, and Menus
Images
The Concurrency Utilities
Regular Expressions and Other Packages

371
397
453
525
581
629
667
687
707
735
773
829
861
909

v

vi

Java: The Complete Reference, Eighth Edition

Part III
29
30
31
32

Part IV
33
34
Appendix

Software Development Using Java
Java Beans
Introducing Swing
Exploring Swing
Servlets

933
945
965
993

Applying Java
Financial Applets and Servlets
Creating a Download Manager in Java
Using Java’s Documentation Comments

1019
1053
1079

Index

1087

Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xxix

Part I
Chapter 1

Chapter 2

The Java Language
The History and Evolution of Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3
Java’s Lineage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
The Birth of Modern Programming: C . . . . . . . . . . . . . . . . . . . . . . . . 4
C++: The Next Step. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
The Stage Is Set for Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
The Creation of Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
The C# Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
How Java Changed the Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Java Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Portability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Java’s Magic: The Bytecode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Servlets: Java on the Server Side . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
The Java Buzzwords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Object-Oriented . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Robust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Multithreaded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Architecture-Neutral. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Interpreted and High Performance . . . . . . . . . . . . . . . . . . . . . . . . . 12
Distributed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Dynamic. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
The Evolution of Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Java SE 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
A Culture of Innovation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
An Overview of Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
Object-Oriented Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Two Paradigms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
The Three OOP Principles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
A First Simple Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Entering the Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

vii

viii

Java: The Complete Reference, Eighth Edition

Chapter 3

Compiling the Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
A Closer Look at the First Sample Program. . . . . . . . . . . . . . . . . . . 24
A Second Short Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Two Control Statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
The if Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
The for Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Using Blocks of Code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Lexical Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Whitespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Separators. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
The Java Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
The Java Class Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Data Types, Variables, and Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .35
Java Is a Strongly Typed Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
The Primitive Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
byte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
short . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
long. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Floating-Point Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
float. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
double. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Characters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Booleans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
A Closer Look at Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Integer Literals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Floating-Point Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Boolean Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Character Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
String Literals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Declaring a Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Dynamic Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
The Scope and Lifetime of Variables . . . . . . . . . . . . . . . . . . . . . . . . 45
Type Conversion and Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Java’s Automatic Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Casting Incompatible Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Automatic Type Promotion in Expressions . . . . . . . . . . . . . . . . . . . . . . . 49
The Type Promotion Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
One-Dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

Contents

Chapter 4

Chapter 5

Chapter 6

Multidimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Alternative Array Declaration Syntax . . . . . . . . . . . . . . . . . . . . . . . . 58
A Few Words About Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
A Note to C/C++ Programmers About Pointers . . . . . . . . . . . . . . . . . . . 59
Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .61
Arithmetic Operators. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
The Basic Arithmetic Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
The Modulus Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Arithmetic Compound Assignment Operators . . . . . . . . . . . . . . . . 63
Increment and Decrement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
The Bitwise Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
The Bitwise Logical Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
The Left Shift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
The Right Shift. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
The Unsigned Right Shift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Bitwise Operator Compound Assignments . . . . . . . . . . . . . . . . . . . 73
Relational Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Boolean Logical Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Short-Circuit Logical Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
The Assignment Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
The ? Operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Operator Precedence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Using Parentheses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Control Statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .81
Java’s Selection Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Iteration Statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
The For-Each Version of the for Loop . . . . . . . . . . . . . . . . . . . . . . . 97
Nested Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Jump Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Using break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Using continue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Introducing Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .109
Class Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
The General Form of a Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
A Simple Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Declaring Objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
A Closer Look at new. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Assigning Object Reference Variables . . . . . . . . . . . . . . . . . . . . . . . . . . 115
Introducing Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
Adding a Method to the Box Class . . . . . . . . . . . . . . . . . . . . . . . . . 116

ix

x

Java: The Complete Reference, Eighth Edition

Chapter 7

Chapter 8

Returning a Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Adding a Method That Takes Parameters . . . . . . . . . . . . . . . . . . . 119
Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Parameterized Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
The this Keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Instance Variable Hiding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Garbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
The finalize( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
A Stack Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
A Closer Look at Methods and Classes . . . . . . . . . . . . . . . . . . . . . . . . .129
Overloading Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Overloading Constructors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Using Objects as Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
A Closer Look at Argument Passing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Returning Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Introducing Access Control. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Understanding static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Introducing final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Arrays Revisited . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Introducing Nested and Inner Classes . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Exploring the String Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Using Command-Line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
Varargs: Variable-Length Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Overloading Vararg Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Varargs and Ambiguity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .161
Inheritance Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Member Access and Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . 163
A More Practical Example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
A Superclass Variable Can Reference a Subclass Object . . . . . . . . 166
Using super. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
Using super to Call Superclass Constructors . . . . . . . . . . . . . . . . . 167
A Second Use for super. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
Creating a Multilevel Hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
When Constructors Are Called . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Method Overriding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Dynamic Method Dispatch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Why Overridden Methods? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Applying Method Overriding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Using Abstract Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Using final with Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
Using final to Prevent Overriding . . . . . . . . . . . . . . . . . . . . . . . . . 184
Using final to Prevent Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . 185
The Object Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

Contents

Chapter 9

Chapter 10

Chapter 11

Packages and Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .187
Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Defining a Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Finding Packages and CLASSPATH . . . . . . . . . . . . . . . . . . . . . . . . 188
A Short Package Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Access Protection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
An Access Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Importing Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Defining an Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Implementing Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Nested Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Applying Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
Variables in Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Interfaces Can Be Extended . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .207
Exception-Handling Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Exception Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Uncaught Exceptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Using try and catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
Displaying a Description of an Exception . . . . . . . . . . . . . . . . . . . 212
Multiple catch Clauses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Nested try Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
throw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
throws . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
finally. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Java’s Built-in Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Creating Your Own Exception Subclasses . . . . . . . . . . . . . . . . . . . . . . . 221
Chained Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Three New JDK 7 Exception Features . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Using Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Multithreaded Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .227
The Java Thread Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Thread Priorities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Synchronization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
The Thread Class and the Runnable Interface . . . . . . . . . . . . . . . 230
The Main Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Creating a Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
Implementing Runnable. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
Extending Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Choosing an Approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Creating Multiple Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Using isAlive( ) and join( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
Thread Priorities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240

xi

xii

Java: The Complete Reference, Eighth Edition

Chapter 12

Chapter 13

Synchronization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Using Synchronized Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
The synchronized Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
Interthread Communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Deadlock. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Suspending, Resuming, and Stopping Threads . . . . . . . . . . . . . . . . . . . 251
Suspending, Resuming, and Stopping Threads Using Java 1.1
and Earlier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
The Modern Way of Suspending, Resuming, and
Stopping Threads. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Obtaining A Thread’s State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
Using Multithreading. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Enumerations, Autoboxing, and Annotations (Metadata) . . . . . . . . . . .259
Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Enumeration Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
The values( ) and valueOf( ) Methods. . . . . . . . . . . . . . . . . . . . . . 262
Java Enumerations Are Class Types . . . . . . . . . . . . . . . . . . . . . . . . 263
Enumerations Inherit Enum. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
Another Enumeration Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
Type Wrappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Character . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
The Numeric Type Wrappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Autoboxing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Autoboxing and Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
Autoboxing/Unboxing Occurs in Expressions . . . . . . . . . . . . . . . 272
Autoboxing/Unboxing Boolean and Character Values . . . . . . . . 274
Autoboxing/Unboxing Helps Prevent Errors . . . . . . . . . . . . . . . . 274
A Word of Warning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
Annotations (Metadata) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
Annotation Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Specifying a Retention Policy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Obtaining Annotations at Run Time by Use of Reflection . . . . . . 277
The AnnotatedElement Interface . . . . . . . . . . . . . . . . . . . . . . . . . 282
Using Default Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
Marker Annotations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
Single-Member Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
The Built-In Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Some Restrictions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
I/O, Applets, and Other Topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .289
I/O Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Byte Streams and Character Streams . . . . . . . . . . . . . . . . . . . . . . . 290
The Predefined Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292

Contents

Chapter 14

Reading Console Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Reading Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Reading Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
Writing Console Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
The PrintWriter Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Reading and Writing Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
Automatically Closing a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
Applet Fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
The transient and volatile Modifiers. . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
Using instanceof. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
strictfp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
Native Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
Problems with Native Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Using assert. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Assertion Enabling and Disabling Options . . . . . . . . . . . . . . . . . . 319
Static Import. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
Invoking Overloaded Constructors Through this( ) . . . . . . . . . . . . . . . 321
Generics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .325
What Are Generics? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
A Simple Generics Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Generics Work Only with Objects. . . . . . . . . . . . . . . . . . . . . . . . . . 330
Generic Types Differ Based on Their Type Arguments. . . . . . . . . 330
How Generics Improve Type Safety . . . . . . . . . . . . . . . . . . . . . . . . 330
A Generic Class with Two Type Parameters . . . . . . . . . . . . . . . . . . . . . . 332
The General Form of a Generic Class. . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Bounded Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Using Wildcard Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Bounded Wildcards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
Creating a Generic Method. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
Generic Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
Generic Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
Raw Types and Legacy Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Generic Class Hierarchies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
Using a Generic Superclass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
A Generic Subclass. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Run-Time Type Comparisons Within a Generic Hierarchy . . . . . 355
Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
Overriding Methods in a Generic Class . . . . . . . . . . . . . . . . . . . . . 358
Type Inference with Generics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
Erasure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
Bridge Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Ambiguity Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Some Generic Restrictions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
Type Parameters Can’t Be Instantiated . . . . . . . . . . . . . . . . . . . . . 365

xiii

xiv

Java: The Complete Reference, Eighth Edition

Restrictions on Static Members. . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
Generic Array Restrictions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
Generic Exception Restriction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367

Part II
Chapter 15

The Java Library
String Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .371
The String Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
String Length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
Special String Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
String Literals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
String Concatenation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
String Concatenation with Other Data Types . . . . . . . . . . . . . . . . 375
String Conversion and toString( ) . . . . . . . . . . . . . . . . . . . . . . . . . 376
Character Extraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
charAt( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
getChars( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
getBytes( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
toCharArray( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
String Comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
equals( ) and equalsIgnoreCase( ). . . . . . . . . . . . . . . . . . . . . . . . . 378
regionMatches( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
startsWith( ) and endsWith( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
equals( ) Versus ==. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
compareTo( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
Searching Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
Modifying a String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
substring( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
concat( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
replace( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
trim( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
Data Conversion Using valueOf( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
Changing the Case of Characters Within a String . . . . . . . . . . . . . . . . . 387
Additional String Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
StringBuffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
StringBuffer Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
length( ) and capacity( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
ensureCapacity( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
setLength( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
charAt( ) and setCharAt( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
getChars( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
append( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
insert( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
reverse( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
delete( ) and deleteCharAt( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393

Contents

Chapter 16

replace( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
substring( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
Additional StringBuffer Methods . . . . . . . . . . . . . . . . . . . . . . . . . . 394
StringBuilder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
Exploring java.lang. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .397
Primitive Type Wrappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Double and Float . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Understanding isInfinite( ) and isNaN( ) . . . . . . . . . . . . . . . . . . . 402
Byte, Short, Integer, and Long . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
Character . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
Additions to Character for Unicode Code Point Support . . . . . . 414
Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
Void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
Runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
Memory Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Executing Other Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
ProcessBuilder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
Using currentTimeMillis( ) to Time Program Execution. . . . . . . 425
Using arraycopy( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
Environment Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
Using clone( ) and the Cloneable Interface . . . . . . . . . . . . . . . . . . . . . 427
Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
ClassLoader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Trigonometric Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Exponential Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
Rounding Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
Miscellaneous Math Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
StrictMath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
Compiler. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
Thread, ThreadGroup, and Runnable . . . . . . . . . . . . . . . . . . . . . . . . . . 436
The Runnable Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
ThreadGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
ThreadLocal and InheritableThreadLocal . . . . . . . . . . . . . . . . . . . . . . 444
Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
RuntimePermission . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
Throwable. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
SecurityManager. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
StackTraceElement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446

xv

xvi

Java: The Complete Reference, Eighth Edition

Chapter 17

Enum. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
ClassValue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
The CharSequence Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
The Comparable Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
The Appendable Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
The Iterable Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
The Readable Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
The AutoCloseable Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
The Thread.UncaughtExceptionHandler Interface . . . . . . . . . . . . . . . 450
The java.lang Subpackages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
java.lang.annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
java.lang.instrument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
java.lang.invoke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
java.lang.management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
java.lang.ref . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
java.lang.reflect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
java.util Part 1: The Collections Framework . . . . . . . . . . . . . . . . . . . . .453
Collections Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
JDK 5 Changed the Collections Framework. . . . . . . . . . . . . . . . . . . . . . 455
Generics Fundamentally Changed the Collections Framework . . 455
Autoboxing Facilitates the Use of Primitive Types . . . . . . . . . . . . 456
The For-Each Style for Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
The Collection Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
The Collection Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
The List Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
The Set Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
The SortedSet Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
The NavigableSet Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
The Queue Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
The Deque Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
The Collection Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
The ArrayList Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
The LinkedList Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
The HashSet Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
The LinkedHashSet Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
The TreeSet Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
The PriorityQueue Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
The ArrayDeque Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
The EnumSet Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
Accessing a Collection via an Iterator. . . . . . . . . . . . . . . . . . . . . . . . . . . 476
Using an Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
The For-Each Alternative to Iterators. . . . . . . . . . . . . . . . . . . . . . . 479
Storing User-Defined Classes in Collections. . . . . . . . . . . . . . . . . . . . . . 480
The RandomAccess Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482

Contents

Chapter 18

Working with Maps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
The Map Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
The Map Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
Comparators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
Using a Comparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
The Collection Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
Why Generic Collections? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
The Legacy Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
The Enumeration Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
Dictionary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
Hashtable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
Using store( ) and load( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
Parting Thoughts on Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
java.util Part 2: More Utility Classes . . . . . . . . . . . . . . . . . . . . . . . . . . .525
StringTokenizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
BitSet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
Calendar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
GregorianCalendar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
TimeZone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
SimpleTimeZone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
Locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
Observable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
The Observer Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
An Observer Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
Timer and TimerTask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 544
Currency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
Formatter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
The Formatter Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
The Formatter Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
Formatting Basics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
Formatting Strings and Characters. . . . . . . . . . . . . . . . . . . . . . . . . 551
Formatting Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
Formatting Time and Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
The %n and %% Specifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
Specifying a Minimum Field Width . . . . . . . . . . . . . . . . . . . . . . . . 555
Specifying Precision. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
Using the Format Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
Justifying Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
The Space, +, 0, and ( Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558

xvii

xviii

Java: The Complete Reference, Eighth Edition

Chapter 19

The Comma Flag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
The # Flag. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
The Uppercase Option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
Using an Argument Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
Closing a Formatter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
The Java printf( ) Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
Scanner. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
The Scanner Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
Scanning Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
Some Scanner Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567
Setting Delimiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
Other Scanner Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572
The ResourceBundle, ListResourceBundle, and
PropertyResourceBundle Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573
Miscellaneous Utility Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . 577
The java.util Subpackages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
java.util.concurrent, java.util.concurrent.atomic,
and java.util.concurrent.locks . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
java.util.jar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
java.util.logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
java.util.prefs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
java.util.regex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
java.util.spi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
java.util.zip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
Input/Output: Exploring java.io . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .581
The I/O Classes and Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
File. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
Using FilenameFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
The listFiles( ) Alternative. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587
Creating Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
The AutoCloseable, Closeable, and Flushable Interfaces . . . . . . . . . . . 588
I/O Exceptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
Two Ways to Close a Stream. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
The Stream Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
The Byte Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
InputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
OutputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
FileInputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
FileOutputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
ByteArrayInputStream. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
ByteArrayOutputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
Filtered Byte Streams. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599
Buffered Byte Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599
SequenceInputStream. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
PrintStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605

Contents

Chapter 20

Chapter 21

DataOutputStream and DataInputStream . . . . . . . . . . . . . . . . . . . 607
RandomAccessFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
The Character Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
Reader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
Writer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
FileReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
FileWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
CharArrayReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
CharArrayWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
BufferedReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
BufferedWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
PushbackReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
PrintWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
The Console Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
Serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
Serializable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
Externalizable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
ObjectOutput. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
ObjectOutputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
ObjectInput . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
ObjectInputStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
A Serialization Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
Stream Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
Exploring NIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .629
The NIO Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
NIO Fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
Buffers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
Channels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
Charsets and Selectors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
Enhancements Added to NIO by JDK 7 . . . . . . . . . . . . . . . . . . . . . . . . . 634
The Path Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634
The Files Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
The Paths Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
The File Attribute Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
The FileSystem, FileSystems, and FileStore Classes . . . . . . . . . . . . 640
Using the NIO System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
Use NIO for Channel-Based I/O . . . . . . . . . . . . . . . . . . . . . . . . . . 640
Use NIO for Stream-Based I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
Use NIO for Path and File System Operations . . . . . . . . . . . . . . . 652
Pre-JDK 7 Channel-Based Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
Read a File, Pre-JDK 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
Write to a File, Pre-JDK 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
Networking. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .667
Networking Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667
The Networking Classes and Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . 668

xix

xx

Java: The Complete Reference, Eighth Edition

Chapter 22

Chapter 23

InetAddress. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Factory Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Instance Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
Inet4Address and Inet6Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
TCP/IP Client Sockets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
URL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
URLConnection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
HttpURLConnection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
The URI Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
TCP/IP Server Sockets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
Datagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
DatagramSocket. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
DatagramPacket. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
A Datagram Example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
The Applet Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .687
Two Types of Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
Applet Basics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
The Applet Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
Applet Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
An Applet Skeleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691
Applet Initialization and Termination . . . . . . . . . . . . . . . . . . . . . . 692
Overriding update( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
Simple Applet Display Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
Requesting Repainting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
A Simple Banner Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
Using the Status Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
The HTML APPLET Tag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
Passing Parameters to Applets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
Improving the Banner Applet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702
getDocumentBase( ) and getCodeBase( ) . . . . . . . . . . . . . . . . . . . . . . . 704
AppletContext and showDocument( ) . . . . . . . . . . . . . . . . . . . . . . . . . . 704
The AudioClip Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
The AppletStub Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
Outputting to the Console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
Event Handling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .707
Two Event Handling Mechanisms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
The Delegation Event Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
Event Sources. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
Event Listeners. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
Event Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
The ActionEvent Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
The AdjustmentEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
The ComponentEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712

Contents

Chapter 24

The ContainerEvent Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
The FocusEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
The InputEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
The ItemEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714
The KeyEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
The MouseEvent Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
The MouseWheelEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
The TextEvent Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718
The WindowEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718
Sources of Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
Event Listener Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
The ActionListener Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
The AdjustmentListener Interface . . . . . . . . . . . . . . . . . . . . . . . . . 721
The ComponentListener Interface . . . . . . . . . . . . . . . . . . . . . . . . 721
The ContainerListener Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 721
The FocusListener Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
The ItemListener Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
The KeyListener Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
The MouseListener Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
The MouseMotionListener Interface . . . . . . . . . . . . . . . . . . . . . . . 722
The MouseWheelListener Interface. . . . . . . . . . . . . . . . . . . . . . . . 722
The TextListener Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
The WindowFocusListener Interface . . . . . . . . . . . . . . . . . . . . . . . 723
The WindowListener Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
Using the Delegation Event Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
Handling Mouse Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
Handling Keyboard Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
Adapter Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
Inner Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731
Anonymous Inner Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732
Introducing the AWT: Working with Windows, Graphics, and Text . . .735
AWT Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 736
Window Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 738
Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 738
Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
Window. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
Frame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
Canvas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
Working with Frame Windows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
Setting the Window’s Dimensions . . . . . . . . . . . . . . . . . . . . . . . . . 740
Hiding and Showing a Window . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Setting a Window’s Title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Closing a Frame Window. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Creating a Frame Window in an Applet . . . . . . . . . . . . . . . . . . . . . . . . . 741
Handling Events in a Frame Window. . . . . . . . . . . . . . . . . . . . . . . 742

xxi

xxii

Java: The Complete Reference, Eighth Edition

Chapter 25

Creating a Windowed Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
Displaying Information Within a Window . . . . . . . . . . . . . . . . . . . . . . . 749
Working with Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
Drawing Lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
Drawing Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 750
Drawing Ellipses and Circles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751
Drawing Arcs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
Drawing Polygons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
Sizing Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 754
Working with Color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
Color Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
Setting the Current Graphics Color . . . . . . . . . . . . . . . . . . . . . . . . 756
A Color Demonstration Applet. . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
Setting the Paint Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757
Working with Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759
Determining the Available Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . 760
Creating and Selecting a Font. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762
Obtaining Font Information. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
Managing Text Output Using FontMetrics. . . . . . . . . . . . . . . . . . . . . . . 764
Displaying Multiple Lines of Text. . . . . . . . . . . . . . . . . . . . . . . . . . 766
Centering Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 767
Multiline Text Alignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 768
Using AWT Controls, Layout Managers, and Menus . . . . . . . . . . . . . . .773
Control Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773
Adding and Removing Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
Responding to Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
The HeadlessException. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
Using Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
Handling Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
Applying Check Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779
Handling Check Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
CheckboxGroup. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
Choice Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 783
Handling Choice Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
Using Lists. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
Handling Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
Managing Scroll Bars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
Handling Scroll Bars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 790
Using a TextField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 792
Handling a TextField. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793
Using a TextArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 794
Understanding Layout Managers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
FlowLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
BorderLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
Using Insets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800

Contents

Chapter 26

Chapter 27

GridLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
CardLayout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802
GridBagLayout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
Menu Bars and Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810
Dialog Boxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
FileDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
Handling Events by Extending AWT Components . . . . . . . . . . . . . . . . 822
Extending Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823
Extending Checkbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
Extending a Check Box Group. . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
Extending Choice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
Extending List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
Extending Scrollbar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
A Word About Overriding paint( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .829
File Formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829
Image Fundamentals: Creating, Loading, and Displaying . . . . . . . . . . 830
Creating an Image Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830
Loading an Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830
Displaying an Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
ImageObserver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832
Double Buffering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833
MediaTracker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836
ImageProducer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
MemoryImageSource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
ImageConsumer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
PixelGrabber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
ImageFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844
CropImageFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844
RGBImageFilter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845
Cell Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857
Additional Imaging Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
The Concurrency Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .861
The Concurrent API Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
java.util.concurrent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
java.util.concurrent.atomic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
java.util.concurrent.locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
Using Synchronization Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
Semaphore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
CountDownLatch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
CyclicBarrier. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871
Exchanger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
Phaser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 875
Using an Executor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
A Simple Executor Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883
Using Callable and Future . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885

xxiii

xxiv

Java: The Complete Reference, Eighth Edition

Chapter 28

Part III
Chapter 29

The TimeUnit Enumeration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 888
The Concurrent Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
Locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
Atomic Operations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 892
Parallel Programming via the Fork/Join Framework . . . . . . . . . . . . . . 893
The Main Fork/Join Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 894
The Divide-and-Conquer Strategy . . . . . . . . . . . . . . . . . . . . . . . . . 897
A Simple First Fork/Join Example . . . . . . . . . . . . . . . . . . . . . . . . . 898
Understanding the Impact of the Level of Parallelism . . . . . . . . . 900
An Example that Uses RecursiveTask. . . . . . . . . . . . . . . . . . . . 903
Executing a Task Asynchronously. . . . . . . . . . . . . . . . . . . . . . . . . . 905
Cancelling a Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 906
Determining a Task’s Completion Status . . . . . . . . . . . . . . . . . . . . 906
Restarting a Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 906
Things to Explore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 906
Some Fork/Join Tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908
The Concurrency Utilities Versus Java’s Traditional Approach . . . . . . 908
Regular Expressions and Other Packages . . . . . . . . . . . . . . . . . . . . . . .909
The Core Java API Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909
Regular Expression Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 911
Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 911
Matcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
Regular Expression Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 913
Demonstrating Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . 913
Two Pattern-Matching Options. . . . . . . . . . . . . . . . . . . . . . . . . . . . 919
Exploring Regular Expressions. . . . . . . . . . . . . . . . . . . . . . . . . . . . 919
Reflection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 919
Remote Method Invocation (RMI) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 923
A Simple Client/Server Application Using RMI . . . . . . . . . . . . . . 923
Text Formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 927
DateFormat Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 927
SimpleDateFormat Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929

Software Development Using Java
Java Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .933
What Is a Java Bean?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 933
Advantages of Java Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 934
Introspection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 934
Design Patterns for Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . 934
Design Patterns for Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936
Methods and Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936
Using the BeanInfo Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936
Bound and Constrained Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937
Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937
Customizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937

Contents

Chapter 30

Chapter 31

Chapter 32

The Java Beans API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 938
Introspector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
PropertyDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
EventSetDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
MethodDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
A Bean Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
Introducing Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .945
The Origins of Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 945
Swing Is Built on the AWT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946
Two Key Swing Features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946
Swing Components Are Lightweight . . . . . . . . . . . . . . . . . . . . . . . 946
Swing Supports a Pluggable Look and Feel . . . . . . . . . . . . . . . . . . 946
The MVC Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947
Components and Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948
Components. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948
Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949
The Top-Level Container Panes . . . . . . . . . . . . . . . . . . . . . . . . . . . 949
The Swing Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950
A Simple Swing Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950
Event Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 954
Create a Swing Applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
Painting in Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959
Painting Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960
Compute the Paintable Area. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961
A Paint Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961
Exploring Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .965
JLabel and ImageIcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965
JTextField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 967
The Swing Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
JButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
JToggleButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971
Check Boxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 973
Radio Buttons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975
JTabbedPane. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 977
JList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 981
JComboBox. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 984
Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 986
JTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 990
Continuing Your Exploration of Swing. . . . . . . . . . . . . . . . . . . . . . . . . . 992
Servlets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .993
Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993
The Life Cycle of a Servlet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994
Servlet Development Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994
Using Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 995

xxv

xxvi

Java: The Complete Reference, Eighth Edition

A Simple Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996
Create and Compile the Servlet Source Code . . . . . . . . . . . . . . . . 997
Start Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998
Start a Web Browser and Request the Servlet . . . . . . . . . . . . . . . . 998
The Servlet API. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998
The javax.servlet Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998
The Servlet Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999
The ServletConfig Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1000
The ServletContext Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1000
The ServletRequest Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1000
The ServletResponse Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . 1000
The GenericServlet Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
The ServletInputStream Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
The ServletOutputStream Class . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
The Servlet Exception Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
Reading Servlet Parameters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
The javax.servlet.http Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1004
The HttpServletRequest Interface . . . . . . . . . . . . . . . . . . . . . . . . 1004
The HttpServletResponse Interface . . . . . . . . . . . . . . . . . . . . . . . 1005
The HttpSession Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006
The HttpSessionBindingListener Interface . . . . . . . . . . . . . . . . . 1006
The Cookie Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1007
The HttpServlet Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1008
The HttpSessionEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009
The HttpSessionBindingEvent Class . . . . . . . . . . . . . . . . . . . . . . 1010
Handling HTTP Requests and Responses . . . . . . . . . . . . . . . . . . . . . . 1010
Handling HTTP GET Requests . . . . . . . . . . . . . . . . . . . . . . . . . . 1010
Handling HTTP POST Requests . . . . . . . . . . . . . . . . . . . . . . . . . 1012
Using Cookies. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1013
Session Tracking. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015

Part IV
Chapter 33

Applying Java
Financial Applets and Servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1019
Finding the Payments for a Loan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1020
The RegPay Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1024
The init( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1024
The makeGUI( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1024
The actionPerformed( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . 1027
The compute( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1028
Finding the Future Value of an Investment . . . . . . . . . . . . . . . . . . . . . 1028
Finding the Initial Investment Required to Achieve
a Future Value. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032
Finding the Initial Investment Needed for a Desired Annuity . . . . . . 1036
Finding the Maximum Annuity for a Given Investment . . . . . . . . . . . 1040
Finding the Remaining Balance on a Loan . . . . . . . . . . . . . . . . . . . . . 1044

Contents

Chapter 34

Appendix

Creating Financial Servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048
Converting the RegPay Applet into a Servlet. . . . . . . . . . . . . . . . 1048
The RegPayS Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048
Some Things to Try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1052
Creating a Download Manager in Java . . . . . . . . . . . . . . . . . . . . . . . . .1053
Understanding Internet Downloads . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053
An Overview of the Download Manager. . . . . . . . . . . . . . . . . . . . . . . . 1054
The Download Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055
The Download Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1058
The Download Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
The download( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
The run( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
The stateChanged( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1062
Action and Accessor Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063
The ProgressRenderer Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063
The DownloadsTableModel Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064
The addDownload( ) Method. . . . . . . . . . . . . . . . . . . . . . . . . . . . 1066
The clearDownload( ) Method. . . . . . . . . . . . . . . . . . . . . . . . . . . 1066
The getColumnClass( ) Method. . . . . . . . . . . . . . . . . . . . . . . . . . 1067
The getValueAt( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
The update( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
The DownloadManager Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
The DownloadManager Variables. . . . . . . . . . . . . . . . . . . . . . . . . 1073
The DownloadManager Constructor . . . . . . . . . . . . . . . . . . . . . . 1074
The verifyUrl( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1074
The tableSelectionChanged( ) Method . . . . . . . . . . . . . . . . . . . . 1075
The updateButtons( ) Method . . . . . . . . . . . . . . . . . . . . . . . . . . . 1075
Handling Action Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1076
Compiling and Running the Download Manager . . . . . . . . . . . . . . . . 1076
Enhancing the Download Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . 1077
Using Java’s Documentation Comments . . . . . . . . . . . . . . . . . . . . . . .1079
The javadoc Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079
@author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080
{@code} . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080
@deprecated . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080
{@docRoot}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
@exception. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
{@inheritDoc}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
{@link}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
{@linkplain} . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
{@literal} . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
@param . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
@return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1082
@see . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1082
@serial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1082

xxvii

xxviii

Java: The Complete Reference, Eighth Edition

@serialData. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@serialField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@since. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@throws . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
{@value} . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The General Form of a Documentation Comment . . . . . . . . . . . . . . .
What javadoc Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
An Example that Uses Documentation Comments . . . . . . . . . . . . . . .

1082
1082
1082
1083
1083
1083
1083
1083
1084

Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1087

Preface

J

ava is one of the world’s most important and widely used computer languages.
Furthermore, it has held that distinction for many years. Unlike some other computer
languages whose influence has waned with the passage of time, Java’s has grown
stronger. Java leapt to the forefront of Internet programming with its first release. Each
subsequent version has solidified that position. Today, it is still the first and best choice for
developing web-based applications. Java is also part of the smartphone revolution because
it is used for Android programming. Simply put: much of the modern world runs on Java
code. Java really is that important.
A key reason for Java’s success is its agility. Since its original 1.0 release, Java has
continually adapted to changes in the programming environment and to changes in the
way that programmers program. Most importantly, it has not just followed the trends, it has
helped create them. Java’s ability to accommodate the fast rate of change in the computing
world is a crucial part of why it has been and continues to be so successful.
Since this book was first published in 1996, it has gone through several editions, each
reflecting the ongoing evolution of Java. This is the Eighth edition, and it has been updated
for Java SE 7. As a result, it contains a substantial amount of new material. For example, it
includes coverage of the Project Coin language enhancements, the expanded features of
NIO (NIO.2), and the Fork/Join Framework. In general, discussions of the new features
are integrated into existing chapters, but because of the many additions to NIO, it is now
discussed in its own chapter. However, the overall structure of the book remains the same.
This means that if you are familiar with the previous edition, you will feel right at home
with this version.

A Book for All Programmers
This book is for all programmers, whether you are a novice or an experienced pro. The
beginner will find its carefully paced discussions and many examples especially helpful. Its
in-depth coverage of Java’s more advanced features and libraries will appeal to the pro. For
both, it offers a lasting resource and handy reference.

xxix

xxx

Java: The Complete Reference, Eighth Edition

What’s Inside
This book is a comprehensive guide to the Java language, describing its syntax, keywords,
and fundamental programming principles. Significant portions of the Java API library are
also examined. The book is divided into four parts, each focusing on a different aspect of
the Java programming environment.
Part I presents an in-depth tutorial of the Java language. It begins with the basics,
including such things as data types, operators, control statements, and classes. It then
moves on to inheritance, packages, interfaces, exception handling, and multithreading.
The final chapters in Part I describe annotations, enumerations, autoboxing, and generics.
I/O and applets are also introduced.
Part II examines key aspects of Java’s standard API library. Topics include strings, I/O,
networking, the standard utilities, the Collections Framework, applets, GUI-based controls,
imaging, and concurrency (including the new Fork/Join Framework).
Part III looks at three important Java technologies: Java Beans, servlets, and Swing.
Part IV contains two chapters that show examples of Java in action. The first chapter
develops several applets that perform various popular financial calculations, such as
computing the regular payment on a loan or the minimum investment needed to withdraw
a desired monthly annuity. This chapter also shows how to convert those applets into
servlets. The second chapter develops a download manager that oversees the downloading
of files. It includes the ability to start, stop, and resume a transfer. Both chapters are adapted
from my book The Art of Java, which I co-authored with James Holmes.

Don’t Forget: Code on the Web
Remember, the source code for all of the examples in this book is available free-of-charge
on the Web at www.oraclepressbooks.com.

Special Thanks
I want to give special thanks to Patrick Naughton, Joe O’Neil, James Holmes, and Danny
Coward.
Patrick Naughton was one of the creators of the Java language. He also helped write
the first edition of this book. For example, among many other contributions, much of the
material in Chapters 19, 21, and 26 was initially provided by Patrick. His insights, expertise,
and energy contributed greatly to the success of that book.
During the preparation of the second and third editions of this book, Joe O’Neil
provided initial drafts for the material now found in Chapters 28, 29, 31, and 32 of this
edition. Joe helped on several of my books and his input has always been top-notch.
James Holmes provided Chapter 34. James is an extraordinary programmer and author.
He was my co-author on The Art of Java and is the author of Struts: The Complete Reference™,
and a co-author of JSF: The Complete Reference™.
Danny Coward is the technical editor for this edition of the book. His advice, insights,
and suggestions were of great value and much appreciated.
HERBERT SCHILDT

For Further Study
Java: The Complete Reference™ is your gateway to the Herb Schildt series of programming
books. Here are some others that you will find of interest.
To learn more about Java programming, we recommend the following:
Herb Schildt’s Java Programming Cookbook
Java: A Beginner’s Guide
Swing: A Beginner’s Guide
The Art Of Java
To learn about C++, you will find these books especially helpful:
C++: The Complete Reference™
Herb Schildt’s C++ Programming Cookbook
C++: A Beginner’s Guide
The Art of C++
C++ From the Ground Up
STL Programming From the Ground Up
To learn about C#, we suggest the following Schildt books:
C#: The Complete Reference™
C#: A Beginner’s Guide
To learn about the C language, the following title will be of interest:
C: The Complete Reference™

When you need solid answers, fast, turn to Herbert Schildt,
the recognized authority on programming.

This page intentionally left blank

PART

I
CHAPTER 1
The History and Evolution
of Java

CHAPTER 2
An Overview of Java

CHAPTER 3
Data Types, Variables,
and Arrays

CHAPTER 4
Operators

CHAPTER 5
Control Statements

CHAPTER 6
Introducing Classes

CHAPTER 7
A Closer Look at Methods
and Classes

CHAPTER 8
Inheritance

CHAPTER 9
Packages and Interfaces

CHAPTER 10
Exception Handling

CHAPTER 11
Multithreaded Programming

The Java Language

CHAPTER 12
Enumerations, Autoboxing,
and Annotations (Metadata)

CHAPTER 13
I/O, Applets, and
Other Topics

CHAPTER 14
Generics

CHAPTER

32
1

The History and Evolution
of Java

To fully understand Java, one must understand the reasons behind its creation, the forces
that shaped it, and the legacy that it inherits. Like the successful computer languages that
came before, Java is a blend of the best elements of its rich heritage combined with the
innovative concepts required by its unique mission. While the remaining chapters of
this book describe the practical aspects of Java—including its syntax, key libraries, and
applications—this chapter explains how and why Java came about, what makes it so
important, and how it has evolved over the years.
Although Java has become inseparably linked with the online environment of the
Internet, it is important to remember that Java is first and foremost a programming
language. Computer language innovation and development occurs for two fundamental
reasons:
• To adapt to changing environments and uses
• To implement refinements and improvements in the art of programming
As you will see, the development of Java was driven by both elements in nearly equal
measure.

Java’s Lineage
Java is related to C++, which is a direct descendant of C. Much of the character of Java is
inherited from these two languages. From C, Java derives its syntax. Many of Java’s objectoriented features were influenced by C++. In fact, several of Java’s defining characteristics
come from—or are responses to—its predecessors. Moreover, the creation of Java was
deeply rooted in the process of refinement and adaptation that has been occurring in
computer programming languages for the past several decades. For these reasons, this
section reviews the sequence of events and forces that led to Java. As you will see, each
innovation in language design was driven by the need to solve a fundamental problem
that the preceding languages could not solve. Java is no exception.

3

4

PART I

The Java Language

The Birth of Modern Programming: C
The C language shook the computer world. Its impact should not be underestimated, because
it fundamentally changed the way programming was approached and thought about. The
creation of C was a direct result of the need for a structured, efficient, high-level language
that could replace assembly code when creating systems programs. As you probably know,
when a computer language is designed, trade-offs are often made, such as the following:
• Ease-of-use versus power
• Safety versus efficiency
• Rigidity versus extensibility
Prior to C, programmers usually had to choose between languages that optimized one
set of traits or the other. For example, although FORTRAN could be used to write fairly
efficient programs for scientific applications, it was not very good for system code. And
while BASIC was easy to learn, it wasn’t very powerful, and its lack of structure made its
usefulness questionable for large programs. Assembly language can be used to produce
highly efficient programs, but it is not easy to learn or use effectively. Further, debugging
assembly code can be quite difficult.
Another compounding problem was that early computer languages such as BASIC,
COBOL, and FORTRAN were not designed around structured principles. Instead, they
relied upon the GOTO as a primary means of program control. As a result, programs
written using these languages tended to produce “spaghetti code”—a mass of tangled
jumps and conditional branches that make a program virtually impossible to understand.
While languages like Pascal are structured, they were not designed for efficiency, and failed
to include certain features necessary to make them applicable to a wide range of programs.
(Specifically, given the standard dialects of Pascal available at the time, it was not practical
to consider using Pascal for systems-level code.)
So, just prior to the invention of C, no one language had reconciled the conflicting
attributes that had dogged earlier efforts. Yet the need for such a language was pressing. By
the early 1970s, the computer revolution was beginning to take hold, and the demand for
software was rapidly outpacing programmers’ ability to produce it. A great deal of effort was
being expended in academic circles in an attempt to create a better computer language.
But, and perhaps most importantly, a secondary force was beginning to be felt. Computer
hardware was finally becoming common enough that a critical mass was being reached. No
longer were computers kept behind locked doors. For the first time, programmers were
gaining virtually unlimited access to their machines. This allowed the freedom to experiment.
It also allowed programmers to begin to create their own tools. On the eve of C’s creation,
the stage was set for a quantum leap forward in computer languages.
Invented and first implemented by Dennis Ritchie on a DEC PDP-11 running the UNIX
operating system, C was the result of a development process that started with an older
language called BCPL, developed by Martin Richards. BCPL influenced a language called
B, invented by Ken Thompson, which led to the development of C in the 1970s. For many
years, the de facto standard for C was the one supplied with the UNIX operating system and
described in The C Programming Language by Brian Kernighan and Dennis Ritchie (PrenticeHall, 1978). C was formally standardized in December 1989, when the American National
Standards Institute (ANSI) standard for C was adopted.

The History and Evolution of Java

The creation of C is considered by many to have marked the beginning of the modern
age of computer languages. It successfully synthesized the conflicting attributes that had so
troubled earlier languages. The result was a powerful, efficient, structured language that
was relatively easy to learn. It also included one other, nearly intangible aspect: it was a
programmer’s language. Prior to the invention of C, computer languages were generally
designed either as academic exercises or by bureaucratic committees. C is different. It was
designed, implemented, and developed by real, working programmers, reflecting the way
that they approached the job of programming. Its features were honed, tested, thought
about, and rethought by the people who actually used the language. The result was a
language that programmers liked to use. Indeed, C quickly attracted many followers
who had a near-religious zeal for it. As such, it found wide and rapid acceptance in the
programmer community. In short, C is a language designed by and for programmers.
As you will see, Java inherited this legacy.

C++: The Next Step
During the late 1970s and early 1980s, C became the dominant computer programming
language, and it is still widely used today. Since C is a successful and useful language, you
might ask why a need for something else existed. The answer is complexity. Throughout the
history of programming, the increasing complexity of programs has driven the need for
better ways to manage that complexity. C++ is a response to that need. To better understand
why managing program complexity is fundamental to the creation of C++, consider the
following.
Approaches to programming have changed dramatically since the invention of the
computer. For example, when computers were first invented, programming was done by
manually toggling in the binary machine instructions by use of the front panel. As long as
programs were just a few hundred instructions long, this approach worked. As programs grew,
assembly language was invented so that a programmer could deal with larger, increasingly
complex programs by using symbolic representations of the machine instructions. As
programs continued to grow, high-level languages were introduced that gave the programmer
more tools with which to handle complexity.
The first widespread language was, of course, FORTRAN. While FORTRAN was an
impressive first step, it is hardly a language that encourages clear and easy-to-understand
programs. The 1960s gave birth to structured programming. This is the method of programming
championed by languages such as C. The use of structured languages enabled programmers
to write, for the first time, moderately complex programs fairly easily. However, even with
structured programming methods, once a project reaches a certain size, its complexity
exceeds what a programmer can manage. By the early 1980s, many projects were pushing
the structured approach past its limits. To solve this problem, a new way to program was
invented, called object-oriented programming (OOP). Object-oriented programming is discussed
in detail later in this book, but here is a brief definition: OOP is a programming methodology
that helps organize complex programs through the use of inheritance, encapsulation, and
polymorphism.
In the final analysis, although C is one of the world’s great programming languages,
there is a limit to its ability to handle complexity. Once the size of a program exceeds a
certain point, it becomes so complex that it is difficult to grasp as a totality. While the
precise size at which this occurs differs, depending upon both the nature of the program
and the programmer, there is always a threshold at which a program becomes unmanageable.

5

Part I

Chapter 1

6

PART I

The Java Language

C++ added features that enabled this threshold to be broken, allowing programmers to
comprehend and manage larger programs.
C++ was invented by Bjarne Stroustrup in 1979, while he was working at Bell Laboratories
in Murray Hill, New Jersey. Stroustrup initially called the new language “C with Classes.”
However, in 1983, the name was changed to C++. C++ extends C by adding object-oriented
features. Because C++ is built on the foundation of C, it includes all of C’s features, attributes,
and benefits. This is a crucial reason for the success of C++ as a language. The invention of
C++ was not an attempt to create a completely new programming language. Instead, it was
an enhancement to an already highly successful one.

The Stage Is Set for Java
By the end of the 1980s and the early 1990s, object-oriented programming using C++ took
hold. Indeed, for a brief moment it seemed as if programmers had finally found the perfect
language. Because C++ blended the high efficiency and stylistic elements of C with the
object-oriented paradigm, it was a language that could be used to create a wide range of
programs. However, just as in the past, forces were brewing that would, once again, drive
computer language evolution forward. Within a few years, the World Wide Web and the
Internet would reach critical mass. This event would precipitate another revolution in
programming.

The Creation of Java
Java was conceived by James Gosling, Patrick Naughton, Chris Warth, Ed Frank, and Mike
Sheridan at Sun Microsystems, Inc. in 1991. It took 18 months to develop the first working
version. This language was initially called “Oak,” but was renamed “Java” in 1995. Between
the initial implementation of Oak in the fall of 1992 and the public announcement of Java
in the spring of 1995, many more people contributed to the design and evolution of the
language. Bill Joy, Arthur van Hoff, Jonathan Payne, Frank Yellin, and Tim Lindholm were
key contributors to the maturing of the original prototype.
Somewhat surprisingly, the original impetus for Java was not the Internet! Instead, the
primary motivation was the need for a platform-independent (that is, architecture-neutral)
language that could be used to create software to be embedded in various consumer
electronic devices, such as microwave ovens and remote controls. As you can probably
guess, many different types of CPUs are used as controllers. The trouble with C and C++
(and most other languages) is that they are designed to be compiled for a specific target.
Although it is possible to compile a C++ program for just about any type of CPU, to do so
requires a full C++ compiler targeted for that CPU. The problem is that compilers are
expensive and time-consuming to create. An easier—and more cost-efficient—solution
was needed. In an attempt to find such a solution, Gosling and others began work on a
portable, platform-independent language that could be used to produce code that would
run on a variety of CPUs under differing environments. This effort ultimately led to the
creation of Java.
About the time that the details of Java were being worked out, a second, and ultimately
more important, factor was emerging that would play a crucial role in the future of Java.
This second force was, of course, the World Wide Web. Had the Web not taken shape at
about the same time that Java was being implemented, Java might have remained a useful
but obscure language for programming consumer electronics. However, with the emergence

The History and Evolution of Java

of the World Wide Web, Java was propelled to the forefront of computer language design,
because the Web, too, demanded portable programs.
Most programmers learn early in their careers that portable programs are as elusive as they
are desirable. While the quest for a way to create efficient, portable (platform-independent)
programs is nearly as old as the discipline of programming itself, it had taken a back seat
to other, more pressing problems. Further, because (at that time) much of the computer
world had divided itself into the three competing camps of Intel, Macintosh, and UNIX,
most programmers stayed within their fortified boundaries, and the urgent need for
portable code was reduced. However, with the advent of the Internet and the Web, the
old problem of portability returned with a vengeance. After all, the Internet consists of a
diverse, distributed universe populated with various types of computers, operating systems,
and CPUs. Even though many kinds of platforms are attached to the Internet, users would
like them all to be able to run the same program. What was once an irritating but lowpriority problem had become a high-profile necessity.
By 1993, it became obvious to members of the Java design team that the problems of
portability frequently encountered when creating code for embedded controllers are also
found when attempting to create code for the Internet. In fact, the same problem that Java
was initially designed to solve on a small scale could also be applied to the Internet on a
large scale. This realization caused the focus of Java to switch from consumer electronics
to Internet programming. So, while the desire for an architecture-neutral programming
language provided the initial spark, the Internet ultimately led to Java’s large-scale success.
As mentioned earlier, Java derives much of its character from C and C++. This is by intent.
The Java designers knew that using the familiar syntax of C and echoing the object-oriented
features of C++ would make their language appealing to the legions of experienced C/C++
programmers. In addition to the surface similarities, Java shares some of the other attributes
that helped make C and C++ successful. First, Java was designed, tested, and refined by real,
working programmers. It is a language grounded in the needs and experiences of the
people who devised it. Thus, Java is a programmer’s language. Second, Java is cohesive and
logically consistent. Third, except for those constraints imposed by the Internet environment,
Java gives you, the programmer, full control. If you program well, your programs reflect it.
If you program poorly, your programs reflect that, too. Put differently, Java is not a language
with training wheels. It is a language for professional programmers.
Because of the similarities between Java and C++, it is tempting to think of Java as
simply the “Internet version of C++.” However, to do so would be a large mistake. Java has
significant practical and philosophical differences. While it is true that Java was influenced
by C++, it is not an enhanced version of C++. For example, Java is neither upwardly nor
downwardly compatible with C++. Of course, the similarities with C++ are significant, and if
you are a C++ programmer, then you will feel right at home with Java. One other point: Java
was not designed to replace C++. Java was designed to solve a certain set of problems. C++
was designed to solve a different set of problems. Both will coexist for many years to come.
As mentioned at the start of this chapter, computer languages evolve for two reasons:
to adapt to changes in environment and to implement advances in the art of programming.
The environmental change that prompted Java was the need for platform-independent
programs destined for distribution on the Internet. However, Java also embodies changes
in the way that people approach the writing of programs. For example, Java enhanced
and refined the object-oriented paradigm used by C++, added integrated support for
multithreading, and provided a library that simplified Internet access. In the final analysis,

7

Part I

Chapter 1

8

PART I

The Java Language

though, it was not the individual features of Java that made it so remarkable. Rather, it was
the language as a whole. Java was the perfect response to the demands of the then newly
emerging, highly distributed computing universe. Java was to Internet programming what
C was to system programming: a revolutionary force that changed the world.

The C# Connection
The reach and power of Java continues to be felt in the world of computer language
development. Many of its innovative features, constructs, and concepts have become part
of the baseline for any new language. The success of Java is simply too important to ignore.
Perhaps the most important example of Java’s influence is C#. Created by Microsoft to
support the .NET Framework, C# is closely related to Java. For example, both share the
same general syntax, support distributed programming, and utilize the same object model.
There are, of course, differences between Java and C#, but the overall “look and feel” of
these languages is very similar. This “cross-pollination” from Java to C# is the strongest
testimonial to date that Java redefined the way we think about and use a computer language.

How Java Changed the Internet
The Internet helped catapult Java to the forefront of programming, and Java, in turn, had
a profound effect on the Internet. In addition to simplifying web programming in general,
Java innovated a new type of networked program called the applet that changed the way
the online world thought about content. Java also addressed some of the thorniest issues
associated with the Internet: portability and security. Let’s look more closely at each of these.

Java Applets
An applet is a special kind of Java program that is designed to be transmitted over the Internet
and automatically executed by a Java-compatible web browser. Furthermore, an applet is
downloaded on demand, without further interaction with the user. If the user clicks a link
that contains an applet, the applet will be automatically downloaded and run in the browser.
Applets are intended to be small programs. They are typically used to display data provided
by the server, handle user input, or provide simple functions, such as a loan calculator, that
execute locally, rather than on the server. In essence, the applet allows some functionality to
be moved from the server to the client.
The creation of the applet changed Internet programming because it expanded the
universe of objects that can move about freely in cyberspace. In general, there are two very
broad categories of objects that are transmitted between the server and the client: passive
information and dynamic, active programs. For example, when you read your e-mail, you
are viewing passive data. Even when you download a program, the program’s code is still
only passive data until you execute it. By contrast, the applet is a dynamic, self-executing
program. Such a program is an active agent on the client computer, yet it is initiated by
the server.
As desirable as dynamic, networked programs are, they also present serious problems
in the areas of security and portability. Obviously, a program that downloads and executes
automatically on the client computer must be prevented from doing harm. It must also be
able to run in a variety of different environments and under different operating systems.
As you will see, Java solved these problems in an effective and elegant way. Let’s look a bit
more closely at each.

Chapter 1

The History and Evolution of Java

9

As you are likely aware, every time you download a “normal” program, you are taking a risk,
because the code you are downloading might contain a virus, Trojan horse, or other harmful
code. At the core of the problem is the fact that malicious code can cause its damage because
it has gained unauthorized access to system resources. For example, a virus program might
gather private information, such as credit card numbers, bank account balances, and
passwords, by searching the contents of your computer’s local file system. In order for Java to
enable applets to be downloaded and executed on the client computer safely, it was necessary
to prevent an applet from launching such an attack.
Java achieved this protection by confining an applet to the Java execution environment
and not allowing it access to other parts of the computer. (You will see how this is
accomplished shortly.) The ability to download applets with confidence that no harm will
be done and that no security will be breached is considered by many to be the single most
innovative aspect of Java.

Portability
Portability is a major aspect of the Internet because there are many different types of
computers and operating systems connected to it. If a Java program were to be run on
virtually any computer connected to the Internet, there needed to be some way to enable
that program to execute on different systems. For example, in the case of an applet, the
same applet must be able to be downloaded and executed by the wide variety of CPUs,
operating systems, and browsers connected to the Internet. It is not practical to have
different versions of the applet for different computers. The same code must work on all
computers. Therefore, some means of generating portable executable code was needed. As
you will soon see, the same mechanism that helps ensure security also helps create portability.

Java’s Magic: The Bytecode
The key that allows Java to solve both the security and the portability problems just described
is that the output of a Java compiler is not executable code. Rather, it is bytecode. Bytecode is
a highly optimized set of instructions designed to be executed by the Java run-time system,
which is called the Java Virtual Machine (JVM). In essence, the original JVM was designed as
an interpreter for bytecode. This may come as a bit of a surprise since many modern languages
are designed to be compiled into executable code because of performance concerns.
However, the fact that a Java program is executed by the JVM helps solve the major
problems associated with web-based programs. Here is why.
Translating a Java program into bytecode makes it much easier to run a program in
a wide variety of environments because only the JVM needs to be implemented for each
platform. Once the run-time package exists for a given system, any Java program can run
on it. Remember, although the details of the JVM will differ from platform to platform, all
understand the same Java bytecode. If a Java program were compiled to native code, then
different versions of the same program would have to exist for each type of CPU connected
to the Internet. This is, of course, not a feasible solution. Thus, the execution of bytecode
by the JVM is the easiest way to create truly portable programs.
The fact that a Java program is executed by the JVM also helps to make it secure.
Because the JVM is in control, it can contain the program and prevent it from generating

Part I

Security

10

PART I

The Java Language

side effects outside of the system. As you will see, safety is also enhanced by certain
restrictions that exist in the Java language.
In general, when a program is compiled to an intermediate form and then interpreted
by a virtual machine, it runs slower than it would run if compiled to executable code.
However, with Java, the differential between the two is not so great. Because bytecode has
been highly optimized, the use of bytecode enables the JVM to execute programs much
faster than you might expect.
Although Java was designed as an interpreted language, there is nothing about Java that
prevents on-the-fly compilation of bytecode into native code in order to boost performance.
For this reason, the HotSpot technology was introduced not long after Java’s initial release.
HotSpot provides a Just-In-Time (JIT) compiler for bytecode. When a JIT compiler is part
of the JVM, selected portions of bytecode are compiled into executable code in real time,
on a piece-by-piece, demand basis. It is important to understand that it is not practical to
compile an entire Java program into executable code all at once, because Java performs
various run-time checks that can be done only at run time. Instead, a JIT compiler compiles
code as it is needed, during execution. Furthermore, not all sequences of bytecode are
compiled—only those that will benefit from compilation. The remaining code is simply
interpreted. However, the just-in-time approach still yields a significant performance boost.
Even when dynamic compilation is applied to bytecode, the portability and safety features
still apply, because the JVM is still in charge of the execution environment.

Servlets: Java on the Server Side
As useful as applets can be, they are just one half of the client/server equation. Not long
after the initial release of Java, it became obvious that Java would also be useful on the
server side. The result was the servlet. A servlet is a small program that executes on the
server. Just as applets dynamically extend the functionality of a web browser, servlets
dynamically extend the functionality of a web server. Thus, with the advent of the servlet,
Java spanned both sides of the client/server connection.
Servlets are used to create dynamically generated content that is then served to the
client. For example, an online store might use a servlet to look up the price for an item in a
database. The price information is then used to dynamically generate a web page that is sent
to the browser. Although dynamically generated content is available through mechanisms
such as CGI (Common Gateway Interface), the servlet offers several advantages, including
increased performance.
Because servlets (like all Java programs) are compiled into bytecode and executed by
the JVM, they are highly portable. Thus, the same servlet can be used in a variety of
different server environments. The only requirements are that the server support the JVM
and a servlet container.

The Java Buzzwords
No discussion of Java’s history is complete without a look at the Java buzzwords. Although
the fundamental forces that necessitated the invention of Java are portability and security,
other factors also played an important role in molding the final form of the language. The
key considerations were summed up by the Java team in the following list of buzzwords:
• Simple
• Secure

Chapter 1

The History and Evolution of Java

11

• Portable
• Robust
• Multithreaded
• Architecture-neutral
• Interpreted
• High performance
• Distributed
• Dynamic
Two of these buzzwords have already been discussed: secure and portable. Let’s examine
what each of the others implies.

Simple
Java was designed to be easy for the professional programmer to learn and use effectively.
Assuming that you have some programming experience, you will not find Java hard to master.
If you already understand the basic concepts of object-oriented programming, learning Java
will be even easier. Best of all, if you are an experienced C++ programmer, moving to Java will
require very little effort. Because Java inherits the C/C++ syntax and many of the objectoriented features of C++, most programmers have little trouble learning Java.

Object-Oriented
Although influenced by its predecessors, Java was not designed to be source-code compatible
with any other language. This allowed the Java team the freedom to design with a blank
slate. One outcome of this was a clean, usable, pragmatic approach to objects. Borrowing
liberally from many seminal object-software environments of the last few decades, Java
manages to strike a balance between the purist’s “everything is an object” paradigm and
the pragmatist’s “stay out of my way” model. The object model in Java is simple and easy to
extend, while primitive types, such as integers, are kept as high-performance nonobjects.

Robust
The multiplatformed environment of the Web places extraordinary demands on a
program, because the program must execute reliably in a variety of systems. Thus, the
ability to create robust programs was given a high priority in the design of Java. To gain
reliability, Java restricts you in a few key areas to force you to find your mistakes early in
program development. At the same time, Java frees you from having to worry about many
of the most common causes of programming errors. Because Java is a strictly typed
language, it checks your code at compile time. However, it also checks your code at run
time. Many hard-to-track-down bugs that often turn up in hard-to-reproduce run-time
situations are simply impossible to create in Java. Knowing that what you have written
will behave in a predictable way under diverse conditions is a key feature of Java.
To better understand how Java is robust, consider two of the main reasons for program
failure: memory management mistakes and mishandled exceptional conditions (that is,
run-time errors). Memory management can be a difficult, tedious task in traditional

Part I

• Object-oriented

12

PART I

The Java Language

programming environments. For example, in C/C++, the programmer must manually allocate
and free all dynamic memory. This sometimes leads to problems, because programmers will
either forget to free memory that has been previously allocated or, worse, try to free some
memory that another part of their code is still using. Java virtually eliminates these problems
by managing memory allocation and deallocation for you. (In fact, deallocation is completely
automatic, because Java provides garbage collection for unused objects.) Exceptional
conditions in traditional environments often arise in situations such as division by zero or
“file not found,” and they must be managed with clumsy and hard-to-read constructs. Java
helps in this area by providing object-oriented exception handling. In a well-written Java
program, all run-time errors can—and should—be managed by your program.

Multithreaded
Java was designed to meet the real-world requirement of creating interactive, networked
programs. To accomplish this, Java supports multithreaded programming, which allows you
to write programs that do many things simultaneously. The Java run-time system comes with
an elegant yet sophisticated solution for multiprocess synchronization that enables you to
construct smoothly running interactive systems. Java’s easy-to-use approach to multithreading
allows you to think about the specific behavior of your program, not the multitasking
subsystem.

Architecture-Neutral
A central issue for the Java designers was that of code longevity and portability. At the time
of Java’s creation, one of the main problems facing programmers was that no guarantee
existed that if you wrote a program today, it would run tomorrow—even on the same
machine. Operating system upgrades, processor upgrades, and changes in core system
resources can all combine to make a program malfunction. The Java designers made
several hard decisions in the Java language and the Java Virtual Machine in an attempt to
alter this situation. Their goal was “write once; run anywhere, any time, forever.” To a great
extent, this goal was accomplished.

Interpreted and High Performance
As described earlier, Java enables the creation of cross-platform programs by compiling into
an intermediate representation called Java bytecode. This code can be executed on any
system that implements the Java Virtual Machine. Most previous attempts at cross-platform
solutions have done so at the expense of performance. As explained earlier, the Java
bytecode was carefully designed so that it would be easy to translate directly into native
machine code for very high performance by using a just-in-time compiler. Java run-time
systems that provide this feature lose none of the benefits of the platform-independent code.

Distributed
Java is designed for the distributed environment of the Internet because it handles TCP/IP
protocols. In fact, accessing a resource using a URL is not much different from accessing a
file. Java also supports Remote Method Invocation (RMI). This feature enables a program to
invoke methods across a network.

Chapter 1

The History and Evolution of Java

13

Java programs carry with them substantial amounts of run-time type information that is used
to verify and resolve accesses to objects at run time. This makes it possible to dynamically link
code in a safe and expedient manner. This is crucial to the robustness of the Java environment,
in which small fragments of bytecode may be dynamically updated on a running system.

The Evolution of Java
The initial release of Java was nothing short of revolutionary, but it did not mark the end of
Java’s era of rapid innovation. Unlike most other software systems that usually settle into a
pattern of small, incremental improvements, Java continued to evolve at an explosive pace.
Soon after the release of Java 1.0, the designers of Java had already created Java 1.1. The
features added by Java 1.1 were more significant and substantial than the increase in the
minor revision number would have you think. Java 1.1 added many new library elements,
redefined the way events are handled, and reconfigured many features of the 1.0 library. It
also deprecated (rendered obsolete) several features originally defined by Java 1.0. Thus,
Java 1.1 both added to and subtracted from attributes of its original specification.
The next major release of Java was Java 2, where the “2” indicates “second generation.”
The creation of Java 2 was a watershed event, marking the beginning of Java’s “modern
age.” The first release of Java 2 carried the version number 1.2. It may seem odd that the
first release of Java 2 used the 1.2 version number. The reason is that it originally referred
to the internal version number of the Java libraries, but then was generalized to refer to
the entire release. With Java 2, Sun repackaged the Java product as J2SE (Java 2 Platform
Standard Edition), and the version numbers began to be applied to that product.
Java 2 added support for a number of new features, such as Swing and the Collections
Framework, and it enhanced the Java Virtual Machine and various programming tools. Java 2
also contained a few deprecations. The most important affected the Thread class in which
the methods suspend( ), resume( ), and stop( ) were deprecated.
J2SE 1.3 was the first major upgrade to the original Java 2 release. For the most part,
it added to existing functionality and “tightened up” the development environment. In
general, programs written for version 1.2 and those written for version 1.3 are source-code
compatible. Although version 1.3 contained a smaller set of changes than the preceding
three major releases, it was nevertheless important.
The release of J2SE 1.4 further enhanced Java. This release contained several important
upgrades, enhancements, and additions. For example, it added the new keyword assert,
chained exceptions, and a channel-based I/O subsystem. It also made changes to the
Collections Framework and the networking classes. In addition, numerous small changes
were made throughout. Despite the significant number of new features, version 1.4
maintained nearly 100 percent source-code compatibility with prior versions.
The next release of Java was J2SE 5, and it was revolutionary. Unlike most of the previous
Java upgrades, which offered important, but measured improvements, J2SE 5 fundamentally
expanded the scope, power, and range of the language. To grasp the magnitude of the
changes that J2SE 5 made to Java, consider the following list of its major new features:
• Generics
• Annotations

Part I

Dynamic

14

PART I

The Java Language

• Autoboxing and auto-unboxing
• Enumerations
• Enhanced, for-each style for loop
• Variable-length arguments (varargs)
• Static import
• Formatted I/O
• Concurrency utilities
This is not a list of minor tweaks or incremental upgrades. Each item in the list represented
a significant addition to the Java language. Some, such as generics, the enhanced for, and
varargs, introduced new syntax elements. Others, such as autoboxing and auto-unboxing,
altered the semantics of the language. Annotations added an entirely new dimension to
programming. In all cases, the impact of these additions went beyond their direct effects.
They changed the very character of Java itself.
The importance of these new features is reflected in the use of the version number “5.”
The next version number for Java would normally have been 1.5. However, the new features
were so significant that a shift from 1.4 to 1.5 just didn’t seem to express the magnitude of
the change. Instead, Sun elected to increase the version number to 5 as a way of emphasizing
that a major event was taking place. Thus, it was named J2SE 5, and the Developer’s Kit was
called JDK 5. However, in order to maintain consistency, Sun decided to use 1.5 as its
internal version number, which is also referred to as the developer version number. The
“5” in J2SE 5 is called the product version number.
The next release of Java was called Java SE 6. Sun once again decided to change the
name of the Java platform. First, notice that the “2” was dropped. Thus, the platform was
now named Java SE, and the official product name was Java Platform, Standard Edition 6.
The Java Developer’s Kit was called JDK 6. As with J2SE 5, the 6 in Java SE 6 is the product
version number. The internal, developer version number is 1.6.
Java SE 6 built on the base of J2SE 5, adding incremental improvements. Java SE 6 added
no major features to the Java language proper, but it did enhance the API libraries, added
several new packages, and offered improvements to the runtime. It also went through several
updates during its (in Java terms) long life cycle, with several upgrades added along the way.
In general, Java SE 6 served to further solidify the advances made by J2SE 5.

Java SE 7
The newest release of Java is called Java SE 7, with the Java Developer’s Kit being called JDK 7,
and an internal version number of 1.7. Java SE 7 is the first major release of Java since Sun
Microsystems was acquired by Oracle (a process that began in April 2009 and that was
completed in January 2010). Java SE 7 contains many new features, including significant
additions to the language and the API libraries. Upgrades to the Java run-time system that
support non-Java languages are also included, but it is the language and library additions
that are of most interest to Java programmers.

The History and Evolution of Java

15

The new language features were developed as part of Project Coin. The purpose of
Project Coin was to identify a number of small changes to the Java language that would be
incorporated into JDK 7. Although these new features are collectively referred to as “small,”
the effects of these changes are quite large in terms of the code they impact. In fact, for
many programmers, these changes may well be the most important new features in Java
SE 7. Here is a list of the new language features:
• A String can now control a switch statement.
• Binary integer literals.
• Underscores in numeric literals.
• An expanded try statement, called try-with-resources, that supports automatic resource
management. (For example, streams can now be closed automatically when they are
no longer needed.)
• Type inference (via the diamond operator) when constructing a generic instance.
• Enhanced exception handling in which two or more exceptions can be caught by a
single catch (multi-catch) and better type checking for exceptions that are rethrown.
• Although not a syntax change, the compiler warnings associated with some types of
varargs methods have been improved, and you have more control over the warnings.
As you can see, even though the Project Coin features were considered small changes to
the language, their benefits will be much larger than the qualifier “small” would suggest. In
particular, the try-with-resources statement will profoundly affect the way that stream-based
code is written. Also, the ability to now use a String to control a switch statement is a
long-desired improvement that will simplify coding in many situations.
Java SE 7 makes several additions to the Java API library. Two of the most important are
the enhancements to the NIO Framework and the addition of the Fork/Join Framework.
NIO (which originally stood for New I/O) was added to Java in version 1.4. However, the
changes proposed for Java SE 7 fundamentally expand its capabilities. So significant are
the changes, that the term NIO.2 is often used.
The Fork/Join Framework provides important support for parallel programming. Parallel
programming is the name commonly given to the techniques that make effective use of
computers that contain more than one processor, including multicore systems. The
advantage that multicore environments offer is the prospect of significantly increased
program performance. The Fork/Join Framework addresses parallel programming by
• Simplifying the creation and use of tasks that can execute concurrently
• Automatically making use of multiple processors
Therefore, by using the Fork/Join Framework, you can easily create scaleable
applications that automatically take advantage of the processors available in the execution
environment. Of course, not all algorithms lend themselves to parallelization, but for those
that do, a significant improvement in execution speed can be obtained.

Part I

Chapter 1

16

PART I

The Java Language

The material in this book has been updated to reflect Java SE 7, with many new
features, updates, and additions indicated throughout.

A Culture of Innovation
Since the beginning, Java has been at the center of a culture of innovation. Its original release
redefined programming for the Internet. The Java Virtual Machine (JVM) and bytecode
changed the way we think about security and portability. The applet (and then the servlet)
made the Web come alive. The Java Community Process (JCP) redefined the way that new
ideas are assimilated into the language. Because Java is used for Android programming, Java
is part of the smartphone revolution. The world of Java has never stood still for very long.
Java SE 7 is the latest release in Java’s ongoing, dynamic history.

CHAPTER

2

An Overview of Java

As in all other computer languages, the elements of Java do not exist in isolation. Rather,
they work together to form the language as a whole. However, this interrelatedness can
make it difficult to describe one aspect of Java without involving several others. Often a
discussion of one feature implies prior knowledge of another. For this reason, this chapter
presents a quick overview of several key features of Java. The material described here will
give you a foothold that will allow you to write and understand simple programs. Most of
the topics discussed will be examined in greater detail in the remaining chapters of Part I.

Object-Oriented Programming
Object-oriented programming (OOP) is at the core of Java. In fact, all Java programs are to
at least some extent object-oriented. OOP is so integral to Java that it is best to understand
its basic principles before you begin writing even simple Java programs. Therefore, this
chapter begins with a discussion of the theoretical aspects of OOP.

Two Paradigms
All computer programs consist of two elements: code and data. Furthermore, a program
can be conceptually organized around its code or around its data. That is, some programs
are written around “what is happening” and others are written around “who is being
affected.” These are the two paradigms that govern how a program is constructed. The first
way is called the process-oriented model. This approach characterizes a program as a series of
linear steps (that is, code). The process-oriented model can be thought of as code acting on
data. Procedural languages such as C employ this model to considerable success. However,
as mentioned in Chapter 1, problems with this approach appear as programs grow larger
and more complex.
To manage increasing complexity, the second approach, called object-oriented programming,
was conceived. Object-oriented programming organizes a program around its data (that is,
objects) and a set of well-defined interfaces to that data. An object-oriented program can
be characterized as data controlling access to code. As you will see, by switching the controlling
entity to data, you can achieve several organizational benefits.

17

18

PART I

The Java Language

Abstraction
An essential element of object-oriented programming is abstraction. Humans manage
complexity through abstraction. For example, people do not think of a car as a set of tens
of thousands of individual parts. They think of it as a well-defined object with its own
unique behavior. This abstraction allows people to use a car to drive to the grocery store
without being overwhelmed by the complexity of the parts that form the car. They can
ignore the details of how the engine, transmission, and braking systems work. Instead,
they are free to utilize the object as a whole.
A powerful way to manage abstraction is through the use of hierarchical classifications.
This allows you to layer the semantics of complex systems, breaking them into more
manageable pieces. From the outside, the car is a single object. Once inside, you see that
the car consists of several subsystems: steering, brakes, sound system, seat belts, heating,
cellular phone, and so on. In turn, each of these subsystems is made up of more specialized
units. For instance, the sound system consists of a radio, a CD player, and/or a tape player.
The point is that you manage the complexity of the car (or any other complex system)
through the use of hierarchical abstractions.
Hierarchical abstractions of complex systems can also be applied to computer programs.
The data from a traditional process-oriented program can be transformed by abstraction
into its component objects. A sequence of process steps can become a collection of messages
between these objects. Thus, each of these objects describes its own unique behavior. You
can treat these objects as concrete entities that respond to messages telling them to do
something. This is the essence of object-oriented programming.
Object-oriented concepts form the heart of Java just as they form the basis for human
understanding. It is important that you understand how these concepts translate into
programs. As you will see, object-oriented programming is a powerful and natural paradigm
for creating programs that survive the inevitable changes accompanying the life cycle of any
major software project, including conception, growth, and aging. For example, once you
have well-defined objects and clean, reliable interfaces to those objects, you can gracefully
decommission or replace parts of an older system without fear.

The Three OOP Principles
All object-oriented programming languages provide mechanisms that help you implement
the object-oriented model. They are encapsulation, inheritance, and polymorphism. Let’s
take a look at these concepts now.

Encapsulation
Encapsulation is the mechanism that binds together code and the data it manipulates, and
keeps both safe from outside interference and misuse. One way to think about encapsulation
is as a protective wrapper that prevents the code and data from being arbitrarily accessed by
other code defined outside the wrapper. Access to the code and data inside the wrapper is
tightly controlled through a well-defined interface. To relate this to the real world, consider
the automatic transmission on an automobile. It encapsulates hundreds of bits of information
about your engine, such as how much you are accelerating, the pitch of the surface you are
on, and the position of the shift lever. You, as the user, have only one method of affecting
this complex encapsulation: by moving the gear-shift lever. You can’t affect the transmission
by using the turn signal or windshield wipers, for example. Thus, the gear-shift lever is a
well-defined (indeed, unique) interface to the transmission. Further, what occurs inside the

An Overview of Java

19

transmission does not affect objects outside the transmission. For example, shifting gears
does not turn on the headlights! Because an automatic transmission is encapsulated, dozens
of car manufacturers can implement one in any way they please. However, from the driver’s
point of view, they all work the same. This same idea can be applied to programming. The
power of encapsulated code is that everyone knows how to access it and thus can use it
regardless of the implementation details—and without fear of unexpected side effects.
In Java, the basis of encapsulation is the class. Although the class will be examined in
great detail later in this book, the following brief discussion will be helpful now. A class defines
the structure and behavior (data and code) that will be shared by a set of objects. Each object
of a given class contains the structure and behavior defined by the class, as if it were stamped
out by a mold in the shape of the class. For this reason, objects are sometimes referred to as
instances of a class. Thus, a class is a logical construct; an object has physical reality.
When you create a class, you will specify the code and data that constitute that class.
Collectively, these elements are called members of the class. Specifically, the data defined by
the class are referred to as member variables or instance variables. The code that operates on
that data is referred to as member methods or just methods. (If you are familiar with C/C++, it
may help to know that what a Java programmer calls a method, a C/C++ programmer calls a
function.) In properly written Java programs, the methods define how the member variables
can be used. This means that the behavior and interface of a class are defined by the methods
that operate on its instance data.
Since the purpose of a class is to encapsulate complexity, there are mechanisms for
hiding the complexity of the implementation inside the class. Each method or variable in a
class may be marked private or public. The public interface of a class represents everything
that external users of the class need to know, or may know. The private methods and data
can only be accessed by code that is a member of the class. Therefore, any other code that
is not a member of the class cannot access a private method or variable. Since the private
members of a class may only be accessed by other parts of your program through the class’
public methods, you can ensure that no improper actions take place. Of course, this means
that the public interface should be carefully designed not to expose too much of the inner
workings of a class (see Figure 2-1).

Inheritance
Inheritance is the process by which one object acquires the properties of another object. This
is important because it supports the concept of hierarchical classification. As mentioned
earlier, most knowledge is made manageable by hierarchical (that is, top-down) classifications.
For example, a Golden Retriever is part of the classification dog, which in turn is part of the
mammal class, which is under the larger class animal. Without the use of hierarchies, each
object would need to define all of its characteristics explicitly. However, by use of inheritance,
an object need only define those qualities that make it unique within its class. It can inherit
its general attributes from its parent. Thus, it is the inheritance mechanism that makes it
possible for one object to be a specific instance of a more general case. Let’s take a closer
look at this process.
Most people naturally view the world as made up of objects that are related to each
other in a hierarchical way, such as animals, mammals, and dogs. If you wanted to describe
animals in an abstract way, you would say they have some attributes, such as size, intelligence,
and type of skeletal system. Animals also have certain behavioral aspects; they eat, breathe,
and sleep. This description of attributes and behavior is the class definition for animals.

Part I

Chapter 2

20

PART I

The Java Language

Figure 2-1 Encapsulation: public methods can be used to protect private data.
If you wanted to describe a more specific class of animals, such as mammals, they would
have more specific attributes, such as type of teeth and mammary glands. This is known as a
subclass of animals, where animals are referred to as mammals’ superclass.
Since mammals are simply more precisely specified animals, they inherit all of the
attributes from animals. A deeply inherited subclass inherits all of the attributes from each
of its ancestors in the class hierarchy.
Inheritance interacts with encapsulation as well. If a given class encapsulates some
attributes, then any subclass will have the same attributes plus any that it adds as part of its
specialization (see Figure 2-2). This is a key concept that lets object-oriented programs grow
in complexity linearly rather than geometrically. A new subclass inherits all of the attributes
of all of its ancestors. It does not have unpredictable interactions with the majority of the
rest of the code in the system.

An Overview of Java

21

Part I

Chapter 2

Figure 2-2 Labrador inherits the encapsulation of all its superclasses.

Polymorphism
Polymorphism (from Greek, meaning “many forms”) is a feature that allows one interface to
be used for a general class of actions. The specific action is determined by the exact nature
of the situation. Consider a stack (which is a last-in, first-out list). You might have a program
that requires three types of stacks. One stack is used for integer values, one for floatingpoint values, and one for characters. The algorithm that implements each stack is the same,
even though the data being stored differs. In a non–object-oriented language, you would be
required to create three different sets of stack routines, with each set using different names.
However, because of polymorphism, in Java you can specify a general set of stack routines
that all share the same names.

22

PART I

The Java Language

More generally, the concept of polymorphism is often expressed by the phrase “one
interface, multiple methods.” This means that it is possible to design a generic interface to a
group of related activities. This helps reduce complexity by allowing the same interface to
be used to specify a general class of action. It is the compiler’s job to select the specific action
(that is, method) as it applies to each situation. You, the programmer, do not need to make
this selection manually. You need only remember and utilize the general interface.
Extending the dog analogy, a dog’s sense of smell is polymorphic. If the dog smells a
cat, it will bark and run after it. If the dog smells its food, it will salivate and run to its bowl.
The same sense of smell is at work in both situations. The difference is what is being smelled,
that is, the type of data being operated upon by the dog’s nose! This same general concept
can be implemented in Java as it applies to methods within a Java program.

Polymorphism, Encapsulation, and Inheritance Work Together
When properly applied, polymorphism, encapsulation, and inheritance combine to produce
a programming environment that supports the development of far more robust and scaleable
programs than does the process-oriented model. A well-designed hierarchy of classes is the
basis for reusing the code in which you have invested time and effort developing and testing.
Encapsulation allows you to migrate your implementations over time without breaking the
code that depends on the public interface of your classes. Polymorphism allows you to create
clean, sensible, readable, and resilient code.
Of the two real-world examples, the automobile more completely illustrates the power
of object-oriented design. Dogs are fun to think about from an inheritance standpoint, but
cars are more like programs. All drivers rely on inheritance to drive different types (subclasses)
of vehicles. Whether the vehicle is a school bus, a Mercedes sedan, a Porsche, or the family
minivan, drivers can all more or less find and operate the steering wheel, the brakes, and
the accelerator. After a bit of gear grinding, most people can even manage the difference
between a stick shift and an automatic, because they fundamentally understand their
common superclass, the transmission.
People interface with encapsulated features on cars all the time. The brake and gas
pedals hide an incredible array of complexity with an interface so simple you can operate
them with your feet! The implementation of the engine, the style of brakes, and the size of
the tires have no effect on how you interface with the class definition of the pedals.
The final attribute, polymorphism, is clearly reflected in the ability of car manufacturers
to offer a wide array of options on basically the same vehicle. For example, you can get an
antilock braking system or traditional brakes, power or rack-and-pinion steering, and 4-, 6-,
or 8-cylinder engines. Either way, you will still press the brake pedal to stop, turn the steering
wheel to change direction, and press the accelerator when you want to move. The same
interface can be used to control a number of different implementations.
As you can see, it is through the application of encapsulation, inheritance, and
polymorphism that the individual parts are transformed into the object known as a car.
The same is also true of computer programs. By the application of object-oriented
principles, the various parts of a complex program can be brought together to form a
cohesive, robust, maintainable whole.
As mentioned at the start of this section, every Java program is object-oriented. Or, put
more precisely, every Java program involves encapsulation, inheritance, and polymorphism.
Although the short example programs shown in the rest of this chapter and in the next few
chapters may not seem to exhibit all of these features, they are nevertheless present. As you

Chapter 2

An Overview of Java

23

A First Simple Program
Now that the basic object-oriented underpinning of Java has been discussed, let’s look at
some actual Java programs. Let’s start by compiling and running the short sample program
shown here. As you will see, this involves a little more work than you might imagine.
/*
This is a simple Java program.
Call this file "Example.java".
*/
class Example {
// Your program begins with a call to main().
public static void main(String args[]) {
System.out.println("This is a simple Java program.");
}
}

NOTE The descriptions that follow use the standard Java SE 7 Development Kit (JDK 7), which is available
from Oracle. If you are using a different Java development environment, then you may need to follow a
different procedure for compiling and executing Java programs. In this case, consult your compiler’s
documentation for details.

Entering the Program
For most computer languages, the name of the file that holds the source code to a program
is immaterial. However, this is not the case with Java. The first thing that you must learn
about Java is that the name you give to a source file is very important. For this example,
the name of the source file should be Example.java. Let’s see why.
In Java, a source file is officially called a compilation unit. It is a text file that contains
(among other things) one or more class definitions. (For now, we will be using source files
that contain only one class.) The Java compiler requires that a source file use the .java
filename extension.
As you can see by looking at the program, the name of the class defined by the program
is also Example. This is not a coincidence. In Java, all code must reside inside a class. By
convention, the name of the main class should match the name of the file that holds the
program. You should also make sure that the capitalization of the filename matches the
class name. The reason for this is that Java is case-sensitive. At this point, the convention
that filenames correspond to class names may seem arbitrary. However, this convention
makes it easier to maintain and organize your programs.

Compiling the Program
To compile the Example program, execute the compiler, javac, specifying the name of the
source file on the command line, as shown here:
C:\>javac Example.java

The javac compiler creates a file called Example.class that contains the bytecode version of
the program. As discussed earlier, the Java bytecode is the intermediate representation of

Part I

will see, many of the features supplied by Java are part of its built-in class libraries, which do
make extensive use of encapsulation, inheritance, and polymorphism.

24

PART I

The Java Language

your program that contains instructions the Java Virtual Machine will execute. Thus, the
output of javac is not code that can be directly executed.
To actually run the program, you must use the Java application launcher called java. To
do so, pass the class name Example as a command-line argument, as shown here:
C:\>java Example

When the program is run, the following output is displayed:
This is a simple Java program.

When Java source code is compiled, each individual class is put into its own output file
named after the class and using the .class extension. This is why it is a good idea to give
your Java source files the same name as the class they contain—the name of the source file
will match the name of the .class file. When you execute java as just shown, you are actually
specifying the name of the class that you want to execute. It will automatically search for a
file by that name that has the .class extension. If it finds the file, it will execute the code
contained in the specified class.

A Closer Look at the First Sample Program
Although Example.java is quite short, it includes several key features that are common to
all Java programs. Let’s closely examine each part of the program.
The program begins with the following lines:
/*
This is a simple Java program.
Call this file "Example.java".
*/

This is a comment. Like most other programming languages, Java lets you enter a remark
into a program’s source file. The contents of a comment are ignored by the compiler.
Instead, a comment describes or explains the operation of the program to anyone who is
reading its source code. In this case, the comment describes the program and reminds you
that the source file should be called Example.java. Of course, in real applications, comments
generally explain how some part of the program works or what a specific feature does.
Java supports three styles of comments. The one shown at the top of the program is
called a multiline comment. This type of comment must begin with /* and end with */.
Anything between these two comment symbols is ignored by the compiler. As the name
suggests, a multiline comment may be several lines long.
The next line of code in the program is shown here:
class Example {

This line uses the keyword class to declare that a new class is being defined. Example
is an identifier that is the name of the class. The entire class definition, including all of its
members, will be between the opening curly brace ({) and the closing curly brace (}). For
the moment, don’t worry too much about the details of a class except to note that in Java,
all program activity occurs within one. This is one reason why all Java programs are (at least
a little bit) object-oriented.

Chapter 2

An Overview of Java

25

// Your program begins with a call to main().

This is the second type of comment supported by Java. A single-line comment begins with
a // and ends at the end of the line. As a general rule, programmers use multiline comments
for longer remarks and single-line comments for brief, line-by-line descriptions. The third
type of comment, a documentation comment, will be discussed in the “Comments” section
later in this chapter.
The next line of code is shown here:
public static void main(String args[ ]) {

This line begins the main( ) method. As the comment preceding it suggests, this is the line
at which the program will begin executing. All Java applications begin execution by calling
main( ). The full meaning of each part of this line cannot be given now, since it involves a
detailed understanding of Java’s approach to encapsulation. However, since most of the
examples in the first part of this book will use this line of code, let’s take a brief look at
each part now.
The public keyword is an access modifier, which allows the programmer to control the
visibility of class members. When a class member is preceded by public, then that member
may be accessed by code outside the class in which it is declared. (The opposite of public is
private, which prevents a member from being used by code defined outside of its class.) In
this case, main( ) must be declared as public, since it must be called by code outside of its
class when the program is started. The keyword static allows main( ) to be called without
having to instantiate a particular instance of the class. This is necessary since main( ) is
called by the Java Virtual Machine before any objects are made. The keyword void simply
tells the compiler that main( ) does not return a value. As you will see, methods may also
return values. If all this seems a bit confusing, don’t worry. All of these concepts will be
discussed in detail in subsequent chapters.
As stated, main( ) is the method called when a Java application begins. Keep in mind
that Java is case-sensitive. Thus, Main is different from main. It is important to understand
that the Java compiler will compile classes that do not contain a main( ) method. But java
has no way to run these classes. So, if you had typed Main instead of main, the compiler
would still compile your program. However, java would report an error because it would be
unable to find the main( ) method.
Any information that you need to pass to a method is received by variables specified
within the set of parentheses that follow the name of the method. These variables are called
parameters. If there are no parameters required for a given method, you still need to include
the empty parentheses. In main( ), there is only one parameter, albeit a complicated one.
String args[ ] declares a parameter named args, which is an array of instances of the class
String. (Arrays are collections of similar objects.) Objects of type String store character
strings. In this case, args receives any command-line arguments present when the program
is executed. This program does not make use of this information, but other programs
shown later in this book will.
The last character on the line is the {. This signals the start of main( )’s body. All of the
code that comprises a method will occur between the method’s opening curly brace and its
closing curly brace.

Part I

The next line in the program is the single-line comment, shown here:

26

PART I

The Java Language

One other point: main( ) is simply a starting place for your program. A complex
program will have dozens of classes, only one of which will need to have a main( ) method
to get things started. Furthermore, in some cases, you won’t need main( ) at all. For example,
when creating applets—Java programs that are embedded in web browsers—you won’t use
main( ) since the web browser uses a different means of starting the execution of applets.
The next line of code is shown here. Notice that it occurs inside main( ).
System.out.println("This is a simple Java program.");

This line outputs the string "This is a simple Java program." followed by a new line on the
screen. Output is actually accomplished by the built-in println( ) method. In this case, println( )
displays the string which is passed to it. As you will see, println( ) can be used to display other
types of information, too. The line begins with System.out. While too complicated to explain
in detail at this time, briefly, System is a predefined class that provides access to the system,
and out is the output stream that is connected to the console.
As you have probably guessed, console output (and input) is not used frequently in most
real-world Java applications. Since most modern computing environments are windowed and
graphical in nature, console I/O is used mostly for simple utility programs, demonstration
programs, and server-side code. Later in this book, you will learn other ways to generate
output using Java. But for now, we will continue to use the console I/O methods.
Notice that the println( ) statement ends with a semicolon. All statements in Java end
with a semicolon. The reason that the other lines in the program do not end in a semicolon
is that they are not, technically, statements.
The first } in the program ends main( ), and the last } ends the Example class definition.

A Second Short Program
Perhaps no other concept is more fundamental to a programming language than that of a
variable. As you probably know, a variable is a named memory location that may be assigned
a value by your program. The value of a variable may be changed during the execution of
the program. The next program shows how a variable is declared and how it is assigned a
value. The program also illustrates some new aspects of console output. As the comments
at the top of the program state, you should call this file Example2.java.
/*
Here is another short example.
Call this file "Example2.java".
*/
class Example2 {
public static void main(String args []) {
int num; // this declares a variable called num
num = 100; // this assigns num the value 100
System.out.println("This is num: " + num);
num = num * 2;
System.out.print("The value of num * 2 is ");

Chapter 2

An Overview of Java

27

System.out.println(num);
}

When you run this program, you will see the following output:
This is num: 100
The value of num * 2 is 200

Let’s take a close look at why this output is generated. The first new line in the program
is shown here:
int num; // this declares a variable called num

This line declares an integer variable called num. Java (like most other languages) requires
that variables be declared before they are used.
Following is the general form of a variable declaration:
type var-name;
Here, type specifies the type of variable being declared, and var-name is the name of the
variable. If you want to declare more than one variable of the specified type, you may use a
comma-separated list of variable names. Java defines several data types, including integer,
character, and floating-point. The keyword int specifies an integer type.
In the program, the line
num = 100; // this assigns num the value 100

assigns to num the value 100. In Java, the assignment operator is a single equal sign.
The next line of code outputs the value of num preceded by the string "This is num:".
System.out.println("This is num: " + num);

In this statement, the plus sign causes the value of num to be appended to the string that
precedes it, and then the resulting string is output. (Actually, num is first converted from an
integer into its string equivalent and then concatenated with the string that precedes it. This
process is described in detail later in this book.) This approach can be generalized. Using
the + operator, you can join together as many items as you want within a single println( )
statement.
The next line of code assigns num the value of num times 2. Like most other languages,
Java uses the * operator to indicate multiplication. After this line executes, num will contain
the value 200.
Here are the next two lines in the program:
System.out.print ("The value of num * 2 is ");
System.out.println (num);

Several new things are occurring here. First, the built-in method print( ) is used to
display the string "The value of num * 2 is ". This string is not followed by a newline. This
means that when the next output is generated, it will start on the same line. The print( )
method is just like println( ), except that it does not output a newline character after each
call. Now look at the call to println( ). Notice that num is used by itself. Both print( ) and
println( ) can be used to output values of any of Java’s built-in types.

Part I

}

28

PART I

The Java Language

Two Control Statements
Although Chapter 5 will look closely at control statements, two are briefly introduced here
so that they can be used in example programs in Chapters 3 and 4. They will also help
illustrate an important aspect of Java: blocks of code.

The if Statement
The Java if statement works much like the IF statement in any other language. Further, it is
syntactically identical to the if statements in C, C++, and C#. Its simplest form is shown here:
if(condition) statement;
Here, condition is a Boolean expression. If condition is true, then the statement is executed.
If condition is false, then the statement is bypassed. Here is an example:
if(num < 100) System.out.println("num is less than 100");

In this case, if num contains a value that is less than 100, the conditional expression is
true, and println( ) will execute. If num contains a value greater than or equal to 100, then
the println( ) method is bypassed.
As you will see in Chapter 4, Java defines a full complement of relational operators
which may be used in a conditional expression. Here are a few:

Operator

Meaning

<

Less than

>

Greater than

==

Equal to

Notice that the test for equality is the double equal sign.
Here is a program that illustrates the if statement:
/*
Demonstrate the if.
Call this file "IfSample.java".
*/
class IfSample {
public static void main(String args[]) {
int x, y;
x = 10;
y = 20;
if(x < y) System.out.println("x is less than y");
x = x * 2;
if(x == y) System.out.println("x now equal to y");

Chapter 2

An Overview of Java

29

// this won't display anything
if(x == y) System.out.println("you won't see this");
}
}

The output generated by this program is shown here:
x is less than y
x now equal to y
x now greater than y

Notice one other thing in this program. The line
int x, y;

declares two variables, x and y, by use of a comma-separated list.

The for Loop
As you may know from your previous programming experience, loop statements are an
important part of nearly any programming language. Java is no exception. In fact, as you
will see in Chapter 5, Java supplies a powerful assortment of loop constructs. Perhaps the
most versatile is the for loop. The simplest form of the for loop is shown here:
for(initialization; condition; iteration) statement;
In its most common form, the initialization portion of the loop sets a loop control
variable to an initial value. The condition is a Boolean expression that tests the loop control
variable. If the outcome of that test is true, the for loop continues to iterate. If it is false,
the loop terminates. The iteration expression determines how the loop control variable is
changed each time the loop iterates. Here is a short program that illustrates the for loop:
/*
Demonstrate the for loop.
Call this file "ForTest.java".
*/
class ForTest {
public static void main(String args[]) {
int x;
for(x = 0; x<10; x = x+1)
System.out.println("This is x: " + x);
}
}

This program generates the following output:
This
This
This
This

is
is
is
is

x:
x:
x:
x:

0
1
2
3

Part I

x = x * 2;
if(x > y) System.out.println("x now greater than y");

30

PART I

The Java Language

This
This
This
This
This
This

is
is
is
is
is
is

x:
x:
x:
x:
x:
x:

4
5
6
7
8
9

In this example, x is the loop control variable. It is initialized to zero in the initialization
portion of the for. At the start of each iteration (including the first one), the conditional
test x < 10 is performed. If the outcome of this test is true, the println( ) statement is
executed, and then the iteration portion of the loop is executed. This process continues
until the conditional test is false.
As a point of interest, in professionally written Java programs you will almost never see
the iteration portion of the loop written as shown in the preceding program. That is, you
will seldom see statements like this:
x = x + 1;

The reason is that Java includes a special increment operator which performs this operation
more efficiently. The increment operator is ++. (That is, two plus signs back to back.) The
increment operator increases its operand by one. By use of the increment operator, the
preceding statement can be written like this:
x++;

Thus, the for in the preceding program will usually be written like this:
for(x = 0; x<10; x++)

You might want to try this. As you will see, the loop still runs exactly the same as it did
before.
Java also provides a decrement operator, which is specified as – –. This operator
decreases its operand by one.

Using Blocks of Code
Java allows two or more statements to be grouped into blocks of code, also called code blocks.
This is done by enclosing the statements between opening and closing curly braces. Once a
block of code has been created, it becomes a logical unit that can be used any place that a
single statement can. For example, a block can be a target for Java’s if and for statements.
Consider this if statement:
if(x < y) { // begin a block
x = y;
y = 0;
} // end of block

Here, if x is less than y, then both statements inside the block will be executed. Thus, the
two statements inside the block form a logical unit, and one statement cannot execute
without the other also executing. The key point here is that whenever you need to logically
link two or more statements, you do so by creating a block.

Chapter 2

An Overview of Java

31

/*
Demonstrate a block of code.
Call this file "BlockTest.java"
*/
class BlockTest {
public static void main(String args[]) {
int x, y;
y = 20;
// the target of this loop is a block
for(x = 0; x<10; x++) {
System.out.println("This is x: " + x);
System.out.println("This is y: " + y);
y = y - 2;
}
}
}

The output generated by this program is shown here:
This
This
This
This
This
This
This
This
This
This
This
This
This
This
This
This
This
This
This
This

is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is

x:
y:
x:
y:
x:
y:
x:
y:
x:
y:
x:
y:
x:
y:
x:
y:
x:
y:
x:
y:

0
20
1
18
2
16
3
14
4
12
5
10
6
8
7
6
8
4
9
2

In this case, the target of the for loop is a block of code and not just a single statement.
Thus, each time the loop iterates, the three statements inside the block will be executed.
This fact is, of course, evidenced by the output generated by the program.
As you will see later in this book, blocks of code have additional properties and uses.
However, the main reason for their existence is to create logically inseparable units of code.

Part I

Let’s look at another example. The following program uses a block of code as the target
of a for loop.

32

PART I

The Java Language

Lexical Issues
Now that you have seen several short Java programs, it is time to more formally describe the
atomic elements of Java. Java programs are a collection of whitespace, identifiers, literals,
comments, operators, separators, and keywords. The operators are described in the next
chapter. The others are described next.

Whitespace
Java is a free-form language. This means that you do not need to follow any special
indentation rules. For instance, the Example program could have been written all on one
line or in any other strange way you felt like typing it, as long as there was at least one
whitespace character between each token that was not already delineated by an operator
or separator. In Java, whitespace is a space, tab, or newline.

Identifiers
Identifiers are used to name things, such as classes, variables, and methods. An identifier
may be any descriptive sequence of uppercase and lowercase letters, numbers, or the
underscore and dollar-sign characters. (The dollar-sign character is not intended for
general use.) They must not begin with a number, lest they be confused with a numeric
literal. Again, Java is case-sensitive, so VALUE is a different identifier than Value. Some
examples of valid identifiers are
AvgTemp

count

a4

$test

this_is_ok

Invalid identifier names include these:
2count

high-temp

Not/ok

Literals
A constant value in Java is created by using a literal representation of it. For example, here
are some literals:
100

98.6

‘X’

“This is a test”

Left to right, the first literal specifies an integer, the next is a floating-point value, the third
is a character constant, and the last is a string. A literal can be used anywhere a value of its
type is allowed.

Comments
As mentioned, there are three types of comments defined by Java. You have already seen
two: single-line and multiline. The third type is called a documentation comment. This type
of comment is used to produce an HTML file that documents your program. The
documentation comment begins with a /** and ends with a */. Documentation comments
are explained in the Appendix.

Chapter 2

An Overview of Java

33

In Java, there are a few characters that are used as separators. The most commonly used
separator in Java is the semicolon. As you have seen, it is used to terminate statements. The
separators are shown in the following table:

Symbol

Name

Purpose

()

Parentheses

Used to contain lists of parameters in method definition and
invocation. Also used for defining precedence in expressions,
containing expressions in control statements, and surrounding
cast types.

{}

Braces

Used to contain the values of automatically initialized arrays.
Also used to define a block of code, for classes, methods, and
local scopes.

[]

Brackets

Used to declare array types. Also used when dereferencing array
values.

;

Semicolon

Terminates statements.

,

Comma

Separates consecutive identifiers in a variable declaration. Also
used to chain statements together inside a for statement.

.

Period

Used to separate package names from subpackages and classes. Also
used to separate a variable or method from a reference variable.

The Java Keywords
There are 50 keywords currently defined in the Java language (see Table 2-1). These
keywords, combined with the syntax of the operators and separators, form the foundation
of the Java language. These keywords cannot be used as identifiers. Thus, they cannot be
used as names for a variable, class, or method.
The keywords const and goto are reserved but not used. In the early days of Java, several
other keywords were reserved for possible future use. However, the current specification for
Java defines only the keywords shown in Table 2-1.

abstract

continue

for

new

switch

assert

default

goto

package

synchronized

boolean

do

if

private

this

break

double

implements

protected

throw

byte

else

import

public

throws

case

enum

instanceof

return

transient

catch

extends

int

short

try

char

final

interface

static

void

class

finally

long

strictfp

volatile

const

float

native

super

while

Table 2-1 Java Keywords

Part I

Separators

34

PART I

The Java Language

In addition to the keywords, Java reserves the following: true, false, and null. These are
values defined by Java. You may not use these words for the names of variables, classes, and
so on.

The Java Class Libraries
The sample programs shown in this chapter make use of two of Java’s built-in methods:
println( ) and print( ). As mentioned, these methods are members of the System class,
which is a class predefined by Java that is automatically included in your programs. In the
larger view, the Java environment relies on several built-in class libraries that contain many
built-in methods that provide support for such things as I/O, string handling, networking,
and graphics. The standard classes also provide support for windowed output. Thus, Java as
a totality is a combination of the Java language itself, plus its standard classes. As you will
see, the class libraries provide much of the functionality that comes with Java. Indeed, part
of becoming a Java programmer is learning to use the standard Java classes. Throughout
Part I of this book, various elements of the standard library classes and methods are
described as needed. In Part II, the class libraries are described in detail.

CHAPTER

3

Data Types, Variables,
and Arrays

This chapter examines three of Java’s most fundamental elements: data types, variables, and
arrays. As with all modern programming languages, Java supports several types of data. You
may use these types to declare variables and to create arrays. As you will see, Java’s approach
to these items is clean, efficient, and cohesive.

Java Is a Strongly Typed Language
It is important to state at the outset that Java is a strongly typed language. Indeed, part
of Java’s safety and robustness comes from this fact. Let’s see what this means. First, every
variable has a type, every expression has a type, and every type is strictly defined. Second,
all assignments, whether explicit or via parameter passing in method calls, are checked for
type compatibility. There are no automatic coercions or conversions of conflicting types as
in some languages. The Java compiler checks all expressions and parameters to ensure that
the types are compatible. Any type mismatches are errors that must be corrected before the
compiler will finish compiling the class.

The Primitive Types
Java defines eight primitive types of data: byte, short, int, long, char, float, double, and
boolean. The primitive types are also commonly referred to as simple types, and both
terms will be used in this book. These can be put in four groups:
• Integers This group includes byte, short, int, and long, which are for whole-valued
signed numbers.
• Floating-point numbers This group includes float and double, which represent
numbers with fractional precision.
• Characters This group includes char, which represents symbols in a character set,
like letters and numbers.
• Boolean This group includes boolean, which is a special type for representing
true/false values.

35

36

PART I

The Java Language

You can use these types as-is, or to construct arrays or your own class types. Thus, they
form the basis for all other types of data that you can create.
The primitive types represent single values—not complex objects. Although Java is
otherwise completely object-oriented, the primitive types are not. They are analogous to
the simple types found in most other non–object-oriented languages. The reason for this
is efficiency. Making the primitive types into objects would have degraded performance
too much.
The primitive types are defined to have an explicit range and mathematical behavior.
Languages such as C and C++ allow the size of an integer to vary based upon the dictates
of the execution environment. However, Java is different. Because of Java’s portability
requirement, all data types have a strictly defined range. For example, an int is always 32 bits,
regardless of the particular platform. This allows programs to be written that are guaranteed
to run without porting on any machine architecture. While strictly specifying the size of an
integer may cause a small loss of performance in some environments, it is necessary in
order to achieve portability.
Let’s look at each type of data in turn.

Integers
Java defines four integer types: byte, short, int, and long. All of these are signed, positive
and negative values. Java does not support unsigned, positive-only integers. Many other
computer languages support both signed and unsigned integers. However, Java’s designers
felt that unsigned integers were unnecessary. Specifically, they felt that the concept of
unsigned was used mostly to specify the behavior of the high-order bit, which defines the sign
of an integer value. As you will see in Chapter 4, Java manages the meaning of the highorder bit differently, by adding a special “unsigned right shift” operator. Thus, the need for
an unsigned integer type was eliminated.
The width of an integer type should not be thought of as the amount of storage it
consumes, but rather as the behavior it defines for variables and expressions of that type.
The Java run-time environment is free to use whatever size it wants, as long as the types
behave as you declared them. The width and ranges of these integer types vary widely, as
shown in this table:

Name

Width

Range

long

64

–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

int

32

–2,147,483,648 to 2,147,483,647

short

16

–32,768 to 32,767

byte

8

–128 to 127

Let’s look at each type of integer.

byte
The smallest integer type is byte. This is a signed 8-bit type that has a range from –128 to
127. Variables of type byte are especially useful when you’re working with a stream of data
from a network or file. They are also useful when you’re working with raw binary data that
may not be directly compatible with Java’s other built-in types.

Chapter 3

Data Types, Variables, and Arrays

37

byte b, c;

short
short is a signed 16-bit type. It has a range from –32,768 to 32,767. It is probably the leastused Java type. Here are some examples of short variable declarations:
short s;
short t;

int
The most commonly used integer type is int. It is a signed 32-bit type that has a range
from –2,147,483,648 to 2,147,483,647. In addition to other uses, variables of type int are
commonly employed to control loops and to index arrays. Although you might think that
using a byte or short would be more efficient than using an int in situations in which the
larger range of an int is not needed, this may not be the case. The reason is that when byte
and short values are used in an expression they are promoted to int when the expression is
evaluated. (Type promotion is described later in this chapter.) Therefore, int is often the
best choice when an integer is needed.

long
long is a signed 64-bit type and is useful for those occasions where an int type is not large
enough to hold the desired value. The range of a long is quite large. This makes it useful
when big, whole numbers are needed. For example, here is a program that computes the
number of miles that light will travel in a specified number of days:
// Compute distance light travels using long variables.
class Light {
public static void main(String args[]) {
int lightspeed;
long days;
long seconds;
long distance;
// approximate speed of light in miles per second
lightspeed = 186000;
days = 1000; // specify number of days here
seconds = days * 24 * 60 * 60; // convert to seconds
distance = lightspeed * seconds; // compute distance
System.out.print("In " + days);
System.out.print(" days light will travel about ");
System.out.println(distance + " miles.");
}
}

Part I

Byte variables are declared by use of the byte keyword. For example, the following
declares two byte variables called b and c:

38

PART I

The Java Language

This program generates the following output:
In 1000 days light will travel about 16070400000000 miles.

Clearly, the result could not have been held in an int variable.

Floating-Point Types
Floating-point numbers, also known as real numbers, are used when evaluating expressions
that require fractional precision. For example, calculations such as square root, or
transcendentals such as sine and cosine, result in a value whose precision requires a floatingpoint type. Java implements the standard (IEEE–754) set of floating-point types and
operators. There are two kinds of floating-point types, float and double, which represent
single- and double-precision numbers, respectively. Their width and ranges are shown here:

Name

Width in Bits

Approximate Range

double

64

4.9e–324 to 1.8e+308

float

32

1.4e–045 to 3.4e+038

Each of these floating-point types is examined next.

float
The type float specifies a single-precision value that uses 32 bits of storage. Single precision is
faster on some processors and takes half as much space as double precision, but will become
imprecise when the values are either very large or very small. Variables of type float are
useful when you need a fractional component, but don’t require a large degree of precision.
For example, float can be useful when representing dollars and cents.
Here are some example float variable declarations:
float hightemp, lowtemp;

double
Double precision, as denoted by the double keyword, uses 64 bits to store a value. Double
precision is actually faster than single precision on some modern processors that have been
optimized for high-speed mathematical calculations. All transcendental math functions,
such as sin( ), cos( ), and sqrt( ), return double values. When you need to maintain accuracy
over many iterative calculations, or are manipulating large-valued numbers, double is the
best choice.
Here is a short program that uses double variables to compute the area of a circle:
// Compute the area of a circle.
class Area {
public static void main(String args[]) {
double pi, r, a;
r = 10.8; // radius of circle
pi = 3.1416; // pi, approximately

Chapter 3

Data Types, Variables, and Arrays

39

System.out.println("Area of circle is " + a);
}
}

Characters
In Java, the data type used to store characters is char. However, C/C++ programmers
beware: char in Java is not the same as char in C or C++. In C/C++, char is 8 bits wide. This
is not the case in Java. Instead, Java uses Unicode to represent characters. Unicode defines a
fully international character set that can represent all of the characters found in all human
languages. It is a unification of dozens of character sets, such as Latin, Greek, Arabic,
Cyrillic, Hebrew, Katakana, Hangul, and many more. For this purpose, it requires 16 bits.
Thus, in Java char is a 16-bit type. The range of a char is 0 to 65,536. There are no negative
chars. The standard set of characters known as ASCII still ranges from 0 to 127 as always,
and the extended 8-bit character set, ISO-Latin-1, ranges from 0 to 255. Since Java is
designed to allow programs to be written for worldwide use, it makes sense that it would use
Unicode to represent characters. Of course, the use of Unicode is somewhat inefficient for
languages such as English, German, Spanish, or French, whose characters can easily be
contained within 8 bits. But such is the price that must be paid for global portability.
NOTE More information about Unicode can be found at http://www.unicode.org.
Here is a program that demonstrates char variables:
// Demonstrate char data type.
class CharDemo {
public static void main(String args[]) {
char ch1, ch2;
ch1 = 88; // code for X
ch2 = 'Y';
System.out.print("ch1 and ch2: ");
System.out.println(ch1 + " " + ch2);
}
}

This program displays the following output:
ch1 and ch2: X Y

Notice that ch1 is assigned the value 88, which is the ASCII (and Unicode) value that
corresponds to the letter X. As mentioned, the ASCII character set occupies the first 127
values in the Unicode character set. For this reason, all the “old tricks” that you may have
used with characters in other languages will work in Java, too.
Although char is designed to hold Unicode characters, it can also be used as an integer
type on which you can perform arithmetic operations. For example, you can add two

Part I

a = pi * r * r; // compute area

40

PART I

The Java Language

characters together, or increment the value of a character variable. Consider the following
program:
// char variables behave like integers.
class CharDemo2 {
public static void main(String args[]) {
char ch1;
ch1 = 'X';
System.out.println("ch1 contains " + ch1);
ch1++; // increment ch1
System.out.println("ch1 is now " + ch1);
}
}

The output generated by this program is shown here:
ch1 contains X
ch1 is now Y

In the program, ch1 is first given the value X. Next, ch1 is incremented. This results in ch1
containing Y, the next character in the ASCII (and Unicode) sequence.
NOTE In the formal specification for Java, char is referred to as an integral type, which means that it is
in the same general category as int, short, long, and byte. However, because its principal use is for
representing Unicode characters, char is commonly considered to be in a category of its own.

Booleans
Java has a primitive type, called boolean, for logical values. It can have only one of two
possible values, true or false. This is the type returned by all relational operators, as in the
case of a < b. boolean is also the type required by the conditional expressions that govern the
control statements such as if and for.
Here is a program that demonstrates the boolean type:
// Demonstrate boolean values.
class BoolTest {
public static void main(String args[]) {
boolean b;
b = false;
System.out.println("b is " + b);
b = true;
System.out.println("b is " + b);
// a boolean value can control the if statement
if(b) System.out.println("This is executed.");
b = false;
if(b) System.out.println("This is not executed.");

Chapter 3

Data Types, Variables, and Arrays

41

}
}

The output generated by this program is shown here:
b is
b is
This
10 >

false
true
is executed.
9 is true

There are three interesting things to notice about this program. First, as you can see,
when a boolean value is output by println( ), "true" or "false" is displayed. Second, the value
of a boolean variable is sufficient, by itself, to control the if statement. There is no need to
write an if statement like this:
if(b == true) …

Third, the outcome of a relational operator, such as <, is a boolean value. This is why the
expression 10>9 displays the value "true." Further, the extra set of parentheses around 10>9
is necessary because the + operator has a higher precedence than the >.

A Closer Look at Literals
Literals were mentioned briefly in Chapter 2. Now that the built-in types have been formally
described, let’s take a closer look at them.

Integer Literals
Integers are probably the most commonly used type in the typical program. Any whole
number value is an integer literal. Examples are 1, 2, 3, and 42. These are all decimal values,
meaning they are describing a base 10 number. There are two other bases which can be used
in integer literals, octal (base eight) and hexadecimal (base 16). Octal values are denoted in
Java by a leading zero. Normal decimal numbers cannot have a leading zero. Thus, the
seemingly valid value 09 will produce an error from the compiler, since 9 is outside of octal’s
0 to 7 range. A more common base for numbers used by programmers is hexadecimal,
which matches cleanly with modulo 8 word sizes, such as 8, 16, 32, and 64 bits. You signify a
hexadecimal constant with a leading zero-x, (0x or 0X). The range of a hexadecimal digit is
0 to 15, so A through F (or a through f ) are substituted for 10 through 15.
Integer literals create an int value, which in Java is a 32-bit integer value. Since Java is
strongly typed, you might be wondering how it is possible to assign an integer literal to one
of Java’s other integer types, such as byte or long, without causing a type mismatch error.
Fortunately, such situations are easily handled. When a literal value is assigned to a byte or
short variable, no error is generated if the literal value is within the range of the target type.
An integer literal can always be assigned to a long variable. However, to specify a long
literal, you will need to explicitly tell the compiler that the literal value is of type long. You
do this by appending an upper- or lowercase L to the literal. For example, 0x7ffffffffffffffL
or 9223372036854775807L is the largest long. An integer can also be assigned to a char as
long as it is within range.

Part I

// outcome of a relational operator is a boolean value
System.out.println("10 > 9 is " + (10 > 9));

42

PART I

The Java Language

Beginning with JDK 7, you can also specify integer literals using binary. To do so, prefix
the value with 0b or 0B. For example, this specifies the decimal value 10 using a binary
literal:
int x = 0b1010;

Among other uses, the addition of binary literals makes it easier to enter values used as
bitmasks. In such a case, the decimal (or hexadecimal) representation of the value does not
visually convey its meaning relative to its use. The binary literal does.
Also beginning with JDK 7, you can embed one or more underscores in an integer
literal. Doing so makes it easier to read large integer literals. When the literal is compiled,
the underscores are discarded. For example, given
int x = 123_456_789;

the value given to x will be 123,456,789. The underscores will be ignored. Underscores can
only be used to separate digits. They cannot come at the beginning or the end of a literal. It
is, however, permissible for more than one underscore to be used between two digits. For
example, this is valid:
int x = 123___456___789;

The use of underscores in an integer literal is especially useful when encoding such
things as telephone numbers, customer ID numbers, part numbers, and so on. They are
also useful for providing visual groupings when specifying binary literals. For example,
binary values are often visually grouped in four-digits units, as shown here:
int x = 0b1101_0101_0001_1010;

Floating-Point Literals
Floating-point numbers represent decimal values with a fractional component. They can be
expressed in either standard or scientific notation. Standard notation consists of a whole
number component followed by a decimal point followed by a fractional component. For
example, 2.0, 3.14159, and 0.6667 represent valid standard-notation floating-point
numbers. Scientific notation uses a standard-notation, floating-point number plus a suffix that
specifies a power of 10 by which the number is to be multiplied. The exponent is indicated
by an E or e followed by a decimal number, which can be positive or negative. Examples
include 6.022E23, 314159E–05, and 2e+100.
Floating-point literals in Java default to double precision. To specify a float literal, you
must append an F or f to the constant. You can also explicitly specify a double literal by
appending a D or d. Doing so is, of course, redundant. The default double type consumes
64 bits of storage, while the smaller float type requires only 32 bits.
Hexadecimal floating-point literals are also supported, but they are rarely used. They
must be in a form similar to scientific notation, but a P or p, rather than an E or e, is used.
For example, 0x12.2P2 is a valid floating-point literal. The value following the P, called the
binary exponent, indicates the power-of-two by which the number is multiplied. Therefore,
0x12.2P2 represents 72.5.

Data Types, Variables, and Arrays

43

Beginning with JDK 7, you can embed one or more underscores in a floating-point
literal. This feature works the same as it does for integer literals, which were just described.
Its purpose is to make it easier to read large floating-point literals. When the literal is
compiled, the underscores are discarded. For example, given
double num = 9_423_497_862.0;

the value given to num will be 9,423,497,862.0. The underscores will be ignored. As is the
case with integer literals, underscores can only be used to separate digits. They cannot
come at the beginning or the end of a literal. It is, however, permissible for more than one
underscore to be used between two digits. It is also permissible to use underscores in the
fractional portion of the number. For example,
double num = 9_423_497.1_0_9;

is legal. In this case, the fractional part is .109.

Boolean Literals
Boolean literals are simple. There are only two logical values that a boolean value can have,
true and false. The values of true and false do not convert into any numerical representation.
The true literal in Java does not equal 1, nor does the false literal equal 0. In Java, the
Boolean literals can only be assigned to variables declared as boolean or used in expressions
with Boolean operators.

Character Literals
Characters in Java are indices into the Unicode character set. They are 16-bit values that
can be converted into integers and manipulated with the integer operators, such as the
addition and subtraction operators. A literal character is represented inside a pair of single
quotes. All of the visible ASCII characters can be directly entered inside the quotes, such as
'a', 'z', and '@'. For characters that are impossible to enter directly, there are several escape
sequences that allow you to enter the character you need, such as ' \" for the single-quote
character itself and ' \n' for the newline character. There is also a mechanism for directly
entering the value of a character in octal or hexadecimal. For octal notation, use the
backslash followed by the three-digit number. For example, ' \141' is the letter 'a'. For
hexadecimal, you enter a backslash-u ( \u), then exactly four hexadecimal digits. For
example, ' \u0061' is the ISO-Latin-1 'a' because the top byte is zero. ' \ua432 ' is a Japanese
Katakana character. Table 3-1 shows the character escape sequences.

String Literals
String literals in Java are specified like they are in most other languages—by enclosing a
sequence of characters between a pair of double quotes. Examples of string literals are
"Hello World"
"two\nlines"
" \"This is in quotes\""

Part I

Chapter 3

44

PART I

The Java Language

Escape Sequence

Description

\ddd

Octal character (ddd)

\uxxxx

Hexadecimal Unicode character (xxxx)

\'

Single quote

\"

Double quote

\\

Backslash

\r

Carriage return

\n

New line (also known as line feed)

\f

Form feed

\t

Tab

\b

Backspace

Table 3-1 Character Escape Sequences
The escape sequences and octal/hexadecimal notations that were defined for character
literals work the same way inside of string literals. One important thing to note about Java
strings is that they must begin and end on the same line. There is no line-continuation
escape sequence as there is in some other languages.
NOTE As you may know, in some other languages, including C/C++, strings are implemented as arrays of
characters. However, this is not the case in Java. Strings are actually object types. As you will see later
in this book, because Java implements strings as objects, Java includes extensive string-handling
capabilities that are both powerful and easy to use.

Variables
The variable is the basic unit of storage in a Java program. A variable is defined by the
combination of an identifier, a type, and an optional initializer. In addition, all variables have
a scope, which defines their visibility, and a lifetime. These elements are examined next.

Declaring a Variable
In Java, all variables must be declared before they can be used. The basic form of a variable
declaration is shown here:
type identifier [ = value ][, identifier [= value ] …];
The type is one of Java’s atomic types, or the name of a class or interface. (Class and
interface types are discussed later in Part I of this book.) The identifier is the name of the
variable. You can initialize the variable by specifying an equal sign and a value. Keep in
mind that the initialization expression must result in a value of the same (or compatible)
type as that specified for the variable. To declare more than one variable of the specified type,
use a comma-separated list.
Here are several examples of variable declarations of various types. Note that some
include an initialization.

int a, b, c;
int d = 3, e, f = 5;
byte z = 22;
double pi = 3.14159;
char x = 'x';

//
//
//
//
//
//

Data Types, Variables, and Arrays

45

declares three ints, a, b, and c.
declares three more ints, initializing
d and f.
initializes z.
declares an approximation of pi.
the variable x has the value 'x'.

The identifiers that you choose have nothing intrinsic in their names that indicates
their type. Java allows any properly formed identifier to have any declared type.

Dynamic Initialization
Although the preceding examples have used only constants as initializers, Java allows
variables to be initialized dynamically, using any expression valid at the time the variable
is declared.
For example, here is a short program that computes the length of the hypotenuse of a
right triangle given the lengths of its two opposing sides:
// Demonstrate dynamic initialization.
class DynInit {
public static void main(String args[]) {
double a = 3.0, b = 4.0;
// c is dynamically initialized
double c = Math.sqrt(a * a + b * b);
System.out.println("Hypotenuse is " + c);
}
}

Here, three local variables—a, b, and c—are declared. The first two, a and b, are initialized
by constants. However, c is initialized dynamically to the length of the hypotenuse (using
the Pythagorean theorem). The program uses another of Java’s built-in methods, sqrt( ),
which is a member of the Math class, to compute the square root of its argument. The key
point here is that the initialization expression may use any element valid at the time of the
initialization, including calls to methods, other variables, or literals.

The Scope and Lifetime of Variables
So far, all of the variables used have been declared at the start of the main( ) method.
However, Java allows variables to be declared within any block. As explained in Chapter 2,
a block is begun with an opening curly brace and ended by a closing curly brace. A block
defines a scope. Thus, each time you start a new block, you are creating a new scope. A scope
determines what objects are visible to other parts of your program. It also determines the
lifetime of those objects.
Many other computer languages define two general categories of scopes: global and
local. However, these traditional scopes do not fit well with Java’s strict, object-oriented
model. While it is possible to create what amounts to being a global scope, it is by far the
exception, not the rule. In Java, the two major scopes are those defined by a class and those
defined by a method. Even this distinction is somewhat artificial. However, since the class
scope has several unique properties and attributes that do not apply to the scope defined

Part I

Chapter 3

46

PART I

The Java Language

by a method, this distinction makes some sense. Because of the differences, a discussion of
class scope (and variables declared within it) is deferred until Chapter 6, when classes are
described. For now, we will only examine the scopes defined by or within a method.
The scope defined by a method begins with its opening curly brace. However, if that
method has parameters, they too are included within the method’s scope. Although this
book will look more closely at parameters in Chapter 6, for the sake of this discussion, they
work the same as any other method variable.
As a general rule, variables declared inside a scope are not visible (that is, accessible)
to code that is defined outside that scope. Thus, when you declare a variable within a
scope, you are localizing that variable and protecting it from unauthorized access and/or
modification. Indeed, the scope rules provide the foundation for encapsulation.
Scopes can be nested. For example, each time you create a block of code, you are
creating a new, nested scope. When this occurs, the outer scope encloses the inner scope.
This means that objects declared in the outer scope will be visible to code within the inner
scope. However, the reverse is not true. Objects declared within the inner scope will not be
visible outside it.
To understand the effect of nested scopes, consider the following program:
// Demonstrate block scope.
class Scope {
public static void main(String args[]) {
int x; // known to all code within main
x = 10;
if(x == 10) { // start new scope
int y = 20; // known only to this block
// x and y both known here.
System.out.println("x and y: " + x + " " + y);
x = y * 2;
}
// y = 100; // Error! y not known here
// x is still known here.
System.out.println("x is " + x);
}
}

As the comments indicate, the variable x is declared at the start of main( )’s scope and is
accessible to all subsequent code within main( ). Within the if block, y is declared. Since a
block defines a scope, y is only visible to other code within its block. This is why outside of
its block, the line y = 100; is commented out. If you remove the leading comment symbol,
a compile-time error will occur, because y is not visible outside of its block. Within the if
block, x can be used because code within a block (that is, a nested scope) has access to
variables declared by an enclosing scope.
Within a block, variables can be declared at any point, but are valid only after they are
declared. Thus, if you define a variable at the start of a method, it is available to all of the
code within that method. Conversely, if you declare a variable at the end of a block, it is
effectively useless, because no code will have access to it. For example, this fragment is
invalid because count cannot be used prior to its declaration:

Data Types, Variables, and Arrays

// This fragment is wrong!
count = 100; // oops! cannot use count before it is declared!
int count;

Here is another important point to remember: variables are created when their scope is
entered, and destroyed when their scope is left. This means that a variable will not hold its
value once it has gone out of scope. Therefore, variables declared within a method will not
hold their values between calls to that method. Also, a variable declared within a block will
lose its value when the block is left. Thus, the lifetime of a variable is confined to its scope.
If a variable declaration includes an initializer, then that variable will be reinitialized
each time the block in which it is declared is entered. For example, consider the next
program:
// Demonstrate lifetime of a variable.
class LifeTime {
public static void main(String args[]) {
int x;
for(x = 0; x < 3; x++) {
int y = -1; // y is initialized each time block is entered
System.out.println("y is: " + y); // this always prints -1
y = 100;
System.out.println("y is now: " + y);
}
}
}

The output generated by this program is shown here:
y
y
y
y
y
y

is: -1
is now: 100
is: -1
is now: 100
is: -1
is now: 100

As you can see, y is reinitialized to –1 each time the inner for loop is entered. Even though
it is subsequently assigned the value 100, this value is lost.
One last point: Although blocks can be nested, you cannot declare a variable to have
the same name as one in an outer scope. For example, the following program is illegal:
// This program will not compile
class ScopeErr {
public static void main(String args[]) {
int bar = 1;
{
// creates a new scope
int bar = 2; // Compile-time error – bar already defined!
}
}
}

47

Part I

Chapter 3

48

PART I

The Java Language

Type Conversion and Casting
If you have previous programming experience, then you already know that it is fairly common
to assign a value of one type to a variable of another type. If the two types are compatible,
then Java will perform the conversion automatically. For example, it is always possible to
assign an int value to a long variable. However, not all types are compatible, and thus, not
all type conversions are implicitly allowed. For instance, there is no automatic conversion
defined from double to byte. Fortunately, it is still possible to obtain a conversion between
incompatible types. To do so, you must use a cast, which performs an explicit conversion
between incompatible types. Let’s look at both automatic type conversions and casting.

Java’s Automatic Conversions
When one type of data is assigned to another type of variable, an automatic type conversion
will take place if the following two conditions are met:
• The two types are compatible.
• The destination type is larger than the source type.
When these two conditions are met, a widening conversion takes place. For example, the
int type is always large enough to hold all valid byte values, so no explicit cast statement is
required.
For widening conversions, the numeric types, including integer and floating-point types,
are compatible with each other. However, there are no automatic conversions from the
numeric types to char or boolean. Also, char and boolean are not compatible with each other.
As mentioned earlier, Java also performs an automatic type conversion when storing a
literal integer constant into variables of type byte, short, long, or char.

Casting Incompatible Types
Although the automatic type conversions are helpful, they will not fulfill all needs. For
example, what if you want to assign an int value to a byte variable? This conversion will not
be performed automatically, because a byte is smaller than an int. This kind of conversion
is sometimes called a narrowing conversion, since you are explicitly making the value narrower
so that it will fit into the target type.
To create a conversion between two incompatible types, you must use a cast. A cast is
simply an explicit type conversion. It has this general form:
(target-type) value
Here, target-type specifies the desired type to convert the specified value to. For example, the
following fragment casts an int to a byte. If the integer’s value is larger than the range of a
byte, it will be reduced modulo (the remainder of an integer division by the) byte’s range.
int a;
byte b;
// …
b = (byte) a;

Data Types, Variables, and Arrays

49

A different type of conversion will occur when a floating-point value is assigned to an
integer type: truncation. As you know, integers do not have fractional components. Thus,
when a floating-point value is assigned to an integer type, the fractional component is lost.
For example, if the value 1.23 is assigned to an integer, the resulting value will simply be 1.
The 0.23 will have been truncated. Of course, if the size of the whole number component is
too large to fit into the target integer type, then that value will be reduced modulo the
target type’s range.
The following program demonstrates some type conversions that require casts:
// Demonstrate casts.
class Conversion {
public static void main(String args[]) {
byte b;
int i = 257;
double d = 323.142;
System.out.println("\nConversion of int to byte.");
b = (byte) i;
System.out.println("i and b " + i + " " + b);
System.out.println("\nConversion of double to int.");
i = (int) d;
System.out.println("d and i " + d + " " + i);
System.out.println("\nConversion of double to byte.");
b = (byte) d;
System.out.println("d and b " + d + " " + b);
}
}

This program generates the following output:
Conversion of int to byte.
i and b 257 1
Conversion of double to int.
d and i 323.142 323
Conversion of double to byte.
d and b 323.142 67

Let’s look at each conversion. When the value 257 is cast into a byte variable, the result is the
remainder of the division of 257 by 256 (the range of a byte), which is 1 in this case. When
the d is converted to an int, its fractional component is lost. When d is converted to a byte, its
fractional component is lost, and the value is reduced modulo 256, which in this case is 67.

Automatic Type Promotion in Expressions
In addition to assignments, there is another place where certain type conversions may
occur: in expressions. To see why, consider the following. In an expression, the precision

Part I

Chapter 3

50

PART I

The Java Language

required of an intermediate value will sometimes exceed the range of either operand. For
example, examine the following expression:
byte a = 40;
byte b = 50;
byte c = 100;
int d = a * b / c;

The result of the intermediate term a * b easily exceeds the range of either of its byte
operands. To handle this kind of problem, Java automatically promotes each byte, short,
or char operand to int when evaluating an expression. This means that the subexpression
a*b is performed using integers—not bytes. Thus, 2,000, the result of the intermediate
expression, 50 * 40, is legal even though a and b are both specified as type byte.
As useful as the automatic promotions are, they can cause confusing compile-time
errors. For example, this seemingly correct code causes a problem:
byte b = 50;
b = b * 2; // Error! Cannot assign an int to a byte!

The code is attempting to store 50 * 2, a perfectly valid byte value, back into a byte
variable. However, because the operands were automatically promoted to int when the
expression was evaluated, the result has also been promoted to int. Thus, the result of the
expression is now of type int, which cannot be assigned to a byte without the use of a cast.
This is true even if, as in this particular case, the value being assigned would still fit in the
target type.
In cases where you understand the consequences of overflow, you should use an explicit
cast, such as
byte b = 50;
b = (byte)(b * 2);

which yields the correct value of 100.

The Type Promotion Rules
Java defines several type promotion rules that apply to expressions. They are as follows: First,
all byte, short, and char values are promoted to int, as just described. Then, if one operand
is a long, the whole expression is promoted to long. If one operand is a float, the entire
expression is promoted to float. If any of the operands are double, the result is double.
The following program demonstrates how each value in the expression gets promoted
to match the second argument to each binary operator:
class Promote {
public static void main(String args[]) {
byte b = 42;
char c = 'a';
short s = 1024;
int i = 50000;
float f = 5.67f;
double d = .1234;
double result = (f * b) + (i / c) - (d * s);

Chapter 3

Data Types, Variables, and Arrays

51

}
}

Let’s look closely at the type promotions that occur in this line from the program:
double result = (f * b) + (i / c) - (d * s);

In the first subexpression, f * b, b is promoted to a float and the result of the subexpression
is float. Next, in the subexpression i/c, c is promoted to int, and the result is of type int.
Then, in d * s, the value of s is promoted to double, and the type of the subexpression is
double. Finally, these three intermediate values, float, int, and double, are considered. The
outcome of float plus an int is a float. Then the resultant float minus the last double is
promoted to double, which is the type for the final result of the expression.

Arrays
An array is a group of like-typed variables that are referred to by a common name. Arrays of
any type can be created and may have one or more dimensions. A specific element in an
array is accessed by its index. Arrays offer a convenient means of grouping related
information.
NOTE If you are familiar with C/C++, be careful. Arrays in Java work differently than they do in those
languages.

One-Dimensional Arrays
A one-dimensional array is, essentially, a list of like-typed variables. To create an array, you first
must create an array variable of the desired type. The general form of a one-dimensional
array declaration is
type var-name[ ];
Here, type declares the element type (also called the base type) of the array. The element type
determines the data type of each element that comprises the array. Thus, the element
type for the array determines what type of data the array will hold. For example, the
following declares an array named month_days with the type “array of int”:
int month_days[];

Although this declaration establishes the fact that month_days is an array variable, no
array actually exists. In fact, the value of month_days is set to null, which represents an array
with no value. To link month_days with an actual, physical array of integers, you must allocate
one using new and assign it to month_days. new is a special operator that allocates memory.
You will look more closely at new in a later chapter, but you need to use it now to
allocate memory for arrays. The general form of new as it applies to one-dimensional
arrays appears as follows:
array-var = new type [size];

Part I

System.out.println((f * b) + " + " + (i / c) + " - " + (d * s));
System.out.println("result = " + result);

52

PART I

The Java Language

Here, type specifies the type of data being allocated, size specifies the number of elements in
the array, and array-var is the array variable that is linked to the array. That is, to use new to
allocate an array, you must specify the type and number of elements to allocate. The elements
in the array allocated by new will automatically be initialized to zero (for numeric types), false
(for boolean), or null (for reference types, which are described in a later chapter). This
example allocates a 12-element array of integers and links them to month_days:
month_days = new int[12];

After this statement executes, month_days will refer to an array of 12 integers. Further, all
elements in the array will be initialized to zero.
Let’s review: Obtaining an array is a two-step process. First, you must declare a variable
of the desired array type. Second, you must allocate the memory that will hold the array,
using new, and assign it to the array variable. Thus, in Java all arrays are dynamically
allocated. If the concept of dynamic allocation is unfamiliar to you, don’t worry. It will
be described at length later in this book.
Once you have allocated an array, you can access a specific element in the array by
specifying its index within square brackets. All array indexes start at zero. For example,
this statement assigns the value 28 to the second element of month_days:
month_days[1] = 28;

The next line displays the value stored at index 3:
System.out.println(month_days[3]);

Putting together all the pieces, here is a program that creates an array of the number of
days in each month:
// Demonstrate a one-dimensional array.
class Array {
public static void main(String args[]) {
int month_days[];
month_days = new int[12];
month_days[0] = 31;
month_days[1] = 28;
month_days[2] = 31;
month_days[3] = 30;
month_days[4] = 31;
month_days[5] = 30;
month_days[6] = 31;
month_days[7] = 31;
month_days[8] = 30;
month_days[9] = 31;
month_days[10] = 30;
month_days[11] = 31;
System.out.println("April has " + month_days[3] + " days.");
}
}

Data Types, Variables, and Arrays

When you run this program, it prints the number of days in April. As mentioned, Java array
indexes start with zero, so the number of days in April is month_days[3] or 30.
It is possible to combine the declaration of the array variable with the allocation of the
array itself, as shown here:
int month_days[] = new int[12];

This is the way that you will normally see it done in professionally written Java programs.
Arrays can be initialized when they are declared. The process is much the same as that
used to initialize the simple types. An array initializer is a list of comma-separated expressions
surrounded by curly braces. The commas separate the values of the array elements. The
array will automatically be created large enough to hold the number of elements you specify
in the array initializer. There is no need to use new. For example, to store the number of
days in each month, the following code creates an initialized array of integers:
// An improved version of the previous program.
class AutoArray {
public static void main(String args[]) {
int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31,
30, 31 };
System.out.println("April has " + month_days[3] + " days.");
}
}

When you run this program, you see the same output as that generated by the previous
version.
Java strictly checks to make sure you do not accidentally try to store or reference values
outside of the range of the array. The Java run-time system will check to be sure that all
array indexes are in the correct range. For example, the run-time system will check the
value of each index into month_days to make sure that it is between 0 and 11 inclusive. If
you try to access elements outside the range of the array (negative numbers or numbers
greater than the length of the array), you will cause a run-time error.
Here is one more example that uses a one-dimensional array. It finds the average of a
set of numbers.
// Average an array of values.
class Average {
public static void main(String args[]) {
double nums[] = {10.1, 11.2, 12.3, 13.4, 14.5};
double result = 0;
int i;
for(i=0; i<5; i++)
result = result + nums[i];
System.out.println("Average is " + result / 5);
}
}

53

Part I

Chapter 3

54

PART I

The Java Language

Multidimensional Arrays
In Java, multidimensional arrays are actually arrays of arrays. These, as you might expect, look
and act like regular multidimensional arrays. However, as you will see, there are a couple
of subtle differences. To declare a multidimensional array variable, specify each additional
index using another set of square brackets. For example, the following declares a twodimensional array variable called twoD:
int twoD[][] = new int[4][5];

This allocates a 4 by 5 array and assigns it to twoD. Internally this matrix is implemented as
an array of arrays of int. Conceptually, this array will look like the one shown in Figure 3-1.
The following program numbers each element in the array from left to right, top to
bottom, and then displays these values:
// Demonstrate a two-dimensional array.
class TwoDArray {
public static void main(String args[]) {
int twoD[][]= new int[4][5];
int i, j, k = 0;
for(i=0; i<4; i++)
for(j=0; j<5; j++) {
twoD[i][j] = k;
k++;
}
for(i=0; i<4; i++) {
for(j=0; j<5; j++)
System.out.print(twoD[i][j] + " ");
System.out.println();
}
}
}

This program generates the following output:
0 1 2
5 6 7
10 11
15 16

3 4
8 9
12 13 14
17 18 19

When you allocate memory for a multidimensional array, you need only specify the
memory for the first (leftmost) dimension. You can allocate the remaining dimensions
separately. For example, this following code allocates memory for the first dimension of
twoD when it is declared. It allocates the second dimension manually.
int twoD[][] = new int[4][];
twoD[0] = new int[5];
twoD[1] = new int[5];
twoD[2] = new int[5];
twoD[3] = new int[5];

Data Types, Variables, and Arrays

55

Part I

Chapter 3

Figure 3-1 A conceptual view of a 4 by 5, two-dimensional array
While there is no advantage to individually allocating the second dimension arrays in
this situation, there may be in others. For example, when you allocate dimensions manually,
you do not need to allocate the same number of elements for each dimension. As stated
earlier, since multidimensional arrays are actually arrays of arrays, the length of each array
is under your control. For example, the following program creates a two-dimensional array
in which the sizes of the second dimension are unequal:
// Manually allocate differing size second dimensions.
class TwoDAgain {
public static void main(String args[]) {
int twoD[][] = new int[4][];
twoD[0] = new int[1];
twoD[1] = new int[2];
twoD[2] = new int[3];
twoD[3] = new int[4];
int i, j, k = 0;
for(i=0; i<4; i++)
for(j=0; j>, shifts all of the bits in a value to the right a specified number of
times. Its general form is shown here:
value >> num
Here, num specifies the number of positions to right-shift the value in value. That is, the >>
moves all of the bits in the specified value to the right the number of bit positions specified
by num.

Chapter 4

Operators

71

int a = 32;
a = a >> 2; // a now contains 8

When a value has bits that are “shifted off,” those bits are lost. For example, the next
code fragment shifts the value 35 to the right two positions, which causes the two low-order
bits to be lost, resulting again in a being set to 8:
int a = 35;
a = a >> 2; // a contains 8

Looking at the same operation in binary shows more clearly how this happens:
00100011 35
>> 2
00001000 8
Each time you shift a value to the right, it divides that value by two—and discards any
remainder. You can take advantage of this for high-performance integer division by 2. Of
course, you must be sure that you are not shifting any bits off the right end.
When you are shifting right, the top (leftmost) bits exposed by the right shift are filled
in with the previous contents of the top bit. This is called sign extension and serves to preserve
the sign of negative numbers when you shift them right. For example, –8 >> 1 is –4, which,
in binary, is
11111000
>> 1
11111100

–8
–4

It is interesting to note that if you shift –1 right, the result always remains –1, since sign
extension keeps bringing in more ones in the high-order bits.
Sometimes it is not desirable to sign-extend values when you are shifting them to the
right. For example, the following program converts a byte value to its hexadecimal string
representation. Notice that the shifted value is masked by ANDing it with 0x0f to discard
any sign-extended bits so that the value can be used as an index into the array of
hexadecimal characters.
// Masking sign extension.
class HexByte {
static public void main(String args[]) {
char hex[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
byte b = (byte) 0xf1;
System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);
}
}

Part I

The following code fragment shifts the value 32 to the right by two positions, resulting
in a being set to 8:

72

PART I

The Java Language

Here is the output of this program:
b = 0xf1

The Unsigned Right Shift
As you have just seen, the >> operator automatically fills the high-order bit with its previous
contents each time a shift occurs. This preserves the sign of the value. However, sometimes
this is undesirable. For example, if you are shifting something that does not represent a
numeric value, you may not want sign extension to take place. This situation is common
when you are working with pixel-based values and graphics. In these cases, you will
generally want to shift a zero into the high-order bit no matter what its initial value was.
This is known as an unsigned shift. To accomplish this, you will use Java’s unsigned, shiftright operator, >>>, which always shifts zeros into the high-order bit.
The following code fragment demonstrates the >>>. Here, a is set to –1, which sets all
32 bits to 1 in binary. This value is then shifted right 24 bits, filling the top 24 bits with
zeros, ignoring normal sign extension. This sets a to 255.
int a = -1;
a = a >>> 24;

Here is the same operation in binary form to further illustrate what is happening:
11111111 11111111 11111111 11111111 –1 in binary as an int
>>>24
00000000 00000000 00000000 11111111 255 in binary as an int
The >>> operator is often not as useful as you might like, since it is only meaningful
for 32- and 64-bit values. Remember, smaller values are automatically promoted to int in
expressions. This means that sign-extension occurs and that the shift will take place on a
32-bit rather than on an 8- or 16-bit value. That is, one might expect an unsigned right shift
on a byte value to zero-fill beginning at bit 7. But this is not the case, since it is a 32-bit value
that is actually being shifted. The following program demonstrates this effect:
// Unsigned shifting a byte value.
class ByteUShift {
static public void main(String args[]) {
char hex[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
byte b = (byte) 0xf1;
byte c = (byte) (b >> 4);
byte d = (byte) (b >>> 4);
byte e = (byte) ((b & 0xff) >> 4);
System.out.println("
b = 0x"
+ hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);
System.out.println("
b >> 4 = 0x"
+ hex[(c >> 4) & 0x0f] + hex[c & 0x0f]);
System.out.println("
b >>> 4 = 0x"
+ hex[(d >> 4) & 0x0f] + hex[d & 0x0f]);

Chapter 4

Operators

73

}
}

The following output of this program shows how the >>> operator appears to do nothing
when dealing with bytes. The variable b is set to an arbitrary negative byte value for this
demonstration. Then c is assigned the byte value of b shifted right by four, which is 0xff
because of the expected sign extension. Then d is assigned the byte value of b unsigned
shifted right by four, which you might have expected to be 0x0f, but is actually 0xff because
of the sign extension that happened when b was promoted to int before the shift. The last
expression sets e to the byte value of b masked to 8 bits using the AND operator, then shifted
right by four, which produces the expected value of 0x0f. Notice that the unsigned shift right
operator was not used for d, since the state of the sign bit after the AND was known.
b = 0xf1
b >> 4 = 0xff
b >>> 4 = 0xff
(b & 0xff) >> 4 = 0x0f

Bitwise Operator Compound Assignments
All of the binary bitwise operators have a compound form similar to that of the algebraic
operators, which combines the assignment with the bitwise operation. For example, the
following two statements, which shift the value in a right by four bits, are equivalent:
a = a >> 4;
a >>= 4;

Likewise, the following two statements, which result in a being assigned the bitwise
expression a OR b, are equivalent:
a = a | b;
a |= b;

The following program creates a few integer variables and then uses compound bitwise
operator assignments to manipulate the variables:
class OpBitEquals {
public static void main(String args[]) {
int a = 1;
int b = 2;
int c = 3;
a |= 4;
b >>= 1;
c <<= 1;
a ^= c;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
}
}

Part I

System.out.println("(b & 0xff) >> 4 = 0x"
+ hex[(e >> 4) & 0x0f] + hex[e & 0x0f]);

74

PART I

The Java Language

The output of this program is shown here:
a = 3
b = 1
c = 6

Relational Operators
The relational operators determine the relationship that one operand has to the other.
Specifically, they determine equality and ordering. The relational operators are shown here:

Operator

Result

==

Equal to

!=

Not equal to

>

Greater than

<

Less than

>=

Greater than or equal to

<=

Less than or equal to

The outcome of these operations is a boolean value. The relational operators are most
frequently used in the expressions that control the if statement and the various loop
statements.
Any type in Java, including integers, floating-point numbers, characters, and Booleans
can be compared using the equality test, ==, and the inequality test, !=. Notice that in Java
equality is denoted with two equal signs, not one. (Remember: a single equal sign is the
assignment operator.) Only numeric types can be compared using the ordering operators.
That is, only integer, floating-point, and character operands may be compared to see which
is greater or less than the other.
As stated, the result produced by a relational operator is a boolean value. For example,
the following code fragment is perfectly valid:
int a = 4;
int b = 1;
boolean c = a < b;

In this case, the result of a>

>>>

<<

>

>=

<

==

!=

&
^
|
&&
||
?:
=

op=

Lowest
Table 4-1 The Precedence of the Java Operators

!

+ (unary)

<=

instanceof

– (unary)

(type-cast)

Chapter 4

Operators

79

Parentheses raise the precedence of the operations that are inside them. This is often
necessary to obtain the result you desire. For example, consider the following expression:
a >> b + 3

This expression first adds 3 to b and then shifts a right by that result. That is, this
expression can be rewritten using redundant parentheses like this:
a >> (b + 3)

However, if you want to first shift a right by b positions and then add 3 to that result,
you will need to parenthesize the expression like this:
(a >> b) + 3

In addition to altering the normal precedence of an operator, parentheses can
sometimes be used to help clarify the meaning of an expression. For anyone reading your
code, a complicated expression can be difficult to understand. Adding redundant but
clarifying parentheses to complex expressions can help prevent confusion later. For
example, which of the following expressions is easier to read?
a | 4 + c >> b & 7
(a | (((4 + c) >> b) & 7))

One other point: parentheses (redundant or not) do not degrade the performance
of your program. Therefore, adding parentheses to reduce ambiguity does not negatively
affect your program.

Part I

Using Parentheses

This page intentionally left blank

CHAPTER

5

Control Statements

A programming language uses control statements to cause the flow of execution to advance
and branch based on changes to the state of a program. Java’s program control statements
can be put into the following categories: selection, iteration, and jump. Selection statements
allow your program to choose different paths of execution based upon the outcome of an
expression or the state of a variable. Iteration statements enable program execution to
repeat one or more statements (that is, iteration statements form loops). Jump statements
allow your program to execute in a nonlinear fashion. All of Java’s control statements are
examined here.

Java’s Selection Statements
Java supports two selection statements: if and switch. These statements allow you to control
the flow of your program’s execution based upon conditions known only during run time.
You will be pleasantly surprised by the power and flexibility contained in these two statements.

if
The if statement was introduced in Chapter 2. It is examined in detail here. The if statement
is Java’s conditional branch statement. It can be used to route program execution through
two different paths. Here is the general form of the if statement:
if (condition) statement1;
else statement2;
Here, each statement may be a single statement or a compound statement enclosed in curly
braces (that is, a block). The condition is any expression that returns a boolean value. The
else clause is optional.
The if works like this: If the condition is true, then statement1 is executed. Otherwise,
statement2 (if it exists) is executed. In no case will both statements be executed. For example,
consider the following:
int a, b;
//...
if(a < b) a = 0;
else b = 0;

81

82

PART I

The Java Language

Here, if a is less than b, then a is set to zero. Otherwise, b is set to zero. In no case are they
both set to zero.
Most often, the expression used to control the if will involve the relational operators.
However, this is not technically necessary. It is possible to control the if using a single boolean
variable, as shown in this code fragment:
boolean dataAvailable;
//...
if (dataAvailable)
ProcessData();
else
waitForMoreData();

Remember, only one statement can appear directly after the if or the else. If you want to
include more statements, you’ll need to create a block, as in this fragment:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else
waitForMoreData();

Here, both statements within the if block will execute if bytesAvailable is greater than zero.
Some programmers find it convenient to include the curly braces when using the if, even
when there is only one statement in each clause. This makes it easy to add another statement
at a later date, and you don’t have to worry about forgetting the braces. In fact, forgetting to
define a block when one is needed is a common cause of errors. For example, consider the
following code fragment:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else
waitForMoreData();
bytesAvailable = n;

It seems clear that the statement bytesAvailable = n; was intended to be executed inside the
else clause, because of the indentation level. However, as you recall, whitespace is insignificant
to Java, and there is no way for the compiler to know what was intended. This code will
compile without complaint, but it will behave incorrectly when run. The preceding example
is fixed in the code that follows:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else {

Chapter 5

Control Statements

83

}

Nested ifs
A nested if is an if statement that is the target of another if or else. Nested ifs are very
common in programming. When you nest ifs, the main thing to remember is that an else
statement always refers to the nearest if statement that is within the same block as the else
and that is not already associated with an else. Here is an example:
if(i == 10) {
if(j < 20) a = b;
if(k > 100) c = d; // this if is
else a = c;
// associated with this else
}
else a = d;
// this else refers to if(i == 10)

As the comments indicate, the final else is not associated with if(j<20) because it is not in
the same block (even though it is the nearest if without an else). Rather, the final else is
associated with if(i==10). The inner else refers to if(k>100) because it is the closest if within
the same block.

The if-else-if Ladder
A common programming construct that is based upon a sequence of nested ifs is the if-elseif ladder. It looks like this:
if(condition)
statement;
else if(condition)
statement;
else if(condition)
statement;
.
.
.
else
statement;
The if statements are executed from the top down. As soon as one of the conditions
controlling the if is true, the statement associated with that if is executed, and the rest of
the ladder is bypassed. If none of the conditions is true, then the final else statement will be
executed. The final else acts as a default condition; that is, if all other conditional tests fail,
then the last else statement is performed. If there is no final else and all other conditions
are false, then no action will take place.
Here is a program that uses an if-else-if ladder to determine which season a particular
month is in.
// Demonstrate if-else-if statements.
class IfElse {
public static void main(String args[]) {
int month = 4; // April
String season;

Part I

waitForMoreData();
bytesAvailable = n;

84

PART I

The Java Language

if(month == 12 || month == 1 || month == 2)
season = "Winter";
else if(month == 3 || month == 4 || month == 5)
season = "Spring";
else if(month == 6 || month == 7 || month == 8)
season = "Summer";
else if(month == 9 || month == 10 || month == 11)
season = "Autumn";
else
season = "Bogus Month";
System.out.println("April is in the " + season + ".");
}
}

Here is the output produced by the program:
April is in the Spring.

You might want to experiment with this program before moving on. As you will find, no
matter what value you give month, one and only one assignment statement within the ladder
will be executed.

switch
The switch statement is Java’s multiway branch statement. It provides an easy way to
dispatch execution to different parts of your code based on the value of an expression. As
such, it often provides a better alternative than a large series of if-else-if statements. Here is
the general form of a switch statement:
switch (expression) {
case value1:
// statement sequence
break;
case value2:
// statement sequence
break;
.
.
.
case valueN :
// statement sequence
break;
default:
// default statement sequence
}
For versions of Java prior to JDK 7, expression must be of type byte, short, int, char, or an
enumeration. (Enumerations are described in Chapter 12.) Beginning with JDK 7, expression

Control Statements

can also be of type String. Each value specified in the case statements must be a unique
constant expression (such as a literal value). Duplicate case values are not allowed. The type
of each value must be compatible with the type of expression.
The switch statement works like this: The value of the expression is compared with each of
the values in the case statements. If a match is found, the code sequence following that case
statement is executed. If none of the constants matches the value of the expression, then the
default statement is executed. However, the default statement is optional. If no case matches
and no default is present, then no further action is taken.
The break statement is used inside the switch to terminate a statement sequence. When a
break statement is encountered, execution branches to the first line of code that follows the
entire switch statement. This has the effect of “jumping out” of the switch.
Here is a simple example that uses a switch statement:
// A simple example of the switch.
class SampleSwitch {
public static void main(String args[]) {
for(int i=0; i<6; i++)
switch(i) {
case 0:
System.out.println("i is zero.");
break;
case 1:
System.out.println("i is one.");
break;
case 2:
System.out.println("i is two.");
break;
case 3:
System.out.println("i is three.");
break;
default:
System.out.println("i is greater than 3.");
}
}
}

The output produced by this program is shown here:
i
i
i
i
i
i

is
is
is
is
is
is

zero.
one.
two.
three.
greater than 3.
greater than 3.

As you can see, each time through the loop, the statements associated with the case
constant that matches i are executed. All others are bypassed. After i is greater than 3,
no case statements match, so the default statement is executed.

85

Part I

Chapter 5

86

PART I

The Java Language

The break statement is optional. If you omit the break, execution will continue on into the
next case. It is sometimes desirable to have multiple cases without break statements between
them. For example, consider the following program:
// In a switch, break statements are optional.
class MissingBreak {
public static void main(String args[]) {
for(int i=0; i<12; i++)
switch(i) {
case 0:
case 1:
case 2:
case 3:
case 4:
System.out.println("i is less than 5");
break;
case 5:
case 6:
case 7:
case 8:
case 9:
System.out.println("i is less than 10");
break;
default:
System.out.println("i is 10 or more");
}
}
}

This program generates the following output:
i
i
i
i
i
i
i
i
i
i
i
i

is
is
is
is
is
is
is
is
is
is
is
is

less than 5
less than 5
less than 5
less than 5
less than 5
less than 10
less than 10
less than 10
less than 10
less than 10
10 or more
10 or more

As you can see, execution falls through each case until a break statement (or the end of the
switch) is reached.
While the preceding example is, of course, contrived for the sake of illustration, omitting
the break statement has many practical applications in real programs. To sample its more
realistic usage, consider the following rewrite of the season example shown earlier. This version
uses a switch to provide a more efficient implementation.
// An improved version of the season program.
class Switch {
public static void main(String args[]) {
int month = 4;

Chapter 5

Control Statements

87

switch (month) {
case 12:
case 1:
case 2:
season = "Winter";
break;
case 3:
case 4:
case 5:
season = "Spring";
break;
case 6:
case 7:
case 8:
season = "Summer";
break;
case 9:
case 10:
case 11:
season = "Autumn";
break;
default:
season = "Bogus Month";
}
System.out.println("April is in the " + season + ".");
}
}

As mentioned, beginning with JDK 7, you can use a string to control a switch statement.
For example,
// Use a string to control a switch statement.
class StringSwitch {
public static void main(String args[]) {
String str = "two";
switch(str) {
case "one":
System.out.println("one");
break;
case "two":
System.out.println("two");
break;
case "three":
System.out.println("three");
break;
default:
System.out.println("no match");
break;
}
}
}

Part I

String season;

88

PART I

The Java Language

As you would expect, the output from the program is
two

The string contained in str (which is "two" in this program) is tested against the case
constants. When a match is found (as it is in the second case), the code sequence associated
with that sequence is executed.
Being able to use strings in a switch statement streamlines many situations. For example,
using a string-based switch is an improvement over using the equivalent sequence of if/else
statements. However, switching on strings is more expensive than switching on integers.
Therefore, it is best to switch on strings only in cases in which the controlling data is already
in string form. In other words, don’t use strings in a switch unnecessarily.

Nested switch Statements
You can use a switch as part of the statement sequence of an outer switch. This is called a
nested switch. Since a switch statement defines its own block, no conflicts arise between the
case constants in the inner switch and those in the outer switch. For example, the following
fragment is perfectly valid:
switch(count) {
case 1:
switch(target) { // nested switch
case 0:
System.out.println("target is zero");
break;
case 1: // no conflicts with outer switch
System.out.println("target is one");
break;
}
break;
case 2: // ...

Here, the case 1: statement in the inner switch does not conflict with the case 1: statement
in the outer switch. The count variable is compared only with the list of cases at the outer
level. If count is 1, then target is compared with the inner list cases.
In summary, there are three important features of the switch statement to note:
• The switch differs from the if in that switch can only test for equality, whereas if can
evaluate any type of Boolean expression. That is, the switch looks only for a match
between the value of the expression and one of its case constants.
• No two case constants in the same switch can have identical values. Of course, a
switch statement and an enclosing outer switch can have case constants in common.
• A switch statement is usually more efficient than a set of nested ifs.
The last point is particularly interesting because it gives insight into how the Java compiler
works. When it compiles a switch statement, the Java compiler will inspect each of the case
constants and create a “jump table” that it will use for selecting the path of execution
depending on the value of the expression. Therefore, if you need to select among a large

Control Statements

89

group of values, a switch statement will run much faster than the equivalent logic coded using
a sequence of if-elses. The compiler can do this because it knows that the case constants are
all the same type and simply must be compared for equality with the switch expression. The
compiler has no such knowledge of a long list of if expressions.

Iteration Statements
Java’s iteration statements are for, while, and do-while. These statements create what we
commonly call loops. As you probably know, a loop repeatedly executes the same set of
instructions until a termination condition is met. As you will see, Java has a loop to fit any
programming need.

while
The while loop is Java’s most fundamental loop statement. It repeats a statement or block
while its controlling expression is true. Here is its general form:
while(condition) {
// body of loop
}
The condition can be any Boolean expression. The body of the loop will be executed as long
as the conditional expression is true. When condition becomes false, control passes to the
next line of code immediately following the loop. The curly braces are unnecessary if only a
single statement is being repeated.
Here is a while loop that counts down from 10, printing exactly ten lines of "tick":
// Demonstrate the while loop.
class While {
public static void main(String args[]) {
int n = 10;
while(n > 0) {
System.out.println("tick " + n);
n--;
}
}
}

When you run this program, it will “tick” ten times:
tick
tick
tick
tick
tick
tick
tick
tick
tick
tick

10
9
8
7
6
5
4
3
2
1

Part I

Chapter 5

90

PART I

The Java Language

Since the while loop evaluates its conditional expression at the top of the loop, the body of
the loop will not execute even once if the condition is false to begin with. For example, in the
following fragment, the call to println() is never executed:
int a = 10, b = 20;
while(a > b)
System.out.println("This will not be displayed");

The body of the while (or any other of Java’s loops) can be empty. This is because a null
statement (one that consists only of a semicolon) is syntactically valid in Java. For example,
consider the following program:
// The target of a loop can be empty.
class NoBody {
public static void main(String args[]) {
int i, j;
i = 100;
j = 200;
// find midpoint between i and j
while(++i < --j); // no body in this loop
System.out.println("Midpoint is " + i);
}
}

This program finds the midpoint between i and j. It generates the following output:
Midpoint is 150

Here is how this while loop works. The value of i is incremented, and the value of j is
decremented. These values are then compared with one another. If the new value of i is still
less than the new value of j, then the loop repeats. If i is equal to or greater than j, the loop
stops. Upon exit from the loop, i will hold a value that is midway between the original values of
i and j. (Of course, this procedure only works when i is less than j to begin with.) As you can
see, there is no need for a loop body; all of the action occurs within the conditional expression,
itself. In professionally written Java code, short loops are frequently coded without bodies
when the controlling expression can handle all of the details itself.

do-while
As you just saw, if the conditional expression controlling a while loop is initially false, then
the body of the loop will not be executed at all. However, sometimes it is desirable to
execute the body of a loop at least once, even if the conditional expression is false to begin
with. In other words, there are times when you would like to test the termination expression
at the end of the loop rather than at the beginning. Fortunately, Java supplies a loop that
does just that: the do-while. The do-while loop always executes its body at least once,
because its conditional expression is at the bottom of the loop. Its general form is

Control Statements

do {
// body of loop
} while (condition);
Each iteration of the do-while loop first executes the body of the loop and then evaluates
the conditional expression. If this expression is true, the loop will repeat. Otherwise, the
loop terminates. As with all of Java’s loops, condition must be a Boolean expression.
Here is a reworked version of the “tick” program that demonstrates the do-while loop. It
generates the same output as before.
// Demonstrate the do-while loop.
class DoWhile {
public static void main(String args[]) {
int n = 10;
do {
System.out.println("tick " + n);
n--;
} while(n > 0);
}
}

The loop in the preceding program, while technically correct, can be written more
efficiently as follows:
do {
System.out.println("tick " + n);
} while(--n > 0);

In this example, the expression (– –n > 0) combines the decrement of n and the test for zero
into one expression. Here is how it works. First, the – –n statement executes, decrementing
n and returning the new value of n. This value is then compared with zero. If it is greater
than zero, the loop continues; otherwise, it terminates.
The do-while loop is especially useful when you process a menu selection, because you will
usually want the body of a menu loop to execute at least once. Consider the following program,
which implements a very simple help system for Java’s selection and iteration statements:
// Using a do-while to process a menu selection
class Menu {
public static void main(String args[])
throws java.io.IOException {
char choice;
do {
System.out.println("Help on: ");
System.out.println(" 1. if");
System.out.println(" 2. switch");
System.out.println(" 3. while");
System.out.println(" 4. do-while");
System.out.println(" 5. for\n");
System.out.println("Choose one:");

91

Part I

Chapter 5

92

PART I

The Java Language

choice = (char) System.in.read();
} while( choice < '1' || choice > '5');
System.out.println("\n");
switch(choice) {
case '1':
System.out.println("The if:\n");
System.out.println("if(condition) statement;");
System.out.println("else statement;");
break;
case '2':
System.out.println("The switch:\n");
System.out.println("switch(expression) {");
System.out.println(" case constant:");
System.out.println("
statement sequence");
System.out.println("
break;");
System.out.println(" //...");
System.out.println("}");
break;
case '3':
System.out.println("The while:\n");
System.out.println("while(condition) statement;");
break;
case '4':
System.out.println("The do-while:\n");
System.out.println("do {");
System.out.println(" statement;");
System.out.println("} while (condition);");
break;
case '5':
System.out.println("The for:\n");
System.out.print("for(init; condition; iteration)");
System.out.println(" statement;");
break;
}
}
}

Here is a sample run produced by this program:
Help on:
1. if
2. switch
3. while
4. do-while
5. for
Choose one:
4
The do-while:
do {
statement;
} while (condition);

Control Statements

93

In the program, the do-while loop is used to verify that the user has entered a valid choice.
If not, then the user is reprompted. Since the menu must be displayed at least once, the
do-while is the perfect loop to accomplish this.
A few other points about this example: Notice that characters are read from the keyboard
by calling System.in.read( ). This is one of Java’s console input functions. Although Java’s
console I/O methods won’t be discussed in detail until Chapter 13, System.in.read( ) is used
here to obtain the user’s choice. It reads characters from standard input (returned as integers,
which is why the return value was cast to char). By default, standard input is line buffered, so
you must press enter before any characters that you type will be sent to your program.
Java’s console input can be a bit awkward to work with. Further, most real-world Java
programs will be graphical and window-based. For these reasons, not much use of console
input has been made in this book. However, it is useful in this context. One other point
to consider: Because System.in.read( ) is being used, the program must specify the
throws java.io.IOException clause. This line is necessary to handle input errors. It is
part of Java’s exception handling features, which are discussed in Chapter 10.

for
You were introduced to a simple form of the for loop in Chapter 2. As you will see, it is a
powerful and versatile construct.
Beginning with JDK 5, there are two forms of the for loop. The first is the traditional form
that has been in use since the original version of Java. The second is the new “for-each” form.
Both types of for loops are discussed here, beginning with the traditional form.
Here is the general form of the traditional for statement:
for(initialization; condition; iteration) {
// body
}
If only one statement is being repeated, there is no need for the curly braces.
The for loop operates as follows. When the loop first starts, the initialization portion of the
loop is executed. Generally, this is an expression that sets the value of the loop control variable,
which acts as a counter that controls the loop. It is important to understand that the initialization
expression is executed only once. Next, condition is evaluated. This must be a Boolean expression.
It usually tests the loop control variable against a target value. If this expression is true, then the
body of the loop is executed. If it is false, the loop terminates. Next, the iteration portion of the
loop is executed. This is usually an expression that increments or decrements the loop control
variable. The loop then iterates, first evaluating the conditional expression, then executing the
body of the loop, and then executing the iteration expression with each pass. This process
repeats until the controlling expression is false.
Here is a version of the “tick” program that uses a for loop:
// Demonstrate the for loop.
class ForTick {
public static void main(String args[]) {
int n;
for(n=10; n>0; n--)

Part I

Chapter 5

94

PART I

The Java Language

System.out.println("tick " + n);
}
}

Declaring Loop Control Variables Inside the for Loop
Often the variable that controls a for loop is needed only for the purposes of the loop and
is not used elsewhere. When this is the case, it is possible to declare the variable inside the
initialization portion of the for. For example, here is the preceding program recoded so
that the loop control variable n is declared as an int inside the for:
// Declare a loop control variable inside the for.
class ForTick {
public static void main(String args[]) {
// here, n is declared inside of the for loop
for(int n=10; n>0; n--)
System.out.println("tick " + n);
}
}

When you declare a variable inside a for loop, there is one important point to remember:
the scope of that variable ends when the for statement does. (That is, the scope of the variable
is limited to the for loop.) Outside the for loop, the variable will cease to exist. If you need to
use the loop control variable elsewhere in your program, you will not be able to declare it
inside the for loop.
When the loop control variable will not be needed elsewhere, most Java programmers
declare it inside the for. For example, here is a simple program that tests for prime numbers.
Notice that the loop control variable, i, is declared inside the for since it is not needed
elsewhere.
// Test for primes.
class FindPrime {
public static void main(String args[]) {
int num;
boolean isPrime;
num = 14;
if(num < 2) isPrime = false;
else isPrime = true;
for(int i=2; i <= num/i; i++) {
if((num % i) == 0) {
isPrime = false;
break;
}
}
if(isPrime) System.out.println("Prime");
else System.out.println("Not Prime");
}
}

Chapter 5

Control Statements

95

There will be times when you will want to include more than one statement in the
initialization and iteration portions of the for loop. For example, consider the loop in the
following program:
class Sample {
public static void main(String args[]) {
int a, b;
b = 4;
for(a=1; a i) {
System.out.println();
continue outer;
}
System.out.print(" " + (i * j));
}
}
System.out.println();
}
}

The continue statement in this example terminates the loop counting j and continues with
the next iteration of the loop counting i. Here is the output of this program:
0
0
0
0
0
0
0
0
0
0

1
2
3
4
5
6
7
8
9

4
6 9
8 12 16
10 15 20
12 18 24
14 21 28
16 24 32
18 27 36

25
30
35
40
45

36
42 49
48 56 64
54 63 72 81

Good uses of continue are rare. One reason is that Java provides a rich set of loop
statements which fit most applications. However, for those special circumstances in which
early iteration is needed, the continue statement provides a structured way to accomplish it.

Part I

Chapter 5

108

PART I

The Java Language

return
The last control statement is return. The return statement is used to explicitly return from a
method. That is, it causes program control to transfer back to the caller of the method. As
such, it is categorized as a jump statement. Although a full discussion of return must wait
until methods are discussed in Chapter 6, a brief look at return is presented here.
At any time in a method the return statement can be used to cause execution to branch
back to the caller of the method. Thus, the return statement immediately terminates the
method in which it is executed. The following example illustrates this point. Here, return
causes execution to return to the Java run-time system, since it is the run-time system that calls
main( ):
// Demonstrate return.
class Return {
public static void main(String args[]) {
boolean t = true;
System.out.println("Before the return.");
if(t) return; // return to caller
System.out.println("This won't execute.");
}
}

The output from this program is shown here:
Before the return.

As you can see, the final println( ) statement is not executed. As soon as return is executed,
control passes back to the caller.
One last point: In the preceding program, the if(t) statement is necessary. Without it, the
Java compiler would flag an “unreachable code” error because the compiler would know that
the last println( ) statement would never be executed. To prevent this error, the if statement is
used here to trick the compiler for the sake of this demonstration.

CHAPTER

6

Introducing Classes

The class is at the core of Java. It is the logical construct upon which the entire Java language
is built because it defines the shape and nature of an object. As such, the class forms the
basis for object-oriented programming in Java. Any concept you wish to implement in a Java
program must be encapsulated within a class.
Because the class is so fundamental to Java, this and the next few chapters will be devoted
to it. Here, you will be introduced to the basic elements of a class and learn how a class can be
used to create objects. You will also learn about methods, constructors, and the this keyword.

Class Fundamentals
Classes have been used since the beginning of this book. However, until now, only the most
rudimentary form of a class has been shown. The classes created in the preceding chapters
primarily exist simply to encapsulate the main( ) method, which has been used to demonstrate
the basics of the Java syntax. As you will see, classes are substantially more powerful than the
limited ones presented so far.
Perhaps the most important thing to understand about a class is that it defines a new
data type. Once defined, this new type can be used to create objects of that type. Thus, a
class is a template for an object, and an object is an instance of a class. Because an object is an
instance of a class, you will often see the two words object and instance used interchangeably.

The General Form of a Class
When you define a class, you declare its exact form and nature. You do this by specifying
the data that it contains and the code that operates on that data. While very simple classes
may contain only code or only data, most real-world classes contain both. As you will see, a
class’ code defines the interface to its data.
A class is declared by use of the class keyword. The classes that have been used up to
this point are actually very limited examples of its complete form. Classes can (and usually
do) get much more complex. A simplified general form of a class definition is shown here:
class classname {
type instance-variable1;

109

110

PART I

The Java Language

type instance-variable2;
// ...
type instance-variableN;
type methodname1(parameter-list) {
// body of method
}
type methodname2(parameter-list) {
// body of method
}
// ...
type methodnameN(parameter-list) {
// body of method
}
}
The data, or variables, defined within a class are called instance variables. The code is
contained within methods. Collectively, the methods and variables defined within a class
are called members of the class. In most classes, the instance variables are acted upon and
accessed by the methods defined for that class. Thus, as a general rule, it is the methods
that determine how a class’ data can be used.
Variables defined within a class are called instance variables because each instance of
the class (that is, each object of the class) contains its own copy of these variables. Thus, the
data for one object is separate and unique from the data for another. We will come back to
this point shortly, but it is an important concept to learn early.
All methods have the same general form as main( ), which we have been using thus far.
However, most methods will not be specified as static or public. Notice that the general
form of a class does not specify a main( ) method. Java classes do not need to have a main( )
method. You only specify one if that class is the starting point for your program. Further,
some kinds of Java applications, such as applets, don’t require a main( ) method at all.
NOTE C++ programmers will notice that the class declaration and the implementation of the methods
are stored in the same place and not defined separately. This sometimes makes for very large .java
files, since any class must be entirely defined in a single source file. This design feature was built into
Java because it was felt that in the long run, having specification, declaration, and implementation all
in one place makes for code that is easier to maintain.

A Simple Class
Let’s begin our study of the class with a simple example. Here is a class called Box that
defines three instance variables: width, height, and depth. Currently, Box does not contain
any methods (but some will be added soon).
class Box {
double width;
double height;
double depth;
}

Introducing Classes

111

As stated, a class defines a new type of data. In this case, the new data type is called Box. You
will use this name to declare objects of type Box. It is important to remember that a class
declaration only creates a template; it does not create an actual object. Thus, the preceding
code does not cause any objects of type Box to come into existence.
To actually create a Box object, you will use a statement like the following:
Box mybox = new Box(); // create a Box object called mybox

After this statement executes, mybox will be an instance of Box. Thus, it will have “physical”
reality. For the moment, don’t worry about the details of this statement.
As mentioned earlier, each time you create an instance of a class, you are creating an
object that contains its own copy of each instance variable defined by the class. Thus, every
Box object will contain its own copies of the instance variables width, height, and depth. To
access these variables, you will use the dot (.) operator. The dot operator links the name of
the object with the name of an instance variable. For example, to assign the width variable
of mybox the value 100, you would use the following statement:
mybox.width = 100;

This statement tells the compiler to assign the copy of width that is contained within the
mybox object the value of 100. In general, you use the dot operator to access both the
instance variables and the methods within an object. One other point: Although commonly
referred to as the dot operator, the formal specification for Java categorizes the . as a separator.
However, since the use of the term “dot operator” is widespread, it is used in this book.
Here is a complete program that uses the Box class:
/* A program that uses the Box class.
Call this file BoxDemo.java
*/
class Box {
double width;
double height;
double depth;
}
// This class declares an object of type Box.
class BoxDemo {
public static void main(String args[]) {
Box mybox = new Box();
double vol;
// assign values to mybox's instance variables
mybox.width = 10;
mybox.height = 20;
mybox.depth = 15;
// compute volume of box
vol = mybox.width * mybox.height * mybox.depth;
System.out.println("Volume is " + vol);
}
}

Part I

Chapter 6

112

PART I

The Java Language

You should call the file that contains this program BoxDemo.java, because the main( )
method is in the class called BoxDemo, not the class called Box. When you compile this
program, you will find that two .class files have been created, one for Box and one for
BoxDemo. The Java compiler automatically puts each class into its own .class file. It is not
necessary for both the Box and the BoxDemo class to actually be in the same source file.
You could put each class in its own file, called Box.java and BoxDemo.java, respectively.
To run this program, you must execute BoxDemo.class. When you do, you will see the
following output:
Volume is 3000.0

As stated earlier, each object has its own copies of the instance variables. This means
that if you have two Box objects, each has its own copy of depth, width, and height. It is
important to understand that changes to the instance variables of one object have no
effect on the instance variables of another. For example, the following program declares
two Box objects:
// This program declares two Box objects.
class Box {
double width;
double height;
double depth;
}
class BoxDemo2 {
public static void main(String args[]) {
Box mybox1 = new Box();
Box mybox2 = new Box();
double vol;
// assign values to mybox1's instance variables
mybox1.width = 10;
mybox1.height = 20;
mybox1.depth = 15;
/* assign different values to mybox2's
instance variables */
mybox2.width = 3;
mybox2.height = 6;
mybox2.depth = 9;
// compute volume of first box
vol = mybox1.width * mybox1.height * mybox1.depth;
System.out.println("Volume is " + vol);
// compute volume of second box
vol = mybox2.width * mybox2.height * mybox2.depth;
System.out.println("Volume is " + vol);
}
}

Chapter 6

Introducing Classes

113

Volume is 3000.0
Volume is 162.0

As you can see, mybox1’s data is completely separate from the data contained in mybox2.

Declaring Objects
As just explained, when you create a class, you are creating a new data type. You can use this
type to declare objects of that type. However, obtaining objects of a class is a two-step process.
First, you must declare a variable of the class type. This variable does not define an object.
Instead, it is simply a variable that can refer to an object. Second, you must acquire an actual,
physical copy of the object and assign it to that variable. You can do this using the new
operator. The new operator dynamically allocates (that is, allocates at run time) memory
for an object and returns a reference to it. This reference is, more or less, the address in
memory of the object allocated by new. This reference is then stored in the variable. Thus,
in Java, all class objects must be dynamically allocated. Let’s look at the details of this
procedure.
In the preceding sample programs, a line similar to the following is used to declare an
object of type Box:
Box mybox = new Box();

This statement combines the two steps just described. It can be rewritten like this to show
each step more clearly:
Box mybox; // declare reference to object
mybox = new Box(); // allocate a Box object

The first line declares mybox as a reference to an object of type Box. After this line
executes, mybox contains the value null, which indicates that it does not yet point to an
actual object. Any attempt to use mybox at this point will result in a compile-time error. The
next line allocates an actual object and assigns a reference to it to mybox. After the second
line executes, you can use mybox as if it were a Box object. But in reality, mybox simply
holds the memory address of the actual Box object. The effect of these two lines of code
is depicted in Figure 6-1.
NOTE Those readers familiar with C/C++ have probably noticed that object references appear to be
similar to pointers. This suspicion is, essentially, correct. An object reference is similar to a memory
pointer. The main difference—and the key to Java’s safety—is that you cannot manipulate references
as you can actual pointers. Thus, you cannot cause an object reference to point to an arbitrary
memory location or manipulate it like an integer.

A Closer Look at new
As just explained, the new operator dynamically allocates memory for an object. It has this
general form:
class-var = new classname ( );

Part I

The output produced by this program is shown here:

114

PART I

The Java Language

Figure 6-1 Declaring an object of type Box
Here, class-var is a variable of the class type being created. The classname is the name of
the class that is being instantiated. The class name followed by parentheses specifies the
constructor for the class. A constructor defines what occurs when an object of a class is
created. Constructors are an important part of all classes and have many significant
attributes. Most real-world classes explicitly define their own constructors within their
class definition. However, if no explicit constructor is specified, then Java will automatically
supply a default constructor. This is the case with Box. For now, we will use the default
constructor. Soon, you will see how to define your own constructors.
At this point, you might be wondering why you do not need to use new for such things
as integers or characters. The answer is that Java’s primitive types are not implemented as
objects. Rather, they are implemented as “normal” variables. This is done in the interest of
efficiency. As you will see, objects have many features and attributes that require Java to
treat them differently than it treats the primitive types. By not applying the same overhead
to the primitive types that applies to objects, Java can implement the primitive types more
efficiently. Later, you will see object versions of the primitive types that are available for your
use in those situations in which complete objects of these types are needed.
It is important to understand that new allocates memory for an object during run time.
The advantage of this approach is that your program can create as many or as few objects as
it needs during the execution of your program. However, since memory is finite, it is possible
that new will not be able to allocate memory for an object because insufficient memory
exists. If this happens, a run-time exception will occur. (You will learn how to handle
exceptions in Chapter 10.) For the sample programs in this book, you won’t need to worry
about running out of memory, but you will need to consider this possibility in real-world
programs that you write.
Let’s once again review the distinction between a class and an object. A class creates a
new data type that can be used to create objects. That is, a class creates a logical framework
that defines the relationship between its members. When you declare an object of a class,
you are creating an instance of that class. Thus, a class is a logical construct. An object has
physical reality. (That is, an object occupies space in memory.) It is important to keep this
distinction clearly in mind.

Chapter 6

Introducing Classes

115

Object reference variables act differently than you might expect when an assignment takes
place. For example, what do you think the following fragment does?
Box b1 = new Box();
Box b2 = b1;

You might think that b2 is being assigned a reference to a copy of the object referred to by
b1. That is, you might think that b1 and b2 refer to separate and distinct objects. However,
this would be wrong. Instead, after this fragment executes, b1 and b2 will both refer to the
same object. The assignment of b1 to b2 did not allocate any memory or copy any part of
the original object. It simply makes b2 refer to the same object as does b1. Thus, any
changes made to the object through b2 will affect the object to which b1 is referring, since
they are the same object.
This situation is depicted here:

Although b1 and b2 both refer to the same object, they are not linked in any other way.
For example, a subsequent assignment to b1 will simply unhook b1 from the original object
without affecting the object or affecting b2. For example:
Box b1 = new Box();
Box b2 = b1;
// ...
b1 = null;

Here, b1 has been set to null, but b2 still points to the original object.
REMEMBER When you assign one object reference variable to another object reference variable, you are
not creating a copy of the object, you are only making a copy of the reference.

Introducing Methods
As mentioned at the beginning of this chapter, classes usually consist of two things: instance
variables and methods. The topic of methods is a large one because Java gives them so much
power and flexibility. In fact, much of the next chapter is devoted to methods. However,
there are some fundamentals that you need to learn now so that you can begin to add
methods to your classes.

Part I

Assigning Object Reference Variables

116

PART I

The Java Language

This is the general form of a method:
type name(parameter-list) {
// body of method
}
Here, type specifies the type of data returned by the method. This can be any valid type,
including class types that you create. If the method does not return a value, its return type
must be void. The name of the method is specified by name. This can be any legal identifier
other than those already used by other items within the current scope. The parameter-list is a
sequence of type and identifier pairs separated by commas. Parameters are essentially
variables that receive the value of the arguments passed to the method when it is called.
If the method has no parameters, then the parameter list will be empty.
Methods that have a return type other than void return a value to the calling routine
using the following form of the return statement:
return value;
Here, value is the value returned.
In the next few sections, you will see how to create various types of methods, including
those that take parameters and those that return values.

Adding a Method to the Box Class
Although it is perfectly fine to create a class that contains only data, it rarely happens. Most
of the time, you will use methods to access the instance variables defined by the class. In
fact, methods define the interface to most classes. This allows the class implementor to
hide the specific layout of internal data structures behind cleaner method abstractions. In
addition to defining methods that provide access to data, you can also define methods that
are used internally by the class itself.
Let’s begin by adding a method to the Box class. It may have occurred to you while
looking at the preceding programs that the computation of a box’s volume was something
that was best handled by the Box class rather than the BoxDemo class. After all, since the
volume of a box is dependent upon the size of the box, it makes sense to have the Box class
compute it. To do this, you must add a method to Box, as shown here:
// This program includes a method inside the box class.
class Box {
double width;
double height;
double depth;
// display volume of a box
void volume() {
System.out.print("Volume is ");
System.out.println(width * height * depth);
}
}
class BoxDemo3 {
public static void main(String args[]) {

Chapter 6

Introducing Classes

117

// assign values to mybox1's instance variables
mybox1.width = 10;
mybox1.height = 20;
mybox1.depth = 15;
/* assign different values to mybox2's
instance variables */
mybox2.width = 3;
mybox2.height = 6;
mybox2.depth = 9;
// display volume of first box
mybox1.volume();
// display volume of second box
mybox2.volume();
}
}

This program generates the following output, which is the same as the previous version.
Volume is 3000.0
Volume is 162.0

Look closely at the following two lines of code:
mybox1.volume();
mybox2.volume();

The first line here invokes the volume( ) method on mybox1. That is, it calls volume( )
relative to the mybox1 object, using the object’s name followed by the dot operator. Thus,
the call to mybox1.volume( ) displays the volume of the box defined by mybox1, and the
call to mybox2.volume( ) displays the volume of the box defined by mybox2. Each time
volume( ) is invoked, it displays the volume for the specified box.
If you are unfamiliar with the concept of calling a method, the following discussion will
help clear things up. When mybox1.volume( ) is executed, the Java run-time system transfers
control to the code defined inside volume( ). After the statements inside volume( ) have
executed, control is returned to the calling routine, and execution resumes with the line of
code following the call. In the most general sense, a method is Java’s way of implementing
subroutines.
There is something very important to notice inside the volume( ) method: the instance
variables width, height, and depth are referred to directly, without preceding them with an
object name or the dot operator. When a method uses an instance variable that is defined
by its class, it does so directly, without explicit reference to an object and without use of the
dot operator. This is easy to understand if you think about it. A method is always invoked
relative to some object of its class. Once this invocation has occurred, the object is known.
Thus, within a method, there is no need to specify the object a second time. This means
that width, height, and depth inside volume( ) implicitly refer to the copies of those
variables found in the object that invokes volume( ).

Part I

Box mybox1 = new Box();
Box mybox2 = new Box();

118

PART I

The Java Language

Let’s review: When an instance variable is accessed by code that is not part of the class
in which that instance variable is defined, it must be done through an object, by use of the
dot operator. However, when an instance variable is accessed by code that is part of the
same class as the instance variable, that variable can be referred to directly. The same thing
applies to methods.

Returning a Value
While the implementation of volume( ) does move the computation of a box’s volume
inside the Box class where it belongs, it is not the best way to do it. For example, what if
another part of your program wanted to know the volume of a box, but not display its
value? A better way to implement volume( ) is to have it compute the volume of the box
and return the result to the caller. The following example, an improved version of the
preceding program, does just that:
// Now, volume() returns the volume of a box.
class Box {
double width;
double height;
double depth;
// compute and return volume
double volume() {
return width * height * depth;
}
}
class BoxDemo4 {
public static void main(String args[]) {
Box mybox1 = new Box();
Box mybox2 = new Box();
double vol;
// assign values to mybox1's instance variables
mybox1.width = 10;
mybox1.height = 20;
mybox1.depth = 15;
/* assign different values to mybox2's
instance variables */
mybox2.width = 3;
mybox2.height = 6;
mybox2.depth = 9;
// get volume of first box
vol = mybox1.volume();
System.out.println("Volume is " + vol);
// get volume of second box
vol = mybox2.volume();
System.out.println("Volume is " + vol);
}
}

Introducing Classes

119

As you can see, when volume( ) is called, it is put on the right side of an assignment
statement. On the left is a variable, in this case vol, that will receive the value returned by
volume( ). Thus, after
vol = mybox1.volume();

executes, the value of mybox1.volume( ) is 3,000 and this value then is stored in vol.
There are two important things to understand about returning values:
• The type of data returned by a method must be compatible with the return type
specified by the method. For example, if the return type of some method is
boolean, you could not return an integer.
• The variable receiving the value returned by a method (such as vol, in this case)
must also be compatible with the return type specified for the method.
One more point: The preceding program can be written a bit more efficiently because
there is actually no need for the vol variable. The call to volume( ) could have been used in
the println( ) statement directly, as shown here:
System.out.println("Volume is" + mybox1.volume());

In this case, when println( ) is executed, mybox1.volume( ) will be called automatically and
its value will be passed to println( ).

Adding a Method That Takes Parameters
While some methods don’t need parameters, most do. Parameters allow a method to be
generalized. That is, a parameterized method can operate on a variety of data and/or be
used in a number of slightly different situations. To illustrate this point, let’s use a very
simple example. Here is a method that returns the square of the number 10:
int square()
{
return 10 * 10;
}

While this method does, indeed, return the value of 10 squared, its use is very limited.
However, if you modify the method so that it takes a parameter, as shown next, then you
can make square( ) much more useful.
int square(int i)
{
return i * i;
}

Now, square( ) will return the square of whatever value it is called with. That is, square( ) is
now a general-purpose method that can compute the square of any integer value, rather
than just 10.
Here is an example:
int x, y;
x = square(5); // x equals 25
x = square(9); // x equals 81

Part I

Chapter 6

120

PART I

The Java Language

y = 2;
x = square(y); // x equals 4

In the first call to square( ), the value 5 will be passed into parameter i. In the second call, i
will receive the value 9. The third invocation passes the value of y, which is 2 in this example.
As these examples show, square( ) is able to return the square of whatever data it is passed.
It is important to keep the two terms parameter and argument straight. A parameter is a
variable defined by a method that receives a value when the method is called. For example,
in square( ), i is a parameter. An argument is a value that is passed to a method when it is
invoked. For example, square(100) passes 100 as an argument. Inside square( ), the
parameter i receives that value.
You can use a parameterized method to improve the Box class. In the preceding
examples, the dimensions of each box had to be set separately by use of a sequence of
statements, such as:
mybox1.width = 10;
mybox1.height = 20;
mybox1.depth = 15;

While this code works, it is troubling for two reasons. First, it is clumsy and error prone.
For example, it would be easy to forget to set a dimension. Second, in well-designed Java
programs, instance variables should be accessed only through methods defined by their
class. In the future, you can change the behavior of a method, but you can’t change the
behavior of an exposed instance variable.
Thus, a better approach to setting the dimensions of a box is to create a method that
takes the dimensions of a box in its parameters and sets each instance variable
appropriately. This concept is implemented by the following program:
// This program uses a parameterized method.
class Box {
double width;
double height;
double depth;
// compute and return volume
double volume() {
return width * height * depth;
}
// sets dimensions of box
void setDim(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
}
class BoxDemo5 {

Introducing Classes

121

public static void main(String args[]) {
Box mybox1 = new Box();
Box mybox2 = new Box();
double vol;
// initialize each box
mybox1.setDim(10, 20, 15);
mybox2.setDim(3, 6, 9);
// get volume of first box
vol = mybox1.volume();
System.out.println("Volume is " + vol);
// get volume of second box
vol = mybox2.volume();
System.out.println("Volume is " + vol);
}
}

As you can see, the setDim( ) method is used to set the dimensions of each box. For
example, when
mybox1.setDim(10, 20, 15);

is executed, 10 is copied into parameter w, 20 is copied into h, and 15 is copied into d.
Inside setDim( ) the values of w, h, and d are then assigned to width, height, and depth,
respectively.
For many readers, the concepts presented in the preceding sections will be familiar.
However, if such things as method calls, arguments, and parameters are new to you, then
you might want to take some time to experiment before moving on. The concepts of the
method invocation, parameters, and return values are fundamental to Java programming.

Constructors
It can be tedious to initialize all of the variables in a class each time an instance is created.
Even when you add convenience functions like setDim( ), it would be simpler and more
concise to have all of the setup done at the time the object is first created. Because the
requirement for initialization is so common, Java allows objects to initialize themselves
when they are created. This automatic initialization is performed through the use of a
constructor.
A constructor initializes an object immediately upon creation. It has the same name as
the class in which it resides and is syntactically similar to a method. Once defined, the
constructor is automatically called immediately after the object is created, before the new
operator completes. Constructors look a little strange because they have no return type,
not even void. This is because the implicit return type of a class’ constructor is the class type
itself. It is the constructor’s job to initialize the internal state of an object so that the code
creating an instance will have a fully initialized, usable object immediately.
You can rework the Box example so that the dimensions of a box are automatically
initialized when an object is constructed. To do so, replace setDim( ) with a constructor.

Part I

Chapter 6

122

PART I

The Java Language

Let’s begin by defining a simple constructor that simply sets the dimensions of each box to
the same values. This version is shown here:
/* Here, Box uses a constructor to initialize the
dimensions of a box.
*/
class Box {
double width;
double height;
double depth;
// This is the constructor for Box.
Box() {
System.out.println("Constructing Box");
width = 10;
height = 10;
depth = 10;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
class BoxDemo6 {
public static void main(String args[]) {
// declare, allocate, and initialize Box objects
Box mybox1 = new Box();
Box mybox2 = new Box();
double vol;
// get volume of first box
vol = mybox1.volume();
System.out.println("Volume is " + vol);
// get volume of second box
vol = mybox2.volume();
System.out.println("Volume is " + vol);
}
}

When this program is run, it generates the following results:
Constructing Box
Constructing Box
Volume is 1000.0
Volume is 1000.0

As you can see, both mybox1 and mybox2 were initialized by the Box( ) constructor
when they were created. Since the constructor gives all boxes the same dimensions, 10 by
10 by 10, both mybox1 and mybox2 will have the same volume. The println( ) statement

Introducing Classes

123

inside Box( ) is for the sake of illustration only. Most constructors will not display anything.
They will simply initialize an object.
Before moving on, let’s reexamine the new operator. As you know, when you allocate an
object, you use the following general form:
class-var = new classname ( );
Now you can understand why the parentheses are needed after the class name. What is
actually happening is that the constructor for the class is being called. Thus, in the line
Box mybox1 = new Box();

new Box( ) is calling the Box( ) constructor. When you do not explicitly define a constructor
for a class, then Java creates a default constructor for the class. This is why the preceding
line of code worked in earlier versions of Box that did not define a constructor. The default
constructor automatically initializes all instance variables to zero. The default constructor is
often sufficient for simple classes, but it usually won’t do for more sophisticated ones. Once
you define your own constructor, the default constructor is no longer used.

Parameterized Constructors
While the Box( ) constructor in the preceding example does initialize a Box object, it is not
very useful—all boxes have the same dimensions. What is needed is a way to construct Box
objects of various dimensions. The easy solution is to add parameters to the constructor. As
you can probably guess, this makes it much more useful. For example, the following version
of Box defines a parameterized constructor that sets the dimensions of a box as specified by
those parameters. Pay special attention to how Box objects are created.
/* Here, Box uses a parameterized constructor to
initialize the dimensions of a box.
*/
class Box {
double width;
double height;
double depth;
// This is the constructor for Box.
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
class BoxDemo7 {
public static void main(String args[]) {

Part I

Chapter 6

124

PART I

The Java Language

// declare, allocate, and initialize Box objects
Box mybox1 = new Box(10, 20, 15);
Box mybox2 = new Box(3, 6, 9);
double vol;
// get volume of first box
vol = mybox1.volume();
System.out.println("Volume is " + vol);
// get volume of second box
vol = mybox2.volume();
System.out.println("Volume is " + vol);
}
}

The output from this program is shown here:
Volume is 3000.0
Volume is 162.0

As you can see, each object is initialized as specified in the parameters to its constructor.
For example, in the following line,
Box mybox1 = new Box(10, 20, 15);

the values 10, 20, and 15 are passed to the Box( ) constructor when new creates the object.
Thus, mybox1’s copy of width, height, and depth will contain the values 10, 20, and 15,
respectively.

The this Keyword
Sometimes a method will need to refer to the object that invoked it. To allow this, Java
defines the this keyword. this can be used inside any method to refer to the current object.
That is, this is always a reference to the object on which the method was invoked. You can
use this anywhere a reference to an object of the current class’ type is permitted.
To better understand what this refers to, consider the following version of Box( ):
// A redundant use of this.
Box(double w, double h, double d) {
this.width = w;
this.height = h;
this.depth = d;
}

This version of Box( ) operates exactly like the earlier version. The use of this is redundant,
but perfectly correct. Inside Box( ), this will always refer to the invoking object. While it is
redundant in this case, this is useful in other contexts, one of which is explained in the next
section.

Chapter 6

Introducing Classes

125

As you know, it is illegal in Java to declare two local variables with the same name inside the
same or enclosing scopes. Interestingly, you can have local variables, including formal
parameters to methods, which overlap with the names of the class’ instance variables. However,
when a local variable has the same name as an instance variable, the local variable hides the
instance variable. This is why width, height, and depth were not used as the names of the
parameters to the Box( ) constructor inside the Box class. If they had been, then width, for
example, would have referred to the formal parameter, hiding the instance variable width.
While it is usually easier to simply use different names, there is another way around this
situation. Because this lets you refer directly to the object, you can use it to resolve any
namespace collisions that might occur between instance variables and local variables. For
example, here is another version of Box( ), which uses width, height, and depth for parameter
names and then uses this to access the instance variables by the same name:
// Use this to resolve name-space collisions.
Box(double width, double height, double depth) {
this.width = width;
this.height = height;
this.depth = depth;
}

A word of caution: The use of this in such a context can sometimes be confusing, and
some programmers are careful not to use local variables and formal parameter names that
hide instance variables. Of course, other programmers believe the contrary—that it is a
good convention to use the same names for clarity, and use this to overcome the instance
variable hiding. It is a matter of taste which approach you adopt.

Garbage Collection
Since objects are dynamically allocated by using the new operator, you might be wondering
how such objects are destroyed and their memory released for later reallocation. In some
languages, such as C++, dynamically allocated objects must be manually released by use of a
delete operator. Java takes a different approach; it handles deallocation for you automatically.
The technique that accomplishes this is called garbage collection. It works like this: when no
references to an object exist, that object is assumed to be no longer needed, and the memory
occupied by the object can be reclaimed. There is no explicit need to destroy objects as in
C++. Garbage collection only occurs sporadically (if at all) during the execution of your
program. It will not occur simply because one or more objects exist that are no longer
used. Furthermore, different Java run-time implementations will take varying approaches to
garbage collection, but for the most part, you should not have to think about it while writing
your programs.

The finalize( ) Method
Sometimes an object will need to perform some action when it is destroyed. For example,
if an object is holding some non-Java resource such as a file handle or character font, then
you might want to make sure these resources are freed before an object is destroyed. To

Part I

Instance Variable Hiding

126

PART I

The Java Language

handle such situations, Java provides a mechanism called finalization. By using finalization,
you can define specific actions that will occur when an object is just about to be reclaimed
by the garbage collector.
To add a finalizer to a class, you simply define the finalize( ) method. The Java run time
calls that method whenever it is about to recycle an object of that class. Inside the finalize( )
method, you will specify those actions that must be performed before an object is destroyed.
The garbage collector runs periodically, checking for objects that are no longer referenced
by any running state or indirectly through other referenced objects. Right before an asset is
freed, the Java run time calls the finalize( ) method on the object.
The finalize( ) method has this general form:
protected void finalize( )
{
// finalization code here
}
Here, the keyword protected is a specifier that prevents access to finalize( ) by code defined
outside its class. This and the other access modifiers are explained in Chapter 7.
It is important to understand that finalize( ) is only called just prior to garbage collection.
It is not called when an object goes out-of-scope, for example. This means that you cannot
know when—or even if—finalize( ) will be executed. Therefore, your program should
provide other means of releasing system resources, etc., used by the object. It must not
rely on finalize( ) for normal program operation.
NOTE If you are familiar with C++, then you know that C++ allows you to define a destructor for a class,
which is called when an object goes out-of-scope. Java does not support this idea or provide for
destructors. The finalize( ) method only approximates the function of a destructor. As you get more
experienced with Java, you will see that the need for destructor functions is minimal because of
Java’s garbage collection subsystem.

A Stack Class
While the Box class is useful to illustrate the essential elements of a class, it is of little
practical value. To show the real power of classes, this chapter will conclude with a more
sophisticated example. As you recall from the discussion of object-oriented programming
(OOP) presented in Chapter 2, one of OOP’s most important benefits is the encapsulation
of data and the code that manipulates that data. As you have seen, the class is the mechanism
by which encapsulation is achieved in Java. By creating a class, you are creating a new data
type that defines both the nature of the data being manipulated and the routines used to
manipulate it. Further, the methods define a consistent and controlled interface to the
class’ data. Thus, you can use the class through its methods without having to worry about
the details of its implementation or how the data is actually managed within the class. In a
sense, a class is like a “data engine.” No knowledge of what goes on inside the engine is
required to use the engine through its controls. In fact, since the details are hidden, its
inner workings can be changed as needed. As long as your code uses the class through
its methods, internal details can change without causing side effects outside the class.
To see a practical application of the preceding discussion, let’s develop one of the
archetypal examples of encapsulation: the stack. A stack stores data using first-in, last-out

Introducing Classes

127

ordering. That is, a stack is like a stack of plates on a table—the first plate put down on the
table is the last plate to be used. Stacks are controlled through two operations traditionally
called push and pop. To put an item on top of the stack, you will use push. To take an item
off the stack, you will use pop. As you will see, it is easy to encapsulate the entire stack
mechanism.
Here is a class called Stack that implements a stack for up to ten integers:
// This class defines an integer stack that can hold 10 values
class Stack {
int stck[] = new int[10];
int tos;
// Initialize top-of-stack
Stack() {
tos = -1;
}
// Push an item onto the stack
void push(int item) {
if(tos==9)
System.out.println("Stack is full.");
else
stck[++tos] = item;
}
// Pop an item from the stack
int pop() {
if(tos < 0) {
System.out.println("Stack underflow.");
return 0;
}
else
return stck[tos--];
}
}

As you can see, the Stack class defines two data items and three methods. The stack of
integers is held by the array stck. This array is indexed by the variable tos, which always
contains the index of the top of the stack. The Stack( ) constructor initializes tos to –1,
which indicates an empty stack. The method push( ) puts an item on the stack. To retrieve
an item, call pop( ). Since access to the stack is through push( ) and pop( ), the fact that the
stack is held in an array is actually not relevant to using the stack. For example, the stack
could be held in a more complicated data structure, such as a linked list, yet the interface
defined by push( ) and pop( ) would remain the same.
The class TestStack, shown here, demonstrates the Stack class. It creates two integer
stacks, pushes some values onto each, and then pops them off.
class TestStack {
public static void main(String args[]) {
Stack mystack1 = new Stack();
Stack mystack2 = new Stack();

Part I

Chapter 6

128

PART I

The Java Language

// push some numbers onto the stack
for(int i=0; i<10; i++) mystack1.push(i);
for(int i=10; i<20; i++) mystack2.push(i);
// pop those numbers off the stack
System.out.println("Stack in mystack1:");
for(int i=0; i<10; i++)
System.out.println(mystack1.pop());
System.out.println("Stack in mystack2:");
for(int i=0; i<10; i++)
System.out.println(mystack2.pop());
}
}

This program generates the following output:
Stack in mystack1:
9
8
7
6
5
4
3
2
1
0
Stack in mystack2:
19
18
17
16
15
14
13
12
11
10

As you can see, the contents of each stack are separate.
One last point about the Stack class. As it is currently implemented, it is possible for the
array that holds the stack, stck, to be altered by code outside of the Stack class. This leaves
Stack open to misuse or mischief. In the next chapter, you will see how to remedy this
situation.

CHAPTER

7

A Closer Look at Methods
and Classes

This chapter continues the discussion of methods and classes begun in the preceding
chapter. It examines several topics relating to methods, including overloading, parameter
passing, and recursion. The chapter then returns to the class, discussing access control, the
use of the keyword static, and one of Java’s most important built-in classes: String.

Overloading Methods
In Java it is possible to define two or more methods within the same class that share the
same name, as long as their parameter declarations are different. When this is the case,
the methods are said to be overloaded, and the process is referred to as method overloading.
Method overloading is one of the ways that Java supports polymorphism. If you have never
used a language that allows the overloading of methods, then the concept may seem
strange at first. But as you will see, method overloading is one of Java’s most exciting and
useful features.
When an overloaded method is invoked, Java uses the type and/or number of arguments
as its guide to determine which version of the overloaded method to actually call. Thus,
overloaded methods must differ in the type and/or number of their parameters. While
overloaded methods may have different return types, the return type alone is insufficient to
distinguish two versions of a method. When Java encounters a call to an overloaded method,
it simply executes the version of the method whose parameters match the arguments used in
the call.
Here is a simple example that illustrates method overloading:
// Demonstrate method overloading.
class OverloadDemo {
void test() {
System.out.println("No parameters");
}
// Overload test for one integer parameter.
void test(int a) {
System.out.println("a: " + a);
}

129

130

PART I

The Java Language

// Overload test for two integer parameters.
void test(int a, int b) {
System.out.println("a and b: " + a + " " + b);
}
// Overload test for a double parameter
double test(double a) {
System.out.println("double a: " + a);
return a*a;
}
}
class Overload {
public static void main(String args[]) {
OverloadDemo ob = new OverloadDemo();
double result;
// call all versions of test()
ob.test();
ob.test(10);
ob.test(10, 20);
result = ob.test(123.25);
System.out.println("Result of ob.test(123.25): " + result);
}
}

This program generates the following output:
No parameters
a: 10
a and b: 10 20
double a: 123.25
Result of ob.test(123.25): 15190.5625

As you can see, test( ) is overloaded four times. The first version takes no parameters,
the second takes one integer parameter, the third takes two integer parameters, and the
fourth takes one double parameter. The fact that the fourth version of test( ) also returns a
value is of no consequence relative to overloading, since return types do not play a role in
overload resolution.
When an overloaded method is called, Java looks for a match between the arguments
used to call the method and the method’s parameters. However, this match need not always
be exact. In some cases, Java’s automatic type conversions can play a role in overload
resolution. For example, consider the following program:
// Automatic type conversions apply to overloading.
class OverloadDemo {
void test() {
System.out.println("No parameters");
}
// Overload test for two integer parameters.
void test(int a, int b) {
System.out.println("a and b: " + a + " " + b);

Chapter 7

A Closer Look at Methods and Classes

131

// Overload test for a double parameter
void test(double a) {
System.out.println("Inside test(double) a: " + a);
}
}
class Overload {
public static void main(String args[]) {
OverloadDemo ob = new OverloadDemo();
int i = 88;
ob.test();
ob.test(10, 20);
ob.test(i); // this will invoke test(double)
ob.test(123.2); // this will invoke test(double)
}
}

This program generates the following output:
No parameters
a and b: 10 20
Inside test(double) a: 88
Inside test(double) a: 123.2

As you can see, this version of OverloadDemo does not define test(int). Therefore,
when test( ) is called with an integer argument inside Overload, no matching method is
found. However, Java can automatically convert an integer into a double, and this conversion
can be used to resolve the call. Therefore, after test(int) is not found, Java elevates i to double
and then calls test(double). Of course, if test(int) had been defined, it would have been
called instead. Java will employ its automatic type conversions only if no exact match is found.
Method overloading supports polymorphism because it is one way that Java implements
the “one interface, multiple methods” paradigm. To understand how, consider the
following. In languages that do not support method overloading, each method must be
given a unique name. However, frequently you will want to implement essentially the same
method for different types of data. Consider the absolute value function. In languages that
do not support overloading, there are usually three or more versions of this function, each
with a slightly different name. For instance, in C, the function abs( ) returns the absolute
value of an integer, labs( ) returns the absolute value of a long integer, and fabs( ) returns
the absolute value of a floating-point value. Since C does not support overloading, each
function has to have its own name, even though all three functions do essentially the same
thing. This makes the situation more complex, conceptually, than it actually is. Although
the underlying concept of each function is the same, you still have three names to
remember. This situation does not occur in Java, because each absolute value method can
use the same name. Indeed, Java’s standard class library includes an absolute value method,
called abs( ). This method is overloaded by Java’s Math class to handle all numeric types.
Java determines which version of abs( ) to call based upon the type of argument.

Part I

}

132

PART I

The Java Language

The value of overloading is that it allows related methods to be accessed by use of a
common name. Thus, the name abs represents the general action that is being performed. It
is left to the compiler to choose the right specific version for a particular circumstance. You,
the programmer, need only remember the general operation being performed. Through
the application of polymorphism, several names have been reduced to one. Although this
example is fairly simple, if you expand the concept, you can see how overloading can help
you manage greater complexity.
When you overload a method, each version of that method can perform any activity you
desire. There is no rule stating that overloaded methods must relate to one another. However,
from a stylistic point of view, method overloading implies a relationship. Thus, while you
can use the same name to overload unrelated methods, you should not. For example, you
could use the name sqr to create methods that return the square of an integer and the
square root of a floating-point value. But these two operations are fundamentally different.
Applying method overloading in this manner defeats its original purpose. In practice, you
should only overload closely related operations.

Overloading Constructors
In addition to overloading normal methods, you can also overload constructor methods. In
fact, for most real-world classes that you create, overloaded constructors will be the norm,
not the exception. To understand why, let’s return to the Box class developed in the
preceding chapter. Following is the latest version of Box:
class Box {
double width;
double height;
double depth;
// This is the constructor for Box.
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}

As you can see, the Box( ) constructor requires three parameters. This means that all
declarations of Box objects must pass three arguments to the Box( ) constructor. For
example, the following statement is currently invalid:
Box ob = new Box();

Since Box( ) requires three arguments, it’s an error to call it without them. This raises
some important questions. What if you simply wanted a box and did not care (or know)
what its initial dimensions were? Or, what if you want to be able to initialize a cube by
specifying only one value that would be used for all three dimensions? As the Box class is
currently written, these other options are not available to you.

A Closer Look at Methods and Classes

133

Fortunately, the solution to these problems is quite easy: simply overload the Box
constructor so that it handles the situations just described. Here is a program that contains
an improved version of Box that does just that:
/* Here, Box defines three constructors to initialize
the dimensions of a box various ways.
*/
class Box {
double width;
double height;
double depth;
// constructor used when all dimensions specified
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// constructor
Box() {
width = -1;
height = -1;
depth = -1;
}

used when no dimensions specified
// use -1 to indicate
// an uninitialized
// box

// constructor used when cube is created
Box(double len) {
width = height = depth = len;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
class OverloadCons {
public static void main(String args[]) {
// create boxes using the various constructors
Box mybox1 = new Box(10, 20, 15);
Box mybox2 = new Box();
Box mycube = new Box(7);
double vol;
// get volume of first box
vol = mybox1.volume();
System.out.println("Volume of mybox1 is " + vol);
// get volume of second box
vol = mybox2.volume();
System.out.println("Volume of mybox2 is " + vol);

Part I

Chapter 7

134

PART I

The Java Language

// get volume of cube
vol = mycube.volume();
System.out.println("Volume of mycube is " + vol);
}
}

The output produced by this program is shown here:
Volume of mybox1 is 3000.0
Volume of mybox2 is -1.0
Volume of mycube is 343.0

As you can see, the proper overloaded constructor is called based upon the parameters
specified when new is executed.

Using Objects as Parameters
So far, we have only been using simple types as parameters to methods. However, it is both
correct and common to pass objects to methods. For example, consider the following short
program:
// Objects may be passed to methods.
class Test {
int a, b;
Test(int i, int j) {
a = i;
b = j;
}
// return true if o is equal to the invoking object
boolean equals(Test o) {
if(o.a == a && o.b == b) return true;
else return false;
}
}
class PassOb {
public static void main(String args[]) {
Test ob1 = new Test(100, 22);
Test ob2 = new Test(100, 22);
Test ob3 = new Test(-1, -1);
System.out.println("ob1 == ob2: " + ob1.equals(ob2));
System.out.println("ob1 == ob3: " + ob1.equals(ob3));
}
}

This program generates the following output:
ob1 == ob2: true
ob1 == ob3: false

A Closer Look at Methods and Classes

135

As you can see, the equals( ) method inside Test compares two objects for equality and
returns the result. That is, it compares the invoking object with the one that it is passed. If
they contain the same values, then the method returns true. Otherwise, it returns false.
Notice that the parameter o in equals( ) specifies Test as its type. Although Test is a class
type created by the program, it is used in just the same way as Java’s built-in types.
One of the most common uses of object parameters involves constructors. Frequently,
you will want to construct a new object so that it is initially the same as some existing object.
To do this, you must define a constructor that takes an object of its class as a parameter. For
example, the following version of Box allows one object to initialize another:
// Here, Box allows one object to initialize another.
class Box {
double width;
double height;
double depth;
// Notice this constructor. It takes an object of type Box.
Box(Box ob) { // pass object to constructor
width = ob.width;
height = ob.height;
depth = ob.depth;
}
// constructor used when all dimensions specified
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// constructor
Box() {
width = -1;
height = -1;
depth = -1;
}

used when no dimensions specified
// use -1 to indicate
// an uninitialized
// box

// constructor used when cube is created
Box(double len) {
width = height = depth = len;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
class OverloadCons2 {
public static void main(String args[]) {
// create boxes using the various constructors

Part I

Chapter 7

136

PART I

The Java Language

Box mybox1 = new Box(10, 20, 15);
Box mybox2 = new Box();
Box mycube = new Box(7);
Box myclone = new Box(mybox1); // create copy of mybox1
double vol;
// get volume of first box
vol = mybox1.volume();
System.out.println("Volume of mybox1 is " + vol);
// get volume of second box
vol = mybox2.volume();
System.out.println("Volume of mybox2 is " + vol);
// get volume of cube
vol = mycube.volume();
System.out.println("Volume of cube is " + vol);
// get volume of clone
vol = myclone.volume();
System.out.println("Volume of clone is " + vol);
}
}

As you will see when you begin to create your own classes, providing many forms of
constructors is usually required to allow objects to be constructed in a convenient and
efficient manner.

A Closer Look at Argument Passing
In general, there are two ways that a computer language can pass an argument to a subroutine.
The first way is call-by-value. This approach copies the value of an argument into the formal
parameter of the subroutine. Therefore, changes made to the parameter of the subroutine
have no effect on the argument. The second way an argument can be passed is call-by-reference.
In this approach, a reference to an argument (not the value of the argument) is passed to
the parameter. Inside the subroutine, this reference is used to access the actual argument
specified in the call. This means that changes made to the parameter will affect the
argument used to call the subroutine. As you will see, although Java uses call-by-value
to pass all arguments, the precise effect differs between whether a primitive type or a
reference type is passed.
When you pass a primitive type to a method, it is passed by value. Thus, a copy of the
argument is made, and what occurs to the parameter that receives the argument has no
effect outside the method. For example, consider the following program:
// Primitive types are passed by value.
class Test {
void meth(int i, int j) {
i *= 2;
j /= 2;
}
}

A Closer Look at Methods and Classes

137

class CallByValue {
public static void main(String args[]) {
Test ob = new Test();
int a = 15, b = 20;
System.out.println("a and b before call: " +
a + " " + b);
ob.meth(a, b);
System.out.println("a and b after call: " +
a + " " + b);
}
}

The output from this program is shown here:
a and b before call: 15 20
a and b after call: 15 20

As you can see, the operations that occur inside meth( ) have no effect on the values of a
and b used in the call; their values here did not change to 30 and 10.
When you pass an object to a method, the situation changes dramatically, because
objects are passed by what is effectively call-by-reference. Keep in mind that when you
create a variable of a class type, you are only creating a reference to an object. Thus, when
you pass this reference to a method, the parameter that receives it will refer to the same
object as that referred to by the argument. This effectively means that objects act as if they
are passed to methods by use of call-by-reference. Changes to the object inside the method
do affect the object used as an argument. For example, consider the following program:
// Objects are passed through their references.
class Test {
int a, b;
Test(int i, int j) {
a = i;
b = j;
}
// pass an object
void meth(Test o) {
o.a *= 2;
o.b /= 2;
}
}
class PassObjRef {
public static void main(String args[]) {
Test ob = new Test(15, 20);

Part I

Chapter 7

138

PART I

The Java Language

System.out.println("ob.a and ob.b before call: " +
ob.a + " " + ob.b);
ob.meth(ob);
System.out.println("ob.a and ob.b after call: " +
ob.a + " " + ob.b);
}
}

This program generates the following output:
ob.a and ob.b before call: 15 20
ob.a and ob.b after call: 30 10

As you can see, in this case, the actions inside meth( ) have affected the object used as an
argument.
REMEMBER When an object reference is passed to a method, the reference itself is passed by use of
call-by-value. However, since the value being passed refers to an object, the copy of that value will
still refer to the same object that its corresponding argument does.

Returning Objects
A method can return any type of data, including class types that you create. For example, in
the following program, the incrByTen( ) method returns an object in which the value of a is
ten greater than it is in the invoking object.
// Returning an object.
class Test {
int a;
Test(int i) {
a = i;
}
Test incrByTen() {
Test temp = new Test(a+10);
return temp;
}
}
class RetOb {
public static void main(String args[]) {
Test ob1 = new Test(2);
Test ob2;
ob2 = ob1.incrByTen();
System.out.println("ob1.a: " + ob1.a);
System.out.println("ob2.a: " + ob2.a);

A Closer Look at Methods and Classes

139

ob2 = ob2.incrByTen();
System.out.println("ob2.a after second increase: "
+ ob2.a);
}
}

The output generated by this program is shown here:
ob1.a: 2
ob2.a: 12
ob2.a after second increase: 22

As you can see, each time incrByTen( ) is invoked, a new object is created, and a reference
to it is returned to the calling routine.
The preceding program makes another important point: Since all objects are
dynamically allocated using new, you don’t need to worry about an object going out-ofscope because the method in which it was created terminates. The object will continue to
exist as long as there is a reference to it somewhere in your program. When there are no
references to it, the object will be reclaimed the next time garbage collection takes place.

Recursion
Java supports recursion. Recursion is the process of defining something in terms of itself. As
it relates to Java programming, recursion is the attribute that allows a method to call itself.
A method that calls itself is said to be recursive.
The classic example of recursion is the computation of the factorial of a number. The
factorial of a number N is the product of all the whole numbers between 1 and N. For
example, 3 factorial is 1 × 2 × 3 ×, or 6. Here is how a factorial can be computed by use
of a recursive method:
// A simple example of recursion.
class Factorial {
// this is a recursive method
int fact(int n) {
int result;
if(n==1) return 1;
result = fact(n-1) * n;
return result;
}
}
class Recursion {
public static void main(String args[]) {
Factorial f = new Factorial();
System.out.println("Factorial of 3 is " + f.fact(3));
System.out.println("Factorial of 4 is " + f.fact(4));
System.out.println("Factorial of 5 is " + f.fact(5));
}
}

Part I

Chapter 7

140

PART I

The Java Language

The output from this program is shown here:
Factorial of 3 is 6
Factorial of 4 is 24
Factorial of 5 is 120

If you are unfamiliar with recursive methods, then the operation of fact( ) may seem
a bit confusing. Here is how it works. When fact( ) is called with an argument of 1, the
function returns 1; otherwise, it returns the product of fact(n–1)*n. To evaluate this
expression, fact( ) is called with n–1. This process repeats until n equals 1 and the calls
to the method begin returning.
To better understand how the fact( ) method works, let’s go through a short example.
When you compute the factorial of 3, the first call to fact( ) will cause a second call to be
made with an argument of 2. This invocation will cause fact( ) to be called a third time with
an argument of 1. This call will return 1, which is then multiplied by 2 (the value of n in the
second invocation). This result (which is 2) is then returned to the original invocation of
fact( ) and multiplied by 3 (the original value of n ). This yields the answer, 6. You might
find it interesting to insert println( ) statements into fact( ), which will show at what level
each call is and what the intermediate answers are.
When a method calls itself, new local variables and parameters are allocated storage on
the stack, and the method code is executed with these new variables from the start. As each
recursive call returns, the old local variables and parameters are removed from the stack,
and execution resumes at the point of the call inside the method. Recursive methods could
be said to “telescope” out and back.
Recursive versions of many routines may execute a bit more slowly than the iterative
equivalent because of the added overhead of the additional function calls. Many recursive
calls to a method could cause a stack overrun. Because storage for parameters and local
variables is on the stack and each new call creates a new copy of these variables, it is possible
that the stack could be exhausted. If this occurs, the Java run-time system will cause an
exception. However, you probably will not have to worry about this unless a recursive
routine runs wild.
The main advantage to recursive methods is that they can be used to create clearer and
simpler versions of several algorithms than can their iterative relatives. For example, the
QuickSort sorting algorithm is quite difficult to implement in an iterative way. Also, some
types of AI-related algorithms are most easily implemented using recursive solutions.
When writing recursive methods, you must have an if statement somewhere to force the
method to return without the recursive call being executed. If you don’t do this, once you
call the method, it will never return. This is a very common error in working with recursion.
Use println( ) statements liberally during development so that you can watch what is going
on and abort execution if you see that you have made a mistake.
Here is one more example of recursion. The recursive method printArray( ) prints the
first i elements in the array values.
// Another example that uses recursion.
class RecTest {
int values[];

A Closer Look at Methods and Classes

141

RecTest(int i) {
values = new int[i];
}
// display array -- recursively
void printArray(int i) {
if(i==0) return;
else printArray(i-1);
System.out.println("[" + (i-1) + "] " + values[i-1]);
}
}
class Recursion2 {
public static void main(String args[]) {
RecTest ob = new RecTest(10);
int i;
for(i=0; i<10; i++) ob.values[i] = i;
ob.printArray(10);
}
}

This program generates the following output:
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]

0
1
2
3
4
5
6
7
8
9

Introducing Access Control
As you know, encapsulation links data with the code that manipulates it. However,
encapsulation provides another important attribute: access control. Through encapsulation,
you can control what parts of a program can access the members of a class. By controlling
access, you can prevent misuse. For example, allowing access to data only through a welldefined set of methods, you can prevent the misuse of that data. Thus, when correctly
implemented, a class creates a “black box” which may be used, but the inner workings of
which are not open to tampering. However, the classes that were presented earlier do not
completely meet this goal. For example, consider the Stack class shown at the end of
Chapter 6. While it is true that the methods push( ) and pop( ) do provide a controlled
interface to the stack, this interface is not enforced. That is, it is possible for another part of
the program to bypass these methods and access the stack directly. Of course, in the wrong
hands, this could lead to trouble. In this section, you will be introduced to the mechanism
by which you can precisely control access to the various members of a class.

Part I

Chapter 7

142

PART I

The Java Language

How a member can be accessed is determined by the access modifier attached to its
declaration. Java supplies a rich set of access modifiers. Some aspects of access control are
related mostly to inheritance or packages. (A package is, essentially, a grouping of classes.)
These parts of Java’s access control mechanism will be discussed later. Here, let’s begin by
examining access control as it applies to a single class. Once you understand the
fundamentals of access control, the rest will be easy.
Java’s access modifiers are public, private, and protected. Java also defines a default
access level. protected applies only when inheritance is involved. The other access modifiers
are described next.
Let’s begin by defining public and private. When a member of a class is modified by
public, then that member can be accessed by any other code. When a member of a class is
specified as private, then that member can only be accessed by other members of its class.
Now you can understand why main( ) has always been preceded by the public modifier. It
is called by code that is outside the program—that is, by the Java run-time system. When
no access modifier is used, then by default the member of a class is public within its own
package, but cannot be accessed outside of its package. (Packages are discussed in the
following chapter.)
In the classes developed so far, all members of a class have used the default access
mode, which is essentially public. However, this is not what you will typically want to be the
case. Usually, you will want to restrict access to the data members of a class—allowing access
only through methods. Also, there will be times when you will want to define methods that
are private to a class.
An access modifier precedes the rest of a member’s type specification. That is, it must
begin a member’s declaration statement. Here is an example:
public int i;
private double j;
private int myMethod(int a, char b) { //...

To understand the effects of public and private access, consider the following program:
/* This program demonstrates the difference between
public and private.
*/
class Test {
int a; // default access
public int b; // public access
private int c; // private access
// methods to access c
void setc(int i) { // set c's value
c = i;
}
int getc() { // get c's value
return c;
}
}

A Closer Look at Methods and Classes

143

class AccessTest {
public static void main(String args[]) {
Test ob = new Test();
// These are OK, a and b may be accessed directly
ob.a = 10;
ob.b = 20;
// This is not OK and will cause an error
ob.c = 100; // Error!

//

// You must access c through its methods
ob.setc(100); // OK
System.out.println("a, b, and c: " + ob.a + " " +
ob.b + " " + ob.getc());
}
}

As you can see, inside the Test class, a uses default access, which for this example is
the same as specifying public. b is explicitly specified as public. Member c is given private
access. This means that it cannot be accessed by code outside of its class. So, inside the
AccessTest class, c cannot be used directly. It must be accessed through its public methods:
setc( ) and getc( ). If you were to remove the comment symbol from the beginning of the
following line,
// ob.c = 100; // Error!

then you would not be able to compile this program because of the access violation.
To see how access control can be applied to a more practical example, consider the
following improved version of the Stack class shown at the end of Chapter 6.
// This class defines an integer stack that can hold 10 values.
class Stack {
/* Now, both stck and tos are private. This means
that they cannot be accidentally or maliciously
altered in a way that would be harmful to the stack.
*/
private int stck[] = new int[10];
private int tos;
// Initialize top-of-stack
Stack() {
tos = -1;
}
// Push an item onto the stack
void push(int item) {
if(tos==9)
System.out.println("Stack is full.");
else
stck[++tos] = item;
}

Part I

Chapter 7

144

PART I

The Java Language

// Pop an item from the stack
int pop() {
if(tos < 0) {
System.out.println("Stack underflow.");
return 0;
}
else
return stck[tos--];
}
}

As you can see, now both stck, which holds the stack, and tos, which is the index of the
top of the stack, are specified as private. This means that they cannot be accessed or altered
except through push( ) and pop( ). Making tos private, for example, prevents other parts of
your program from inadvertently setting it to a value that is beyond the end of the stck array.
The following program demonstrates the improved Stack class. Try removing the
commented-out lines to prove to yourself that the stck and tos members are, indeed,
inaccessible.
class TestStack {
public static void main(String args[]) {
Stack mystack1 = new Stack();
Stack mystack2 = new Stack();
// push some numbers onto the stack
for(int i=0; i<10; i++) mystack1.push(i);
for(int i=10; i<20; i++) mystack2.push(i);
// pop those numbers off the stack
System.out.println("Stack in mystack1:");
for(int i=0; i<10; i++)
System.out.println(mystack1.pop());
System.out.println("Stack in mystack2:");
for(int i=0; i<10; i++)
System.out.println(mystack2.pop());
// these statements are not legal
// mystack1.tos = -2;
// mystack2.stck[3] = 100;
}
}

Although methods will usually provide access to the data defined by a class, this does
not always have to be the case. It is perfectly proper to allow an instance variable to be
public when there is good reason to do so. For example, most of the simple classes in this
book were created with little concern about controlling access to instance variables for the
sake of simplicity. However, in most real-world classes, you will need to allow operations on
data only through methods. The next chapter will return to the topic of access control. As
you will see, it is particularly important when inheritance is involved.

Chapter 7

A Closer Look at Methods and Classes

145

There will be times when you will want to define a class member that will be used
independently of any object of that class. Normally, a class member must be accessed only
in conjunction with an object of its class. However, it is possible to create a member that can
be used by itself, without reference to a specific instance. To create such a member, precede
its declaration with the keyword static. When a member is declared static, it can be accessed
before any objects of its class are created, and without reference to any object. You can declare
both methods and variables to be static. The most common example of a static member is
main( ). main( ) is declared as static because it must be called before any objects exist.
Instance variables declared as static are, essentially, global variables. When objects of
its class are declared, no copy of a static variable is made. Instead, all instances of the class
share the same static variable.
Methods declared as static have several restrictions:
• They can only directly call other static methods.
• They can only directly access static data.
• They cannot refer to this or super in any way. (The keyword super relates to
inheritance and is described in the next chapter.)
If you need to do computation in order to initialize your static variables, you can
declare a static block that gets executed exactly once, when the class is first loaded. The
following example shows a class that has a static method, some static variables, and a static
initialization block:
// Demonstrate static variables, methods, and blocks.
class UseStatic {
static int a = 3;
static int b;
static void meth(int x)
System.out.println("x
System.out.println("a
System.out.println("b
}

{
= " + x);
= " + a);
= " + b);

static {
System.out.println("Static block initialized.");
b = a * 4;
}
public static void main(String args[]) {
meth(42);
}
}

As soon as the UseStatic class is loaded, all of the static statements are run. First, a is
set to 3, then the static block executes, which prints a message and then initializes b to a*4
or 12. Then main( ) is called, which calls meth( ), passing 42 to x. The three println( )
statements refer to the two static variables a and b, as well as to the local variable x.

Part I

Understanding static

146

PART I

The Java Language

Here is the output of the program:
Static block initialized.
x = 42
a = 3
b = 12

Outside of the class in which they are defined, static methods and variables can be
used independently of any object. To do so, you need only specify the name of their class
followed by the dot operator. For example, if you wish to call a static method from outside
its class, you can do so using the following general form:
classname.method( )
Here, classname is the name of the class in which the static method is declared. As you
can see, this format is similar to that used to call non-static methods through objectreference variables. A static variable can be accessed in the same way—by use of the dot
operator on the name of the class. This is how Java implements a controlled version of
global methods and global variables.
Here is an example. Inside main( ), the static method callme( ) and the static variable b
are accessed through their class name StaticDemo.
class StaticDemo {
static int a = 42;
static int b = 99;
static void callme() {
System.out.println("a = " + a);
}
}
class StaticByName {
public static void main(String args[]) {
StaticDemo.callme();
System.out.println("b = " + StaticDemo.b);
}
}

Here is the output of this program:
a = 42
b = 99

Introducing final
A field can be declared as final. Doing so prevents its contents from being modified,
making it, essentially, a constant. This means that you must initialize a final field when
it is declared. You can do this in one of two ways: First, you can give it a value when it is
declared. Second, you can assign it a value within a constructor. The first approach is the
most common. Here is an example:

final
final
final
final
final

int
int
int
int
int

A Closer Look at Methods and Classes

147

FILE_NEW = 1;
FILE_OPEN = 2;
FILE_SAVE = 3;
FILE_SAVEAS = 4;
FILE_QUIT = 5;

Subsequent parts of your program can now use FILE_OPEN, etc., as if they were constants,
without fear that a value has been changed. It is a common coding convention to choose all
uppercase identifiers for final fields, as this example shows.
In addition to fields, both method parameters and local variables can be declared final.
Declaring a parameter final prevents it from being changed within the method. Declaring a
local variable final prevents it from being assigned a value more than once.
The keyword final can also be applied to methods, but its meaning is substantially
different than when it is applied to variables. This additional usage of final is described
in the next chapter, when inheritance is described.

Arrays Revisited
Arrays were introduced earlier in this book, before classes had been discussed. Now that
you know about classes, an important point can be made about arrays: they are implemented
as objects. Because of this, there is a special array attribute that you will want to take
advantage of. Specifically, the size of an array—that is, the number of elements that an array
can hold—is found in its length instance variable. All arrays have this variable, and it will
always hold the size of the array. Here is a program that demonstrates this property:
// This program demonstrates the length array member.
class Length {
public static void main(String args[]) {
int a1[] = new int[10];
int a2[] = {3, 5, 7, 1, 8, 99, 44, -10};
int a3[] = {4, 3, 2, 1};
System.out.println("length of a1 is " + a1.length);
System.out.println("length of a2 is " + a2.length);
System.out.println("length of a3 is " + a3.length);
}
}

This program displays the following output:
length of a1 is 10
length of a2 is 8
length of a3 is 4

As you can see, the size of each array is displayed. Keep in mind that the value of length
has nothing to do with the number of elements that are actually in use. It only reflects the
number of elements that the array is designed to hold.
You can put the length member to good use in many situations. For example, here is
an improved version of the Stack class. As you might recall, the earlier versions of this class

Part I

Chapter 7

148

PART I

The Java Language

always created a ten-element stack. The following version lets you create stacks of any size.
The value of stck.length is used to prevent the stack from overflowing.
// Improved Stack class that uses the length array member.
class Stack {
private int stck[];
private int tos;
// allocate and initialize stack
Stack(int size) {
stck = new int[size];
tos = -1;
}
// Push an item onto the stack
void push(int item) {
if(tos==stck.length-1) // use length member
System.out.println("Stack is full.");
else
stck[++tos] = item;
}
// Pop an item from the stack
int pop() {
if(tos < 0) {
System.out.println("Stack underflow.");
return 0;
}
else
return stck[tos--];
}
}
class TestStack2 {
public static void main(String args[]) {
Stack mystack1 = new Stack(5);
Stack mystack2 = new Stack(8);
// push some numbers onto the stack
for(int i=0; i<5; i++) mystack1.push(i);
for(int i=0; i<8; i++) mystack2.push(i);
// pop those numbers off the stack
System.out.println("Stack in mystack1:");
for(int i=0; i<5; i++)
System.out.println(mystack1.pop());
System.out.println("Stack in mystack2:");
for(int i=0; i<8; i++)
System.out.println(mystack2.pop());
}
}

A Closer Look at Methods and Classes

149

Notice that the program creates two stacks: one five elements deep and the other eight
elements deep. As you can see, the fact that arrays maintain their own length information
makes it easy to create stacks of any size.

Introducing Nested and Inner Classes
It is possible to define a class within another class; such classes are known as nested classes.
The scope of a nested class is bounded by the scope of its enclosing class. Thus, if class B is
defined within class A, then B does not exist independently of A. A nested class has access
to the members, including private members, of the class in which it is nested. However, the
enclosing class does not have access to the members of the nested class. A nested class that
is declared directly within its enclosing class scope is a member of its enclosing class. It is
also possible to declare a nested class that is local to a block.
There are two types of nested classes: static and non-static. A static nested class is one
that has the static modifier applied. Because it is static, it must access the non-static members
of its enclosing class through an object. That is, it cannot refer to non-static members of its
enclosing class directly. Because of this restriction, static nested classes are seldom used.
The most important type of nested class is the inner class. An inner class is a non-static
nested class. It has access to all of the variables and methods of its outer class and may refer
to them directly in the same way that other non-static members of the outer class do.
The following program illustrates how to define and use an inner class. The class named
Outer has one instance variable named outer_x, one instance method named test( ), and
defines one inner class called Inner.
// Demonstrate an inner class.
class Outer {
int outer_x = 100;
void test() {
Inner inner = new Inner();
inner.display();
}
// this is an inner class
class Inner {
void display() {
System.out.println("display: outer_x = " + outer_x);
}
}
}
class InnerClassDemo {
public static void main(String args[]) {
Outer outer = new Outer();
outer.test();
}
}

Part I

Chapter 7

150

PART I

The Java Language

Output from this application is shown here:
display: outer_x = 100

In the program, an inner class named Inner is defined within the scope of class Outer.
Therefore, any code in class Inner can directly access the variable outer_x. An instance
method named display( ) is defined inside Inner. This method displays outer_x on the
standard output stream. The main( ) method of InnerClassDemo creates an instance of
class Outer and invokes its test( ) method. That method creates an instance of class Inner
and the display( ) method is called.
It is important to realize that an instance of Inner can be created only within the scope
of class Outer. The Java compiler generates an error message if any code outside of class
Outer attempts to instantiate class Inner. In general, an inner class instance must be
created by an enclosing scope.
As explained, an inner class has access to all of the members of its enclosing class, but
the reverse is not true. Members of the inner class are known only within the scope of the
inner class and may not be used by the outer class. For example,
// This program will not compile.
class Outer {
int outer_x = 100;
void test() {
Inner inner = new Inner();
inner.display();
}
// this is an inner class
class Inner {
int y = 10; // y is local to Inner
void display() {
System.out.println("display: outer_x = " + outer_x);
}
}
void showy() {
System.out.println(y); // error, y not known here!
}
}
class InnerClassDemo {
public static void main(String args[]) {
Outer outer = new Outer();
outer.test();
}
}

Here, y is declared as an instance variable of Inner. Thus, it is not known outside of that
class and it cannot be used by showy( ).

A Closer Look at Methods and Classes

151

Although we have been focusing on inner classes declared as members within an outer
class scope, it is possible to define inner classes within any block scope. For example, you
can define a nested class within the block defined by a method or even within the body of
a for loop, as this next program shows:
// Define an inner class within a for loop.
class Outer {
int outer_x = 100;
void test() {
for(int i=0; i<10; i++) {
class Inner {
void display() {
System.out.println("display: outer_x = " + outer_x);
}
}
Inner inner = new Inner();
inner.display();
}
}
}
class InnerClassDemo {
public static void main(String args[]) {
Outer outer = new Outer();
outer.test();
}
}

The output from this version of the program is shown here:
display:
display:
display:
display:
display:
display:
display:
display:
display:
display:

outer_x
outer_x
outer_x
outer_x
outer_x
outer_x
outer_x
outer_x
outer_x
outer_x

=
=
=
=
=
=
=
=
=
=

100
100
100
100
100
100
100
100
100
100

While nested classes are not applicable to all situations, they are particularly helpful
when handling events. We will return to the topic of nested classes in Chapter 22. There
you will see how inner classes can be used to simplify the code needed to handle certain
types of events. You will also learn about anonymous inner classes, which are inner classes that
don’t have a name.
One final point: Nested classes were not allowed by the original 1.0 specification for
Java. They were added by Java 1.1.

Part I

Chapter 7

152

PART I

The Java Language

Exploring the String Class
Although the String class will be examined in depth in Part II of this book, a short
exploration of it is warranted now, because we will be using strings in some of the example
programs shown toward the end of Part I. String is probably the most commonly used class
in Java’s class library. The obvious reason for this is that strings are a very important part of
programming.
The first thing to understand about strings is that every string you create is actually an
object of type String. Even string constants are actually String objects. For example, in the
statement
System.out.println("This is a String, too");

the string "This is a String, too" is a String object.
The second thing to understand about strings is that objects of type String are immutable;
once a String object is created, its contents cannot be altered. While this may seem like a
serious restriction, it is not, for two reasons:
• If you need to change a string, you can always create a new one that contains the
modifications.
• Java defines a peer class of String, called StringBuffer, which allows strings to
be altered, so all of the normal string manipulations are still available in Java.
(StringBuffer is described in Part II of this book.)
Strings can be constructed in a variety of ways. The easiest is to use a statement like this:
String myString = "this is a test";

Once you have created a String object, you can use it anywhere that a string is allowed.
For example, this statement displays myString:
System.out.println(myString);

Java defines one operator for String objects: +. It is used to concatenate two strings. For
example, this statement
String myString = "I" + " like " + "Java.";

results in myString containing "I like Java."
The following program demonstrates the preceding concepts:
// Demonstrating Strings.
class StringDemo {
public static void main(String args[]) {
String strOb1 = "First String";
String strOb2 = "Second String";
String strOb3 = strOb1 + " and " + strOb2;
System.out.println(strOb1);
System.out.println(strOb2);

Chapter 7

A Closer Look at Methods and Classes

153

System.out.println(strOb3);
}

The output produced by this program is shown here:
First String
Second String
First String and Second String

The String class contains several methods that you can use. Here are a few. You can test
two strings for equality by using equals( ). You can obtain the length of a string by calling
the length( ) method. You can obtain the character at a specified index within a string by
calling charAt( ). The general forms of these three methods are shown here:
boolean equals(secondStr)
int length( )
char charAt(index)
Here is a program that demonstrates these methods:
// Demonstrating some String methods.
class StringDemo2 {
public static void main(String args[]) {
String strOb1 = "First String";
String strOb2 = "Second String";
String strOb3 = strOb1;
System.out.println("Length of strOb1: " +
strOb1.length());
System.out.println("Char at index 3 in strOb1: " +
strOb1.charAt(3));
if(strOb1.equals(strOb2))
System.out.println("strOb1 == strOb2");
else
System.out.println("strOb1 != strOb2");
if(strOb1.equals(strOb3))
System.out.println("strOb1 == strOb3");
else
System.out.println("strOb1 != strOb3");
}
}

This program generates the following output:
Length of strOb1: 12
Char at index 3 in strOb1: s
strOb1 != strOb2
strOb1 == strOb3

Part I

}

154

PART I

The Java Language

Of course, you can have arrays of strings, just like you can have arrays of any other type
of object. For example:
// Demonstrate String arrays.
class StringDemo3 {
public static void main(String args[]) {
String str[] = { "one", "two", "three" };
for(int i=0; i

Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.6
Linearized                      : No
Universal                       : PDF
Author                          : Herbert Schildt
Code Mantra                     : LLC
Www Codemantra Com              : )
Modify Date                     : 2011:06:23 15:21:44+05:30
Create Date                     : 2011:06:19 10:16:09+05:30
EBX PUBLISHER                   : McGraw-Hill Education
Page Layout                     : SinglePage
Page Mode                       : UseOutlines
Has XFA                         : No
Page Count                      : 1153
XMP Toolkit                     : XMP toolkit 2.9.1-13, framework 1.6
About                           : uuid:bdff6b9d-7b56-44b9-b7fa-6a44bed37015
Producer                        : Acrobat Distiller 7.0.5 for Macintosh
Trapped                         : False
Code Mantra 002 C0020 LLC       : http://www.codemantra.com
Universal 0020 PDF              : The process that creates this PDF constitutes a trade secret of codeMantra, LLC and is protected by the copyright laws of the United States
Metadata Date                   : 2011:06:23 15:21:44+05:30
Creator Tool                    : Adobe InDesign CS5 (7.0)
Document ID                     : uuid:65c51333-c3f8-496d-b6bb-d17594ac5db2
Instance ID                     : uuid:81168ca3-4d74-4940-9efe-1dfbbc26f375
Format                          : application/pdf
Title                           : Java
Creator                         : Herbert Schildt
EXIF Metadata provided by
EXIF.tools

Navigation menu