A Programmer's Guide To Java® SE 8 Oracle Certified Associate (OCA) Programmer’s Java SCJP Certification Third Edition

A%20Programmer%E2%80%99s%20Guide%20to%20Java%20SCJP%20Certification%20Third%20Edition

A%20Programmer%E2%80%99s%20Guide%20to%20Java%20SCJP%20Certification%20Third%20Edition

User Manual:

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

DownloadA Programmer's Guide To Java® SE 8 Oracle Certified Associate (OCA) Programmer’s Java SCJP Certification Third Edition
Open PDF In BrowserView PDF
A Programmer’s Guide to
Java™ SCJP Certification
Third Edition

This page intentionally left blank

A Programmer’s Guide to
Java™ SCJP Certification
A Comprehensive Primer
Third Edition

Khalid A. Mughal
Rolf W. Rasmussen

Upper Saddle River, New Jersey • Boston • Indianapolis • San Francisco
New York • Toronto • Montreal • London • Munich • Paris • Madrid
Capetown • Sidney • Tokyo • Singapore • Mexico City

Many of the designations used by manufacturers and sellers to distinguish their products
are claimed as trademarks. Where those designations appear in this book, and the publisher
was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals.
The authors and publisher have taken care in the preparation of this book, but make no
expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or
arising out of the use of the information or programs contained herein.
The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special sales, which may include electronic versions and/or custom covers and
content particular to your business, training goals, marketing focus, and branding interests.
For more information, please contact:
U.S. Corporate and Government Sales
(800) 382-3419
corpsales@pearsontechgroup.com
For sales outside the United States please contact:
International Sales
international@pearson.com
Visit us on the Web: informit.com/aw
Library of Congress Cataloging-in-Publication Data
Mughal, Khalid Azim.
A programmer's guide to Java SCJP certification : a comprehensive primer / Khalid A.
Mughal, Rolf W. Rasmussen.—3rd ed.
p. cm.
Previously published under title: A programmer’s guide to Java certification.
Includes bibliographical references and index.
ISBN 0-321-55605-4 (pbk. : alk. paper)
1. Electronic data processing personnel--Certification. 2. Operating systems (Computers)—Examinations--Study guides. 3. Java (Computer program language)--Examinations-Study guides. I. Rasmussen, Rolf (Rolf W.) II. Mughal, Khalid Azim. Programmer’s guide
to Java certification. III. Title.
QA76.3.M846 2008
005.2'762--dc22

2008048822

Copyright © 2009 Pearson Education, Inc.
All rights reserved. Printed in the United States of America. This publication is protected by
copyright, and permission must be obtained from the publisher prior to any prohibited
reproduction, storage in a retrieval system, or transmission in any form or by any means,
electronic, mechanical, photocopying, recording, or likewise. For information regarding
permissions, write to:
ISBN-13: 978-0-321-55605-9
ISBN-10:
0-321-55605-4
Text printed in the United States on recycled paper at Courier in Stoughton, Massachusetts.
First printing, December 2008

To the loving memory of my mother, Zubaida Begum,
and my father, Mohammed Azim.
—K.A.M.

For Olivia E. Rasmussen and
Louise J. Dahlmo.
—R.W.R.

This page intentionally left blank

Contents Overview

Foreword
Preface
1 Basics of Java Programming

xxxv
xxxvii
1

2 Language Fundamentals

19

3 Declarations

39

4 Access Control

103

5 Operators and Expressions

159

6 Control Flow

203

7 Object-Oriented Programming

283

8 Nested Type Declarations

351

9 Object Lifetime

389

10 Fundamental Classes

423

11 Files and Streams

467

12 Localization, Pattern Matching and Formatting

531

13 Threads

613

14 Generics

661
vii

viii

CONTENTS

15 Collections and Maps

747

A Taking the SCJP 1.6 Exam

851

B Objectives for the SCJP 1.6 Exam

857

C Objectives for the SCJP 1.6 Upgrade Exam

863

D Annotated Answers to Review Questions

869

E Solutions to Programming Exercises

935

F Mock Exam

959

G Number Systems and Number Representation
Index

1005
1013

Contents

List of Figures

xxiii

List of Tables

xxvii

List of Examples

xxix

Foreword

xxxv

Preface
1 Basics of Java Programming
1.1
1.2
1.3

1.4
1.5
1.6
1.7
1.8
1.9
1.10

Introduction
Classes
Declaring Members: Fields and Methods
Objects
Class Instantiation, Reference Values, and References
Object Aliases
Instance Members
Invoking Methods
Static Members
Inheritance
Aggregation
Tenets of Java
Review Questions
Java Programs
Sample Java Application
Essential Elements of a Java Application
Compiling and Running an Application
Review Questions
Chapter Summary
Programming Exercise

xxxvii
1
2
2
3
4
4
6
6
7
7
10
12
13
13
15
15
15
16
17
18
18

ix

x

CONTENTS

2

Language Fundamentals
2.1

2.2

2.3

2.4

3

Basic Language Elements
Lexical Tokens
Identifiers
Keywords
Literals
Integer Literals
Floating-Point Literals
Boolean Literals
Character Literals
String Literals
White Spaces
Comments
Review Questions
Primitive Data Types
Integer Types
The char Type
The Floating-Point Types
The boolean Type
Review Questions
Variable Declarations
Declaring and Initializing Variables
Reference Variables
Initial Values for Variables
Default Values for Fields
Initializing Local Variables of Primitive Data Types
Initializing Local Reference Variables
Lifetime of Variables
Review Questions
Chapter Summary
Programming Exercise

Declarations
3.1
3.2

3.3

3.4

Class Declarations
JavaBeans Standard
Naming Patterns for Properties
Naming Patterns for the Event Model
Method Declarations
Statements
Instance Methods and the Object Reference this
Method Overloading
Constructors
The Default Constructor
Overloaded Constructors
Review Questions

19
20
20
20
20
21
22
22
23
23
25
25
26
27
28
28
29
29
30
31
31
31
32
33
33
34
35
35
36
37
37

39
40
41
41
42
44
45
45

47
48
49
51
52

CONTENTS

xi

3.5

3.6

3.7

3.8

3.9

Enumerated Types
Declaring Typesafe Enums
Using Typesafe Enums
Declaring Enum Constructors and Members
Implicit Static Methods for Enum Types
Inherited Methods from the Enum Class
Extending Enum Types: Constant-Specific Class Bodies
Declaring Typesafe Enums Revisited
Review Questions
Arrays
Declaring Array Variables
Constructing an Array
Initializing an Array
Using an Array
Anonymous Arrays
Multidimensional Arrays
Review Questions
Parameter Passing
Passing Primitive Data Values
Passing Reference Values
Passing Arrays
Array Elements as Actual Parameters
final Parameters
Variable Arity Methods
Calling a Varargs Method
Varargs and Non-Varargs Method Calls
The main() Method
Program Arguments
Review Questions
Chapter Summary
Programming Exercises

4 Access Control
4.1
4.2

4.3
4.4
4.5
4.6

Java Source File Structure
Packages
Defining Packages
Using Packages
Compiling Code into Packages
Running Code from Packages
Searching for Classes
The JAR Utility
System Properties
Review Questions
Scope Rules
Class Scope for Members

54
54
54
55
57
58
59
62
63
69
70
70
71
72
74
75
79
81
82
84
86
87
89
90
91
93
94
95
96
100
101

103
104
105
106
107
115
117
117
120
122
123
129
129

xii

CONTENTS

4.7
4.8

4.9

4.10

5

Block Scope for Local Variables
Accessibility Modifiers for Top-Level Type Declarations
Other Modifiers for Classes
abstract Classes
final Classes
Review Questions
Member Accessibility Modifiers
public Members
protected Members
Default Accessibility for Members
private Members
Review Questions
Other Modifiers for Members
static Members
final Members
abstract Methods
synchronized Methods
native Methods
transient Fields
volatile Fields
Review Questions
Chapter Summary
Programming Exercise

Operators and Expressions
5.1

5.2

5.3
5.4

5.5

Conversions
Widening and Narrowing Primitive Conversions
Widening and Narrowing Reference Conversions
Boxing and Unboxing Conversions
Other Conversions
Type Conversion Contexts
Assignment Context
Method Invocation Context
Casting Context of the Unary Type Cast Operator: (type)
Numeric Promotion Context
Precedence and Associativity Rules for Operators
Evaluation Order of Operands
Left-Hand Operand Evaluation First
Operand Evaluation before Operation Execution
Left to Right Evaluation of Argument Lists
The Simple Assignment Operator =
Assigning Primitive Values
Assigning References
Multiple Assignments
Type Conversions in Assignment Context

131
132
135
135
136
138
138
139
141
142
143
144
146
147
148
150
150
151
152
153
154
157
157

159
160
160
161
162
162
163
164
164
164
165
166
168
168
168
169
169
169
169
170
171

CONTENTS

xiii

5.6

5.7
5.8

5.9
5.10
5.11

5.12

5.13

5.14
5.15

Review Questions
Arithmetic Operators: *, /, %, +, Arithmetic Operator Precedence and Associativity
Evaluation Order in Arithmetic Expressions
Range of Numeric Values
Unary Arithmetic Operators: -, +
Multiplicative Binary Operators: *, /, %
Additive Binary Operators: +, Numeric Promotions in Arithmetic Expressions
Arithmetic Compound Assignment Operators: *=, /=, %=, +=, -=
Review Questions
The Binary String Concatenation Operator +
Variable Increment and Decrement Operators: ++, -The Increment Operator ++
The Decrement Operator -Review Questions
Boolean Expressions
Relational Operators: <, <=, >, >=
Equality
Primitive Data Value Equality: ==, !=
Object Reference Equality: ==, !=
Object Value Equality
Boolean Logical Operators: !, ^, &, |
Operand Evaluation for Boolean Logical Operators
Boolean Logical Compound Assignment Operators: &=, ^=, |=
Conditional Operators: &&, ||
Short-Circuit Evaluation
Review Questions
The Conditional Operator: ?:
Other Operators: new, [], instanceof
Chapter Summary
Programming Exercise

6 Control Flow
6.1
6.2

6.3

Overview of Control Flow Statements
Selection Statements
The Simple if Statement
The if-else Statement
The switch Statement
Review Questions
Iteration Statements
The while Statement
The do-while Statement
The for(;;) Statement
The for(:) Statement

173
174
174
174
175
177
178
180
180
182
184
185
186
187
187
188
190
190
191
191
192
193
194
195
195
196
197
199
201
201
202
202

203
204
204
204
205
207
212
216
217
217
218
220

xiv

CONTENTS

6.4

6.5
6.6

6.7

6.8
6.9
6.10

7

Transfer Statements
Labeled Statements
The break Statement
The continue Statement
The return Statement
Review Questions
Stack-Based Execution and Exception Propagation
Exception Types
The Exception Class
The RuntimeException Class
The Error Class
Checked and Unchecked Exceptions
Defining New Exceptions
Exception Handling: try, catch, and finally
The try Block
The catch Block
The finally Block
The throw Statement
The throws Clause
Review Questions
Assertions
The assert Statement and the AssertionError Class
Compiling Assertions
Runtime Enabling and Disabling of Assertions
Using Assertions
Review Questions
Chapter Summary
Programming Exercises

Object-Oriented Programming
7.1

7.2

7.3

7.4
7.5

Single Implementation Inheritance
Inheritance Hierarchy
Relationships: is-a and has-a
The Supertype-Subtype Relationship
Overriding Methods
Instance Method Overriding
Covariant return in Overriding Methods
Overriding vs. Overloading
Hiding Members
Field Hiding
Static Method Hiding
The Object Reference super
Review Questions
Chaining Constructors Using this() and super()
The this() Constructor Call

223
223
224
226
228
229
235
239
241
241
242
243
244
245
245
246
251
255
257
260
265
265
267
269
272
276
279
279

283
284
286
286
287
288
288
290
292
294
294
294
295
297
302
302

CONTENTS

xv

7.6

7.7

7.8
7.9
7.10
7.11

7.12
7.13
7.14

The super() Constructor Call
Review Questions
Interfaces
Defining Interfaces
Abstract Method Declarations
Implementing Interfaces
Extending Interfaces
Interface References
Constants in Interfaces
Review Questions
Arrays and Subtyping
Arrays and Subtype Covariance
Array Store Check
Reference Values and Conversions
Reference Value Assignment Conversions
Method Invocation Conversions Involving References
Overloaded Method Resolution
Reference Casting and the instanceof Operator
The Cast Operator
The instanceof Operator
Review Questions
Polymorphism and Dynamic Method Lookup
Inheritance Versus Aggregation
Basic Concepts in Object-Oriented Design
Encapsulation
Cohesion
Coupling
Review Questions
Chapter Summary
Programming Exercises

8 Nested Type Declarations
8.1
8.2

8.3

8.4

8.5

Overview of Nested Type Declarations
Static Member Types
Declaring and Using Static Member Types
Accessing Members in Enclosing Context
Non-Static Member Classes
Instantiating Non-Static Member Classes
Accessing Members in Enclosing Context
Review Questions
Local Classes
Accessing Declarations in Enclosing Context
Instantiating Local Classes
Anonymous Classes
Extending an Existing Class

305
308
309
310
310
312
313
314
314
315
317
317
319
319
320
323
324
327
327
328
332
340
342
345
345
346
346
347
349
349

351
352
355
355
357
359
360
362
367
371
372
374
377
377

xvi

CONTENTS

Implementing an Interface
Instantiating Anonymous Classes
Accessing Declarations in Enclosing Context
Review Questions
Chapter Summary
Programming Exercise

9

Object Lifetime
9.1
9.2
9.3
9.4
9.5
9.6
9.7
9.8
9.9
9.10
9.11

Garbage Collection
Reachable Objects
Facilitating Garbage Collection
Object Finalization
Finalizer Chaining
Invoking Garbage Collection Programmatically
Review Questions
Initializers
Field Initializer Expressions
Static Initializer Blocks
Instance Initializer Blocks
Constructing Initial Object State
Review Questions
Chapter Summary

10 Fundamental Classes
10.1
10.2
10.3

10.4

Overview of the java.lang Package
The Object Class
Review Questions
The Wrapper Classes
Common Wrapper Class Constructors
Common Wrapper Class Utility Methods
Numeric Wrapper Classes
The Character Class
The Boolean Class
Review Questions
The String Class
Immutability
Creating and Initializing Strings
The CharSequence Interface
Reading Characters from a String
Comparing Strings
Character Case in a String
Concatenation of Strings
Searching for Characters and Substrings
Extracting Substrings
Converting Primitive Values and Objects to Strings

379
380
380
382
386
386

389
390
390
392
396
397
398
401
406
406
410
413
416
420
422

423
424
424
428
428
429
430
433
436
437
437
439
439
439
442
443
445
446
446
448
449
450

CONTENTS

xvii

10.5

Formatting Values
Pattern Matching
Review Questions
The StringBuilder and the StringBuffer Classes
Thread-Safety
Mutability
Constructing String Builders
Reading and Changing Characters in String Builders
Constructing Strings from String Builders
Appending, Inserting, and Deleting Characters in String Builders
Controlling String Builder Capacity
Review Questions
Chapter Summary
Programming Exercises

11 Files and Streams
11.1
11.2

11.3

11.4

11.5
11.6

Input and Output
The File Class
Querying the File System
File or Directory Existence
File and Directory Permissions
Listing Directory Entries
Creating New Files and Directories
Renaming Files and Directories
Deleting Files and Directories
Byte Streams: Input Streams and Output Streams
File Streams
Filter Streams
Reading and Writing Binary Values
Review Questions
Character Streams: Readers and Writers
Print Writers
Writing Text Files
Reading Text Files
Using Buffered Writers
Using Buffered Readers
The Standard Input, Output, and Error Streams
Comparison of Byte Streams and Character Streams
The Console class
Review Questions
Object Serialization
The ObjectOutputStream Class
The ObjectInputStream Class
Customizing Object Serialization
Serialization and Inheritance

450
452
452
456
456
456
457
457
458
458
460
461
464
465

467
468
468
470
472
472
473
473
474
474
475
477
479
479
484
488
490
492
494
495
496
499
500
500
506
510
511
512
517
519

xviii

CONTENTS

Review Questions
Chapter Summary
Programming Exercise

12 Localization, Pattern Matching, and Formatting
12.1
12.2
12.3

12.4

12.5

12.6

12.7

The java.util.Locale Class
The java.util.Date Class
The java.util.Calendar Class
Static Factory Methods to Create a Calendar
Interoperability with the Date Class
Selected get and set Methods
Manipulating a Calendar
Comparing Calendars
The java.text.DateFormat Class
Static Factory Methods to Create a Date/Time Formatter
Formatting Dates
Parsing Strings to Date/Time
Managing the Calendar and the Number Formatter
The java.text.NumberFormat Class
Static Factory Methods to Create a Number Formatter
Formatting Numbers and Currency
Parsing Strings to Numbers
Specifying the Number of Digits
Review Questions
String Pattern Matching Using Regular Expressions
Regular Expression Fundamentals
Escaping Metacharacters
The java.util.regex.Pattern Class
The java.util.regex.Matcher Class
The java.util.Scanner Class
Review Questions
Formatting Values
Overview
Defining Format Specifiers
Conversion Categories and Formatting Conversions
Selected Format Exceptions
Using the format() Method
Review Questions
Chapter Summary
Programming Exercises

522
529
530

531
532
535
536
537
537
537
539
540
541
541
542
543
545
546
546
546
547
547
551
554
554
561
562
566
571
582
593
593
595
597
601
602
604
610
610

13 Threads

613

13.1
13.2
13.3

614
614
615

Multitasking
Overview of Threads
The Main Thread

CONTENTS

xix

13.4

13.5

13.6

Thread Creation
Implementing the Runnable Interface
Extending the Thread Class
Review Questions
Synchronization
Locks
Synchronized Methods
Synchronized Blocks
Review Questions
Thread Transitions
Thread States
Thread Priorities
Thread Scheduler
Running and Yielding
Sleeping and Waking Up
Waiting and Notifying
Joining
Blocking for I/O
Thread Termination
Deadlocks
Review Questions
Chapter Summary
Programming Exercises

14 Generics
14.1
14.2

14.3
14.4

14.5

14.6

Introducing Generics
Generic Types and Parameterized Types
Generic Types
Parameterized Types
Generic Interfaces
Extending Generic Types
Raw Types and Unchecked Warnings
Collections and Generics
Wildcards
The Subtype Covariance Problem with Parameterized Types
Wildcard Types
Subtype Covariance: ? extends Type
Subtype Contravariance: ? super Type
Subtype Bivariance: ?
Subtype Invariance: Type
Some Restrictions on Wildcard Types
Using References of Wildcard Parameterized Types
Generic Reference Assignment
Using Parameterized References to Call Set and Get Methods
Bounded Type Parameters
Multiple Bounds

615
616
619
622
626
626
627
629
631
634
634
638
638
639
640
640
647
649
650
651
653
658
659

661
662
663
663
665
666
668
670
672
673
673
675
675
676
677
677
677
678
679
680
684
686

xx

CONTENTS

14.7
14.8

14.9
14.10

14.11
14.12

14.13

Review Questions
Implementing a Simplified Generic Stack
Generic Methods and Constructors
Generic Method Declaration
Calling Generic Methods
Wildcard Capture
Capture Conversion
Flexibility with Wildcard Parameterized Types
Nested Wildcards
Wildcard Parameterized Types as Formal Parameters
Flexible Comparisons with Wildcards
Recursive Bounds
Type Erasure
Bridge Methods
Implications for Overloading and Overriding
Method Signature
Implications for Overloading
Implications for Overriding
Limitations and Restrictions on Generic Types
Reifiable Types
Implications for instanceof operator
Implications for Casting
Implications for Arrays
Implications for Varargs
Implications for Exception Handling
Implications for Nested Classes
Other Implications
Review Questions
Chapter Summary
Programming Exercises

15 Collections and Maps
15.1

15.2

15.3

Comparing Objects
The equals() Method
The hashCode() Method
The Comparable Interface
The Comparator Interface
Review Questions
The Java Collections Framework
Core Interfaces
Implementations
Collections
Basic Operations
Bulk Operations
Iterators

686
695
697
699
700
703
705
705
705
707
709
712
714
716
716
716
717
718
722
722
723
724
726
729
730
731
733
734
744
745

747
748
751
760
765
771
775
777
778
780
784
784
785
785

CONTENTS

xxi

Array Operations
Review Questions
15.4 Sets
The HashSet and LinkedHashSet Classes
15.5 The SortedSet and NavigableSet Interfaces
The SortedSet Interface
The NavigableSet Interface
The TreeSet Class
15.6 Lists
The ArrayList, LinkedList, and Vector Classes
15.7 Queues
The Queue Interface
The PriorityQueue and LinkedList Classes
The Deque Interface
The ArrayDeque and LinkedList Class
Review Questions
15.8 Maps
Basic Operations
Bulk Operations
Collection Views
15.9 Map Implementations
The HashMap, LinkedHashMap, and Hashtable Classes
15.10 The SortedMap and NavigableMap Interfaces
The SortedMap Interface
The NavigableMap Interface
The TreeMap Class
Review Questions
15.11 Working with Collections
Ordering Elements in Lists
Searching in Collections
Changing Elements in Collections
Sorting Arrays
Searching in Arrays
Creating List Views of Arrays
Miscellaneous Utility Methods in the Arrays Class
Review Questions
Chapter Summary
Programming Exercises

A

Taking the SCJP 1.6 Exam
A.1
A.2

Preparing for the Programmer Exam
Registering for the Exam
Obtaining an Exam Voucher
Signing Up for the Test
Contact Information

790
791
796
796
800
800
801
802
804
806
809
809
810
813
815
816
821
821
822
822
823
823
826
826
827
828
833
838
838
840
841
842
843
845
846
846
849
850

851
851
852
852
852
852

xxii

CONTENTS

A.3

A.4

A.5

B

After Taking the Exam
How the Examination Is Conducted
The Testing Locations
Utilizing the Allotted Time
The Exam Program
The Questions
Types of Questions Asked
Types of Answers Expected
Topics Covered by the Questions
Moving on to Other Java Technology Exams

Objectives for the SCJP 1.6 Exam

853
853
853
853
854
854
854
855
855
856

857

C Objectives for the SCJP 1.6 Upgrade Exam

863

D Annotated Answers to Review Questions

869

E Solutions to Programming Exercises

935

F Mock Exam

959

G

Number Systems and Number Representation
G.1

G.2
G.3

G.4

Number Systems
Binary, Octal, and Hexadecimal Number System
Converting Binary Numbers to Decimals
Converting Octal and Hexadecimal Numbers to Decimals
Relationship between Binary, Octal, and Hexadecimal Numbers
Converting Decimals
Converting Decimals to Binary Numbers
Converting Decimals to Octal and Hexadecimal Numbers
Representing Integers
Calculating 2’s Complement

Index

1005
1005
1005
1006
1007
1007
1008
1008
1009
1010
1011

1013

List of Figures

1.1
UML Notation
for Classes
Chapter
11
1.2 UML Notation for Objects
1.3 Aliases
1.4 Class Diagram Showing Static Members of a Class
1.5 Members of a Class
1.6 Class Diagram Depicting Inheritance Relationship
1.7 Class Diagram Depicting Aggregation
2.1
Primitive
Chapter
2 19Data Types in Java
3.1
The Event
Chapter
3 39Model
3.2 Array of Arrays
3.3 Parameter Passing: Primitive Data Values
3.4 Parameter Passing: Reference Values
3.5 Parameter Passing: Arrays
4.1
Java Source
Chapter
4 103File Structure
4.2 Package Hierarchy
4.3 File Hierarchy
4.4 Searching for Classes
4.5 Searching in JAR files
4.6 Block Scope
4.7 Public Accessibility
4.8 Protected Accessibility
4.9 Default Accessibility
4.10 Private Accessibility
5.1
Widening
Primitive Conversions
Chapter
5 159
5.2 Overflow and Underflow in Floating-point Arithmetic
5.3 Numeric Promotion in Arithmetic Expressions
6.1
Activity
Diagram for if Statements
Chapter
6 203
6.2 Activity Diagram for a switch Statement
6.3 Activity Diagram for the while Statement
6.4 Activity Diagram for the do-while Statement
6.5 Activity Diagram for the for Statement
6.6 Enhanced for Statement
6.7 Method Execution

3
5
6
8
9
10
12
28
43
78
84
85
87
104
105
116
118
121
132
141
142
143
144
160
176
181
205
208
217
218
219
221
237

xxiii

xxiv

LIST OF FIGURES

6.8 Exception Propagation
6.9 Partial Exception Inheritance Hierarchy
6.10 The try-catch-finally Construct
6.11 Exception Handling (Scenario 1)
6.12 Exception Handling (Scenario 2)
6.13 Exception Handling (Scenario 3)
6.14 Execution of the Simple assert Statement (with Assertions Enabled)
6.15 Package Hierarchy
7.1
Inheritance
Chapter
7 283Hierarchy
7.2 Inheritance Relations
7.3 Reference Type Hierarchy: Arrays and Subtype Covariance
7.4 Type Hierarchy to Illustrate Polymorphism
7.5 Implementing Data Structures by Inheritance and Aggregation
8.1
Static Member
Chapter
8 351 Classes and Interfaces
8.2 Outer Object with Associated Inner Objects
8.3 Nested Classes and Inheritance
8.4 Local Classes and Inheritance Hierarchy
9.1
Memory
Organization at Runtime
Chapter
9 389
10.1
Partial10
Inheritance
Hierarchy in the java.lang Package
Chapter
423
10.2 Converting Values Between Primitive, Wrapper, and String Types
11.1
Partial11
Byte
Stream Inheritance Hierarchies
Chapter
467
11.2 Stream Chaining for Reading and Writing Binary Values to a File
11.3 Partial Character Stream Inheritance Hierarchies
11.4 Setting up a PrintWriter to Write to a File
11.5 Setting up Readers to read Characters
11.6 Buffered Writers
11.7 Buffered Readers
11.8 Keyboard and Display as Console
11.9 Object Stream Chaining
13.1
Spawning
Threads Using a Runnable Object
Chapter
12 613
13
531
13.2 Spawning Threads—Extending the Thread Class
13.3 Thread States
13.4 Running and Yielding
13.5 Sleeping and Waking up
13.6 Waiting and Notifying
13.7 Thread Communication
13.8 Stack Users
13.9 Joining of Threads
13.10 Deadlock
14.1
Extending
Generic Types
Chapter
14 661
14.2 No Subtype Covariance for Parameterized Types
14.4 Partial Type Hierarchy for Node
14.3 Partial Type Hierarchy for Node
14.5 Partial Type Hierarchy for Selected Parameterized Types of Node
14.6 Flexible Comparisons with Wildcards
15.1
The Core
Interfaces
Chapter
15 747

238
240
246
248
249
250
266
271
287
314
318
340
342
358
362
366
374
392
424
429
476
481
489
493
494
496
497
501
511
616
620
635
639
640
641
642
643
648
652
668
674
676
676
678
709
778

LIST OF FIGURES

15.2
15.3
15.4
G.1

XXV

The Core Collection Interfaces and Their Implementations
The Core Map Interfaces and Their Implementations
Bulk Operations on Collections
Converting between Binary, Octal, and Hexadecimal

781
782
785
1008

This page intentionally left blank

List of Tables

Chapter 1 11.1
2.1
Chapter 2 19
2.2
2.3
2.4
2.5
2.6
2.7
2.8
2.9
2.10
2.11
2.12
2.13
2.14
3.1
Chapter 3 39
4.1
Chapter 4 103
4.2
4.3
4.4
4.5
5.1
Chapter 5 159
5.2
5.3
5.4
5.5
5.6
5.7
5.9
5.8
5.10
5.11
5.12

Terminology for Class Members
Keywords in Java
Reserved Literals in Java
Reserved Keywords not Currently in Use
Examples of Literals
Examples of Decimal, Octal, and Hexadecimal Literals
Examples of Character Literals
Escape Sequences
Examples of Escape Sequence \ddd
Range of Integer Values
Range of Character Values
Range of Floating-Point Values
Boolean Values
Summary of Primitive Data Types
Default Values
Parameter Passing By Value
Accessing Members within a Class
Summary of Accessibility Modifiers for Top-Level Types
Summary of Other Modifiers for Types
Summary of Accessibility Modifiers for Members
Summary of Other Modifiers for Members
Selected Conversion Contexts and Conversion Categories
Operator Summary
Examples of Truncated Values
Arithmetic Operators
Examples of Arithmetic Expression Evaluation
Arithmetic Compound Assignment Operators
Relational Operators
Reference Equality Operators
Primitive Data Value Equality Operators
Truth-Values for Boolean Logical Operators
Boolean Logical Compound Assignment Operators
Conditional Operators

10
21
21
21
21
22
23
24
25

28
29
29
30
30
33
82
130
135
137
144
153
163
167
172
174
180
183
191
192
192
195
196
196

xxvii

xxviii

LIST OF TABLES

5.13
6.1
Chapter 6 203

Chapter
Chapter
Chapter

Chapter

Chapter
Chapter

Chapter

6.2
6.3
7.1
7 283
7.2
8.1
8 351
9
1011.1
11
389
423
467
11.2
11.3
11.4
11.5
11.6
11.7
1212.1
531
12.2
12.3
12.4
12.5
12.6
12.7
12.8
12.9
12.10
12.11
12.12
12.13
12.14
12.15
12.16
12.18
12.17
1313.1
613
1414.1
661
14.2
14.3
14.4
14.5
14.6
1515.1
747
15.2
15.3
G.1
G.2

Truth-values for Conditional Operators
197
The return Statement
228
Granularities for Enabling and Disabling Assertions at Runtime
269
Enabling and Disabling Assertions in All System Classes at Runtime 272
Overriding vs. Overloading
293
Types and Values
317
Overview of Type Declarations
354
Selected Input Streams
477
Selected Output Streams
477
480
The DataInput and DataOutput Interfaces
Selected Readers
488
Selected Writers
490
Print Methods of the PrintWriter Class
491
Correspondence Between Selected Byte and Character Streams
500
Selected Language Codes
532
Selected Country Codes
532
Selected Predefined Locales for Languages
533
Selected Predefined Locales for Countries
533
Selected Field Numbers to Indicate Information in a Calendar
537
Selected Constants that Represent Values in a Calendar
538
Formatting Styles for Date and Time
542
Selected Characters
555
Selected Character Classes
556
Selected Predefined Character Classes
557
Boundary Matchers
557
Selected Logical Operators
558
Quantifier Classification
561
Implications of the Limit Value in the split() Method
564
Formatting Conversions
596
Flags
597
Selected Format Exceptions
601
Selected Time/Date Composition Conversions
601
Thread States
636
Summary of Subtyping Relationships for Generic Types
675
Get and Set Operations Using Parameterized References
682
Summary of Get and Set Operations using Parameterized References 684
Examples of Type Erasure
714
Examples of Reifiable Types
723
Examples of Non-Reifiable Types
723
Core Interfaces in the Collections Framework
779
Summary of Collection and Map Implementations
782
Bulk Operations and Set Logic
796
Number Systems
1005
Representing Signed byte Values Using 2’s Complement
1010

List of Examples

1.1
Basic Elements
of a Class Declaration
Chapter
11
1.2 Static Members in Class Declaration
1.3 Defining a Subclass
1.4 An Application
2.1
Default
for Fields
Chapter
2 Values
19
2.2 Flagging Uninitialized Local Variables of Primitive Data Types
2.3 Flagging Uninitialized Local Reference Variables
3.1
A JavaBean
Chapter
3 39
3.2 Using the this Reference
3.3 Namespaces
3.4 Using Enums
3.5 Declaring Enum Constructors and Members
3.6 Declaring Constant-Specific Class Bodies
3.7 Using Arrays
3.8 Using Anonymous Arrays
3.9 Using Multidimensional Arrays
3.10 Passing Primitive Values
3.11 Passing Reference Values
3.12 Passing Arrays
3.13 Array Elements as Primitive Data Values
3.14 Array Elements as Reference Values
3.15 Calling a Varargs Method
3.16 Passing Program Arguments
4.1
Defining
Packages and Using Type Import
Chapter
4 103
4.2 Single Static Import
4.3 Avoiding the Interface Constant Antipattern
4.4 Importing Enum Constants
4.5 Shadowing by Importing
4.6 Conflict in Importing Static Method with the Same Signature
4.7 Importing Nested Static Types
4.8 Using Properties
4.9 Class Scope
4.10 Accessibility Modifiers for Classes and Interfaces

3
8
11
15
33
34
35
42
46
49
55
56
60
73
75
78
83
84
86
88
88
91
95
107
110
110
111
112
113
114
123
131
133

xxix

xxx

LIST OF EXAMPLES

4.11 Abstract Classes
4.12 Public Accessibility of Members
4.13 Accessing Static Members
4.14 Accessing Final Members
4.15 Synchronized Methods
5.1
Operand
Evaluation Order
Chapter
5 159
5.2 Numeric Promotion in Arithmetic Expressions
5.3 Short-Circuit Evaluation Involving Conditional Operators
6.1
Fall Through
Chapter
6 203 in a switch Statement
6.2 Using break in a switch Statement
6.3 Nested switch Statement
6.4 Enums in switch Statement
6.5 The break Statement
6.6 Labeled break Statement
6.7 The continue Statement
6.8 Labeled continue Statement
6.9 The return Statement
6.10 Method Execution
6.11 The try-catch Construct
6.12 Exception Propagation
6.13 The try-catch-finally Construct
6.14 The try-finally Construct
6.15 The finally Block and the return Statement
6.16 Throwing Exceptions
6.17 The throws Clause
6.18 Using Assertions
7.1
Extending
Classes: Inheritance and Accessibility
Chapter
7 283
7.2 Overriding, Overloading, and Hiding
7.3 Using the super Keyword
7.4 Constructor Overloading
7.5 The this() Constructor Call
7.6 The super() Constructor Call
7.7 Interfaces
7.8 Variables in Interfaces
7.9 Assigning and Passing Reference Values
7.10 Choosing the Most Specific Method (Simple Case)
7.11 Overloaded Method Resolution
7.12 The instanceof and Cast Operators
7.13 Using the instanceof Operator
7.14 Polymorphism and Dynamic Method Lookup
7.15 Implementing Data Structures by Inheritance and Aggregation
8.1
Overview
of Type Declarations
Chapter
8 351
8.2 Static Member Types
8.3 Importing Static Member Types
8.4 Accessing Members in Enclosing Context (Static Member Classes)
8.5 Defining and Instantiating Non-static Member Classes

136
139
147
149
151
175
181
198
208
210
211
212
224
225
226
227
228
236
247
250
253
254
255
256
258
267
285
290
296
302
304
305
311
315
320
325
326
329
330
341
342
353
355
357
358
360

LIST OF EXAMPLES

Special Form of this and new Constructs in Non-static
Member Classes
8.7 Inheritance Hierarchy and Enclosing Context
8.8 Extending Inner Classes
8.9 Access in Enclosing Context (Local Classes)
8.10 Instantiating Local Classes
8.11 Objects of Local Classes as Caches
8.12 Defining Anonymous Classes
8.13 Accessing Declarations in Enclosing Context (Anonymous Classes)
9.1
Garbage
Collection Eligibility
Chapter
9 389
9.2 Using Finalizers
9.3 Invoking Garbage Collection
9.4 Initializer Expression Order and Method Calls
9.5 Exceptions in Initializer Expressions
9.6 Static Initializers and Forward References
9.7 Static Initializer Blocks and Exceptions
9.8 Instance Initializers and Forward References
9.9 Instance Initializer Block in Anonymous Class
9.10 Exception Handling in Instance Initializer Blocks
9.11 Object State Construction
9.12 Initialization under Object State Construction
10.1
Methods
the Object class
Chapter
10 in
423
10.2 String Representation of Integers
10.3 String Construction and Equality
10.4 Reading Characters from a String
11.1
Listing11
Files
Chapter
467Under a Directory
11.2 Copy a File
11.3 Reading and Writing Binary Values
11.4 Demonstrating Readers and Writers, and Character Encoding
11.5 Changing Passwords
11.6 Object Serialization
11.7 Non-Serializable Objects
11.8 Customized Serialization
11.9
Serialization and Inheritance
12.1
Understanding
Chapter
12 531 Locales
12.2 Using the Date class
12.3 Using the Calendar Class
12.4 Formatting Date/Time
12.5 Using the DateFormat class
12.6 Using the NumberFormat class
12.7 Splitting
12.8 String Pattern Matching
12.9 Match and Replace
12.10 Tokenizing Mode
12.11 Parsing Primitive Values and Strings
12.12 Using Delimiters and Patterns with a Scanner

xxxi

8.6

363
365
367
371
374
376
378
381
394
397
400
408
409
411
412
414
414
415
417
419
426
435
441
444
474
478
482
498
503
513
515
518
520
534
536
540
543
544
548
565
568
570
573
576
580

xxxii

LIST OF EXAMPLES

12.13 Multi-Line Mode
12.14 Using the format() Method
13.1
Implementing
Chapter
13 613 the Runnable Interface
13.2 Extending the Thread Class
13.3 Mutual Exclusion
13.4 Thread States
13.5 Waiting and Notifying
13.6 Joining of Threads
13.7 Thread Termination
13.8 Deadlock
14.1
A Legacy
Class
Chapter
14 661
14.2 A Generic Class for Nodes
14.3 A Generic Interface and its Implementation
14.4 Extending Generic Types
14.5 Unchecked Warnings
14.6 Illustrating Get and Set Operations Using Parameterized References
14.7 Implementing a Simplified Generic Stack
14.8 Declaring and Calling Generic Methods
14.9 Flexible Comparisons
14.10 Using Recursive Bounds
14.11 Using the @Override Annotation
14.12 Subsignatures
14.13 Overriding from Generic Supertype
14.14 Missing Supertype Parameterization
14.15 Genericity Cannot Be Added to Inherited Methods
14.16 Type Parameter in throws Clause
14.17 Generic Nested Classes
15.1
A Test15
Case
for Version Numbers
Chapter
747
15.2 Not Overriding the equals() and the hashCode() Methods
15.3 Testing the equals() and the hashCode() Methods
15.4 Implementing the equals() Method
15.5 Implications of Overriding the equals() Method
15.6 Implementing the hashCode() Method
15.7 Implications of Overriding the hashCode() Method
15.8 Implementing the compareTo() Method of the Comparable Interface
15.9 Implications of Implementing the compareTo() Method
15.10 Natural Ordering and Total Ordering
15.11 Using a Comparator for Version Numbers
15.12 Using an Iterator
15.13 Using a for(:) Loop to Iterate Over a Collection
15.14 Converting Collections to Arrays
15.15 Traversing Over Sets
15.16 Using Sets
15.17 Using Navigable Sets
15.18 Using Lists
15.19 Using Priority Queues

582
603
618
621
628
637
644
648
651
652
662
664
667
669
671
681
695
697
710
712
719
720
720
721
722
731
732
749
752
752
756
759
762
765
767
769
773
774
786
789
790
797
799
803
808
811

LIST OF EXAMPLES

15.20
15.21
15.22

xxxiii

Using Deques as a Stack and as a FIFO Queue
Using Maps
Using Navigable Maps

815
825
831

This page intentionally left blank

Foreword

Consider the following observations:
• Software continues to become ever more pervasive, ever more ubiquitous in
our lives.
• Incompetence seems to be the only thing we can count on in today’s world
and, especially, in the domain of software.
• The Java programming language has become a lingua franca for programmers
all over the world.
One can draw varied conclusions from these comments. One of them is that it is of
great importance that programmers working with the Java programming language should be as competent as possible.
The Java certification program is an important effort aimed at precisely this goal.
Practitioners looking to obtain such certification need good quality training materials, which brings us to this book.
Programming is still more of an art than a science, and will continue to be so for
the foreseeable future. Mastering the intricacies of a large and complex programming language is a challenging task that requires time and effort, and above all
experience.
Real programming requires more than just mastery of a programming language. It
requires mastery of a computing platform, with a rich set of libraries. These libraries are designed to simplify the task of building realistic applications, and they do.
Again, the practitioner is faced with a daunting task.
To address the clear need for professional training material, a plethora of books
have been written purporting to tutor programmers in the programming language
and platform skills they require.
The choice is as mind boggling as the material within the books themselves.
Should one try Java for Frontally Lobotomized Simians or Postmodern Java Dialectics?
The readership for these books is largely self selecting. I trust that if you, the reader,

xxxv

xxxvi

FOREWORD

have gotten this far, you are looking for something that is intelligent, yet practical.
This book is one of the finest efforts in this crowded arena. It brings a necessary
level of academic rigor to an area much in need of it, while retaining an essentially
pragmatic flavor.
The material in this book is probably all you need to pass the Java certification
exam. It certainly isn’t all you need to be a good software engineer. You must continue learning about new technologies. The hardest part of this is dealing with
things that are completely different from what you are familiar with. Yet this is
what distinguishes the top flight engineer from the mediocre one. Keep an open
mind; it pays.
Gilad Bracha
Computational Theologist
Sun Java Software
http://java.sun.com/people/gbracha/

Preface

Writing the Third Edition
The exam for the Sun Certified Programmer for Java Platform, Standard Edition 6,
has changed considerably since the second edition of this book was published. The
most noticeable change in the current version of the Sun Certified Java Programmer (SCJP) 1.6 exam is the inclusion of the features of Java 5, and the shifting of
emphasis towards analyzing code scenarios, rather than individual language constructs. In our opinion, the new exam demands an even greater understanding and
actual experience of the language, rather than mere recitation of facts. Proficiency
in the language is the key to success.
Since the emphasis of the SCJP 1.6 exam is on the core features of Java, the third
edition provides even greater in-depth coverage of the relevant topics. The book
covers not just the exam objectives, but also supplementary topics that aid in mastering the exam topics.
The third edition is still a one-source guide for the SCJP 1.6 exam: it provides a mixture of theory and practice for the exam. Use the book to learn Java, pass the SCJP
1.6 exam, and afterwards, use it as a handy language guide. The book also has an
appendix devoted to the SCJP 1.6 Upgrade exam.
We have taken into consideration the feedback we have received from readers. The
many hours spent in handling the deluge of e-mail have not been in vain. Every
single e-mail is appreciated and is hereby acknowledged.
Preparing the third edition dispelled our illusions about newer editions being, to
put it colloquially, a piece of cake. Every sentence from the second edition has been
weighed carefully, and not many paragraphs have escaped rewriting. UML (Unified Modeling Language) is also extensively employed in this edition. Numerous
new review questions have been added. In covering the new topics and expanding
the existing ones, new examples, figures, and tables were also specifically created
for the third edition.

xxxvii

xxxviii

PREFACE

About This Book
This book provides extensive coverage of the Java programming language and its
core Application Programming Interfaces (APIs), with particular emphasis on its
syntax and usage. The book is primarily intended for professionals who want to
prepare for the SCJP 1.6 exam, but it is readily accessible to any programmer who
wants to master the language. For both purposes, it provides in-depth coverage of
essential features of the language and its core APIs.
There is a great and increasing demand for certified Java programmers. Sun
Microsystems has defined the SCJP 1.6 exam as one that professionals can take to
validate their skills. The certification provides the IT industry with a standard to
use for hiring such professionals, and allows the professionals to turn their Java
skills into credentials that are important for career advancement.
The book provides extensive coverage of all the objectives defined for the exam by
Sun. But the exam objectives are selective and do not include many of the essential
features of Java. This book covers many additional topics that every Java programmer should master in order to be proficient. In this regard, the book is a comprehensive primer for learning the Java programming language. After mastering the
language by working through this book, the reader can confidently sit for the
exam.
This book is not a complete reference for Java, as it does not attempt to list every
member of every class from the Java Development Kit (JDK) API documentation.
The purpose is not to document the JDK APIs. This book does not teach
programming techniques. The emphasis is on the Java programming language
features, their syntax and correct usage through code examples.
The book assumes little background in programming. We believe the exam is
accessible to any programmer who works through the book. A Java programmer
can easily skip over material that is well understood and concentrate on parts that
need reinforcing, whereas a programmer new to Java will find the concepts
explained from basic principles.
Each topic is explained and discussed thoroughly with examples, and backed by
review questions and exercises to reinforce the concepts. The book is not biased
toward any particular platform, but provides platform-specific details where
necessary.

Using the Book
The reader can choose a linear or a non-linear route through the book, depending
on her programming background. Non-Java programmers wishing to migrate to
Java can read Chapter 1, which provides a short introduction to object-oriented
programming concepts, and the procedure for compiling and running Java appli-

PREFACE

xxxix

cations. For those preparing for the SCJP 1.6 exam, the book has a separate appendix providing all the pertinent information on taking the exam.
The table of contents; listings of tables, examples, and figures; and a comprehensive index facilitate locating topics discussed in the book.
In particular, we draw attention to the following features:
Exam Objectives
0.1 Exam objectives are stated clearly at the start of every chapter.
0.2 The number in front of the objective identfies the objective as defined by
Sun.
0.3 The objectives are organized into major sections, detailing the curriculum
for the exam.
0.4 The exam objectives are reproduced in Appendix B where, for each section
of the syllabus, references are included to point the reader to relevant topics
for the exam.

Supplementary Objectives
• Supplementary objectives cover topics that are not on the exam, but which
we believe are important for mastering the topics that are on the exam.
• Any supplementary objectives are listed as bullets at the beginning of a
chapter.

Review Questions
Review questions are provided after every major topic, in order to test and reinforce the material. These review questions reflect the kinds of questions that can be
asked on the actual exam. Annotated answers to the review questions are provided
in a separate appendix.
Example 0.1

Example Source Code

We encourage experimenting with the code examples in order to reinforce the
material from the book. These can be downloaded from the book Web site (see
p. xli).
Java code is written in a mono-spaced font. Lines of code in the examples or in code
snippets are referenced in the text by a number, which is specified by using a
single-line comment in the code. For example, in the following code snippet, the
call to the method doSomethingInteresting() hopefully does something interesting
at (1).

xl

PREFACE
// ...
doSomethingInteresting();
// ...

// (1)

Names of classes and interfaces start with an uppercase letter. Names of packages,
variables, and methods start with a lowercase letter. Constants are all in uppercase
letters. Interface names begin with the prefix 'I'. Coding conventions are followed, except when we have had to deviate in the interest of space or clarity.

Chapter Summary
Each chapter concludes with a summary of the topics, pointing out the major concepts discussed in the chapter.

Programming Exercises
Programming exercises at the end of each chapter provide the opportunity to put
concepts into practice. Solutions to the programming exercises are provided in a
separate appendix.

Mock Exam
A complete mock exam is provided in a separate appendix, which the reader can
try when she is ready.

Java SE API Documentation
A vertical gray bar is used to highlight methods and fields found in the classes
of the core Java APIs.
Any explanation following the API information is also similarly highlighted.
In order to obtain optimal benefit from using this book in preparing for the SCJP
1.6 exam, we strongly recommend installing the latest version (1.6 or newer) of the
JDK and its accompanying API documentation. The book focuses solely on Java,
and does not acknowledge previous versions.

Java Platform Upgrade Exam
For those who have taken the Sun Certified Programmer for Java Platform 1.5
Exam, and would like to prepare for the Sun Certified Programmer for Java Platform 1.6 Upgrade Exam, we have provided an appendix with details of the
upgrade exam. The appendix contains the upgrade exam objectives, and for each
section of the syllabus, references are included to point the reader to topics essential for the upgrade exam.

PREFACE

xli

Book Web Site
This book is backed by a Web site providing auxiliary material:
http://www.ii.uib.no/~khalid/pgjc3e/

The contents of the Web site include the following:
• source code for all the examples and programming exercises in the book
• mock exam engine
• errata
• links to miscellaneous Java resources (certification, discussion groups, tools, etc.)
Information about the Java Standard Edition and its documentation can be found
at the following Web site:
http://java.sun.com/javase/

The current authoritative technical reference for the Java programming language,
The Java Language Specification, Third Edition (also published by Addison-Wesley),
can be found at this Web site:
http://java.sun.com/docs/books/jls/

Request for Feedback
Considerable effort has been made to ensure the accuracy of the contents of this
book. Several Java professionals have proofread the manuscript. All code
examples (including code fragments) have been compiled and tested on various
platforms. In the final analysis, any errors remaining are the sole responsibility of
the authors.
Any questions, comments, suggestions, and corrections are welcome. Let us know
whether the book was helpful or detrimental for your purposes. Any feedback is
valuable. The authors can be reached by the following e-mail alias:
pgjc3e@ii.uib.no

About the Authors
Khalid A. Mughal
Khalid A. Mughal is an Associate Professor at the Department of Informatics at
the University of Bergen, Norway. Professor Mughal is responsible for designing
and implementing various courses, which use Java, at the Department of Informatics. Over the years, he has taught Programming Languages (Java, C/C++,
Pascal), Software Engineering (Object-Oriented System Development), Data-

xlii

PREFACE

bases (Data Modeling and Database Management Systems), and Compiler Techniques. He has also given numerous courses and seminars at various levels in
object-oriented programming and system development, using Java and Javarelated technology, both at the University and for the IT industry. He is the principal author of the book, responsible for writing the material covering the Java
topics.
Professor Mughal is also the principal author of an introductory Norwegian textbook on programming in Java (Java som første programmeringsspråk/Java as First Programming Language, Third Edition, Cappelen Akademisk Forlag, ISBN-10: 82-0224554-0, 2006), which he co-authored with Torill Hamre and Rolf W. Rasmussen.
Together they have also published another textbook for a 2-semester course in programming (Java Actually: A Comprehensive Primer in Programming, Cengage Learning, ISBN-10: 1844809331, 2008).
His current work involves applying Object Technology in the development of content management systems for publication on the Web, and security issues related
to web applications. For the past seven years he has been responsible for developing and running web-based programming courses in Java, which are offered to offcampus students.
He is also a member of the Association for Computing Machinery (ACM).

Rolf W. Rasmussen
Rolf W. Rasmussen is the System Development Manager at vizrt, a company that
develops solutions for the TV broadcast industry, including real-time 3D graphic
renderers, and content and control systems.
Rasmussen works mainly on control and automation systems, video processing,
typography, and real-time visualization. He has worked on clean room implementations of the Java class libraries in the past, and is a contributor to the Free Software Foundation.
Over the years, Rasmussen has worked both academically and professionally with
numerous programming languages, including Java. He is primarily responsible
for developing the review questions and answers, the programming exercises and
their solutions, the mock exam, and all the practical aspects related to taking the
SCJP exam presented in this book.
As mentioned above, he is also a co-author of two introductory textbooks on programming in Java.

Acknowledgments (First Edition)
A small application for drawing simple shapes is used in the book to illustrate
various aspects of GUI building. The idea for this application, as far as we know,

PREFACE

xliii

first appeared in Appendix D of Data Structures and Problem Solving Using Java
(M.A. Weiss, Addison-Wesley, 1998).
At Addison-Wesley-Longman (AWL), we would like to thank Emma Mitchell for
the support and the guidance she provided us right from the start of this project,
Martin Klopstock at AWL for accommodating the non-standard procedure
involved in getting the book to the printing press, Clive Birks at CRB Associates for
providing the professional look to the contents of this book, and finally, Sally
Mortimore at AWL for seeing us over the finishing line. The efforts of other professionals behind the scenes at AWL are also acknowledged.
Many reviewers have been involved during the course of writing this book. First
of all, we would like to thank the five anonymous reviewers commissioned by
AWL to review the initial draft. Their input was useful in the subsequent writing
of this book.
Several people have provided us with feedback on different parts of the material
at various stages: Jon Christian Lønningdal, Tord Kålsrud, Kjetil Iversen, Roy
Oma, and Arne Løkketangen. Their help is hereby sincerely acknowledged.
We are also very grateful to Laurence Vanhelsuwé, Kris Laporte, Anita Jacob, and
Torill Hamre for taking on the daunting task of reviewing the final draft, and
providing us with extensive feedback at such short notice. We would like to thank
Marit Mughal for reading the manuscript with the trained eye of a veteran English
schoolteacher.
We now understand why family members are invariably mentioned in a preface.
Without our families’ love, support, and understanding this book would have
remained a virtual commodity. Khalid would like to thank Marit, Nina, and Laila
for their love, and for being his pillars of support, during the writing of this book.
Thanks also to the folks in Birmingham for cheering us on. Rolf would like to thank
Liv, Rolf V., Knut, and Elisabeth for enduring the strange working hours producing
this book has entailed. A special thanks to Marit for providing us with scrumptious
dinners for consumption at the midnight hour.

Acknowledgments (Second Edition)
Feedback from many readers helped us to improve the first edition. We would like
to thank the following readers for their input in this effort:
Michael F. Adolf, Tony Alicea, Kåre Auglænd, Jorge L. Barroso, Andre Beland, Darren Bruning, Paul Campbell, Roger Chang, Joanna Chappel, Laurian M Chirica,
Arkadi Choufrine, Barry Colston, John Cotter, Frédéric Demers, Arthur De Souza,
djc, William Ekiel, Darryl Failla, John Finlay, Christopher R. Gardner, Marco Garcia, Peter Gieser, George, Paul Graf, Shyamsundar Gururaj, Ray Ho, Leonardo
Holanda, Zhu Hongjun, Kara Van Horn, Peter Horst, Nain Hwu, Kent Johnson,
Samir Kanparia, Oleksiy Karpenko, Jeffrey Kenyon, Young Jin Kim, Kenneth

xliv

PREFACE

Kisser, Billy Kutulas, Yi-Ming Lai, Robert M. Languedoc, Steve Lasley, Winser Lo,
Naga Madipalli, Craig Main, Avinash Mandsaurwale, Thomas Mathai, S. Mehra,
Yuan Meng, Simon Miller, William Moore, Anders Morch, George A. Murakami,
Sandy Nemecek, Chun Pan, Abigail García Patiño, Anil Philip, Alfred Raouf, Peter
Rorden, Christian Seifert, Gurpreet Singh, Christopher Stanwood, Swaminathan
Subramanian, Siva Sundaram, Manju Swamy, John Sweeney, Harmon Taylor,
Andrew Tolopko, Ravi Verma, Per J. Walstrøm, Chun Wang, James Ward, Winky,
Chun Wang, Jimmy Yang, Jennie Yip, Yanqu Zhou, and Yingting Zhou.
At the UK office of Addison-Wesley/Pearson Education, we would like to thank
our former editor Simon Plumtree for his unceasing support and patience while
we slogged on with the second edition. We would also like to acknowledge the
help and support of the following professionals, past and present, at the London
office: Alison Birtwell, Sally Carter, Karen Sellwood and Katherin Ekstrom. A special thanks to Karen Mosman (who has since moved on to another job) for her
encouragement and advice.
During the last lap of getting the book to the printing press, we were in the capable
hands of Ann Sellers at the US office of Addison-Wesley/Pearson Education. We
would like to acknowledge her efforts and that of other professionals—in particular, Greg Doench, Jacquelyn Doucette, Amy Fleischer, Michael Mullen, and Dianne
Russell—who helped to get this book through the door and on to the bookshelf.
Thanks also to Mike Hendrickson for always lending an ear when we met at the
OOPSLA conferences, and pointing us in the right direction with our book plans.
We would like to thank the folks at Whizlabs Software for their collaboration in
producing the contents for the CD accompanying this book. Those guys certainly know the business of developing exam simulators for certification in Java
technology.
We were fortunate in having two Java gurus—Laurence Vanhelsuwé and Marcus
Green—to do the technical review of the second edition. As he did for the first edition, Laurence came through and provided us with invaluable feedback, from the
minutiae of writing technical books to many technical issues relating to the Java
programming language. Marcus put the manuscript through his severe certification scrutiny regarding the specifics of the SCJP exam. We are sorry to have upset
their plans for Easter holidays, and hasten to thank them most profusely for taking
on the task.
We cannot thank enough our own in-house, private copy-editor: Marit Seljeflot
Mughal. She diligently and relentlessly read numerous drafts of the manuscript,
usually at very short notice. Marit claims that if she understood what we had written, then a computer-literate person should have no problem whatsoever. This
claim remains to be substantiated. If any commas are not used correctly, then it is
entirely our fault, in spite of being repeatedly shown how to use them.
We are also indebted to many Java-enabled individuals for providing us valuable
feedback on parts of the manuscript for the second edition. This includes Pradeep
Chopra, Seema R., and Gaurav Kohli at Whizlabs Software. Unfortunately for us,

PREFACE

xlv

they only had time to read part of the manuscript. Thanks also to Torill Hamre at
the Nansen Environmental and Remote Sensing Center, Bergen, for her useful
comments and suggestions. We also thank the following Master students at the
Department of Informatics, University of Bergen, for providing useful feedback:
Mikal Carlsen, Yngve Espelid, Yngve A. Aas, Sigmund Nysæter, Torkel Holm, and
Eskil Saatvedt.
Family support saw us through this writing project as well. Our families have put
up with our odd and long working hours, endured our preoccupation and our
absence at the dining table. Khalid would like to acknowledge the love and support of his wife, Marit, and daughters, Nina and Laila, while working on this book.
Rolf would like to thank Liv, Rolf V., Knut, and Elisabeth for their love, patience
and support.

Acknowledgments (Third Edition)
Many readers have sent us e-mails testifying that the Programmer’s Guide contributed toward their success on the exam. That is the best advertisement we can hope
for. The feedback we have received since the publication of the second edition has
had an impact on improving the third edition. In particular, we would like to thank
the following diligent readers for their contributions:
Bret ABMac, Einar Andresen, Brian Bradshaw, Nicola Cammillini, Juan Carlos
Castro, Sweta Doshi, David Featherstone, Danish Halim, Niels Harremoës, John
Holcroft, Leong Jern-Kuan, Rajesh Kesarwani, Ken Kisser, Shampa Kumar, Tony
LaPaso, Kaydell Leavitt, Luba Leyzerenok, Adam Lorentzon, Chuck Meier, Philip
Mitchell, Sigmund Nysæter, Pat Owens, Sanket Reddy, Raul Saavedra, Oliver Schoettler, Wayne Schroeter, Mark Sullivan, Myoung Son, Bob Souther, Anthony Tang,
Frederik Uyttersprot.
Erik Ernst was kind enough to review the chapter on Java generics, for which we
are very grateful. The generics chapter was also reviewed by Erik Andreas Brandstadmoen and Kristian Berg. Our sincere thanks to all of you. The pages of feedback we received helped to clarify many subtleties, and made us realize that some
dark corners of Java generics are best avoided by mere mortals.
Selected chapters for the third edition were also vetted by the following Java developers in the Bergen area: Olve Hansen, David J.M. Karlsen and Lars Søraas. Many
thanks for taking time out from your busy schedule to provide us with your feedback. Our thanks also to Helge W. Johnsen and Amund Trovåg for feedback on
review questions regarding new features in Java 1.5.
Our award for Reviewer Par Excellence goes to Jennie Yip. The meticulous notes
she provided for the ten chapters of the second edition have had a profound effect
on shaping the third edition. Any chance that the feat can be repeated with the
third edition? Please name your price.

xlvi

PREFACE

This time around we were again fortunate enough to have Marcus Green as our
technical reviewer. We have heeded his feedback that has kept us, we hope, on the
straight and narrow as far as the exam is concerned, and curbed our enthusiasm
for including every Java topic that we fancied. Our sincere thanks for the review
you provided us.
At Pearson, we would like to thank Greg Doench and Michelle Housley for managing the publication of this edition. We are also grateful to the people behind the
scenes at Pearson who helped get the book to the printing press.
Khalid would like to thank the Computer Science Department at Cornell University, where he spent a significant part of his sabbatical (Fall 2007/Spring 2008)
working on the third edition. A better place for such an endeavour would be hard
to come by.
We cannot thank enough Marit Seljeflot Mughal who has been our personal quality
controller, acting as an amalgamated draft reader, copy editor, and proofreader.
What she sanctioned we could confidently allow to be seen by the light of day, saving us many embarrassing mistakes, both technical and non-technical. We don’t
know if it is for us or for the love of Java that you scrutinize the endless drafts that
we lay in your path.
Any mistakes or errors remaining are an oversight on our part. Rest assured that
every possible effort has been made to get the facts straight.
Without family support this edition would still be wishful thinking. Khalid would
like to thank Marit, Laila, Nina and Kenneth for their love, support and understanding—particularly, while working on this book.
—Khalid A. Mughal
Rolf W. Rasmussen
September 2008
Ithaca, New York, USA
Bergen, Norway

Basics of Java Programming

1

Supplementary Objectives
• Introduce the basic terminology and concepts in object-oriented
programming: classes, objects, references, fields, methods, members,
inheritance, aggregation.
• Identify the essential elements of a Java program.
• Learn how to compile and run a Java program.

1

2

CHAPTER 1: BASICS OF JAVA PROGRAMMING

1.1 Introduction
Before embarking on the road to Java programmer certification, it is important to
understand the basic terminology and concepts in object-oriented programming
(OOP). In this chapter, the emphasis is on providing an introduction rather than
exhaustive coverage. In-depth coverage of the concepts follows in subsequent
chapters of the book.
Java supports the writing of many different kinds of executables: applications,
applets, and servlets. The basic elements of a Java application are introduced in
this chapter. The old adage that practice makes perfect is certainly true when learning a programming language. To encourage programming on the computer, the
mechanics of compiling and running a Java application are outlined.

1.2 Classes
One of the fundamental ways in which we handle complexity is in abstractions. An
abstraction denotes the essential properties and behaviors of an object that
differentiate it from other objects. The essence of OOP is modelling abstractions,
using classes and objects. The hard part in this endeavor is finding the right
abstraction.
A class denotes a category of objects, and acts as a blueprint for creating such
objects. A class models an abstraction by defining the properties and behaviors for
the objects representing the abstraction. An object exhibits the properties and
behaviors defined by its class. The properties of an object of a class are also called
attributes, and are defined by fields in Java. A field in a class is a variable which can
store a value that represents a particular property of an object. The behaviors of an
object of a class are also known as operations, and are defined using methods in Java.
Fields and methods in a class declaration are collectively called members.
An important distinction is made between the contract and the implementation that
a class provides for its objects. The contract defines what services, and the implementation defines how these services are provided by the class. Clients (i.e., other
objects) only need to know the contract of an object, and not its implementation, in
order to avail themselves of the object’s services.
As an example, we will implement different versions of a class that models the
abstraction of a stack that can push and pop characters. The stack will use an array
of characters to store the characters, and a field to indicate the top element in the
stack. Using Unified Modeling Language (UML) notation, a class called CharStack
is graphically depicted in Figure 1.1, which models the abstraction. Both fields and
method names are shown in Figure 1.1a.

3

1.2: CLASSES
Figure 1.1

UML Notation for Classes
Class Name
Fields

CharStack
stackArray

CharStack

topOfStack

Methods

push()
pop()
peek()
isEmpty()
isFull()

(b) Abbreviated Form

(a) Expanded Form

Declaring Members: Fields and Methods
Example 1.1 shows the declaration of the class CharStack depicted in Figure 1.1. Its
intention is to illustrate the salient features of a class declaration in Java, and not
the effective implementation of stacks.
A class declaration consists of a series of member declarations. In the case of the
class CharStack, it has two fields declared at (1):
• stackArray, which is an array to hold the elements of the stack (in this case,
characters)
• topOfStack, which denotes the top element of the stack (i.e., the index of the last
character stored in the array)
The class CharStack has five methods, declared at (3), that implement the essential
operations on a stack:
• push() pushes a character on to the stack
• pop() removes and returns the top element of the stack
• peek() returns the top element of the stack for inspection
• isEmpty() determines whether the stack is empty
• isFull() determines whether the stack is full
The class declaration also has a method-like declaration with the same name as the
class, (2). Such declarations are called constructors. As we shall see, a constructor is
executed when an object is created from the class. However, the implementation
details in the example are not important for the present discussion.
Example 1.1

Basic Elements of a Class Declaration
//Source Filename: CharStack.java
public class CharStack {
// Class name
// Class Declarations:

4

CHAPTER 1: BASICS OF JAVA PROGRAMMING
// Fields:
private char[] stackArray;
private int
topOfStack;

(1)
// The array implementing the stack.
// The top of the stack.

// Constructor:
public CharStack(int capacity) {
stackArray = new char[capacity];
topOfStack = -1;
}
// Methods:
public void push(char element)
public char pop()
public char peek()
public boolean isEmpty()
public boolean isFull()

{
{
{
{
{

(2)

(3)
stackArray[++topOfStack] = element; }
return stackArray[topOfStack--]; }
return stackArray[topOfStack]; }
return topOfStack < 0; }
return topOfStack == stackArray.length - 1; }

}

1.3 Objects
Class Instantiation, Reference Values, and References
The process of creating objects from a class is called instantiation. An object is an
instance of a class. The object is constructed using the class as a blueprint and is
a concrete instance of the abstraction that the class represents. An object must be
created before it can be used in a program.
A reference value is returned when an object is created. A reference value denotes a
particular object. An object reference (or simply reference) is a variable that can store
a reference value. A reference thus provides a handle to an object, as it can indirectly denote an object whose reference value it holds. In Java, an object can only
be manipulated via its reference value, or equivalently by a reference that holds its
reference value.
The process of creating objects usually involves the following steps:
1.

Declaration of a variable to store the reference value of an object.
This involves declaring a reference variable of the appropriate class to store the
reference value of the object.
// Declaration of two reference variables that will refer to
// two distinct objects, namely two stacks of characters, respectively.
CharStack stack1, stack2;

2.

Creating an object.
This involves using the new operator in conjunction with a call to a constructor,
to create an instance of the class.
// Create two distinct stacks of chars.

5

1.3: OBJECTS
stack1 = new CharStack(10); // Stack length: 10 chars
stack2 = new CharStack(5); // Stack length: 5 chars

The new operator creates an instance of the CharStack class and returns the reference value of this instance. The reference value can be assigned to a reference
variable of the appropriate class. The reference variable can then be used to
manipulate the object whose reference value is stored in the reference variable.
Each object has its own copy of the fields declared in the class declaration. The
two stacks, referenced by stack1 and stack2, will have their own stackArray and
topOfStack fields.
The purpose of the constructor call on the right side of the new operator is
to initialize the newly created object. In this particular case, for each new
CharStack instance created using the new operator, the constructor creates an
array of characters. The length of this array is given by the value of the argument to the constructor. The constructor also initializes the topOfStack field.
The declaration of a reference and the instantiation of the class can also be combined, as in the following declaration statement:
CharStack stack1 = new CharStack(10),
stack2 = new CharStack(5);

Figure 1.2 shows the UML notation for objects. The graphical representation of an
object is very similar to that of a class. Figure 1.2 shows the canonical notation,
where the name of the reference variable denoting the object is prefixed to the class
name with a colon (':'). If the name of the reference variable is omitted, as in Figure 1.2b, this denotes an anonymous object. Since objects in Java do not have
names, but are denoted by references, a more elaborate notation is shown in Figure
1.2c, where objects representing references of the CharStack class explicitly refer to
CharStack objects. In most cases, the more compact notation will suffice.
Figure 1.2

UML Notation for Objects

stack1:CharStack

stack2:CharStack

(a) Standard Notation for Objects
:CharStack
(b) Anonymous Object

stack1:Ref(CharStack)

:CharStack

stack2:Ref(CharStack)

:CharStack

(c) Explicit References for Java Objects

6

CHAPTER 1: BASICS OF JAVA PROGRAMMING

Object Aliases
An object can be referred by several references, meaning that they store the reference value of the same object. Such references are called aliases. The object can be
manipulated via any one of its aliases, as each one refers to the same object.
// Create two distinct stacks of chars.
CharStack stackA = new CharStack(12); // Stack length: 12 chars
CharStack stackB = new CharStack(6); // Stack length: 6 chars
stackB = stackA;
// (1) aliases after assignment
// The stack previously referenced by stackB can now be garbage collected.

Two stacks are created in the code above. Before the assignment at (1), the situation
is as depicted in Figure 1.3a. After the assignment at (1), the reference variables
stackA and stackB will denote the same stack, as depicted in Figure 1.3b. The reference value in stackA is assigned to stackB. The reference variables stackA and stackB
are aliases after the assignment, as they refer to the same object. What happens to
the stack object that was denoted by the reference variable stackB before the assignment? When objects are no longer in use, their memory is, if necessary, reclaimed
and reallocated for other objects. This is called automatic garbage collection. Garbage
collection in Java is taken care of by the runtime system.
Figure 1.3

Aliases
stackA:Ref(CharStack)

:CharStack

stackB:Ref(CharStack)

:CharStack
(a) Before

stackA:Ref(CharStack)

:CharStack

stackB:Ref(CharStack)

:CharStack

(b) After Assignment

1.4 Instance Members
Each object created will have its own copies of the fields defined in its class. The
fields of an object are called instance variables. The values of the instance variables
in an object comprise its state. Two distinct objects can have the same state, if their
instance variables have the same values. The methods of an object define its behavior. These methods are called instance methods. It is important to note that these
methods pertain to each object of the class. This should not be confused with the

7

1.5: STATIC MEMBERS

implementation of the methods, which is shared by all instances of the class.
Instance variables and instance methods, which belong to objects, are collectively
called instance members, to distinguish them from static members, which belong to
the class only. Static members are discussed in Section 1.5.

Invoking Methods
Objects communicate by message passing. This means that an object can be made
to exhibit a particular behavior by sending the appropriate message to the object.
In Java, this is done by calling a method on the object using the binary infix dot
('.') operator. A method call spells out the complete message: the object that is the
receiver of the message, the method to be invoked, and the arguments to the
method, if any. The method invoked on the receiver can also send information back
to the sender, via a single return value. The method called must be one that is
defined for the object, otherwise the compiler reports an error.
CharStack stack = new CharStack(5);
// Create a stack
stack.push('J');
// (1) Character 'J' pushed
char c = stack.pop();
// (2) One character popped and returned: 'J'
stack.printStackElements(); // (3) Compile-time error: No such method in CharStack

The sample code above invokes methods on the object denoted by the reference
variable stack. The method call at (1) pushes one character on the stack, and the
method call at (2) pops one character off the stack. Both push() and pop() methods
are defined in the class CharStack. The push() method does not return any value, but
the pop() method returns the character popped. Trying to invoke a method named
printStackElements on the stack results in a compile-time error, as no such method
is defined in the class CharStack.
The dot ('.') notation can also be used with a reference to access the fields of an
object. The use of the dot notation is governed by the accessibility of the member.
The fields in the class CharStack have private accessibility, indicating that they are
not accessible from outside the class:
stack.topOfStack++;

// Compile-time error: topOfStack is a private field.

1.5 Static Members
In some cases, certain members should only belong to the class, and not be part of
any object created from the class. An example of such a situation is when a class
wants to keep track of how many objects of the class have been created. Defining
a counter as an instance variable in the class declaration for tracking the number of
objects created does not solve the problem. Each object created will have its own
counter field. Which counter should then be updated? The solution is to declare the
counter field as being static. Such a field is called a static variable. It belongs to the
class, and not to any object of the class. A static variable is initialized when the class
is loaded at runtime. Similarly, a class can have static methods that belong to the

8

CHAPTER 1: BASICS OF JAVA PROGRAMMING

class, and not to any specific objects of the class. Static variables and static methods
are collectively known as static members, and are declared with the keyword static.
Figure 1.4 shows the class diagram for the class CharStack. It has been augmented
by two static members that are shown underlined. The augmented definition of the
CharStack class is given in Example 1.2. The field counter is a static variable declared
at (1). It will be allocated and initialized to the default value 0 when the class is
loaded. Each time an object of the CharStack class is created, the constructor at (2)
is executed. The constructor explicitly increments the counter in the class. The
method getInstanceCount() at (3) is a static method belonging to the class. It returns
the counter value when called.
Figure 1.4

Class Diagram Showing Static Members of a Class
CharStack
stackArray
topOfStack
counter
push()
pop()
peek()
...
getInstanceCount()

Example 1.2

Static Members in Class Declaration
//Filename CharStack.java
public class CharStack {
// Instance variables:
private char[] stackArray;
private int
topOfStack;

// The array implementing the stack.
// The top of the stack.

// Static variable
private static int counter;

// (1)

// Constructor now increments the counter for each object created.
public CharStack(int capacity) {
// (2)
stackArray = new char[capacity];
topOfStack = -1;
counter++;
}
// Instance methods:
public void push(char element)
public char pop()
public char peek()
public boolean isEmpty()
public boolean isFull()

{
{
{
{
{

stackArray[++topOfStack] = element; }
return stackArray[topOfStack--]; }
return stackArray[topOfStack]; }
return topOfStack < 0; }
return topOfStack == stackArray.length - 1; }

9

1.5: STATIC MEMBERS
// Static method
public static int getInstanceCount() { return counter; }

(3)

}

Figure 1.5 shows the classification of the members in the class CharStack using the
terminology we have introduced so far. Table 1.1 at the end of this section, provides
a summary of the terminology used in defining members of a class.
Clients can access static members in the class by using the class name. The following code invokes the getInstanceCount() method in the class CharStack:
int count = CharStack.getInstanceCount(); // Class name to invoke static method
Figure 1.5

Members of a Class
Class Name CharStack
Instance members belong to objects

Attributes

Static members belong to the class

Instance variables

Static variables

stackArray
topOfStack

counter

Fields

Members

Behaviour

Instance methods

Static methods

push()
pop()
peek()
isEmpty()
isFull()

getInstanceCount()

Objects

Methods

Class

Static members can also be accessed via object references, but this is considered
bad style:
CharStack stack1;
int count1 = stack1.getInstanceCount();

// Reference invokes static method

Static members in a class can be accessed both by the class name and via object references, but instance members can only be accessed by object references.

10

CHAPTER 1: BASICS OF JAVA PROGRAMMING
Table 1.1 Terminology for Class Members

Instance Members

These are instance variables and instance methods of an
object. They can only be accessed or invoked through an
object reference.

Instance Variable

A field that is allocated when the class is instantiated, i.e.,
when an object of the class is created. Also called non-static
field.

Instance Method

A method that belongs to an instance of the class. Objects of
the same class share its implementation.

Static Members

These are static variables and static methods of a class. They
can be accessed or invoked either by using the class name or
through an object reference.

Static Variable

A field that is allocated when the class is loaded. It belongs
to the class and not to any specific object of the class. Also
called static field or class variable.

Static Method

A method which belongs to the class and not to any object of
the class. Also called class method.

1.6 Inheritance
There are two fundamental mechanisms for building new classes from existing
ones: inheritance and aggregation. It makes sense to inherit from an existing class
Vehicle to define a class Car, since a car is a vehicle. The class Vehicle has several
parts; therefore, it makes sense to define a composite object of the class Vehicle that
has constituent objects of such classes as Motor, Axle, and GearBox, which make up a
vehicle.
Inheritance is illustrated by an example that implements a stack of characters that
can print its elements on the terminal. This new stack has all the properties and
behaviors of the CharStack class, but it also has the additional capability of printing
its elements. Given that this printable stack is a stack of characters, it can be
derived from the CharStack class. This relationship is shown in Figure 1.6. The class
PrintableCharStack is called the subclass, and the class CharStack is called the superclass. The CharStack class is a generalization for all stacks of characters, whereas the
Figure 1.6

Class Diagram Depicting Inheritance Relationship
Superclass

CharStack

Generalization

ž
Subclass

PrintableCharStack

Specialization

11

1.6: INHERITANCE

class PrintableCharStack is a specialization of stacks of characters that can also print
their elements.
In Java, deriving a new class from an existing class requires the use of the extends
clause in the subclass declaration. A subclass can extend only one superclass. The
subclass can inherit members of the superclass. The following code fragment
implements the PrintableCharStack class:
class PrintableCharStack extends CharStack {
// Instance method
public void printStackElements() {
// ... implementation of the method...
}

// (1)
// (2)

// The constructor calls the constructor of the superclass explicitly.
public PrintableCharStack(int capacity) { super(capacity); }
// (3)
}

The PrintableCharStack class extends the CharStack class at (1). Implementing the
printStackElements() method in the PrintableCharStack class requires access to the
field stackArray from the superclass CharStack. However, this field is private and
therefore not accessible in the subclass. The subclass can access these fields if the
accessibility of the fields is changed to protected in the CharStack class. Example 1.3
uses a version of the class CharStack, which has been modified accordingly. Implementation of the printStackElements() method is shown at (2). The constructor of
the PrintableCharStack class at (3) calls the constructor of the superclass CharStack
in order to initialize the stack properly.
Example 1.3

Defining a Subclass
// Source Filename: CharStack.java
public class CharStack {
// Instance variables
protected char[] stackArray; // The array that implements the stack.
protected int
topOfStack; // The top of the stack.
// The rest of the definition is the same as in Example 1.2.
}

//Filename: PrintableCharStack.java
public class PrintableCharStack extends CharStack {

// (1)

// Instance method
public void printStackElements() {
// (2)
for (int i = 0; i <= topOfStack; i++)
System.out.print(stackArray[i]); // print each char on terminal
System.out.println();
}
// Constructor calls the constructor of the superclass explicitly.
PrintableCharStack(int capacity) { super(capacity); }
// (3)
}

12

CHAPTER 1: BASICS OF JAVA PROGRAMMING

Objects of the PrintableCharStack class will respond just like the objects of the CharStack class, but they will also have the additional functionality defined in the
subclass:
PrintableCharStack pcStack = new PrintableCharStack(3);
pcStack.push('H');
pcStack.push('i');
pcStack.push('!');
pcStack.printStackElements();
// Prints "Hi!" on the terminal

1.7 Aggregation
When building new classes from existing classes using aggregation, a composite
object is built from the constituent objects that are its parts.
Java supports aggregation of objects by reference, since objects cannot contain
other objects explicitly. The fields can only contain values of primitive data types
or reference values to other objects. Each object of the CharStack class has a field to
store the reference value of an array object that holds the characters. Each stack
object also has a field of primitive data type int to store the index value that
denotes the top of stack. This is reflected in the definition of the CharStack class,
which contains an instance variable for each of these parts. In contrast to the
constituent objects whose reference values are stored in fields, the values of primitive data types are themselves stored in the fields of the composite object. The
aggregation relationship is depicted by the UML diagram in Figure 1.7, showing
that each object of the CharStack class will have one array object of type char associated with it.

Figure 1.7

Class Diagram Depicting Aggregation
CharStack
stackArray
topOfStack
push()
pop()
peek()
...

has
1

Array of char

13

1.8: TENETS OF JAVA

1.8 Tenets of Java
• Code in Java must be encapsulated in classes.
• There are two kinds of values in Java: there are objects that are instances of
classes or arrays, and there are atomic values of primitive data types.
• References denote objects and are used to manipulate objects.
• Objects in Java cannot contain other objects; they can only contain references to
other objects.
• During execution, reclamation of objects that are no longer in use is managed
by the runtime system.

Review Questions
1.1

Which statement about methods is true?
Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

1.2

A method is an implementation of an abstraction.
A method is an attribute defining the property of a particular abstraction.
A method is a category of objects.
A method is an operation defining the behavior for a particular abstraction.
A method is a blueprint for making operations.

Which statement about objects is true?
Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

1.3

An object is what classes are instantiated from.
An object is an instance of a class.
An object is a blueprint for creating concrete realization of abstractions.
An object is a reference.
An object is a variable.

Which is the first line of a constructor declaration in the following code?
public class Counter {
int current, step;
public Counter(int startValue, int
setCurrent(startValue);
setStep(stepValue);
}
public int getCurrent()
public void setCurrent(int value)
public void setStep(int stepValue)
}

// (1)
stepValue) {

// (2)

{ return current; }
{ current = value; }
{ step = stepValue; }

// (3)
// (4)
// (5)

14

CHAPTER 1: BASICS OF JAVA PROGRAMMING

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
1.4

(1)
(2)
(3)
(4)
(5)

Given that Thing is a class, how many objects and how many reference variables are
created by the following code?
Thing item, stuff;
item = new Thing();
Thing entity = new Thing();

Select the two correct answers.
(a)
(b)
(c)
(d)
(e)
(f)
1.5

One object is created.
Two objects are created.
Three objects are created.
One reference variable is created.
Two reference variables are created.
Three reference variables are created.

Which statement about instance members is true?
Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

1.6

An instance member is also called a static member.
An instance member is always a field.
An instance member is never a method.
An instance member belongs to an instance, not to the class as a whole.
An instance member always represents an operation.

How do objects communicate in Java?
Select the one correct answer.
(a)
(b)
(c)
(d)

1.7

They communicate by modifying each other’s fields.
They communicate by modifying the static variables of each other’s classes.
They communicate by calling each other’s instance methods.
They communicate by calling static methods of each other’s classes.

Given the following code, which statements are true?
class A {
int value1;
}
class B extends A {
int value2;
}

1.10: SAMPLE JAVA APPLICATION

15

Select the two correct answers.
(a)
(b)
(c)
(d)
(e)
(f)

Class A extends class B.
Class B is the superclass of class A.
Class A inherits from class B.
Class B is a subclass of class A.
Objects of class A have a field named value2.
Objects of class B have a field named value1.

1.9 Java Programs
A Java source file can contain more than one class declaration. Each source file name
has the extension .java. The JDK enforces the rule that any class in the source file
that has public accessibility must be declared in its own file; meaning that such a
public class must be declared in a source file whose file name comprises the name
of this public class with .java as its extension. The above rule implies that a source
file can only contain at the most one public class. If the source file contains a public
class, the file naming rule must be obeyed.
Each class declaration in a source file is compiled into a separate class file, containing Java byte code. The name of this file comprises the name of the class with .class
as its extension. The JDK provides tools for compiling and running programs, as
explained in the next section. The classes in the Java standard library are already
compiled, and the JDK tools know where to find them.

1.10 Sample Java Application
An application is just a synonym for a program: source code that is compiled and
directly executed. In order to create an application in Java, the program must have
a class that defines a method named main, which is the starting point for the execution of any application.

Essential Elements of a Java Application
Example 1.4 is an example of an application in which a client uses the CharStack
class to reverse a string of characters.
Example 1.4

An Application
// Source Filename: CharStack.java
public class CharStack {
// Same as in Example 1.2.
}

16

CHAPTER 1: BASICS OF JAVA PROGRAMMING
//Filename: Client.java
public class Client {
public static void main(String[] args) {
// Create a stack.
CharStack stack = new CharStack(40);
// Create a string to push on the stack:
String str = "!no tis ot nuf era skcatS";
int length = str.length();
System.out.println("Original string: " + str);
// Push the string char by char onto the stack:
for (int i = 0; i < length; i++) {
stack.push(str.charAt(i));
}
System.out.print("Reversed string: ");
// Pop and print each char from the stack:
while (!stack.isEmpty()) { // Check if the stack is not empty.
System.out.print(stack.pop());
}
System.out.println();
}
}

Output from the program:
Original string: !no tis ot nuf era skcatS
Reversed string: Stacks are fun to sit on!

The public class Client defines a method with the name main. To start the application, the main() method in this public class is invoked by the Java interpreter, also
called the Java Virtual Machine (JVM). The method header of this main() method
should be declared as shown in the following method stub:
public static void main(String[] args)
{ /* Implementation */ }

// Method header

The main() method has public accessibility, i.e., it is accessible from any class. The
keyword static means the method belongs to the class. The keyword void means
the method does not return any value. The parameter list, (String[] args), is an
array of strings that can be used to pass information to the main() method when the
application is started.

Compiling and Running an Application
Java source files can be compiled using the Java compiler tool javac, which is part
of the JDK.

1.10: SAMPLE JAVA APPLICATION

17

The source file Client.java contains the declaration of the Client class. The source
file can be compiled by giving the following command at the command line. (The
character > is the command prompt.)
>javac Client.java

This creates the class file Client.class containing the Java byte code for the Client
class. The Client class uses the CharStack class, and if the file CharStack.class does
not already exist, the compiler will also compile the source file CharStack.java.
Compiled classes can be executed by the Java interpreter java, which is also part of
the JDK. Example 1.4 can be run by giving the following command in the command line:
>java Client

Note that only the name of the class is specified, resulting in the execution starting
in the main() method of the specified class. The application in Example 1.4 terminates when the execution of the main() method is completed.

Review Questions
1.8

Which command from the JDK should be used to compile the following source
code contained in a file named SmallProg.java?
public class SmallProg {
public static void main(String[] args) { System.out.println("Good luck!"); }
}

Select the one correct answer.
(a) java SmallProg
(b) javac SmallProg
(c) java SmallProg.java
(d) javac SmallProg.java
(e) java SmallProg main
1.9

Which command from the JDK should be used to execute the main() method of a
class named SmallProg?
Select the one correct answer.
(a) java SmallProg
(b) javac SmallProg
(c) java SmallProg.java
(d) java SmallProg.class
(e) java SmallProg.main()

18

CHAPTER 1: BASICS OF JAVA PROGRAMMING

Chapter Summary
The following information was included in this chapter:
• basic concepts in OOP, and how they are supported in Java
• essential elements of a Java application
• compiling and running Java applications

Programming Exercise
1.1

Modify the program from Example 1.4 to use the PrintableCharStack class,
rather than the CharStack class from Example 1.2. Utilize the printStackElements() method from the PrintableCharStack class. Is the new program behaviorwise any different from Example 1.4?

Language Fundamentals

2

Exam Objectives
1.3 Develop code that declares, initializes, and uses primitives, arrays, enums,
and objects as static, instance, and local variables. Also, use legal
identifiers for variable names.
❍

For arrays, see Section 3.6, p. 69.

❍

For enums, see Section 3.5, p. 54.

❍

For initializers, see Section 9.7, p. 406.

Supplementary Objectives
• Be able to identify the basic elements of the Java programming language:
keywords, identifiers, literals and primitive data types.
• Understand the scope of variables.
• Understand initializing variables with default values.

19

20

CHAPTER 2: LANGUAGE FUNDAMENTALS

2.1 Basic Language Elements
Like any other programming language, the Java programming language is defined
by grammar rules that specify how syntactically legal constructs can be formed using
the language elements, and by a semantic definition that specifies the meaning of
syntactically legal constructs.

Lexical Tokens
The low-level language elements are called lexical tokens (or just tokens) and are the
building blocks for more complex constructs. Identifiers, numbers, operators, and
special characters are all examples of tokens that can be used to build high-level
constructs like expressions, statements, methods, and classes.

Identifiers
A name in a program is called an identifier. Identifiers can be used to denote classes,
methods, variables, and labels.
In Java, an identifier is composed of a sequence of characters, where each character
can be either a letter or a digit. However, the first character in an identifier must be
a letter. Since Java programs are written in the Unicode character set (see p. 23), the
definitions of letter and digit are interpreted according to this character set. Note
that connecting punctuation (such as underscore _) and any currency symbol (such as
$, ¢, ¥, or £) are allowed as letters, but should be avoided in identifier names.
Identifiers in Java are case sensitive, for example, price and Price are two different
identifiers.

Examples of Legal Identifiers
number, Number, sum_$, bingo, $$_100, mål, grüß

Examples of Illegal Identifiers
48chevy, all@hands, grand-sum

The name 48chevy is not a legal identifier as it starts with a digit. The character @ is
not a legal character in an identifier. It is also not a legal operator, so that all@hands
cannot be interpreted as a legal expression with two operands. The character - is
also not a legal character in an identifier. However, it is a legal operator so grandsum could be interpreted as a legal expression with two operands.

Keywords
Keywords are reserved words that are predefined in the language and cannot be
used to denote other entities. All the keywords are in lowercase, and incorrect
usage results in compilation errors.

21

2.1: BASIC LANGUAGE ELEMENTS

Keywords currently defined in the language are listed in Table 2.1. In addition,
three identifiers are reserved as predefined literals in the language: the null reference, and the boolean literals true and false (see Table 2.2). Keywords currently
reserved, but not in use, are listed in Table 2.3. A reserved word cannot be used as
an identifier. The index contains references to relevant sections where currently
used keywords are explained.
Table 2.1

Table 2.2

Keywords in Java
abstract

default

if

private

this

assert

do

implements

protected

throw

boolean

double

import

public

throws

break

else

instanceof

return

transient

byte

enum

int

short

try

case

extends

interface

static

void

catch

final

long

strictfp

volatile

char

finally

native

super

while

class

float

new

switch

continue

for

package

synchronized

Reserved Literals in Java
null

Table 2.3

true

false

Reserved Keywords not Currently in Use
const

goto

Literals
A literal denotes a constant value, i.e., the value that a literal represents remains
unchanged in the program. Literals represent numerical (integer or floating-point),
character, boolean or string values. In addition, there is the literal null that represents the null reference.
Table 2.4

Examples of Literals
Integer

2000

0

-7

Floating-point

3.14

-3.14

.5

0.5

Character

'a'

'A'

'0'

':'

Boolean

true

false

String

"abba"

"3.14"

"for"

'-'

')'

"a piece of the action"

22

CHAPTER 2: LANGUAGE FUNDAMENTALS

Integer Literals
Integer data types comprise the following primitive data types: int, long, byte, and
short (see Section 2.2, p. 28).
The default data type of an integer literal is always int, but it can be specified as
long by appending the suffix L (or l) to the integer value. Without the suffix, the
long literals 2000L and 0l will be interpreted as int literals. There is no direct way to
specify a short or a byte literal.
In addition to the decimal number system, integer literals can also be specified in
octal (base 8) and hexadecimal (base 16) number systems. Octal and hexadecimal
numbers are specified with a 0 and 0x (or 0X) prefix respectively. Examples of decimal, octal and hexadecimal literals are shown in Table 2.5. Note that the leading 0
(zero) digit is not the uppercase letter O. The hexadecimal digits from a to f can also
be specified with the corresponding uppercase forms (A to F). Negative integers
(e.g. -90) can be specified by prefixing the minus sign (-) to the magnitude of the
integer regardless of number system (e.g., -0132 or -0X5A). Number systems and
number representation are discussed in Appendix G. Java does not support literals
in binary notation.
Table 2.5

Examples of Decimal, Octal, and Hexadecimal Literals
Decimal

Octal

Hexadecimal

8

010

0x8

10L

012L

0xaL

16

020

0x10

27

033

0x1B

90L

0132L

0x5aL

-90

-0132

-0x5A

2147483647 (i.e., 2 -1)

017777777777

0x7fffffff

-2147483648 (i.e., -231)

-020000000000

-0x80000000

1125899906842624L (i.e., 250)

040000000000000000L

0x4000000000000L

31

Floating-Point Literals
Floating-point data types come in two flavors: float or double.
The default data type of a floating-point literal is double, but it can be explicitly
designated by appending the suffix D (or d) to the value. A floating-point literal can
also be specified to be a float by appending the suffix F (or f).
Floating-point literals can also be specified in scientific notation, where E (or e)
stands for Exponent. For example, the double literal 194.9E-2 in scientific notation is
interpreted as 194.9 × 10-2 (i.e., 1.949).

23

2.1: BASIC LANGUAGE ELEMENTS

Examples of double Literals
0.0
0.49
49.0
4.9E+1

0.0d
.49
49.
4.9E+1D

0D
.49D
49D
4.9e1d

4900e-2

.49E2

Examples of float Literals
0.0F
0.49F
49.0F
4.9E+1F

0f
.49F
49.F
4900e-2f

49F
.49E2F

Note that the decimal point and the exponent are optional and that at least one
digit must be specified.

Boolean Literals
The primitive data type boolean represents the truth-values true or false that are
denoted by the reserved literals true or false, respectively.

Character Literals
A character literal is quoted in single-quotes ('). All character literals have the
primitive data type char.
A character literal is represented according to the 16-bit Unicode character set,
which subsumes the 8-bit ISO-Latin-1 and the 7-bit ASCII characters. In Table 2.6,
note that digits (0 to 9), upper-case letters (A to Z), and lower-case letters (a to z) have
contiguous Unicode values. A Unicode character can always be specified as a fourdigit hexadecimal number (i.e., 16 bits) with the prefix \u.
Table 2.6

Examples of Character Literals

Character Literal

Character Literal using
Unicode value

Character

' '

'\u0020'

Space

'0'

'\u0030'

0

'1'

'\u0031'

1

'9'

'\u0039'

9

'A'

'\u0041'

A

'B'

'\u0042'

B

'Z'

'\u005a'

Z

'a'

'\u0061'

a

'b'

'\u0062'

b

Continues

24

CHAPTER 2: LANGUAGE FUNDAMENTALS
Table 2.6

Examples of Character Literals (Continued)

Character Literal

Character Literal using
Unicode value

Character

'z'

'\u007a'

z

'Ñ'

'\u0084'

Ñ

'å'

'\u008c'

å

'ß'

'\u00a7'

ß

Escape Sequences
Certain escape sequences define special characters, as shown in Table 2.7. These
escape sequences can be single-quoted to define character literals. For example, the
character literals '\t' and '\u0009' are equivalent. However, the character literals
'\u000a' and '\u000d' should not be used to represent newline and carriage return
in the source code. These values are interpreted as line-terminator characters by
the compiler, and will cause compile time errors. You should use the escape
sequences '\n' and '\r', respectively, for correct interpretation of these characters
in the source code.
Table 2.7

Escape Sequences
Escape Sequence

Unicode Value

Character

\b

\u0008

Backspace (BS)

\t

\u0009

Horizontal tab (HT or TAB)

\n

\u000a

Linefeed (LF) a.k.a. Newline (NL)

\f

\u000c

Form feed (FF)

\r

\u000d

Carriage return (CR)

\'

\u0027

Apostrophe-quote, a.k.a. single quote

\"

\u0022

Quotation mark, a.k.a. double quote

\\

\u005c

Backslash

We can also use the escape sequence \ddd to specify a character literal as an octal
value, where each digit d can be any octal digit (0–7), as shown in Table 2.8. The
number of digits must be three or fewer, and the octal value cannot exceed \377,
i.e., only the first 256 characters can be specified with this notation.

25

2.1: BASIC LANGUAGE ELEMENTS
Table 2.8

Examples of Escape Sequence \ddd
Escape Sequence \ddd

Character Literal

'\141'

'a'

'\46'

'&'

'\60'

'0'

String Literals
A string literal is a sequence of characters which must be enclosed in double quotes
and must occur on a single line. All string literals are objects of the class String (see
Section 10.4, p. 439).
Escape sequences as well as Unicode values can appear in string literals:
"Here comes a tab.\t And here comes another one\u0009!"
"What's on the menu?"
"\"String literals are double-quoted.\""
"Left!\nRight!"
"Don't split
me up!"

(1)
(2)
(3)
(4)
(5)

In (1), the tab character is specified using the escape sequence and the Unicode
value, respectively. In (2), the single apostrophe need not be escaped in strings, but
it would be if specified as a character literal ('\''). In (3), the double quotes in the
string must be escaped. In (4), we use the escape sequence \n to insert a newline.
(5) generates a compile time error, as the string literal is split over several lines.
Printing the strings from (1) to (4) will give the following result:
Here comes a tab.
And here comes another one
What's on the menu?
"String literals are double-quoted."
Left!
Right!

!

One should also use the escape sequences \n and \r, respectively, for correct interpretation of the characters \u000a (newline) and \u000d (form feed) in string literals.

White Spaces
A white space is a sequence of spaces, tabs, form feeds, and line terminator characters in a Java source file. Line terminators can be newline, carriage return, or a carriage return-newline sequence.
A Java program is a free-format sequence of characters that is tokenized by the compiler, i.e., broken into a stream of tokens for further analysis. Separators and operators help to distinguish tokens, but sometimes white space has to be inserted

26

CHAPTER 2: LANGUAGE FUNDAMENTALS

explicitly as a separator. For example, the identifier classRoom will be interpreted as
a single token, unless white space is inserted to distinguish the keyword class from
the identifier Room.
White space aids not only in separating tokens, but also in formatting the program
so that it is easy to read. The compiler ignores the white spaces once the tokens are
identified.

Comments
A program can be documented by inserting comments at relevant places in the
source code. These comments are for documentation purposes only and are
ignored by the compiler.
Java provides three types of comments to document a program:
• A single-line comment: // ... to the end of the line
• A multiple-line comment: /* ... */
• A documentation (Javadoc) comment: /** ... */

Single-Line Comment
All characters after the comment-start sequence // through to the end of the line
constitute a single-line comment.
// This comment ends at the end of this line.
int age;
// From comment-start sequence to the end of the line is a comment.

Multiple-Line Comment
A multiple-line comment, as the name suggests, can span several lines. Such a comment starts with the sequence /* and ends with the sequence */.
/* A comment
on several
lines.
*/

The comment-start sequences (//, /*, /**) are not treated differently from other
characters when occurring within comments, and are thus ignored. This means
that trying to nest multiple-line comments will result in a compile time error:
/* Formula for alchemy.
gold = wizard.makeGold(stone);
/* But it only works on Sundays. */
*/

The second occurrence of the comment-start sequence /* is ignored. The last occurrence of the sequence */ in the code is now unmatched, resulting in a syntax error.

REVIEW QUESTIONS

27

Documentation Comment
A documentation comment is a special-purpose comment that is used by the javadoc
tool to generate HTML documentation for the program. Documentation comments
are usually placed in front of classes, interfaces, methods, and field definitions.
Special tags can be used inside a documentation comment to provide more specific
information. Such a comment starts with the sequence /** and ends with the
sequence */:
/**
* This class implements a gizmo.
* @author K.A.M.
* @version 3.0
*/

For details on the javadoc tool, see the tools documentation provided by the JDK.

Review Questions
2.1

Which of the following is not a legal identifier?
Select the one correct answer.
(a) a2z
(b) ödipus
(c) 52pickup
(d) _class
(e) ca$h

2.2

Which statement is true?
Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
(f)

2.3

new and delete are keywords in the Java language.
try, catch, and thrown are keywords in the Java language.
static, unsigned, and long are keywords in the Java language.
exit, class, and while are keywords in the Java language.
return, goto, and default are keywords in the Java language.
for, while, and next are keywords in the Java language.

Which statement about the following comment is true?
/* // */

Select the one correct answer.
(a) The comment is not valid. The multiple-line comment (/* ... */) does not
end correctly, since the comment-end sequence */ is a part of the single-line
comment (// ...).
(b) It is a completely valid comment. The // part is ignored by the compiler.
(c) This combination of comments is illegal, and will result in a compile
time error.

28

CHAPTER 2: LANGUAGE FUNDAMENTALS

2.2 Primitive Data Types
Figure 2.1 gives an overview of the primitive data types in Java.
Primitive data types in Java can be divided into three main categories:
• integral types—represent signed integers (byte, short, int, long) and unsigned
character values (char)
• floating-point types (float, double)—represent fractional signed numbers
• boolean type (boolean)—represents logical values
Figure 2.1

Primitive Data Types in Java
Primitive data types

Boolean type

Numeric types
Integral types

Floating-point types

Character type

Integer types

char

byte short int long

boolean

float double

Primitive data values are not objects. Each primitive data type defines the range of
values in the data type, and operations on these values are defined by special
operators in the language (see Chapter 5).
Each primitive data type also has a corresponding wrapper class that can be used
to represent a primitive value as an object. Wrapper classes are discussed in Section
10.3, p. 428.

Integer Types
Table 2.9

Range of Integer Values

Data Type

Width
(bits)

Minimum value
MIN_VALUE

Maximum value
MAX_VALUE

byte

8

-27 (-128)

27-1 (+127)

short

16

-215 (-32768)

215-1 (+32767)

int

32

-231 (-2147483648)

231-1 (+2147483647)

long

64

-263 (-9223372036854775808L)

263-1 (+9223372036854775807L)

29

2.2: PRIMITIVE DATA TYPES

Integer data types are byte, short, int, and long (see Table 2.9). Their values are
signed integers represented by 2’s complement (see Section G.4, p. 1010).

The char Type
Table 2.10

Range of Character Values
Data
Type

Width (bits)

Minimum Unicode value

Maximum Unicode value

char

16

0x0 (\u0000)

0xffff (\uffff)

The data type char represents characters (see Table 2.10). Their values are unsigned
integers that denote all the 65536 (216) characters in the 16-bit Unicode character
set. This set includes letters, digits, and special characters.
The first 128 characters of the Unicode set are the same as the 128 characters of the
7-bit ASCII character set, and the first 256 characters of the Unicode set correspond
to the 256 characters of the 8-bit ISO Latin-1 character set.
The integer types and the char type are collectively called integral types.

The Floating-Point Types
Table 2.11

Range of Floating-Point Values

Data Type

Width
(bits)

Minimum Positive Value
MIN_VALUE

Maximum Positive Value
MAX_VALUE

float

32

1.401298464324817E-45f

3.402823476638528860e+38f

double

64

4.94065645841246544e-324

1.79769313486231570e+308

Floating-point numbers are represented by the float and double data types.
Floating-point numbers conform to the IEEE 754-1985 binary floating-point standard. Table 2.11 shows the range of values for positive floating-point numbers, but
these apply equally to negative floating-point numbers with the '-' sign as a prefix. Zero can be either 0.0 or -0.0.
Since the size for representation is a finite number of bits, certain floating-point
numbers can only be represented as approximations. For example, the value of the
expression (1.0/3.0) is represented as an approximation due to the finite number
of bits used.

30

CHAPTER 2: LANGUAGE FUNDAMENTALS

The boolean Type
Table 2.12

Boolean Values
Data Type

Width

True Value Literal

False Value Literal

boolean

not applicable

true

false

The data type boolean represents the two logical values denoted by the literals true
and false (see Table 2.12).
Boolean values are produced by all relational (see Section 5.10, p. 190), conditional
(see Section 5.13, p. 196) and boolean logical operators (see Section 5.12, p. 194), and
are primarily used to govern the flow of control during program execution.
Table 2.13 summarizes the pertinent facts about the primitive data types: their
width or size, which indicates the number of the bits required to store a primitive
value; their range of legal values, which is specified by the minimum and the maximum values permissible; and the name of the corresponding wrapper class (see
Section 10.3, p. 428).
Table 2.13

Summary of Primitive Data Types
Data Type

Width (bits)

Minimum Value, Maximum Value

Wrapper Class

boolean

not applicable

true, false

Boolean

byte

8

-27, 27-1

Byte

short

16

-2 , 2 -1

Short

char

16

0x0, 0xffff

Character

int

32

-231, 231-1

Integer

long

64

-2 , 2 -1

Long

float

32

±1.40129846432481707e-45f,
±3.402823476638528860e+38f

Float

double

64

±4.94065645841246544e-324,
±1.79769313486231570e+308

Double

15

63

15

63

31

2.3: VARIABLE DECLARATIONS

Review Questions
2.4

Which of the following do not denote a primitive data value in Java?
Select the two correct answers.
(a) "t"
(b) 'k'
(c) 50.5F
(d) "hello"
(e) false

2.5

Which of the following primitive data types are not integer types?
Select the three correct answers.
(a) boolean
(b) byte
(c) float
(d) short
(e) double

2.6

Which integral type in Java has the exact range from -2147483648 (-231) to
2147483647 (231-1), inclusive?

Select the one correct answer.
(a) byte
(b) short
(c) int
(d) long
(e) char

2.3 Variable Declarations
A variable stores a value of a particular type. A variable has a name, a type, and a
value associated with it. In Java, variables can only store values of primitive data
types and reference values of objects. Variables that store reference values of
objects are called reference variables (or object references or simply references).

Declaring and Initializing Variables
Variable declarations are used to specify the type and the name of variables. This
implicitly determines their memory allocation and the values that can be stored in
them. Examples of declaring variables that can store primitive values:
char a, b, c;
double area;
boolean flag;

// a, b and c are character variables.
// area is a floating-point variable.
// flag is a boolean variable.

32

CHAPTER 2: LANGUAGE FUNDAMENTALS

The first declaration above is equivalent to the following three declarations:
char a;
char b;
char c;

A declaration can also include an initialization expression to specify an appropriate initial value for the variable:
int i = 10,
j = 101;
long big = 2147483648L;

// i is an int variable with initial value 10.
// j is an int variable with initial value 101.
// big is a long variable with specified initial value.

Reference Variables
An reference variable can store the reference value of an object, and can be used to
manipulate the object denoted by the reference value.
A variable declaration that specifies a reference type (i.e., a class, an array, or an
interface name) declares a reference variable. Analogous to the declaration of variables of primitive data types, the simplest form of reference variable declaration
only specifies the name and the reference type. The declaration determines what
objects can be referenced by a reference variable. Before we can use a reference
variable to manipulate an object, it must be declared and initialized with the reference value of the object.
Pizza yummyPizza;
// Variable yummyPizza can reference objects of class Pizza.
Hamburger bigOne,
// Variable bigOne can reference objects of class Hamburger,
smallOne; // and so can variable smallOne.

It is important to note that the declarations above do not create any objects of class
Pizza or Hamburger. The above declarations only create variables that can store references of objects of the specified classes.
A declaration can also include an initializer expression to create an object whose
reference value can be assigned to the reference variable:
Pizza yummyPizza = new Pizza("Hot&Spicy"); // Declaration with initializer.

The reference variable yummyPizza can reference objects of class Pizza. The keyword
new, together with the constructor call Pizza("Hot&Spicy"), creates an object of the
class Pizza. The reference value of this object is assigned to the variable yummyPizza.
The newly created object of class Pizza can now be manipulated through the reference variable yummyPizza.
Initializers for initializing fields in objects, and static variables in classes and interfaces are discussed in Section 9.7, p. 406.
Reference variables for arrays are discussed in Section 3.6, p. 69.

33

2.4: INITIAL VALUES FOR VARIABLES

2.4 Initial Values for Variables
Default Values for Fields
Default values for fields of primitive data types and reference types are listed in
Table 2.14. The value assigned depends on the type of the field.
Table 2.14

Default Values
Data Type

Default Value

boolean

false

char

'\u0000'

Integer (byte, short, int, long)

0L for long, 0 for others

Floating-point (float, double)

0.0F or 0.0D

Reference types

null

If no initialization is provided for a static variable either in the declaration or in a
static initializer block (see Section 9.9, p. 410), it is initialized with the default value
of its type when the class is loaded.
Similarly, if no initialization is provided for an instance variable either in the declaration or in an instance initializer block (see Section 9.10, p. 413), it is initialized
with the default value of its type when the class is instantiated.
The fields of reference types are always initialized with the null reference value if
no initialization is provided.
Example 2.1 illustrates default initialization of fields. Note that static variables are
initialized when the class is loaded the first time, and instance variables are initialized accordingly in every object created from the class Light.
Example 2.1

Default Values for Fields
public class Light {
// Static variable
static int counter;

// Default value 0 when class is loaded.

// Instance variables:
int
noOfWatts = 100; // Explicitly set to 100.
boolean indicator;
// Implicitly set to default value false.
String location;
// Implicitly set to default value null.
public static void main(String[] args) {
Light bulb = new Light();
System.out.println("Static variable counter: "
+ Light.counter);
System.out.println("Instance variable noOfWatts: " + bulb.noOfWatts);

34

CHAPTER 2: LANGUAGE FUNDAMENTALS
System.out.println("Instance variable indicator: " + bulb.indicator);
System.out.println("Instance variable location: " + bulb.location);
return;
}
}

Output from the program:
Static variable counter: 0
Instance variable noOfWatts: 100
Instance variable indicator: false
Instance variable location: null

Initializing Local Variables of Primitive Data Types
Local variables are variables that are declared in methods, constructors, and blocks
(see Chapter 3, p. 39). Local variables are not initialized when they are created at
method invocation, that is, when the execution of a method is started. The same
applies in constructors and blocks. Local variables must be explicitly initialized
before being used. The compiler will report as errors any attempts to use uninitialized local variables.
Example 2.2

Flagging Uninitialized Local Variables of Primitive Data Types
public class TooSmartClass {
public static void main(String[] args) {
int weight = 10, thePrice;
if (weight < 10) thePrice = 1000;
if (weight > 50) thePrice = 5000;
if (weight >= 10) thePrice = weight*10;
System.out.println("The price is: " + thePrice);

// (1) Local variables

// (2) Always executed.
// (3)

}
}

In Example 2.2, the compiler complains that the local variable thePrice used in the
println statement at (3) may not be initialized. However, it can be seen that at runtime, the local variable thePrice will get the value 100 in the last if-statement at (2),
before it is used in the println statement. The compiler does not perform a rigorous
analysis of the program in this regard. It only compiles the body of a conditional
statement if it can deduce the condition to be true. The program will compile correctly if the variable is initialized in the declaration, or if an unconditional assignment is made to the variable.
Replacing the declaration of the local variables at (1) in Example 2.2 with the following declaration solves the problem:
int weight = 10, thePrice = 0;

// (1') Both local variables initialized.

2.4: INITIAL VALUES FOR VARIABLES

35

Initializing Local Reference Variables
Local reference variables are bound by the same initialization rules as local variables of primitive data types.
Example 2.3

Flagging Uninitialized Local Reference Variables
public class VerySmartClass {
public static void main(String[] args) {
String importantMessage;
// Local reference variable
System.out.println("The message length is: " + importantMessage.length());
}
}

In Example 2.3, the compiler complains that the local variable importantMessage
used in the println statement may not be initialized. If the variable importantMessage is set to the value null, the program will compile. However, a runtime error
(NullPointerException) will occur when the code is executed, since the variable
importantMessage will not denote any object. The golden rule is to ensure that a reference variable, whether local or not, is assigned a reference to an object before it
is used, that is, ensure that it does not have the value null.
The program compiles and runs if we replace the declaration with the following
declaration of the local variable, which creates a string literal and assigns its reference value to the local reference variable importantMessage:
String importantMessage = "Initialize before use!";

Arrays and their default values are discussed in Section 3.6, p. 69.

Lifetime of Variables
The lifetime of a variable, that is, the time a variable is accessible during execution,
is determined by the context in which it is declared. The lifetime of a variable is
also called scope, and is discussed in more detail in Section 4.6, p. 129. We distinguish between lifetime of variables in three contexts:
• Instance variables—members of a class, and created for each object of the class.
In other words, every object of the class will have its own copies of these variables, which are local to the object. The values of these variables at any given
time constitute the state of the object. Instance variables exist as long as the
object they belong to is in use at runtime.
• Static variables—also members of a class, but not created for any specific object
of the class and, therefore, belong only to the class (see Section 4.6, p. 129). They
are created when the class is loaded at runtime, and exist as long as the class is
available at runtime.

36

CHAPTER 2: LANGUAGE FUNDAMENTALS

• Local variables (also called method automatic variables)—declared in methods,
constructors, and blocks; and created for each execution of the method, constructor, or block. After the execution of the method, constructor, or block completes, local (non-final) variables are no longer accessible.

Review Questions
2.7

Which declarations are valid?
Select the three correct answers.
(a) char a = '\u0061';
(b) char 'a' = 'a';
(c) char \u0061 = 'a';
(d) ch\u0061r a = 'a';
(e) ch'a'r a = 'a';

2.8

Given the following code within a method, which statement is true?
int a, b;
b = 5;

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
2.9

Local variable a is not declared.
Local variable b is not declared.
Local variable a is declared but not initialized.
Local variable b is declared but not initialized.
Local variable b is initialized but not declared.

In which of these variable declarations will the variable remain uninitialized
unless it is explicitly initialized?
Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

2.10

Declaration of an instance variable of type int.
Declaration of a static variable of type float.
Declaration of a local variable of type float.
Declaration of a static variable of type Object.
Declaration of an instance variable of type int[].

What will be the result of compiling and running the following program?
public class Init {
String title;
boolean published;
static int total;
static double maxPrice;

PROGRAMMING EXERCISE

37

public static void main(String[] args) {
Init initMe = new Init();
double price;
if (true)
price = 100.00;
System.out.println("|" + initMe.title + "|" + initMe.published + "|" +
Init.total + "|" + Init.maxPrice + "|" + price+ "|");
}
}

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

The program will fail to compile.
The program will compile, and print |null|false|0|0.0|0.0|, when run.
The program will compile, and print |null|true|0|0.0|100.0|, when run.
The program will compile, and print | |false|0|0.0|0.0|, when run.
The program will compile, and print |null|false|0|0.0|100.0|, when run.

Chapter Summary
The following information was included in this chapter:
• basic language elements: identifiers, keywords, literals, white space, and
comments
• primitive data types: integral, floating-point, and boolean
• notational representation of numbers in decimal, octal, and hexadecimal systems
• declaration and initialization of variables, including reference variables
• usage of default values for instance variables and static variables
• lifetime of instance variables, static variables, and local variables

Programming Exercise
2.1

The following program has several errors. Modify the program so that it will
compile and run without errors.
// Filename: Temperature.java
PUBLIC CLASS temperature {
PUBLIC void main(string args) {
double fahrenheit = 62.5;
*/ Convert /*
double celsius = f2c(fahrenheit);
System.out.println(fahrenheit + 'F' + " = " + Celsius + 'C');
}
double f2c(float fahr) {
RETURN (fahr - 32) * 5 / 9;
}
}

This page intentionally left blank

Declarations

3

Exam Objectives
1.3 Develop code that declares, initializes, and uses primitives, arrays, enums,
and objects as static, instance, and local variables. Also, use legal
identifiers for variable names.
❍

Enums and arrays are covered in this chapter.

❍

For primitive types, see Section 2.2, p. 28.

❍

For initialization of static, instance, and local variables, see Section 2.3, p. 31.

For initializers, see Section 9.7, p. 406.
1.4 Develop code that declares both static and non-static methods, and—if
appropriate—use method names that adhere to the JavaBeans naming
standards. Also develop code that declares and uses a variable-length
argument list.
1.5 Given a code example, determine if a method is correctly overriding or
overloading another method, and identify legal return values (including
covariant returns), for the method.
❍

❍

For overloaded method resolution, see Section 7.10, p. 324.

❍

For overriding methods, see Section 7.2, p. 288.

❍

For return values, see Section 6.4, p. 228.

For covariant return, see Section 7.2, p. 290.
1.6 Given a set of classes and superclasses, develop constructors for one or
more of the classes. Given a class declaration, determine if a default
constructor will be created and, if so, determine the behavior of that
constructor. Given a nested or non-nested class listing, write code to
instantiate the class.
❍

❍

For constructor chaining, see Section 7.5, p. 302, and Section 9.11, p. 416.

For instantiating nested classes, see Chapter 8.
7.2 Given an example of a class and a command-line, determine the expected
runtime behavior.
7.3 Determine the effect upon object references and primitive values when
they are passed into methods that perform assignments or other
modifying operations on the parameters.
❍

❍

For conversions in assignment and method invocation contexts, see Section 5.2,
p. 163.
39

40

CHAPTER 3: DECLARATIONS

3.1 Class Declarations
A class declaration introduces a new reference type. It has the following general
syntax:
 class 
  // Class header
{ // Class body








}

In the class header, the name of the class is preceded by the keyword class. In
addition, the class header can specify the following information:
• accessibility modifier (see Section 4.7, p. 132)
• additional class modifiers (see Section 4.8, p. 135)
• a formal type parameter list, if the class is generic (see Section 14.2, p. 663)
• any class it extends (see Section 7.1, p. 284)
• any interfaces it implements (see Section 7.6, p. 309)
The class body can contain member declarations which comprise:
• field declarations (see Section 2.3, p. 31)
• method declarations (see Section 3.3, p. 44)
• nested class, enum, and interface declarations (see Section 8.1, p. 352)
Members declared static belong to the class and are called static members. Nonstatic members belong to the objects of the class and are called instance members. In
addition, the following can be declared in a class body:
• constructor declarations (see Section 3.4, p. 48)
• static and instance initializer blocks (see Section 9.7, p. 406)
The member declarations, constructor declarations, and initializer blocks can
appear in any order in the class body.
In order to understand what code can be legally declared in a class, we distinguish
between static context and non-static context. A static context is defined by static
methods, static field initializers, and static initializer blocks. A non-static context
is defined by instance methods, constructors, non-static field initializers, and
instance initializer blocks. By static code we mean expressions and statements in a
static context, and similarly by non-static code we mean expressions and statements

3.2: JAVABEANS STANDARD

41

in a non-static context. One crucial difference between the two contexts is that
static code can only refer to other static members.

3.2 JavaBeans Standard
The JavaBeans Standard allows reusable software components to be modelled in
Java so that these components can be assembled to create sophisticated applications. In particular, builder tools can take advantage of how these components are
specified, in order to build new applications based on these components. The JavaBeans specification specifies the rules for defining such components (called JavaBeans). The interested reader is encouraged to consult this documentation (see
http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html) for
details since we only cover the basic fundamentals for creating JavaBeans .

Naming Patterns for Properties
The rules of the JavaBean specification stipulate naming patterns for declaring properties of JavaBeans. A naming pattern defines a standard naming convention. A
property of an object is normally defined as a field in the object, which is usually
not directly accessible by clients (see Example 3.1). A JavaBean should adhere to
the following naming patterns when specifying its properties:
• The properties are assumed to be private, and their names start with a lowercase letter. Example 3.1 shows that the JavaBean class Light has three properties.
• In order to retrieve and change values of its properties, a JavaBean provides
getter and setter methods for them. Example 3.1 shows a JavaBean with three
getter and three setter methods for its properties.
• For a property, the setter method starts with the prefix set. The rest of the
method name is assumed to be a property name, where the first letter of the
property name has been converted to uppercase. In Example 3.1, the value of
the property noOfWatts can be changed by the setter method setNoOfWatts().
Setter methods are public and void, having a parameter of the same type as that
of the property.
• For a property, the getter method starts with the prefix get. The rest of the
method name is assumed to be a property name, where the first letter of the
property name has been converted to uppercase. In Example 3.1, the value of
the property noOfWatts can be retrieved by the getter method getNoOfWatts().
For a boolean property, the getter method can start with the prefix get or is. In
Example 3.1, the value of the boolean property indicator can be retrieved by the
getter method isIndicator().
Getter methods are no-argument public methods that return a value of the
same type as the parameter of the corresponding setter method.

42

Example 3.1

CHAPTER 3: DECLARATIONS

A JavaBean
public class Light {
// Properties:
private int
noOfWatts;
private String location;
private boolean indicator;

// wattage
// placement
// on or off

// Setters
public void setNoOfWatts(int noOfWatts)
{ this.noOfWatts = noOfWatts; }
public void setLocation(String location)
{ this.location = location; }
public void setIndicator(boolean indicator) { this.indicator = indicator; }
// Getters
public int
getNoOfWatts() { return noOfWatts; }
public String getLocation() { return location; }
public boolean isIndicator() { return indicator; }
}

Naming Patterns for the Event Model
A listener is an object that is interested in being notified when a particular event
takes place. The origin of this event is usually an object called the source, which
notifies interested listeners when the event occurs. In this setup, a listener can be
added to or removed from the list of listeners notified by a source about the occurrence of a particular event. This setup is the basis of the event model which is
depicted in Figure 3.1.
The JavaBean specification stipulates naming patterns for the event model to facilitate its use by builder tools to assemble event-based applications. Figure 3.1 shows
where the naming patterns for handling events of type X are applied:
• An event class with the name XEvent, that extends the java.util.EventObject
class.
public class XEvent extends java.util.EventObject {
public XEvent(Object source) {
super(source);
}
}

• A listener interface with the name XListener, that specifies the specific method
to be called in a listener when an event of the type XEvent occurs. The listener
interface extends the java.util.EventListener interface.
public interface XListener extends java.util.EventListener {
public void methodAInXListener(XEvent ev);
}

A listener interested in XEvents must implement the XListener interface, and
must be registered with the source in order to be informed about XEvents.

43

3.2: JAVABEANS STANDARD
Figure 3.1 The Event Model

addXListener(listener)

event X

source
addXListener(XListener l)
removeXListener(XListener l)
methodAInXListener(

) : XEvent

«interface»
java.util.EventObject

listener
methodAInXListener(XEvent e)

«interface»
XListener

«interface»
java.util.EventListener

methodAInXListener(XEvent e)
A listener interested in XEvent events is registered with the source using the addXListener() method.
The listener must implement the XListener interface in order to recieve events of type XEvent.
The listener is informed about events of type XEvent via the methodAInXListener()
in the XListener interface.

public class ListenerObject implements XListener {
public void methodAInXListener(XEvent e) { /* ... */ }
}

• A source for XEvent, that implements the methods addXListener() and removeXListener(). These methods are used to add or remove a listener interested in
XEvents, respectively. The parameter of these methods is of the type XListener.
public class SourceObject {
public synchronized void addXListener(XListener listener) { /* ... */ }
public synchronized void removeXListener(XListener listener) { /* ... */ }
}

Note that there are no naming patterns defined for the names of the source and the
listener classes. Neither is there any standard convention for naming the methods
specified in the listener interface.

44

CHAPTER 3: DECLARATIONS

3.3 Method Declarations
The general syntax of a method declaration is
   
()  // Method header
{ // Method body




}

In addition to the name of the method, the method header can specify the following information:
• scope or accessibility modifier (see Section 4.9, p. 138)
• additional method modifiers (see Section 4.10, p. 146)
• a formal type parameter list, if the declaration is for a generic method (see Section
14.8, p. 697)
• the type of the return value, or void if the method does not return any value (see
Section 6.4, p. 228)
• a formal parameter list (see below)
• checked exceptions thrown by the method are specified in a throws clause (see
Section 6.9, p. 257)
The formal parameter list is a comma-separated list of parameters for passing information to the method when the method is invoked by a method call (see Section 3.7,
p. 81). An empty parameter list must be specified by ( ). Each parameter is a simple
variable declaration consisting of its type and name:
  
The parameter names are local to the method (see Section 4.6, p. 131). The parameter modifier final is discussed in Section 3.7 on page 89.
The signature of a method comprises the method name and the formal parameter
list only.
The method body is a block containing the local declarations and the statements of the
method. Local variable declarations are discussed in Section 2.3 on page 31, and
nested local class declarations in Section 8.4 on page 371.
Like member variables, member methods can be characterized as:
• instance methods
• static methods, which are discussed in Section 4.10, p. 148.

3.3: METHOD DECLARATIONS

45

Statements
Statements in Java can be grouped into various categories. Variable declarations
with explicit initialization of the variables are called declaration statements (see Section 2.3, p. 31, and Section 3.6, p. 71). Other basic forms of statements are control
flow statements (see Section 6.1, p. 204) and expression statements.
An expression statement is an expression terminated by a semicolon. The expression
is evaluated for its side effect and its value discarded. Only certain types of expressions have meaning as statements. They include the following:
• assignments (see Section 5.5, p. 169)
• increment and decrement operators (see Section 5.8, p. 186)
• method calls (see Section 3.7, p. 81)
• object creation expressions with the new operator (see Section 5.15, p. 201)
A solitary semicolon denotes the empty statement that does nothing.
A block, {}, is a compound statement which can be used to group zero or more local
declarations and statements (see Section 4.6, p. 131). Blocks can be nested, since a
block is a statement that can contain other statements. A block can be used in any
context where a simple statement is permitted. The compound statement which is
embodied in a block, begins at the left brace, {, and ends with a matching right
brace, }. Such a block must not be confused with an array initialization block in
declaration statements (see Section 3.6, p. 71).
Labeled statements are discussed in Section 6.4 on page 223.

Instance Methods and the Object Reference this
Instance methods belong to every object of the class and can only be invoked on
objects. All members defined in the class, both static and non-static, are accessible
in the context of an instance method. The reason is that all instance methods are
passed an implicit reference to the current object, that is, the object on which the
method is being invoked. The current object can be referenced in the body of the
instance method by the keyword this. In the body of the method, the this reference
can be used like any other object reference to access members of the object. In fact,
the keyword this can be used in any non-static context. The this reference can be
used as a normal reference to reference the current object, but the reference cannot
be modified—it is a final reference (Section 4.10, p. 148).
The this reference to the current object is useful in situations where a local variable
hides, or shadows, a field with the same name. In Example 3.2, the two parameters
noOfWatts and indicator in the constructor of the Light class have the same names
as the fields in the class. The example also declares a local variable location, which
has the same name as one of the fields. The reference this can be used to distinguish the fields from the local variables. At (1), the this reference is used to identify
the field noOfWatts, which is assigned the value of the parameter noOfWatts. Without

46

CHAPTER 3: DECLARATIONS

the this reference at (2), the value of the parameter indicator is assigned back to
this parameter, and not to the field by the same name, resulting in a logical error.
Similarly at (3), without the this reference, it is the local variable location that is
assigned the value of the parameter site, and not the field by the same name.
Example 3.2

Using the this Reference
public class Light {
// Fields:
int
noOfWatts;
boolean indicator;
String location;

// wattage
// on or off
// placement

// Constructor
public Light(int noOfWatts, boolean indicator, String site) {
String location;
this.noOfWatts = noOfWatts;
indicator = indicator;
location = site;
this.superfluous();
superfluous();

//
//
//
//
//

(1) Assignment to field.
(2) Assignment to parameter.
(3) Assignment to local variable.
(4)
equivalent to call at (4)

}
public void superfluous() { System.out.println(this); }

// (5)

public static void main(String[] args) {
Light light = new Light(100, true, "loft");
System.out.println("No. of watts: " + light.noOfWatts);
System.out.println("Indicator: "
+ light.indicator);
System.out.println("Location: "
+ light.location);
}
}

Output from the program:
Light@df6ccd
Light@df6ccd
No. of watts: 100
Indicator: false
Location: null

If a member is not shadowed by a local declaration, the simple name member is considered a short-hand notation for this.member. In particular, the this reference can
be used explicitly to invoke other methods in the class. This is illustrated at (4) in
Example 3.2, where the method superfluous() is called.
If, for some reason, a method needs to pass the current object to another method,
it can do so using the this reference. This is illustrated at (5) in Example 3.2, where
the current object is passed to the println() method.

47

3.3: METHOD DECLARATIONS

Note that the this reference cannot be used in a static context, as static code is not
executed in the context of any object.

Method Overloading
Each method has a signature, which comprises the name of the method, and the
types and order of the parameters in the formal parameter list. Several method
implementations may have the same name, as long as the method signatures differ.
This is called method overloading. Since overloaded methods have the same name,
their parameter lists must be different.
Rather than inventing new method names, method overloading can be used
when the same logical operation requires multiple implementations. The Java
standard library makes heavy use of method overloading. For example, the class
java.lang.Math contains an overloaded method min(), which returns the minimum
of two numeric values.
public
public
public
public

static
static
static
static

double min(double a, double b)
float min(float a, float b)
int min(int a, int b)
long min(long a, long b)

In the following examples, five implementations of the method methodA are shown:
void
int
int
long
long

methodA(int a, double b) {
methodA(int a)
{
methodA()
{
methodA(double a, int b) {
methodA(int x, double y) {

/* ...
return
return
return
return

*/
a;
1;
b;
x;

}
}
}
}
}

//
//
//
//
//

(1)
(2)
(3)
(4)
(5) Not OK.

The corresponding signatures of the five methods are as follows:
methodA(int, double)
methodA(int)
methodA()
methodA(double, int)
methodA(int, double)

1'
2': Number of parameters.
3': Number of parameters.
4': Order of parameters.
5': Same as 1'.

The first four implementations of the method named methodA are overloaded correctly, each time with a different parameter list and, therefore, different signatures.
The declaration at (5) has the same signature methodA(int, double) as the declaration at (1) and is, therefore, not a valid overloading of this method.
void bake(Cake k) { /* ... */ }
void bake(Pizza p) { /* ... */ }

// (1)
// (2)

int
double

// (3)
// (4) Not OK. Same signature.

halfIt(int a) { return a/2; }
halfIt(int a) { return a/2.0; }

The method named bake is correctly overloaded at (1) and (2), with two different
signatures. In the implementation, changing just the return type (as shown at (3)
and (4) above), is not enough to overload a method, and will be flagged as a compile-time error. The parameter list in the declarations must be different.

48

CHAPTER 3: DECLARATIONS

Only methods declared in the same class and those that are inherited by the class
can be overloaded. Overloaded methods should be considered as individual methods that just happen to have the same name. Methods with the same name are
allowed, since methods are identified by their signature. At compile time, the right
implementation of an overloaded method is chosen based on the signature of the
method call. Details of method overloading resolution can be found in Section 7.10
on page 324. Method overloading should not be confused with method overriding
(see Section 7.2, p. 288).

3.4 Constructors
The main purpose of constructors is to set the initial state of an object, when the
object is created by using the new operator.
A constructor has the following general syntax:
  ()
 // Constructor header
{ // Constructor body




}

Constructor declarations are very much like method declarations. However, the
following restrictions on constructors should be noted:
• Modifiers other than an accessibility modifier are not permitted in the constructor header. For accessibility modifiers for constructors, see Section 4.9 on
page 138.
• Constructors cannot return a value and, therefore, do not specify a return
type, not even void, in the constructor header. But their declaration can use
the return statement that does not return a value in the constructor body (Section 6.4, p. 228).
• The constructor name must be the same as the class name.
Class names and method names exist in different namespaces. Thus, there are no
name conflicts in Example 3.3, where a method declared at (2) has the same name
as the constructor declared at (1). However, using such naming schemes is strongly
discouraged.

49

3.4: CONSTRUCTORS

Example 3.3

Namespaces
public class Name {
Name() {
// (1)
System.out.println("Constructor");
}
void Name() {
// (2)
System.out.println("Method");
}
public static void main(String[] args) {
new Name().Name();
// (3) Constructor call followed by method call.
}
}

Output from the program:
Constructor
Method

The Default Constructor
A default constructor is a constructor without any parameters, i.e., it is a no-parameter constructor. It has the following signature:
()
If a class does not specify any constructors, then an implicit default constructor is
generated for the class by the compiler. The implicit default constructor is equivalent to the following implementation:
() { super(); }

// No parameters. Calls superclass constructor.

The only action taken by the implicit default constructor is to call the superclass
constructor. This ensures that the inherited state of the object is initialized properly
(see Section 7.5, p. 302). In addition, all instance variables in the object are set to the
default value of their type, barring those that are initialized by an initialization
expression in their declaration.
In the following code, the class Light does not specify any constructors.
class Light {
// Fields:
int
noOfWatts;
boolean indicator;
String location;
// No constructors
//...
}

// wattage
// on or off
// placement

50

CHAPTER 3: DECLARATIONS
class Greenhouse {
// ...
Light oneLight = new Light();
}

// (1) Call to implicit default constructor.

In the code above, the following implicit default constructor is called when a Light
object is created by the object creation expression at (1):
Light() { super(); }

Creating an object using the new operator with the implicit default constructor, as
at (1), will initialize the fields of the object to their default values (that is, the fields
noOfWatts, indicator, and location in a Light object will be initialized to 0, false, and
null, respectively).
A class can choose to provide an implementation of the default constructor. In the
following example, the class Light provides an explicit default constructor at (1).
Note that it has the same name as the class, and that it does not specify any parameters.
class Light {
// ...
// Explicit
Light() {
noOfWatts
indicator
location
}
//...
}

Default Constructor:
// (1)
= 50;
= true;
= "X";

class Greenhouse {
// ...
Light extraLight = new Light();
}

// (2) Call of explicit default constructor.

The explicit default constructor ensures that any object created with the object creation expression new Light(), as at (2), will have its fields noOfWatts, indicator and
location initialized to 50, true and "X", respectively.
If a class defines any explicit constructors, it can no longer rely on the implicit
default constructor to set the state of its objects. If such a class requires a default
constructor, its implementation must be provided. In the example below, the class
Light only provides a non-default constructor at (1). It is called at (2) when an
object of the class Light is created with the new operator. Any attempt to call the
default constructor will be flagged as a compile-time error, as shown at (3).
class Light {
// ...
// Only non-default Constructor:
Light(int noOfWatts, boolean indicator, String location) {
this.noOfWatts = noOfWatts;
this.indicator = indicator;
this.location = location;
}

// (1)

51

3.4: CONSTRUCTORS
//...
}
class Greenhouse {
// ...
Light moreLight = new Light(100, true, "Greenhouse");
//Light firstLight = new Light();
error.
}

// (2) OK.
// (3) Compile-time

Overloaded Constructors
Like methods, constructors can also be overloaded. Since the constructors in a class
all have the same name as the class, their signatures are differentiated by their
parameter lists. In the following example, the class Light now provides both an
explicit implementation of the default constructor at (1) and a non-default constructor at (2). The constructors are overloaded, as is evident by their signatures.
The non-default constructor is called when an object of the class Light is created at
(3), and the default constructor is likewise called at (4). Overloading of constructors allows appropriate initialization of objects on creation, depending on the constructor invoked (see also chaining of constructors in Section 7.5, p. 302.)
class Light {
// ...
// Explicit
Light() {
noOfWatts
indicator
location
}

Default Constructor:
// (1)
= 50;
= true;
= "X";

// Non-default Constructor:
Light(int noOfWatts, boolean indicator, String location) { // (2)
this.noOfWatts = noOfWatts;
this.indicator = indicator;
this.location = location;
}
//...
}
class Greenhouse {
// ...
Light moreLight = new Light(100, true, "Greenhouse");
Light firstLight = new Light();
}

// (3) OK.
// (4) OK.

52

CHAPTER 3: DECLARATIONS

Review Questions
3.1

Which one of these declarations is a valid method declaration?
Select the one correct answer.
(a) void method1
{ /*
(b) void method2()
{ /*
(c) void method3(void) { /*
(d) method4()
{ /*
(e) method5(void)
{ /*

3.2

...
...
...
...
...

*/
*/
*/
*/
*/

}
}
}
}
}

Which statements, when inserted at (1), will not result in compile-time errors?
public class ThisUsage {
int planets;
static int suns;
public void gaze() {
int i;
// (1) INSERT STATEMENT HERE
}
}

Select the three correct answers.
(a) i = this.planets;
(b) i = this.suns;
(c) this = new ThisUsage();
(d) this.i = 4;
(e) this.suns = planets;
3.3

Given the following pairs of method declarations, which statements are true?
void fly(int distance) {}
int fly(int time, int speed) { return time*speed; }
void fall(int time) {}
int fall(int distance) { return distance; }
void glide(int time) {}
void Glide(int time) {}

Select the two correct answers.
(a) The first pair of methods will compile, and overload the method name fly.
(b) The second pair of methods will compile, and overload the method name
fall.
(c) The third pair of methods will compile, and overload the method name glide.
(d) The second pair of methods will not compile.
(e) The third pair of methods will not compile.

53

3.4: CONSTRUCTORS

3.4

Given a class named Book, which one of these constructor declarations is valid for
the class Book?
Select the one correct answer.
(a) Book(Book b) {}
(b) Book Book() {}
(c) private final Book() {}
(d) void Book() {}
(e) public static void Book(String[] args) {}
(f) abstract Book() {}

3.5

Which statements are true?
Select the two correct answers.
(a)
(b)
(c)
(d)
(e)

3.6

A class must define a constructor.
A constructor can be declared private.
A constructor can return a value.
A constructor must initialize all fields when a class is instantiated.
A constructor can access the non-static members of a class.

What will be the result of compiling the following program?
public class MyClass {
long var;
public void MyClass(long param) { var = param; }
public static void main(String[] args) {
MyClass a, b;
a = new MyClass();
b = new MyClass(5);
}

// (1)

// (2)
// (3)

}

Select the one correct answer.
(a) A compilation error will occur at (1), since constructors cannot specify a
return value.
(b) A compilation error will occur at (2), since the class does not have a default
constructor.
(c) A compilation error will occur at (3), since the class does not have a constructor that takes one argument of type int.
(d) The program will compile without errors.

54

CHAPTER 3: DECLARATIONS

3.5 Enumerated Types
An enumerated type defines a finite set of symbolic names and their values. These symbolic names are usually called enum constants or named constants. One way to define
such constants is to declare them as final, static variables in a class (or interface)
declaration:
public class MachineState
public static final int
public static final int
public static final int
}

{
BUSY = 1;
IDLE = 0;
BLOCKED = -1;

Such constants are not typesafe, as any int value can be used where we need to use
a constant declared in the MachineState class. Such a constant must be qualified by
the class (or interface) name, unless the class is extended (or the interface is implemented). When such a constant is printed, only its value (for example, 0), and not
its name (for example, IDLE) is printed. A constant also needs recompiling if its
value is changed, as the values of such constants are compiled into the client code.
An enumerated type in Java is much more powerful than the approach outlined
above. It is certainly more convenient to use than implementing one from scratch
using the typesafe enum pattern (see Effective Java by Josh Bloch, ISBN-10:
0321356683).

Declaring Typesafe Enums
The canonical form of declaring an enum type is shown below.
enum MachineState { BUSY, IDLE, BLOCKED } // Canonical form

The keyword enum is used to declare an enum type. The basic notation requires the
type name and a comma-separated list of enum constants. In this case, the name of the
enum type is MachineState. It defines three enum constants. An enum constant can
be any legal Java identifier, but the convention is to use uppercase letters in the
name. Essentially, an enum declaration defines a reference type that has a finite
number of permissible values referenced by the enum constants, and the compiler
ensures they are used in a typesafe manner.

Using Typesafe Enums
Example 3.4 illustrates using enum constants. An enum type is essentially used as
any other reference type, and the restrictions are noted later in this section. Enum
constants are in fact final, static variables of the enum type, and they are implicitly initialized with objects of the enum type when the enum type is loaded at runtime. Since the enum constants are static members, they can be accessed using the
name of the enum type—analogous to accessing static members in a class.

55

3.5: ENUMERATED TYPES

Example 3.4 shows a machine client that uses a machine whose state is an enum
constant. From Example 3.4, we see that an enum constant can be passed as an
argument, as shown as (1), and we can declare references whose type is an enum
type, as shown as (3), but we cannot create new constants (that is, objects) of the
enum type MachineState. An attempt to do so at (5), results in a compile-time error.
The string representation of an enum constant is its name, as shown at (4). Note
that it is not possible to pass a type of value other than a MachineState enum constant in the call to the method setState() of the Machine class, as shown at (2).
Example 3.4

Using Enums
// Filename: MachineState.java
public enum MachineState { BUSY, IDLE, BLOCKED }

// Filename: Machine.java
public class Machine {
private MachineState state;
public void setState(MachineState state) { this.state = state; }
public MachineState getState() { return this.state; }
}

// Filename: MachineClient.java
public class MachineClient {
public static void main(String[] args) {
Machine machine = new Machine();
machine.setState(MachineState.IDLE);
// machine.setState(1);

// (1) Passed as a value.
// (2) Compile-time error!

MachineState state = machine.getState();
// (3) Declaring a reference.
System.out.println(
"The machine state is: " + state
// (4) Printing the enum name.
);
// MachineState newState = new MachineState();// (5) Compile-time error!
}
}

Output from the program:
The machine state is: IDLE

Declaring Enum Constructors and Members
An enum type declaration is a special kind of reference type declaration. It can
declare constructors and other members as in an ordinary class, but the enum constants must be declared before any other declarations (see the declaration of the

56

CHAPTER 3: DECLARATIONS

enum type Meal in Example 3.5). The list of enum constants must be terminated by
a semi-colon (;). Each enum constant name can be followed by an argument list
that is passed to the constructor of the enum type having the matching parameter
signature.
In Example 3.5, the enum type Meal contains a constructor declaration at (1) with
the following signature:
Meal(int, int)

Each enum constant is specified with an argument list with the signature (int,
int) that matches the constructor signature. In addition, the enum declaration
declares two fields for the meal time at (3), and two instance methods to retrieve
the meal time at (4).
When the enum type is loaded at runtime, the constructor is run for each enum
constant, passing the argument values specified for the enum constant. For the Meal
enum type, three objects are created that are initialized with the specified argument values, and are referenced by the three enum constants, respectively. Note
that each enum constant is a final, static reference that stores the reference value
of an object of the enum type, and methods of the enum type can be called on this
object by using the enum constant name. This is illustrated at (5) in Example 3.5 by
calling methods on the object referenced by the enum constant Meal.BREAKFAST.
An implicit standard constructor is created if no constructors are provided for the
enum type. As mentioned earlier, an enum type cannot be instantiated using the
new operator. The constructors cannot be called explicitly. The only accessibility
modifier allowed for a constructor is private.
Example 3.5

Declaring Enum Constructors and Members
// Filename: Meal.java
public enum Meal {
BREAKFAST(7,30), LUNCH(12,15), DINNER(19,45);

}

// (1)

// Non-default constructor
Meal(int hh, int mm) {
assert (hh >= 0 && hh <= 23): "Illegal hour.";
assert (mm >= 0 && mm <= 59): "Illegal mins.";
this.hh = hh;
this.mm = mm;
}

(2)

// Fields for the meal time:
private int hh;
private int mm;

(3)

// Instance methods:
public int getHour() { return this.hh; }
public int getMins() { return this.mm; }

(4)

57

3.5: ENUMERATED TYPES
// Filename: MealAdministrator.java
public class MealAdministrator {
public static void main(String[] args) {
System.out.printf(
// (5)
"Please note that no eggs will be served at %s, %02d:%02d.%n",
Meal.BREAKFAST, Meal.BREAKFAST.getHour(), Meal.BREAKFAST.getMins()
);
System.out.println("Meal times are as follows:");
Meal[] meals = Meal.values();
for (Meal meal : meals)
System.out.printf("%s served at %02d:%02d%n",
meal, meal.getHour(), meal.getMins()
);

// (6)
// (7)

Meal formalDinner = Meal.valueOf("DINNER");
// (8)
System.out.printf("Formal dress is required for %s at %02d:%02d.%n",
formalDinner, formalDinner.getHour(), formalDinner.getMins()
);
}
}

Output from the program:
Please note that no eggs will be served at BREAKFAST, 07:30.
Meal times are as follows:
BREAKFAST served at 07:30
LUNCH served at 12:15
DINNER served at 19:45
Formal dress is required for DINNER at 19:45.

Implicit Static Methods for Enum Types
All enum types implicitly have the following static methods, and methods with
these names cannot be declared in an enum type declaration:
static EnumTypeName[] values()

Returns an array containing the enum constants of this enum type, in the order
they are specified.
static EnumTypeName valueOf(String name)

Returns the enum constant with the specified name. An IllegalArgumentException is thrown if the specified name does not match the name of an enum constant. The specified name is not qualified with the enum type name.
The static method values() is called at (6) in Example 3.5 to create an array of enum
constants. This array is traversed in the for(:) loop at (7), printing the information
about each meal. The for(:) loop is discussed in Section 6.3, p. 220.

58

CHAPTER 3: DECLARATIONS

The static method valueOf() is called at (8) in Example 3.5 to retrieve the enum constant that has the specified name "DINNER". A printf statement is used to print the
information about the meal denoted by this enum constant.

Inherited Methods from the Enum Class
All enum types are subtypes of the java.lang.Enum class which provides the default
behavior. All enum types are comparable (Section 15.1, p. 765) and serializable
(Section 11.6, p. 510).
All enum types inherit the following final methods from the java.lang.Enum class,
and these methods can therefore not be overridden by an enum type:
protected final Object clone()

An instance of an enum type cannot be cloned (see Section 10.2, p. 424). The
method throws an CloneNotSupportedException.
final int compareTo(E o)

The natural order of the enum constants in an enum type is according to their
ordinal values (see the ordinal() method below). The compareTo() method in the
Comparable interface is discussed in Section 15.1, p. 765.
final boolean equals(Object other)

This method returns true if the specified object is equal to this enum constant
(Section 15.1, p. 751).
protected final void finalize()

An enum constant cannot be finalized, because this final method effectively
prevents enum types from implementing their own finalize() method (see
Section 9.4, p. 396).
final Class getDeclaringClass()

This method returns the Class object corresponding to this enum constant's
enum type (see Section 10.2, p. 424).
final int hashCode()

This method returns a hash code for this enum constant (see Section 15.1, p. 760).
final String name()

This method returns the name of this enum constant, exactly as declared in its
enum declaration.
final int ordinal()

This method returns the ordinal value of this enum constant (that is, its position
in its enum type declaration). The first enum constant is assigned an ordinal
value of zero. If the ordinal value of an enum constant is less than the ordinal
value of another enum constant of the same enum type, the former occurs
before the latter in the enum type declaration.

3.5: ENUMERATED TYPES

59

Note that the equality test implemented by the equals() method is based on reference equality (==) of the enum constants, not on value equality (Section 5.11, p. 193).
An enum type has a finite number of distinct objects. Comparing two enum references for equality means determining whether they store the reference value of the
same enum contant, i.e., whether the references are aliases. Thus, for any two enum
references meal1 and meal2, the expression meal1.equals(meal2) and meal1 == meal2
are equivalent.
The Enum class also overrides the toString() method from the Object class (see Section 10.2, p. 424). The toString() method returns the name of the enum constant,
but it is not final, and can be overridden by an enum type. Example 3.6 uses some
of the methods mentioned in this subsection.

Extending Enum Types: Constant-Specific Class Bodies
A review of subtyping (Section 7.1, p. 284), overriding (Section 7.2, p. 288), and anonymous classes (Section 8.5, p. 377) can be helpful before diving into this subsection.
Constant-specific class bodies define anonymous classes inside an enum type, i.e.,
they implicitly extend the enclosing enum type. The enum type Meal in Example
3.6 declares constant-specific class bodies for its constants. The following skeletal
code declares the constant-specific class body for the enum constant BREAKFAST:
BREAKFAST(7,30) {
// (1) Start of constant-specific class body
public double mealPrice(Day day) { // (2) Overriding abstract method
...
}
public String toString() {
// (3) Overriding method from the Enum
class
...
}
}
// (4) End of constant-specific class body

The constant-specific class body, as the name implies, is a class body that is specific
to a particular enum constant. As any class body, it is enclosed in braces, { }. It is
declared immediately after the enum constant and any constructor arguments. In
the code above, it starts at (1) and ends at (4). Like any class body, it can contain
member declarations. In the above case, the body contains two method declarations: an implementation of the method mealPrice() at (2) that overrides the
abstract method declaration at (7) in the enclosing enum supertype, and an implementation of the toString() method at (3) that overrides the one inherited by the
Meal enum type from the superclass java.lang.Enum.
The constant-specific class body is an anonymous class, i.e., a class with no name.
Each constant-specific class body defines a distinct, albeit anonymous, subtype of
the enclosing enum type. In the code above, the constant-specific class body
defines a subtype of the Meal enum type. It inherits members of the enclosing enum
supertype, that are not private, overridden, or hidden. When the enum type Meal
is loaded at runtime, this constant-specific class body is instantiated, and the reference value of the instance is assigned to the enum constant BREAKFAST. Note that the

60

CHAPTER 3: DECLARATIONS

type of the enum constant is Meal, which is the supertype of the anonymous subtype represented by the constant-specific class body. Since supertype references
can refer to subtype objects, the above assignment is legal.
Each enum constant overrides the abstract method mealPrice() declared in the
enclosing enum supertype, i.e., provides an implementation for the method. The
compiler will report an error if this is not the case. Although the enum type declaration specifies an abstract method, the enum type declaration is not declared
abstract—contrary to an abstract class. Given that the references meal and day are
of the enum types Meal and Day from Example 3.6, respectively, the method call
meal.mealPrice(day)

will execute the mealPrice() method from the constant-specific body of the enum
constant denoted by the reference meal.
Two constant-specific class bodies, associated with the enum constants BREAKFAST
and LUNCH, override the toString() method from the Enum class. Note that the
toString() method is not overridden in the Meal enum type, but in the anonymous
classes represented by two constant-specific class bodies. The third enum constant,
DINNER, relies on the toString() method inherited from the Enum class.
Constructors, abstract methods, and static methods cannot be declared in a constantspecific class body. Instance methods declared in constant-specific class bodies are
only accessible if they override methods in the enclosing enum supertype.
Example 3.6

Declaring Constant-Specific Class Bodies
// Filename: Day.java
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// Filename: Meal.java
public enum Meal {
// Each enum constant defines a constant-specific class body
BREAKFAST(7,30) {
public double mealPrice(Day day) {
double breakfastPrice = 10.50;
if (day.equals(Day.SATURDAY) || day == Day.SUNDAY)
breakfastPrice *= 1.5;
return breakfastPrice;
}
public String toString() {
return "Breakfast";
}
},
LUNCH(12,15) {
public double mealPrice(Day day) {
double lunchPrice = 20.50;
switch (day) {
case SATURDAY: case SUNDAY:

// (1)
// (2)

// (3)

// (4)
// (5)

61

3.5: ENUMERATED TYPES
lunchPrice *= 2.0;
}
return lunchPrice;
}
public String toString() {
return "Lunch";
}
},
DINNER(19,45) {
public double mealPrice(Day day) {
// (6)
double dinnerPrice = 25.50;
if (day.compareTo(Day.SATURDAY) >= 0 && day.compareTo(Day.SUNDAY) <= 0)
dinnerPrice *= 2.5;
return dinnerPrice;
}
};
// Abstract method implemented in constant-specific class bodies.
abstract double mealPrice(Day day);

// (7)

// Enum constructor:
Meal(int hh, int mm) {
assert (hh >= 0 && hh <= 23): "Illegal hour.";
assert (mm >= 0 && mm <= 59): "Illegal mins.";
this.hh = hh;
this.mm = mm;
}
// Instance fields: Time for the meal.
private int hh;
private int mm;
// Instance methods:
public int getHour() { return this.hh; }
public int getMins() { return this.mm; }
}
// Filename: MealPrices.java
public class MealPrices {
public static void main(String[] args) {
System.out.printf(
"Please note that %s, %02d:%02d, on %s costs $%.2f.%n",
Meal.BREAKFAST.name(),
Meal.BREAKFAST.getHour(), Meal.BREAKFAST.getMins(),
Day.MONDAY,
Meal.BREAKFAST.mealPrice(Day.MONDAY)
);

// (8)

// (9)

// (10)

System.out.println("Meal prices on " + Day.SATURDAY + " are as follows:");
Meal[] meals = Meal.values();
for (Meal meal : meals)
System.out.printf(
"%s costs $%.2f.%n", meal, meal.mealPrice(Day.SATURDAY)
// (11)

62

CHAPTER 3: DECLARATIONS
);
}
}

Output from the program:
Please note that BREAKFAST, 07:30, on MONDAY costs $10.50.
Meal prices on SATURDAY are as follows:
Breakfast costs $15.75.
Lunch costs $41.00.
DINNER costs $63.75.

In Example 3.6, the mealPrice() method declaration at (2) uses both the equals()
method and the == operator to compare enum constants for equality. The mealPrice() method declaration at (5) uses enum constants in a switch statement (Section 6.2, p. 207). Note that the case labels in the switch statement are enum constant
names, without the enum type name. The mealPrice() method declaration at (6)
uses the compareTo() method to compare enum constants.
The main() method at (8) in Example 3.6 demonstrates calling the mealPrice()
method in the constant-specific class bodies. The mealPrice() method is called at
(10) and (11). Example 3.6 also illustrates the difference between the name() and the
toString() methods of the enum types. The name() method is called at (9), and the
toString() method is called at (10) and (11). The name() method always prints the
enum constant name exactly as it was declared. Which toString() method is executed depends on whether the toString() method in the Enum class is overridden.
Only the constant-specific class bodies of the enum constants BREAKFAST and LUNCH
override this method. The output from the program confirms this to be the case.

Declaring Typesafe Enums Revisited
An enum type can be declared as a top-level type. Enum types can also be nested,
but only within other static members, or other top-level type declarations (Section
8.2, p. 355). When nested, it is implicitly static, and can be declared with the keyword static. The following skeletal code shows the two enum types Day and Meal
declared as static members in the class MealPrices:
public class MealPrices {
public enum Day { /* ... */ }

// Static member

public static enum Meal { /* ... */ }

// Static member

public static void main(String[] args) { /* ... */ }

// Static method

}

An enum type cannot be explicitly extended using the extends clause. An enum
type is implicitly final, unless it contains constant-specific class bodies. If it
declares constant-specific class bodies, it is implicitly extended. No matter what, it
cannot be explicitly declared final.

63

3.5: ENUMERATED TYPES

An enum type cannot be declared abstract, regardless of whether each abstract
method is overridden in the constant-specific class body of every enum constant.
Like a class, an enum can implement interfaces.
public interface ITimeInfo {
public int getHour();
public int getMins();
}
public enum Meal implements ITimeInfo {
// ...
public int getHour() { return this.hh; }
public int getMins() { return this.mm; }
}

The Java Collections Framework provides a special purpose set implementation
(java.util.EnumSet) and a special purpose map implementation (java.util.EnumMap)
for use with enum types. These special purpose implementations provide better
performance for enum types than the general purpose counterparts, and are worth
checking out.

Review Questions
3.7

Which statements about the enum type are true?
Select the three correct answers.
(a) An enum type is a subclass of the abstract class java.lang.Enum, hence it is Comparable and Serializable.
(b) An enum type can implement interfaces.
(c) We can instantiate an enum type using the new operator.
(d) An enum type can define constructors.
(e) We can explicitly use the extend clause to extend an enum type.
(f) Enum types do not inherit members from the Object class.

3.8

What will be the result of attempting to compile and run the following code?
public enum Drill {
ATTENTION("Attention!"), EYES_RIGHT("Eyes right!"),
EYES_LEFT("Eyes left!"), AT_EASE("At ease!");
private String command;
Drill(String command) {
this.command = command;
}
public static void main(String[] args) {
System.out.println(ATTENTION);
System.out.println(AT_EASE);
}
}

// (1)
// (2)

64

CHAPTER 3: DECLARATIONS

Select the one correct answer.
(a) The code compiles, but reports a ClassNotFoundException when run, since an
enum type cannot be run as a standalone application.
(b) The compiler reports errors in (1) and (2), as the constants must be qualified
by the enum type name Drill.
(c) The compiler reports errors in (1) and (2), as the constants cannot be accessed
in a static context.
(d) The code compiles and prints:
ATTENTION
AT_EASE

(e) The code compiles and prints:
Attention!
At ease!

(f) None of the above.
3.9

What will be the result of compiling and running the following code?
import java.util.Arrays;
public enum Priority {
ONE(1) { public String toString() { return "LOW"; } },
TWO(2),
THREE(3) { public String toString() { return "NORMAL"; } },
FOUR(4),
FIVE(5) { public String toString() { return "HIGH"; } };

// (1)
// (2)
// (3)

private int pValue;
Priority(int pValue) {
this.pValue = pValue;
}
public static void main(String[] args) {
System.out.println(Arrays.toString(Priority.values()));
}
}

Select the one correct answer.
(a) The code compiles, but reports a ClassNotFoundException when run, since an
enum type cannot be run as a standalone application.
(b) The compiler reports syntax errors in (1), (2), and (3).
(c) The code compiles and prints:
[LOW, TWO, NORMAL, FOUR, HIGH]

(d) The code compiles and prints:
[ONE, TWO, THREE, FOUR, HIGH]

(e) None of the above.

65

3.5: ENUMERATED TYPES

3.10

Which statement about the following program is true?
public enum Scale {
GOOD('C'), BETTER('B'), BEST('A');
private char grade;
Scale(char grade) {
this.grade = grade;
}
abstract public char getGrade();
public static void main (String[] args) {
System.out.println (GOOD.getGrade());
}

// (1)

}

Select the one correct answer.
(a) Since the enum type declares an abstract method, the enum type must be
declared as abstract.
(b) The method call GOOD.getGrade() in (1) can be written without the enum type
name.
(c) An enum type cannot declare an abstract method.
(d) An enum type can declare an abstract method, but each enum constant must
provide an implementation.
3.11

What will be the result of compiling and running the following code?
public enum TrafficLight {
RED("Stop"), YELLOW("Caution"), GREEN("Go");
private String action;
TrafficLight(String action) {
this.action = action;
}
public static void main(String[] args) {
TrafficLight green = new TrafficLight("Go");
System.out.println(GREEN.equals(green));
}
}

Select the one correct answer.
(a)
(b)
(c)
(d)

The code will compile and print: true.
The code will compile and print: false.
The code will not compile, as an enum type cannot be instantiated.
An enum type does not have the equals() method.

66

CHAPTER 3: DECLARATIONS

3.12

Given the following program:
public enum Scale2 {
GOOD('C')
{ public char getGrade() { return grade; } },
BETTER('B') { public char getGrade() { return grade; } },
BEST('A')
{ public char getGrade() { return grade; } };
private char grade;
Scale2(char grade) {
this.grade = grade;
}
// (1) INSERT CODE HERE
public static void main (String[] args) {
System.out.println(GOOD.getGrade());
}
}

Which code, when inserted at (1), will make the program print C?
Select the two correct answers.
(a) public char getGrade() { return grade; }
(b) public int getGrade() { return grade; }
(c) abstract public int getGrade();
(d) abstract public char getGrade();
3.13

Given the following program:
enum Scale3 {
GOOD(Grade.C), BETTER(Grade.B), BEST(Grade.A);
enum Grade {A, B, C}
private Grade grade;
Scale3(Grade grade) {
this.grade = grade;
}
public Grade getGrade() { return grade; }
}
public class Scale3Client {
public static void main (String[] args) {
System.out.println(/* (1) INSERT CODE HERE */);
}
}

Which code, when inserted at (1), will make the program print true?
Select the four correct answers.
(a) Scale3.GOOD.getGrade() != Scale3.Grade.C
(b) Scale3.GOOD.getGrade().compareTo(Scale3.Grade.C) != 0
(c) Scale3.GOOD.getGrade().compareTo(Scale3.Grade.A) > 0

67

3.5: ENUMERATED TYPES

(d)
(e)
(f)
(g)
3.14

Scale3.GOOD.compareTo(Scale3.BEST) > 0
Scale3.GOOD.getGrade() instanceof Scale3.Grade
Scale3.GOOD instanceof Scale3
Scale3.GOOD.getGrade().toString().equals(Scale3.Grade.C.toString())

What will be the result of compiling and running the following code?
public enum Scale5 {
GOOD, BETTER, BEST;
public char getGrade() {
char grade = '\u0000';
switch(this){
case GOOD:
grade = 'C'; break;
case BETTER: grade = 'B'; break;
case BEST:
grade = 'A'; break;
}
return grade;
}
public static void main (String[] args) {
System.out.println(GOOD.getGrade());
}
}

Select the one correct answer.
(a) The program will not compile, as the switch expression is not compatible with
the case labels.
(b) The program will not compile, as enum constants cannot be used as case
labels.
(c) The case labels must be qualified with the enum type name.
(d) The program compiles, and when run, prints: C
(e) The program compiles, and when run, prints: GOOD
(f) None of the above.
3.15

Given the following code:
package p1;
enum March {LEFT, RIGHT}
public class Defence {
enum March {LEFT, RIGHT}
static enum Military {
INFANTRY, AIRFORCE;
enum March {LEFT, RIGHT}
}
class Secret {
enum March {LEFT, RIGHT}
}
static class Open {
enum March {LEFT, RIGHT}
}
public static void declareWar() {

// (1)
// (2)

// (3)

// (4)

// (5)

68

CHAPTER 3: DECLARATIONS
enum March {LEFT, RIGHT}
}
public void declarePeace() {
enum March {LEFT, RIGHT}
}

// (6)

// (7)

}

Which enum declarations are not legal?
Select the three correct answers.
(a)
(b)
(c)
(d)
(e)
(f)
(g)
3.16

The enum declaration at (1) is not legal.
The enum declaration at (2) is not legal.
The enum declaration at (3) is not legal.
The enum declaration at (4) is not legal.
The enum declaration at (5) is not legal.
The enum declaration at (6) is not legal.
The enum declaration at (7) is not legal.

Given the following code:
public enum Direction {
EAST, WEST, NORTH, SOUTH;
public static void main (String[] args) {
// (1) INSERT LOOP HERE
}
}

Which loops, when inserted independently at (1), will give the following output:
EAST
WEST
NORTH
SOUTH

Select the three correct answers.
(a) for (Direction d : Direction.values()) {
System.out.println(d);
}

(b) for (Direction d : Direction.values()) {
System.out.println(d.name());
}

(c) for (String name : Direction.names()) {
System.out.println(name);
}

(d) for (Direction d : java.util.Arrays.asList(Direction.values())) {
System.out.println(d);
}

(e) for (Direction d : java.util.Arrays.asList(Direction.class)) {
System.out.println(d);
};

69

3.6: ARRAYS

3.17

What will be the result of compiling and running the following code?
enum Rank {
FIRST(20), SECOND(0), THIRD(8);
Rank(int value) {
System.out.print(value);
}
}
public class EnumCreation {
public static void main (String[] args) {
System.out.println("\n" + Rank.values().length);
}
}

Select the one correct answer.
(a) The program will compile and print:
3

(b) The program will compile and print:
2008
3

(c) The program will compile. When run, it will print:
2008

and throw an exception.
(d) None of the above.

3.6 Arrays
An array is a data structure that defines an indexed collection of a fixed number of
homogeneous data elements. This means that all elements in the array have the
same data type. A position in the array is indicated by a non-negative integer value
called the index. An element at a given position in the array is accessed using the
index. The size of an array is fixed and cannot be changed.
In Java, arrays are objects. Arrays can be of primitive data types or reference types.
In the former case, all elements in the array are of a specific primitive data type. In
the latter case, all elements are references of a specific reference type. References in
the array can then denote objects of this reference type or its subtypes. Each array
object has a final field called length, which specifies the array size, i.e., the number
of elements the array can accommodate. The first element is always at index 0 and
the last element at index n-1, where n is the value of the length field in the array.
Simple arrays are one-dimensional arrays, that is, a simple list of values. Since arrays
can store reference values, the objects referenced can also be array objects. Thus,
multi-dimensional arrays are implemented as array of arrays.

70

CHAPTER 3: DECLARATIONS

Passing array references as parameters is discussed in Section 3.7. Type conversions for array references on assignment and on method invocation are discussed
in Section 7.7, p. 317.

Declaring Array Variables
A one-dimensional array variable declaration has either the following syntax:
[] ;
or
 [];
where  can be a primitive data type or a reference type. The array
variable  has the type []. Note that the array size is not
specified. This means that the array variable  can be assigned the reference value of an array of any length, as long as its elements have .
It is important to understand that the declaration does not actually create an array.
It only declares a reference that can refer to an array object.
int anIntArray[], oneInteger;
Pizza[] mediumPizzas, largePizzas;

The two declarations above declare anIntArray and mediumPizzas to be reference
variables that can refer to arrays of int values and arrays of Pizza objects, respectively. The variable largePizzas can denote an array of pizzas, but the variable
oneInteger cannot denote an array of int values—it is a simple variable of the type
int .
The [] notation can also be specified after a variable name to declare it as an array
variable, but then it only applies to this variable.
An array variable that is declared as a member of a class, but is not initialized to
any array, will be initialized to the default reference value null. This default initialization does not apply to local reference variables and, therefore, does not apply to
local array variables either (see Section 2.4, p. 33). This should not be confused with
initialization of the elements of an array during array construction.

Constructing an Array
An array can be constructed for a fixed number of elements of a specific type, using
the new operator. The reference value of the resulting array can be assigned to an
array variable of the corresponding type. The syntax of the array creation expression
is shown on the right-hand side of the following assignment statement:
 = new  [];

71

3.6: ARRAYS

The minimum value of  is 0, in other words, zero-length arrays can be
constructed in Java. If the array size is negative, a NegativeArraySizeException is
thrown.
Given the following array declarations:
int anIntArray[], oneInteger;
Pizza[] mediumPizzas, largePizzas;

the arrays can be constructed as follows:
anIntArray
= new int[10];
mediumPizzas = new Pizza[5];
largePizzas = new Pizza[3];

// array for 10 integers
// array of 5 pizzas
// array of 3 pizzas

The array declaration and construction can be combined.
[]  = new [];
Here the array type [] must be assignable to the array type [] (Section 7.7, p. 317). When the array is constructed, all its elements are initialized to the default value for . This is true for both member and
local arrays when they are constructed.
In all examples below, the code constructs the array, and the array elements are
implicitly initialized to their default value. For example, all elements of the array
anIntArray get the value 0, and all element of the array mediumPizzas get the value
null when the arrays are constructed.
int[] anIntArray = new int[10];

// Default element value: 0.

Pizza[] mediumPizzas = new Pizza[5];

// Default element value: null.

// Pizza class extends Object class
Object[] objArray = new Pizza[3];

// Default element value: null.

// Pizza class implements Eatable interface
Eatable[] eatables = new Pizza[2];

// Default element value: null.

The value of the field length in each array is set to the number of elements specified
during the construction of the array; for example, mediumPizzas.length has the
value 5.
Once an array has been constructed, its elements can also be explicitly initialized
individually; for example, in a loop. The examples in the rest of this section make
use of a loop to traverse the elements of an array for various purposes.

Initializing an Array
Java provides the means of declaring, constructing, and explicitly initializing an
array in one declaration statement:
[]  = {  };

72

CHAPTER 3: DECLARATIONS

This form of initialization applies to member as well as local arrays. The  is a comma-separated list of zero or more expressions. Such an array
initialization block results in the construction and initialization of the array.
int[] anIntArray = {13, 49, 267, 15, 215};

The array anIntArray is declared as an array of ints. It is constructed to hold 5
elements (equal to the length of the list of expressions in the block), where the first
element is initialized to the value of the first expression (13), the second element to
the value of the second expression (49), and so on.
// Pizza class extends Object class
Object[] objArray = { new Pizza(), new Pizza(), null };

The array objArray is declared as an array of the Object class, constructed to hold
three elements. The initialization code sets the first two elements of the array to
refer to two Pizza objects, while the last element is initialized to the null reference.
Note that the number of objects created in the above declaration statement is actually three: the array object with three references and the two Pizza objects.
The expressions in the  are evaluated from left to right, and the
array name obviously cannot occur in any of the expressions in the list. In the
examples above, the  is terminated by the right curly bracket,
}, of the block. The list can also be legally terminated by a comma. The following
array has length two, and not three:
Topping[] pizzaToppings = { new Topping("cheese"), new Topping("tomato"), };

The declaration statement at (1) in the following code defines an array of four
String objects, while the declaration statement at (2) shows that a String object is
not the same as an array of char.
// Array with 4 String objects:
String[] pets = {"crocodiles", "elephants", "crocophants", "elediles"}; // (1)
// Array of 3 characters:
char[] charArray = {'a', 'h', 'a'};

// (2) Not the same as "aha".

Using an Array
The array object is referenced by the array name, but individual array elements are
accessed by specifying an index with the [] operator. The array element access
expression has the following syntax:
 []
Each individual element is treated as a simple variable of the element type. The
index is specified by the , which can be any expression that evaluates to a non-negative int value. Since the lower bound of an array is always 0,
the upper bound is one less than the array size, that is, .length-1. The
ith element in the array has index (i-1). At runtime, the index value is automatically checked to ensure that it is within the array index bounds. If the index value

73

3.6: ARRAYS

is less than 0, or greater than or equal to .length, an ArrayIndexOutOfBoundsException is thrown. A program can either check the index explicitly or catch
the exception (see Section 6.5, p. 235), but an illegal index is typically an indication
of a program bug.
In the array element access expression, the  can be any expression that
returns a reference to an array. For example, the following expression returns the
character 'H' at index 1 in the character array returned by a call to the toCharArray()
method of the String class: "AHA".toCharArray()[1].
The array operator [] is used to declare array types (Topping[]), specify array size
(new Topping[3]), and to access array elements (toppings[1]). This operator is not
used when the array reference is manipulated, for example, in an array reference
assignment (see Section 7.9, p. 320), or when the array reference is passed as an
actual parameter in a method call (see Section 3.7, p. 86).
Example 3.7 shows traversal of arrays. The loop at (3) initializes the local array
trialArray declared at (2) five times with pseudo-random numbers (from 0.0 to
100.0), by calling the method randomize() declared at (5). The minimum value in the
array is found by calling the method findMinimum() declared at (6), and is stored in
the array storeMinimum declared at (1). The loop at (4) prints the minimum values
from the trials. The start value of the loop variable is initially set to 0. The loop condition tests whether the loop variable is less than the length of the array; this guarantees that the index will not go out of bounds.
Example 3.7

Using Arrays
public class Trials {
public static void main(String[] args) {
// Declare and construct the local arrays:
double[] storeMinimum = new double[5];
double[] trialArray = new double[15];
for (int i = 0; i < storeMinimum.length; ++i) {

// (1)
// (2)
// (3)

// Initialize the array.
randomize(trialArray);
// Find and store the minimum value.
storeMinimum[i] = findMinimum(trialArray);
}
// Print the minimum values:
for (int i = 0; i < storeMinimum.length; ++i)
System.out.printf("%.4f%n", storeMinimum[i]);

(4)

public static void randomize(double[] valArray) {
for (int i = 0; i < valArray.length; ++i)
valArray[i] = Math.random() * 100.0;
}

// (5)

}

74

CHAPTER 3: DECLARATIONS
public static double findMinimum(double[] valArray) {
// Assume the array has at least one element.
double minValue = valArray[0];
for (int i = 1; i < valArray.length; ++i)
minValue = Math.min(minValue, valArray[i]);
return minValue;
}

// (6)

}

Possible output from the program:
6.9330
2.7819
6.7427
18.0849
26.2462

Anonymous Arrays
As shown earlier in this section, the following declaration statement
[]  = new []; // (1)
int[] intArray = new int[5];

can be used to construct arrays using an array creation expression. The size of the
array is specified in the array creation expression, which creates the array and initializes the array elements to their default values. On the other hand, the following
declaration statement
[]  = {  };

// (2)

int[] intArray = {3, 5, 2, 8, 6};

both creates the array and initializes the array elements to specific values given in the
array initializer block. However, the array initialization block is not an expression.
Java has another array creation expression, called anonymous array, which allows
the concept of the array creation expression from (1) and the array initializer block
from (2) to be combined, to create and initialize an array object:
new [] {  }
new int[] {3, 5, 2, 8, 6}

The construct has enough information to create a nameless array of a specific type.
Neither the name of the array nor the size of the array is specified. The construct
returns the reference value of the newly-created array, which can be assigned to
references and passed as argument in method calls. In particular, the following two
examples of declaration statements are equivalent.
int[] intArray = {3, 5, 2, 8, 6};

// (1)

int[] intArray = new int[] {3, 5, 2, 8, 6}; // (2)

75

3.6: ARRAYS

In (1), an array initializer block is used to create and initialize the elements. In (2),
an anonymous array expression is used. It is tempting to use the array initialization block as an expression; for example, in an assignment statement, as a short cut
for assigning values to array elements in one go. However, this is illegal—instead,
an anonymous array expression should be used.
int[] daysInMonth;
daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Not ok.
daysInMonth = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // ok.

The concept of anonymous arrays is similar to that of anonymous classes (see Section
8.5, p. 377): they both combine the definition and the creation of objects into one
operation.
In Example 3.8, an anonymous array is constructed at (1), and passed as a parameter to the static method findMinimum() defined at (2). Note that no array name or
array size is specified for the anonymous array.
Example 3.8

Using Anonymous Arrays
public class AnonArray {
public static void main(String[] args) {
System.out.println("Minimum value: " +
findMinimum(new int[] {3, 5, 2, 8, 6}));
}
public static int findMinimum(int[] dataSeq) {
// Assume the array has at least one element.
int min = dataSeq[0];
for (int index = 1; index < dataSeq.length; ++index)
if (dataSeq[index] < min)
min = dataSeq[index];
return min;
}

// (1)

// (2)

}

Output from the program:
Minimum value: 2

Multidimensional Arrays
Since an array element can be an object reference and arrays are objects, array
elements can themselves reference other arrays. In Java, an array of arrays can be
defined as follows:
[][]...[] ;
or

76

CHAPTER 3: DECLARATIONS

 [][]...[];
In fact, the sequence of square bracket pairs, [], indicating the number of dimensions, can be distributed as a postfix to both the element type and the array name.
Arrays of arrays are also often called multidimensional arrays.
The following declarations are all equivalent:
int[][] mXnArray;
int[]
mXnArray[];
int
mXnArray[][];

// 2-dimensional array
// 2-dimensional array
// 2-dimensional array

It is customary to combine the declaration with the construction of the multidimensional array.
int[][] mXnArray = new int[4][5];

// 4 x 5 matrix of ints

The previous declaration constructs an array mXnArray of four elements, where each
element is an array (row) of 5 int values. The concept of rows and columns is often
used to describe the dimensions of a 2-dimensional array, which is often called a
matrix. However, such an interpretation is not dictated by the Java language.
Each row in the previous matrix is denoted by mXnArray[i], where 0 d i  4. Each
element in the ith row, mXnArray[i], is accessed by mXnArray[i][j], where 0 d j  5.
The number of rows is given by mXnArray.length, in this case 4, and the number of
values in the ith row is given by mXnArray[i].length, in this case 5 for all the rows,
where 0 d i  4.
Multidimensional arrays can also be constructed and explicitly initialized using
array initializer blocks discussed for simple arrays. Note that each row is an array
which uses an array initializer block to specify its values:
double[][] identityMatrix = {
{1.0, 0.0, 0.0, 0.0 }, // 1. row
{0.0, 1.0, 0.0, 0.0 }, // 2. row
{0.0, 0.0, 1.0, 0.0 }, // 3. row
{0.0, 0.0, 0.0, 1.0 } // 4. row
}; // 4 x 4 Floating-point matrix

Arrays in a multidimensional array need not have the same length, and are often
called ragged arrays. The array of arrays pizzaGalore in the code below will have five
rows, the first four rows have different lengths but the fifth row is left unconstructed.
Pizza[][] pizzaGalore = {
{ new Pizza(), null, new Pizza() },
{ null, new Pizza()},
new Pizza[1],
{},
null
};

//
//
//
//
//

1.
2.
3.
4.
5.

row
row
row
row
row

is
is
is
is
is

an array of 3 elements.
an array of 2 elements.
an array of 1 element.
an array of 0 elements.
not constructed.

When constructing multidimensional arrays with the new operator, the length of
the deeply nested arrays may be omitted. In which case, these arrays are left
unconstructed. For example, an array of arrays to represent a room on a floor in a

77

3.6: ARRAYS

hotel on a street in a city can have the type HotelRoom[][][][]. From left to right, the
square brackets represent indices for street, hotel, floor, and room, respectively.
This 4-dimensional array of arrays can be constructed piecemeal, starting with the
leftmost dimension and proceeding to the rightmost.
HotelRoom[][][][] rooms = new HotelRoom[10][5][][];

// Just streets and hotels.

The above declaration constructs the array of arrays rooms partially with ten streets,
where each street has five hotels. Floors and rooms can be added to a particular
hotel on a particular street:
rooms[0][0]
= new HotelRoom[3][]; // 3 floors in 1st. hotel on 1st. street.
rooms[0][0][0]
= new HotelRoom[8];
// 8 rooms on 1st. floor in this hotel.
rooms[0][0][0][0] = new HotelRoom();
// Initializes 1st. room on this floor.

The code below constructs an array of arrays matrix, where the first row has one
element, the second row has two elements, and the third row has three elements.
Note that the outer array is constructed first. The second dimension is constructed in a loop that constructs the array in each row. The elements in the multidimensional array will be implicitly initialized to the default double value (0.0D).
In Figure 3.2, the array of arrays matrix is depicted after the elements have been
explicitly initialized.
double[][] matrix = new double[3][];

// No. of rows.

for (int i = 0; i < matrix.length; ++i)
matrix[i] = new double[i + 1];

// Construct a row.

Two other ways of initializing such an array of arrays are shown below. The first
one uses array initializer blocks, and the second one uses an anonymous array of
arrays.
double[][] matrix2 = {
{0.0},
{0.0, 0.0},
{0.0, 0.0, 0.0}
}

//
//
//
//

Using array initializer blocks.
1. row
2. row
3. row

double[][] matrix3 = new double[][] { // Using an anonymous array of arrays.
{0.0},
// 1. row
{0.0, 0.0},
// 2. row
{0.0, 0.0, 0.0}
// 3. row
}

The type of the variable matrix is double[][], i.e., a two-dimensional array of double
values. The type of the variable matrix[i] (where 0 d i matrix.length) is double[],
i.e., a one-dimensional array of double values. The type of the variable matrix[i][j]
(where 0 dimatrix.length and 0 djmatrix[i].length) is double, i.e., a simple variable of type double.
Nested loops are a natural match for manipulating multidimensional arrays. In
Example 3.9, a rectangular 4 u 3 int matrix is declared and constructed at (1). The
program finds the minimum value in the matrix. The outer loop at (2) traverses the
rows (mXnArray[i], where 0 dimXnArray.length), and the inner loop at (3) traverses

78

CHAPTER 3: DECLARATIONS
Figure 3.2 Array of Arrays

matrix[0]:double[]
length = 1
matrix:double[][]

[0]

8.5
matrix[1]:double[]

length = 3
[0] :Ref(double[])
[1] :Ref(double[])
[2] :Ref(double[])

matrix[2]:double[]
length = 3
[0]
[1]
[2]

1.5
2.9
5.5

length = 2
[0]
[1]

6.3
4.4

matrix[2][1]

the elements in each row in turn (mXnArray[i][j], where 0 djmXnArray[i].length).
The outer loop is executed mXnArray.length times, or 4 times, and the inner loop is
executed (mXnArray.length) u (mXnArray[i].length), or 12 times, since all rows have
the same length, 3.
The for(:) loop also provides a safe and convenient way of traversing an array, and
ample examples are provided in Section 6.3, p. 220.
The Java standard library also provides the class java.util.Arrays that contains
various static methods for manipulating arrays, such as sorting and searching (see
Section 15.11, p. 842).
Example 3.9

Using Multidimensional Arrays
public class MultiArrays {
public static void main(String[] args) {
// Declare and construct the M X N matrix.
int[][] mXnArray = {
{16, 7, 12}, // 1. row
{ 9, 20, 18}, // 2. row
{14, 11, 5}, // 3. row
{ 8, 5, 10} // 4. row
}; // 4 x 3 int matrix

// (1)

// Find the minimum value in a M X N matrix:
int min = mXnArray[0][0];
for (int i = 0; i < mXnArray.length; ++i)
// (2)
// Find min in mXnArray[i], i.e. in the row given by index i:
for (int j = 0; j < mXnArray[i].length; ++j)
// (3)
min = Math.min(min, mXnArray[i][j]);
System.out.println("Minimum value: " + min);
}
}

79

3.6: ARRAYS

Output from the program:
Minimum value: 5

Review Questions
3.18

Given the following declaration, which expression returns the size of the array,
assuming the array has been initialized?
int[] array;

Select the one correct answer.
(a) array[].length()
(b) array.length()
(c) array[].length
(d) array.length
(e) array[].size()
(f) array.size()
3.19

Is it possible to create arrays of length zero?
Select the one correct answer.
(a)
(b)
(c)
(d)

Yes, you can create arrays of any type with length zero.
Yes, but only for primitive data types.
Yes, but only for arrays of reference types.
No, you cannot create zero-length arrays, but the main() method may be passed
a zero-length array of Strings when no program arguments are specified.
(e) No, it is not possible to create arrays of length zero in Java.
3.20

Which one of the following array declaration statements is not legal?
Select the one correct answer.
(a) int []a[] = new int [4][4];
(b) int a[][] = new int [4][4];
(c) int a[][] = new int [][4];
(d) int []a[] = new int [4][];
(e) int [][]a = new int [4][4];

3.21

Which of these array declaration statements are not legal?
Select the two correct answers.
(a) int[] i[] = { { 1, 2 }, { 1 }, {}, { 1, 2, 3 } };
(b) int i[] = new int[2] {1, 2};
(c) int i[][] = new int[][] { {1, 2, 3}, {4, 5, 6} };
(d) int i[][] = { { 1, 2 }, new int[ 2 ] };

80

CHAPTER 3: DECLARATIONS

(e) int i[4] = { 1, 2, 3, 4 };
3.22

What would be the result of compiling and running the following program?
// Filename: MyClass.java
class MyClass {
public static void main(String[] args) {
int size = 20;
int[] arr = new int[ size ];
for (int i = 0; i < size; ++i) {
System.out.println(arr[i]);
}
}
}

Select the one correct answer.
(a) The code will not compile, because the array type int[] is incorrect.
(b) The program will compile, but will throw an ArrayIndexOutOfBoundsException
when run.
(c) The program will compile and run without error, but will produce no output.
(d) The program will compile and run without error, and will print the numbers
0 through 19.
(e) The program will compile and run without error, and will print 0 twenty
times.
(f) The program will compile and run without error, and will print null twenty
times.
3.23

What would be the result of compiling and running the following program?
public class DefaultValuesTest {
int[] ia = new int[1];
boolean b;
int i;
Object o;
public static void main(String[] args) {
DefaultValuesTest instance = new DefaultValuesTest();
instance.print();
}
public void print() {
System.out.println(ia[0] + " " + b + " " + i + " " + o);
}
}

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
(f)

The program will fail to compile because of uninitialized variables.
The program will throw a java.lang.NullPointerException when run.
The program will print: 0 false NaN null.
The program will print: 0 false 0 null.
The program will print: null 0 0 null.
The program will print: null false 0 null.

81

3.7: PARAMETER PASSING

3.7 Parameter Passing
Objects communicate by calling methods on each other. A method call is used to
invoke a method on an object. Parameters in the method call provide one way of
exchanging information between the caller object and the callee object (which need
not be different).
Defining methods is discussed in Section 3.3, p. 44. Invoking static methods on
classes is discussed in Section 4.10, p. 147.
The syntax of a method call can be any one of the following:
. ()
. ()
 ()
The  must be an expression that evaluates to a reference value
denoting the object on which the method is called. If the caller and the callee are
the same,  can be omitted (see discussion on the this reference in
Section 3.3, p. 45). The  can be the fully qualified name (see Section 4.2,
p. 105) of the class. The  is comma-separated if there is more
than one parameter. The parentheses are mandatory even if the actual parameter
list is empty. This distinguishes the method call from field access. One can specify
fully qualified names for classes and packages using the dot operator.
objRef.doIt(time, place);
int i = java.lang.Math.abs(-1);
int j = Math.abs(-1);
someMethod(ofValue);
someObjRef.make().make().make();

//
//
//
//
//

Explicit object reference
Fully qualified class name
Simple class name
Object or class implicitly implied
make() returns a reference value

The dot operator ('.') has left associativity. In the last code line, the first call of the
make() method returns a reference value that denotes the object on which to execute
the next call, and so on. This is an example of call chaining.
Each actual parameter (also called an argument) is an expression that is evaluated,
and whose value is passed to the method when the method is invoked. Its value
can vary from invocation to invocation. Formal parameters are parameters defined
in the method declaration (see Section 3.3, p. 44) and are local to the method (see Section 2.4, p. 36).
In Java, all parameters are passed by value, that is, an actual parameter is evaluated
and its value is assigned to the corresponding formal parameter. Table 3.1 summarizes the value that is passed depending on the type of the formal parameter. In the
case of primitive data types, the data value of the actual parameter is passed. If the
actual parameter is a reference to an object (i.e., instantiation of a class, enum, or
array), the reference value is passed and not the object itself. If the actual parameter
is an array element of a primitive data type, its data value is passed, and if the array
element is a reference to an object, then its reference value is passed.

82

CHAPTER 3: DECLARATIONS
Table 3.1 Parameter Passing By Value

Data Type of the Formal Parameters

Value Passed

Primitive data types

Primitive data value

Class or enum type

Reference value

Array type

Reference value

It should also be stressed that each invocation of a method has its own copies of the
formal parameters, as is the case for any local variables in the method (Section 6.5,
p. 235).
The order of evaluation in the actual parameter list is always from left to right. The
evaluation of an actual parameter can be influenced by an earlier evaluation of an
actual parameter. Given the following declaration:
int i = 4;

the method call
leftRight(i++, i);

is effectively the same as
leftRight(4, 5);

and not as
leftRight(4, 4);

Section 5.2, p. 164, provides an overview of conversions that can take place in a
method invocation context. Method invocation conversions for primitive values
are discussed in the next subsection (p. 82), and those for reference types are discussed in Section 7.10, p. 323. Calling variable arity methods and generic methods
is discussed in Section 3.8, p. 90, and in Section 14.8, p. 697, respectively.
For the sake of simplicity, the examples in subsequent sections primarily show
method invocation on the same object or the same class. The parameter passing
mechanism is no different when different objects or classes are involved.

Passing Primitive Data Values
An actual parameter is an expression that is evaluated first and the resulting value
is then assigned to the corresponding formal parameter at method invocation. The
use of this value in the method has no influence on the actual parameter. In particular, when the actual parameter is a variable of a primitive data type, the value of
the variable is copied to the formal parameter at method invocation. Since formal
parameters are local to the method, any changes made to the formal parameter will
not be reflected in the actual parameter after the call completes.
Legal type conversions between actual parameters and formal parameters of primitive data types are summarized here from Table 5.1, p. 163:

83

3.7: PARAMETER PASSING

• widening primitive conversion
• unboxing conversion, followed by an optional widening primitive conversion
These conversions are illustrated by invoking the following method
static void doIt(long i) { /* ... */ }

with the following code:
Integer intRef = 34;
Long longRef = 34L;
doIt(34);
// (1) Primitive widening conversion: long <-- int
doIt(longRef);
// (2) Unboxing: long <-- Long
doIt(intRef);
// (3) Unboxing, followed by primitive widening conversion:
//
long <-- int <-- Integer

However, for parameter passing, there are no implicit narrowing conversions for
integer constant expressions (see Section 5.2, p. 164).
Example 3.10 Passing Primitive Values
public class CustomerOne {
public static void main (String[] args) {
PizzaFactory pizzaHouse = new PizzaFactory();
int pricePrPizza = 15;
double totPrice = pizzaHouse.calcPrice(4, pricePrPizza);
System.out.println("Value of pricePrPizza: " + pricePrPizza);
}
}
class PizzaFactory {
public double calcPrice(int numberOfPizzas, double pizzaPrice) {
pizzaPrice = pizzaPrice/2.0;
// Changes price.
return numberOfPizzas * pizzaPrice;
}
}

// (1)
// Unchanged.

// (2)

Output from the program:
Value of pricePrPizza: 15

In Example 3.10, the method calcPrice() is defined in the class PizzaFactory at (2).
It is called from the CustomerOne.main() method at (1). The value of the first actual
parameter, 4, is copied to the int formal parameter numberOfPizzas. Note that the
second actual parameter pricePrPizza is of the type int, while the corresponding
formal parameter pizzaPrice is of the type double. Before the value of the actual
parameter pricePrPizza is copied to the formal parameter pizzaPrice, it is implicitly
widened to a double. The passing of primitive values is illustrated in Figure 3.3.
The value of the formal parameter pizzaPrice is changed in the calcPrice()
method, but this does not affect the value of the actual parameter pricePrPizza on

84

CHAPTER 3: DECLARATIONS
Figure 3.3 Parameter Passing: Primitive Data Values

Method Call
double totPrice = pizzaHouse.calcPrice(

4

Actual Parameters
, pricePrPizza

15

);

Primitive widening conversion
15.OD
Method Definition
public double calcPrice(int numberOfPizzas
pizzaPrice = pizzaPrice/2.0;
return numberOfPizzas * pizzaPrice;
}

4

Formal Parameters
, double pizzaPrice

15.OD

) {

return. It still has the value 15. The bottom line is that the formal parameter is a
local variable, and changing its value does not affect the value of the actual parameter.

Passing Reference Values
If the actual parameter expression evaluates to a reference value, the resulting reference value is assigned to the corresponding formal parameter reference at
method invocation. In particular, if an actual parameter is a reference to an object,
the reference value stored in the actual parameter is passed. This means that both
the actual parameter and the formal parameter are aliases to the object denoted by
this reference value during the invocation of the method. In particular, this implies
that changes made to the object via the formal parameter will be apparent after the
call returns.
Type conversions between actual and formal parameters of reference types are
discussed in Section 7.10, p. 323.
Example 3.11 Passing Reference Values
public class CustomerTwo {
public static void main (String[] args) {
Pizza favoritePizza = new Pizza();
// (1)
System.out.println("Meat on pizza before baking: " + favoritePizza.meat);
bake(favoritePizza);
// (2)
System.out.println("Meat on pizza after baking: " + favoritePizza.meat);
}
public static void bake(Pizza pizzaToBeBaked) {
// (3)
pizzaToBeBaked.meat = "chicken"; // Change the meat on the pizza.
pizzaToBeBaked = null;
// (4)
}
}
class Pizza {
String meat = "beef";
}

// (5)

85

3.7: PARAMETER PASSING

Output from the program:
Meat on pizza before baking: beef
Meat on pizza after baking: chicken

In Example 3.11, a Pizza object is created at (1). Any object of the class Pizza created
using the class declaration at (5) always results in a beef pizza. In the call to the
bake() method at (2), the reference value of the object referenced by the actual
parameter favoritePizza is assigned to the formal parameter pizzaToBeBaked in the
declaration of the bake() method at (3).
One particular consequence of passing reference values to formal parameters is
that any changes made to the object via formal parameters will be reflected back in
the calling method when the call returns. In this case, the reference favoritePizza
will show that chicken has been substituted for beef on the pizza. Setting the formal parameter pizzaToBeBaked to null at (4) does not change the reference value in
the actual parameter favoritePizza. The situation at method invocation, and just
before return from method bake(), is illustrated in Figure 3.4.
Figure 3.4 Parameter Passing: Reference Values

Actual Parameter
favoritePizza:Ref(Pizza)

:Pizza

Copying of reference
value creates aliases.

meat = "beef"

pizzaToBeBaked:Ref(Pizza)
Formal Parameter
(a) At Method Call
Actual Parameter
favoritePizza:Ref(Pizza)

After return, the actual parameter still denotes
the same object whose state has changed.

:Pizza
meat = "chicken"

pizzaToBeBaked:Ref(Pizza)
Formal Parameter
(b) Just before Return

86

CHAPTER 3: DECLARATIONS

In summary, the formal parameter can only change the state of the object whose
reference value was passed to the method.
The parameter passing strategy in Java is call-by-value and not call-by-reference,
regardless of the type of the parameter. Call-by-reference would have allowed
values in the actual parameters to be changed via formal parameters; that is, the
value in pricePrPizza to be halved in Example 3.10 and favoritePizza to be set to null
in Example 3.11. However, this cannot be directly implemented in Java.

Passing Arrays
The discussion on passing reference values in the previous section is equally
valid for arrays, as arrays are objects in Java. Method invocation conversions for
array types are discussed along with those for other reference types in Section
7.10, p. 323.
Example 3.12 Passing Arrays
public class Percolate {
public static void main (String[] args) {
int[] dataSeq = {6,4,8,2,1};
// Create and initialize an array.
// Write array before percolation:
printIntArray(dataSeq);
// Percolate:
for (int index = 1; index < dataSeq.length; ++index)
if (dataSeq[index-1] > dataSeq[index])
swap(dataSeq, index-1, index);

// (1)

// Write array after percolation:
printIntArray(dataSeq);
}
public static void swap(int[] intArray, int i, int j) {
// (2)
int tmp = intArray[i]; intArray[i] = intArray[j]; intArray[j] = tmp;
}

}

public static void swap(int v1, int v2) {
int tmp = v1; v1 = v2; v2 = tmp;
}

// (3)

public static void printIntArray(int[] array) {
for (int value : array)
System.out.print(" " + value);
System.out.println();
}

// (4)

87

3.7: PARAMETER PASSING

Output from the program:
6 4 8 2 1
4 6 2 1 8

In Example 3.12, the idea is to repeatedly swap neighboring elements in an integer
array until the largest element in the array percolates to the last position in the array.
Note that in the declaration of the method swap() at (2), the formal parameter
intArray is of the array type int[]. The swap() method is called in the main() method
at (1), where one of the actual parameters is the array variable dataSeq. The reference value of the array variable dataSeq is assigned to the array variable intArray at
method invocation. After return from the call to the swap() method, the array variable dataSeq will reflect the changes made to the array via the corresponding formal parameter. This situation is depicted in Figure 3.5 at the first call and return
from the swap() method, indicating how values of elements at indices 0 and 1 in the
array have been swapped.
However, the declaration of the swap() method at (3) will not swap two values. The
method call
swap(dataSeq[index-1], dataSeq[index]);

will have no effect on the array elements, as the swapping is done on the values of
the formal parameters.
Figure 3.5 Parameter Passing: Arrays

Actual Parameter
dataSeq:Ref(int[])
Formal Parameter
intArray:Ref(int[])

:int[]
[0]
[1]
[2]
[3]
[4]

8
4
6
2
1

(a) At first call to the swap() method

Actual Parameter
dataSeq:Ref(int[])
Formal Parameter
intArray:Ref(int[])

:int[]
[0]
[1]
[2]
[3]
[4]

4
8
6
2
1

(b) Just before first return from the swap() method

The method printIntArray() at (4) also has a formal parameter of array type int[].
Note that the formal parameter is specified as an array reference using the [] notation, but this notation is not used when an array is passed as an actual parameter.

Array Elements as Actual Parameters
Array elements, like other variables, can store values of primitive data types or reference values of objects. In the latter case, it means they can also be arrays, i.e.,
arrays of arrays (see Section 3.6, p. 74). If an array element is of a primitive data

88

CHAPTER 3: DECLARATIONS

type, its data value is passed, and if it is a reference to an object, the reference value
is passed. The method invocation conversions apply to the values of array elements as well.
Example 3.13 Array Elements as Primitive Data Values
public class FindMinimum {
public static void main(String[] args) {
int[] dataSeq = {6,4,8,2,1};
int minValue = dataSeq[0];
for (int index = 1; index < dataSeq.length; ++index)
minValue = minimum(minValue, dataSeq[index]);

// (1)

System.out.println("Minimum value: " + minValue);
}
public static int minimum(int i, int j) {
return (i <= j) ? i : j;
}

// (2)

}

Output from the program:
Minimum value: 1

In Example 3.13, note that the value of all but one element of the array dataSeq is
retrieved and passed consecutively at (1) to the formal parameter j of the minimum()
method defined at (2). The discussion in Section 3.7 on call-by-value also applies
to array elements that have primitive values.
Example 3.14 Array Elements as Reference Values
public class FindMinimumMxN {
public static void main(String[] args) {
int[][] matrix = { {8,4},{6,3,2},{7} };
int min = findMinimum(matrix[0]);
for (int i = 1; i < matrix.length; ++i) {
int minInRow = findMinimum(matrix[i]);
if (min > minInRow) min = minInRow;
}
System.out.println("Minimum value in matrix: " + min);

// (1)
// (2)
// (3)

}
public static int findMinimum(int[] seq) {
int min = seq[0];
for (int i = 1; i < seq.length; ++i)

// (4)

3.7: PARAMETER PASSING

89

min = Math.min(min, seq[i]);
return min;
}
}

Output from the program:
Minimum value in matrix: 2

In Example 3.14, note that the formal parameter seq of the findMinimum() method
defined at (4) is an array variable. The variable matrix denotes an array of arrays
declared at (1) simulating a multidimensional array, which has three rows, where
each row is a simple array. The first row, denoted by matrix[0], is passed to the
findMinimum() method in the call at (2). Each remaining row is passed by its reference value in the call to the findMinimum() method at (3).

final Parameters
A formal parameter can be declared with the keyword final preceding the parameter declaration in the method declaration. A final parameter is also known as a
blank final variable; that is, it is blank (uninitialized) until a value is assigned to it,
(e.g., at method invocation) and then the value in the variable cannot be changed
during the lifetime of the variable (see also the discussion in Section 4.10, p. 148).
The compiler can treat final variables as constants for code optimization purposes.
Declaring parameters as final prevents their values from being changed inadvertently. Whether a formal parameter is declared as final, does not affect the caller’s
code.
The declaration of the method calcPrice() from Example 3.10 is shown below, with
the formal parameter pizzaPrice declared as final.
public double calcPrice(int numberOfPizzas, final double pizzaPrice) { // (2’)
pizzaPrice = pizzaPrice/2.0;
// (3) Not allowed.
return numberOfPizzas * pizzaPrice;
}

If this declaration of the calcPrice() method is compiled, the compiler will not
allow the value of the final parameter pizzaPrice to be changed at (3) in the body
of the method.
As another example, the declaration of the method bake() from Example 3.11 is
shown below, with the formal parameter pizzaToBeBaked declared as final.
public static void bake(final Pizza pizzaToBeBaked) { // (3)
pizzaToBeBaked.meat = "chicken";
// (3a) Allowed.
pizzaToBeBaked = null;
// (4) Not allowed.
}

If this declaration of the bake() method is compiled, the compiler will not allow the
reference value of the final parameter pizzaToBeBaked to be changed at (4) in the

90

CHAPTER 3: DECLARATIONS

body of the method. Note that this applies to the reference value in the final parameter, not the object denoted by this parameter. The state of the object can be
changed as before, as shown at (3a).

3.8 Variable Arity Methods
A fixed arity method must be called with the same number of actual parameters
(also called arguments) as the number of formal parameters specified in its declaration. If the method declaration specifies two formal parameters, every call of this
method must specify exactly two arguments. We say that the arity of this method
is 2. In other words, the arity of such a method is fixed, and it is equal to the
number of formal parameters specified in the method declaration.
Java also allows declaration of variable arity methods; meaning that the number of
arguments in its call can be varied. As we shall see, invocations of such a method
may contain more actual parameters than formal parameters. Variable arity methods are heavily employed in formatting text representation of values (see Section
12.7, p. 593). The variable arity method System.out.printf() is used in many examples for this purpose.
The last formal parameter in a variable arity method declaration is declared as follows:
... 
The ellipsis (...) is specified between the  and the .
The  can be a primitive type, a reference type, or a type parameter.
Whitespace can be specified on both sides of the ellipsis. Such a parameter is usually called a varargs parameter.
Apart from the varargs parameter, a variable arity method is identical to a fixed
arity method. The method publish() below is a variable arity method:
public static void publish(int n, String... data) {
// (int, String[])
System.out.println("n: " + n + ", data size: " + data.length);
}

The varargs parameter in a variable arity method is always interpreted as having
the type:
[]
In the body of the publish() method, the varargs parameter data has the type
String[], i.e., a simple array of Strings.
Only one varargs parameter is permitted in the formal parameter list, and it is
always the last parameter in the formal parameter list. Given that the method declaration has n formal parameters, and the method call has k actual parameters, k
must be equal to or greater than n-1. The last k-n+1 actual parameters are evaluated
and stored in an array whose reference value is passed as the value of the actual

91

3.8: VARIABLE ARITY METHODS

parameter. In the case of the publish() method, n is equal to 2, so k can be 1, 2, 3,
and so on. The following invocations of the publish() method show what arguments are passed in each method call:
publish(1);
publish(2, "two");
publish(3, "two", "three");

// (1, new String[] {})
// (2, new String[] {"two"})
// (3, new String[] {"two", "three"})

Each method call results in an implicit array being created, and passed as argument. This array can contain zero or more argument values that do not correspond
to the formal parameters preceding the varargs parameter. This array is referenced
by the varargs parameter data in the method declaration. The calls above would
result in the publish() method printing:
n: 1, data size: 0
n: 2, data size: 1
n: 3, data size: 2

Calling a Varargs Method
Example 3.15 illustrates various aspects of calling a varargs method. The method
flexiPrint() in the VarargsDemo class has a varargs parameter:
public static void flexiPrint(Object... data) { // Object[]
//...
}

The varargs method prints the name of the Class object representing the actual array
that is passed. It prints the number of elements in this array, and also the text representation of each element in the array.
The method flexiPrint() is called in the main() method. First with the values of
primitive types and Strings ((1) to (8)), then it is called with the program arguments
supplied in the command line, ((9) to (11)).
Compiling the program results in a warning, which we ignore for the time being.
The program can still be run, as shown in Example 3.15. The numbers at the end of
the lines in the output relate to numbers in the code, and are not printed by the program.
Example 3.15 Calling a Varargs Method
public class VarargsDemo {
public static void flexiPrint(Object... data) { // Object[]
// Print the name of the Class object for the varargs parameter.
System.out.print("\nType: " + data.getClass().getName());
System.out.println("

No. of elements: " + data.length);

for(int i = 0; i < data.length; i++)

92

CHAPTER 3: DECLARATIONS
System.out.print(data[i] + " ");
if (data.length != 0)
System.out.println();
}
public static void main(String... args) {
int
day
= 1;
String month = "March";
int
year = 2009;
// Passing primitives and non-array types.
flexiPrint();
// (1)
flexiPrint(day);
// (2)
flexiPrint(day, month);
// (3)
//
flexiPrint(day, month, year);
// (4)
//
//
// Passing an array type.
Object[] dateInfo = {day,
month,
year};
flexiPrint(dateInfo);
flexiPrint((Object) dateInfo);
flexiPrint(new Object[] {dateInfo});

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

new Object[] {}
new Object[] {new Integer(day)}
new Object[] {new Integer(day),
month}
new Object[] {new Integer(day),
month,
new Integer(year)}

(5) new Object[] {new Integer(day),
month,
new Integer(year)}
(6) Non-varargs call
(7) new Object[] {(Object) dateInfo}
(8) Non-varargs call

// Explicit varargs or non-varargs call.
flexiPrint(args);
// (9) Warning!
flexiPrint((Object) args);
// (10) Explicit varargs call.
flexiPrint((Object[]) args);
// (11) Explicit non-varargs call
}
}

Compiling the program:
>javac VarargsDemo.java
VarargsDemo.java:39: warning: non-varargs call of varargs method with inexact
argument type for last parameter;
cast to java.lang.Object for a varargs call
cast to java.lang.Object[] for a non-varargs call and to suppress this warning
flexiPrint(args);
// (10) Warning!
^
1 warning

Running the program:
>java VarargsDemo To arg or not to arg
Type: [Ljava.lang.Object;

No. of elements: 0

(1)

Type: [Ljava.lang.Object;
1

No. of elements: 1

(2)

Type: [Ljava.lang.Object;
1 March

No. of elements: 2

(3)

93

3.8: VARIABLE ARITY METHODS
Type: [Ljava.lang.Object;
1 March 2009

No. of elements: 3

(4)

Type: [Ljava.lang.Object;
1 March 2009

No. of elements: 3

(6)

Type: [Ljava.lang.Object; No. of elements: 1
[Ljava.lang.Object;@1eed786

(7)

Type: [Ljava.lang.Object; No. of elements: 1
[Ljava.lang.Object;@1eed786

(8)

Type: [Ljava.lang.String;
To arg or not to arg

(9)

No. of elements: 6

Type: [Ljava.lang.Object; No. of elements: 1
[Ljava.lang.String;@187aeca

(10)

Type: [Ljava.lang.String;
To arg or not to arg

(11)

No. of elements: 6

Varargs and Non-Varargs Method Calls
The calls in (1) to (4) are all varargs calls, as an implicit Object array is created, in
which the values of the actual parameters are stored. The reference value of this
array is passed to the method. The printout shows that the type of the parameter
is actually an array of Objects ([Ljava.lang.Object;).
The call at (6) is different from the previous calls, in that the actual parameter is an
array that has the same type (Object[]) as the varargs parameter, without having to
create an implicit array. In such a case, no implicit array is created, and the reference value of the array dateInfo is passed to the method. See also the result from
this call at (6) in the output. The call at (6) is a non-varargs call, where no implicit
array is created:
flexiPrint(dateInfo);

// (6) Non-varargs call

However, if the actual parameter is cast to the type Object as in (7), a varargs call is
executed:
flexiPrint((Object) dateInfo);

// (7) new Object[] {(Object) dateInfo}

The type of the actual argument is now not the same as that of the varargs parameter, resulting in an array of the type Object[] being created, in which the array
dateInfo is stored as an element. The printout at (7) shows that only the text representation of the dateInfo array is printed, and not its elements, as it is the sole element of the implicit array.

94

CHAPTER 3: DECLARATIONS

The call at (8) is a non-varargs call, for the same reason as the call in (6), but now the
array dateInfo is explicitly stored as an element in an array of the type Object[] that
matches the type of the varargs parameter:
flexiPrint(new Object[] {dateInfo}); // (8) Non-varargs call

The compiler issues a warning for the call at (9):
flexiPrint(args);

// (9) Warning!

The actual parameter args is an array of the type String[], which is a subtype of
Object[]—the type of the varargs parameter. The array args can be passed in a nonvarargs call as an array of the type String[], or in a varargs call as an element in an
implicitly created array of the type Object[]. Both calls are feasible and valid in this
case. Note that the compiler chooses a non-varargs call rather than a varargs call,
but also issues a warning. The result at (9) confirms this course of action.
The array args of the type String[] is explicitly passed as an Object in a varargs call
at (10), similar to the call at (7):
flexiPrint((Object) args);

// (10) Explicit varargs call.

The array args of type String[] is explicitly passed as an array of the type Object[]
in a non-varargs call at (11). This call is equivalent to the call at (9), where the widening reference conversion is implicit, but now without a warning at compile time.
The two calls print the same information, as evident from the output at (9) and (11):
flexiPrint((Object[]) args);

// (11) Explicit non-varargs call

The compiler will complain if an attempt is made to overload the method flexiPrint() in the class VarargsDemo, as shown in the following code:
public static void flexiPrint(Object... data) { }
public static void flexiPrint(Object[] data) { }

// Compile-time error!
// Compile-time error!

These declarations would result in two methods with equivalent signatures in the
same class, if this was permitted. Overloading and overriding of methods with
varargs is discussed in Section 7.10, p. 324. The implications that generics have for
varargs are discussed in Section 14.13, p. 729.

3.9 The main() Method
The mechanics of compiling and running Java applications using the JDK are outlined in Section 1.10. The java command executes a method called main in the class
specified on the command line. Any class can have a main() method, but only the
main() method of the class specified in the java command is executed to start a Java
application.
The main() method must have public accessibility so that the interpreter can call
this method (see Section 4.9, p. 138). It is a static method belonging to the class, so
that no object of the class is required to start the execution (see Section 4.10, p. 147).

3.9: THE main() METHOD

95

It does not return a value, that is, it is declared void (see Section 6.4, p. 228). It
always has an array of String objects as its only formal parameter. This array contains any arguments passed to the program on the command line (see p. 95). The
following method header declarations fit the bill, and any one of them can be used
for the main() method:
public static void main(String[] args)
public static void main(String... args)

// Method header
// Method header

The above requirements do not exclude specification of additional modifiers (see
Section 4.10, p. 146) or any throws clause (see Section 6.9, p. 257). The main() method
can also be overloaded like any other method (see Section 3.3, p. 47). The JVM
ensures that the main() method having the above method header is the starting
point of program execution.

Program Arguments
Any arguments passed to the program on the command line can be accessed in the
main() method of the class specified on the command line:
>java Colors red green blue

These arguments are called program arguments. Note that the command name, java,
and the class name Colors are not passed to the main() method of the class Colors,
nor are any other options that are specified in the command line.
Since the formal parameter of the main() method is an array of String objects, individual String elements in the array can be accessed by using the [] operator.
In Example 3.16, the three arguments "red", "green", and "blue" can be accessed in
the main() method of the Colors class as args[0], args[1], and args[2], respectively.
The total number of arguments is given by the field length of the String array args.
Note that program arguments can only be passed as strings, and must be explicitly
converted to other values by the program, if necessary.
When no arguments are specified on the command line, an array of zero String elements is created and passed to the main() method. This means that the reference
value of the formal parameter in the main() method is never null.
Program arguments supply information to the application, which can be used to
tailor the runtime behavior of the application according to user requirements.
Example 3.16 Passing Program Arguments
public class Colors {
public static void main(String[] args) {
System.out.println("No. of program arguments: " + args.length);
for (int i = 0; i < args.length; i++)
System.out.println("Argument no. " + i + " (" + args[i] + ") has " +
args[i].length() + " characters.");
}
}

96

CHAPTER 3: DECLARATIONS

Running the program:
>java Colors red green blue
No. of program arguments: 3
Argument no. 0 (red) has 3 characters.
Argument no. 1 (green) has 5 characters.
Argument no. 2 (blue) has 4 characters.

Review Questions
3.24

What will be printed when the following program is run?
public class ParameterPass {
public static void main(String[] args) {
int i = 0;
addTwo(i++);
System.out.println(i);
}
static void addTwo(int i) {
i += 2;
}
}

Select the one correct answer.
(a)
(b)
(c)
(d)
3.25

0
1
2
3

What will be the result of compiling and running the following program?
public class Passing
public static void
int a = 0; int b
int[] bArr = new

{
main(String[] args) {
= 0;
int[1]; bArr[0] = b;

inc1(a); inc2(bArr);
System.out.println("a=" + a + " b=" + b + " bArr[0]=" + bArr[0]);
}
public static void inc1(int x) { x++; }
public static void inc2(int[] x) { x[0]++; }
}

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

The code will fail to compile, since x[0]++; is not a legal statement.
The code will compile and will print "a=1 b=1 bArr[0]=1", when run.
The code will compile and will print "a=0 b=1 bArr[0]=1", when run.
The code will compile and will print "a=0 b=0 bArr[0]=1", when run.
The code will compile and will print "a=0 b=0 bArr[0]=0", when run.

3.9: THE main() METHOD

3.26

97

Which statements, when inserted at (1), will cause a compilation error?
public class ParameterUse {
static void main(String[] args) {
int a = 0;
final int b = 1;
int[] c = { 2 };
final int[] d = { 3 };
useArgs(a, b, c, d);
}
static void useArgs(final int a, int b, final int[] c, int[] d) {
// (1) INSERT STATEMENT HERE.
}
}

Select the two correct answers.
(a) a++;
(b) b++;
(c) b = a;
(d) c[0]++;
(e) d[0]++;
(f) c = d;
3.27

Which method declarations are valid declarations?
Select the three correct answers.
(a) void compute(int... is) { }
(b) void compute(int is...) { }
(c) void compute(int... is, int i, String... ss) { }
(d) void compute(String... ds) { }
(e) void compute(String... ss, int len) { }
(f) void compute(char[] ca, int... is) { }

3.28

Given the following code:
public class RQ800_40 {
static void print(Object... obj) {
System.out.println("Object...: " + obj[0]);
}
public static void main(String[] args) {
// (1) INSERT METHOD CALL HERE.
}
}

Which method call, when inserted at (1), will not result in the following output
from the program:
Object...: 9

Select the one correct answer.
(a) print("9", "1", "1");
(b) print(9, 1, 1);

98

CHAPTER 3: DECLARATIONS

(c)
(d)
(e)
(f)
3.29

print(new
print(new
print(new
print(new

int[] {9, 1, 1});
Integer[] {9, 1, 1});
String[] {"9", "1", "1"});
Object[] {"9", "1", "1"});

What will be the result of compiling and running the following program?
public class RQ800_20 {
static void compute(int... is) {
System.out.print("|");
for(int i : is) {
System.out.print(i + "|");
}
System.out.println();
}
static void compute(int[] ia, int... is) {
compute(ia);
compute(is);
}
static void compute(int[] inta, int[]... is) {
for(int[] ia : is) {
compute(ia);
}
}
public static void main(String[] args) {
compute(new int[] {10, 11}, new int[] {12, 13, 14});
compute(15, 16);
compute(new int[] {17, 18}, new int[][] {{19}, {20}});
compute(null, new int[][] {{21}, {22}});
}
}

// (1)

// (2)

// (3)

//
//
//
//

(4)
(5)
(6)
(7)

Select the one correct answer.
(a) The program does not compile because of errors in one or more calls to the
compute() method.
(b) The program compiles, but throws a NullPointerException when run.
(c) The program compiles and prints:
|10|11|
|12|13|14|
|15|16|
|19|
|20|
|21|
|22|

(d) The program compiles and prints:
|12|13|14|
|15|16|
|10|11|
|19|
|20|
|21|
|22|

3.9: THE main() METHOD

3.30

Which of these method declarations are valid declarations of the main() method
that would be called by the JVM in order to start the execution of a Java application?
Select the three correct answers.
(a) static void main(String[] args) { /* ... */ }
(b) public static int main(String[] args) { /* ... */ }
(c) public static void main(String args) { /* ... */ }
(d) final public static void main(String[] arguments) { /* ... */ }
(e) public int main(Strings[] args, int argc) { /* ... */ }
(f) static public void main(String args[]) { /* ... */ }
(g) static public void main(String... args) { /* ... */ }

3.31

Which of the following are reserved keywords?
Select the three correct answers.
(a) public
(b) static
(c) void
(d) main
(e) String
(f) args

3.32

Given the class
// File name: Args.java
public class Args {
public static void main(String[] args) {
System.out.println(args[0] + " " + args[args.length-1]);
}
}

what would be the result of executing the following command line?
>java Args In politics stupidity is not a handicap

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
(f)
3.33

99

The program will throw an ArrayIndexOutOfBoundsException.
The program will print "java handicap".
The program will print "Args handicap".
The program will print "In handicap".
The program will print "Args a".
The program will print "In a".

Which statement about the following program is true?
class MyClass {
public static void main(String[] args) {
String[] numbers = { "one", "two", "three", "four" };
if (args.length == 0) {

100

CHAPTER 3: DECLARATIONS
System.out.println("no arguments");
} else {
System.out.println(numbers[ args.length ] + " arguments");
}
}
}

Select the one correct answer.
(a) The program will fail to compile.
(b) The program will throw a NullPointerException when run with no program
arguments.
(c) The program will print "no arguments" and "two arguments" when called with
zero and three program arguments, respectively.
(d) The program will print "no arguments" and "three arguments" when called
with zero and three program arguments, respectively.
(e) The program will print "no arguments" and "four arguments" when called with
zero and three program arguments, respectively.
(f) The program will print "one arguments" and "four arguments" when called
with zero and three program arguments, respectively.

Chapter Summary
The following information was included in this chapter:
• overview of declarations that can be specified in a class
• understanding pattern names for properties and the event model in the JavaBeans standard
• defining methods, usage of the this reference in an instance method, and
method overloading
• defining constructors, usage of the default constructor, and overloading of
constructors
• declaring and using enum types, and extending them implicitly
• explanation of declaration, construction, initialization, and usage of both oneand multi-dimensional arrays, including anonymous arrays
• parameter passing, both primitive values and object references, including
arrays and array elements; and declaring final parameters
• declaring and calling methods with varargs
• declaration of the main() method whose execution starts the application
• passing program arguments to the main() method

101

PROGRAMMING EXERCISES

Programming Exercises
3.1

Imagine you are creating an application that has a number of different tools a
user may invoke. These tools need a special context to work in. The context
describes the current active selection in the application. The selection consists of
a reference to an arbitrary object. We wish to create a JavaBean representing an
editing context that the tools may use. The JavaBean should contain the aforementioned selection reference. We do not want to allow direct manipulation of
the reference, but want to have methods in the editing context that allow anyone
to get and set the current selection.
Write such a JavaBean. Be sure to get the accessibility right.

3.2

Write a program to grade a short multiple-choice quiz. The correct answers for
the quiz are:
1.
2.
3.
4.

C
A
B
D

5.
6.
7.
8.

B
C
C
A

Assume that the pass marks are 5 out of 8. The program stores the correct
answers in an array. The submitted answers are specified as program arguments. Let X represent a question that was not answered on the quiz. Use an
enum type to represent the result of answering a question.
The program calculates and prints a report along the following lines:
Question Submitted Ans. Correct Ans. Result
1
C
C
CORRECT
2
B
A
WRONG
3
B
B
CORRECT
4
D
D
CORRECT
5
B
B
CORRECT
6
C
C
CORRECT
7
A
C
WRONG
8
X
A
UNANSWERED
No. of correct answers:
5
No. of wrong answers:
2
No. of questions unanswered: 1
The candidate PASSED.

This page intentionally left blank

Access Control

4

Exam Objectives
1.1 Develop code that declares classes (including abstract and all forms of
nested classes), interfaces, and enums, and includes the appropriate use of
package and import statements (including static imports).
❍

The package and import statements are covered in this chapter.

❍

For class declarations, see Section 3.1, p. 40.

❍

For abstract classes, see Section 4.8, p. 135.

❍

For nested classes, see Chapter 8, p. 351.

❍

For interfaces, see Section 7.6, p. 309.

❍ For enums, see Section 3.5, p. 54.
7.1 Given a code example and a scenario, write code that uses the appropriate
access modifiers, package declarations, and import statements to interact
with (through access or inheritance) the code in the example.
7.5 Given the fully-qualified name of a class that is deployed inside and/or
outside a JAR file, construct the appropriate directory structure for that
class. Given a code example and a classpath, determine whether the
classpath will allow the code to compile successfully.

Supplementary Objectives
• Creating JAR files.
• Using system properties.

103

104

CHAPTER 4: ACCESS CONTROL

4.1 Java Source File Structure
The structure of a skeletal Java source file is depicted in Figure 4.1. A Java source
file can have the following elements that, if present, must be specified in the following order:
1.
2.

3.

An optional package declaration to specify a package name. Packages are discussed in Section 4.2.
Zero or more import declarations. Since import declarations introduce type or
static member names in the source code, they must be placed before any type
declarations. Both type and static import statements are discussed in Section 4.2.
Any number of top-level type declarations. Class, enum, and interface declarations are collectively known as type declarations. Since these declarations
belong to the same package, they are said to be defined at the top level, which
is the package level.
The type declarations can be defined in any order. Technically, a source file
need not have any such declaration, but that is hardly useful.
The JDK imposes the restriction that at the most one public class declaration
per source file can be defined. If a public class is defined, the file name must
match this public class. If the public class name is NewApp, the file name must be
NewApp.java.
Classes are discussed in Section 3.1, p. 40, and interfaces are discussed in Section 7.6, p. 309.

Note that except for the package and the import statements, all code is encapsulated
in classes and interfaces. No such restriction applies to comments and white space.
Figure 4.1

Java Source File Structure
// Filename: NewApp.java
// PART 1: (OPTIONAL) package declaration
package com.company.project.fragilePackage;
// PART 2: (ZERO OR MORE) import declarations
import java.io.*;
import java.util.*;
import static java.lang.Math.*;
// PART 3: (ZERO OR MORE) top-level class and interface declarations
public class NewApp { }
class A { }
interface IX { }
class B { }
interface IY { }
enum C { FIRST, SECOND, THIRD }
// end of file

105

4.2: PACKAGES

4.2 Packages
A package in Java is an encapsulation mechanism that can be used to group related
classes, interfaces, enums, and subpackages.
Figure 4.2 shows an example of a package hierarchy, comprising a package called
wizard that contains two other packages: pandorasBox and spells. The package
pandorasBox has a class called Clown that implements an interface called Magic, also
found in the same package. In addition, the package pandorasBox has a class called
LovePotion and a subpackage called artifacts containing a class called Ailment. The
package spells has two classes: Baldness and LovePotion. The class Baldness is a subclass of class Ailment found in the subpackage artifacts in the package pandorasBox.
The dot (.) notation is used to uniquely identify package members in the package
hierarchy. The class wizard.pandorasBox.LovePotion is different from the class
wizard.spells.LovePotion. The Ailment class can be easily identified by the name
wizard.pandorasBox.artifacts.Ailment. This is called the fully qualified name of the
type. Note that the fully qualified name of the type in a named package comprises
the fully qualified name of the package and the simple name of the type. The simple
type name Ailment and the fully qualified package name wizard.pandorasBox.artifacts
together define the fully qualified type name wizard.pandorasBox.artifacts.Ailment.
Analogously, the fully qualified name of a subpackage comprises the fully qualified
name of the parent package and the simple name of the subpackage.
Java programming environments usually map the fully qualified name of packages
to the underlying (hierarchical) file system. For example, on a Unix system, the class
file LovePotion.class corresponding to the fully qualified name wizard.pandorasBox.LovePotion would be found under the directory wizard/pandorasBox.
Figure 4.2

Package Hierarchy
wizard
pandorasBox

«interface»
Magic

spells

LovePotion
artifacts

Clown
Ailment

Baldness

LovePotion

106

CHAPTER 4: ACCESS CONTROL

Conventionally, a global naming scheme based on the Internet domain names is
used to uniquely identify packages. If the above package wizard was implemented
by a company called Sorcerers Limited that owns the domain sorcerersltd.com, its
fully qualified name would be:
com.sorcerersltd.wizard

Because domain names are unique, packages with this naming scheme are globally
identifiable. It is not advisable to use the top-level package names java and sun, as
these are reserved for the Java designers.
The subpackage wizard.pandorasBox.artifacts could easily have been placed elsewhere, as long as it was uniquely identified. Subpackages in a package do not
affect the accessibility of the other package members. For all intents and purposes,
subpackages are more an organizational feature rather than a language feature.
Accessibility of members in a package is discussed in Section 4.6. Accessibility of
members defined in type declarations is discussed in Section 4.9.

Defining Packages
A package hierarchy represents an organization of the Java classes and interfaces.
It does not represent the source code organization of the classes and interfaces. The
source code is of no consequence in this regard. Each Java source file (also called
compilation unit) can contain zero or more type declarations, but the compiler produces a separate class file containing the Java byte code for each of them. A type
declaration can indicate that its Java byte code be placed in a particular package,
using a package declaration.
The package statement has the following syntax:
package ;

At most one package declaration can appear in a source file, and it must be the first
statement in the source file. The package name is saved in the Java byte code for
the types contained in the package.
Note that this scheme has two consequences. First, all the classes and interfaces in
a source file will be placed in the same package. Second, several source files can be
used to specify the contents of a package.
If a package declaration is omitted in a compilation unit, the Java byte code for the
declarations in the compilation unit will belong to an unnamed package (also called
the default package), which is typically synonymous with the current working directory on the host system.
Example 4.1 illustrates how the packages in Figure 4.2 can be defined using the
package declaration. There are four compilation units. Each compilation unit has a
package declaration, ensuring that the type declarations are compiled into the cor-

rect package. The complete code can be found in Example 4.10 on page 133.

107

4.2: PACKAGES

Example 4.1

Defining Packages and Using Type Import
//File: Clown.java
package wizard.pandorasBox;

// (1) Package declaration

import wizard.pandorasBox.artifacts.Ailment; // (2) Importing class
public class Clown implements Magic { /* ... */ }
interface Magic { /* ... */ }

//File: LovePotion.java
package wizard.pandorasBox;

// (1) Package declaration

public class LovePotion { /* ... */ }

//File: Ailment.java
package wizard.pandorasBox.artifacts;

// (1) Package declaration

public class Ailment { /* ... */ }

//File: Baldness.java
package wizard.spells;

// (1)Package declaration

import wizard.pandorasBox.*;
import wizard.pandorasBox.artifacts.*;

// (2) Type-import-on-demand
// (3) Import from subpackage

public class Baldness extends Ailment {
wizard.pandorasBox.LovePotion tlcOne;
LovePotion tlcTwo;
// ...
}

// (4) Abbreviated name for Ailment
// (5) Fully qualified name
// (6) Class in same package

class LovePotion { /* ... */ }

Using Packages
The import facility in Java makes it easier to use the contents of packages. This subsection discusses importing reference types and static members of reference types from
packages.

Importing Reference Types
The accessibility of types (classes and interfaces) in a package determines their access
from other packages. Given a reference type that is accessible from outside a package, the reference type can be accessed in two ways. One way is to use the fully qualified name of the type. However, writing long names can become tedious. The

108

CHAPTER 4: ACCESS CONTROL

second way is to use the import declaration that provides a shorthand notation for
specifying the name of the type, often called type import.
The import declarations must be the first statement after any package declaration in
a source file. The simple form of the import declaration has the following syntax:
import

;

This is called single-type-import. As the name implies, such an import declaration
provides a shorthand notation for a single type. The simple name of the type (that
is, its identifier) can now be used to access this particular type. Given the following
import declaration:
import wizard.pandorasBox.Clown;

the simple name Clown can be used in the source file to refer to this class.
Alternatively, the following form of the import declaration can be used:
import

.*;

This is called type-import-on-demand. It allows any type from the specified package
to be accessed by its simple name.
An import declaration does not recursively import subpackages. The declaration
also does not result in inclusion of the source code of the types. The declaration
only imports type names (that is, it makes type names available to the code in a
compilation unit).
All compilation units implicitly import the java.lang package (see Section 10.1, p.
424). This is the reason why we can refer to the class String by its simple name, and
need not use its fully qualified name java.lang.String all the time.
Example 4.1 shows several usages of the import statement. Here we will draw
attention to the class Baldness in the file Baldness.java. This class relies on two
classes that have the same simple name LovePotion but are in different packages:
wizard.pandorasBox and wizard.spells, respectively. To distinguish between the two
classes, we can use their fully qualified names. However, since one of them is in the
same package as the class Baldness, it is enough to fully qualify the class from the
other package. This solution is used in Example 4.1 at (5). Such name conflicts can
usually be resolved by using variations of the import statement together with fully
qualified names.
The class Baldness extends the class Ailment, which is in the subpackage artifacts
of the wizard.pandorasBox package. The import declaration at (3) is used to import
the types from the subpackage artifacts.
The following example shows how a single-type-import declaration can be used to
disambiguate a type name when access to the type is ambiguous by its simple
name. The following import statement allows the simple name List as shorthand
for the java.awt.List type as expected:
import java.awt.*;

// imports all reference types from java.awt

109

4.2: PACKAGES

Given the following two import declarations:
import java.awt.*;
import java.util.*;

// imports all type names from java.awt
// imports all type names from java.util

the simple name List is now ambiguous as both the types java.util.List and
java.awt.List match.
Adding a single-type-import declaration for the java.awt.List type last allows the
simple name List as a shorthand notation for this type:
import java.awt.*;
import java.util.*;
import java.awt.List;

// imports all type names from java.awt
// imports all type names from java.util
// imports the type List from java.awt explicitly

Importing Static Members of Reference Types
Analogous to the type import facility, Java also allows import of static members of
reference types from packages, often called static import.
Static import allows accessible static members (static fields, static methods, static
member classes, enum, and interfaces) declared in a type to be imported, so that
they can be used by their simple name, and therefore need not be qualified. The
import applies to the whole compilation unit, and importing from the unnamed
package is not permissible.
The two forms of static import are shown below:
// Single-static-import: imports a specific static member from the designated type
import static .;
// Static-import-on-demand: imports all static members in the designated type
import static .*;

Both forms require the use of the keyword static. In both cases, the fully qualified
name of the reference type we are importing from is required.
The first form allows single static import of individual static members, and is demonstrated in Example 4.2. The constant PI, which is a static field in the class
java.lang.Math, is imported at (1). Note the use of the fully qualified name of the
type in the static import statement. The static method named sqrt from the class
java.lang.Math is imported at (2). Only the name of the static method is specified in
the static import statement. No parameters are listed. Use of any other static member from the Math class requires that the fully qualifying name of the class be specified. Since types from the java.lang package are imported implicitly, the fully
qualified name of the Math class is not necessary, as shown at (3).
Static import on demand is easily demonstrated by replacing the two import statements in Example 4.2 by the following import statement:
import static java.lang.Math.*;

We can also dispense with the use of the class name Math in (3), as all static members
from the Math class are now imported:
double hypotenuse = hypot(x, y);

// (3’) Type name can now be omitted.

110

Example 4.2

CHAPTER 4: ACCESS CONTROL

Single Static Import
import static java.lang.Math.PI;
// (1) Static field
import static java.lang.Math.sqrt;
// (2) Static method
// Only specified static members are imported.
public class CalculateI {
public static void main(String[] args) {
double x = 3.0, y = 4.0;
double squareroot = sqrt(y);
// Simple name of static method
double hypotenuse = Math.hypot(x, y); // (3) Requires type name.
double area = PI * y * y;
// Simple name of static field
System.out.printf("Square root: %.2f, hypotenuse: %.2f, area: %.2f%n",
squareroot, hypotenuse, area);
}
}

Output from the program:
Square root: 2.00, hypotenuse: 5.00, area: 50.27

Using static import avoids the interface constant antipattern, as illustrated in Example 4.3. The static import statement at (1) allows the interface constants in the package mypkg to be accessed by their simple names. The static import facility avoids the
MyFactory class having to implement the interface in order to access the constants by
their simple name:
public class MyFactory implements mypkg.IMachineState {
// ...
}

Example 4.3

Avoiding the Interface Constant Antipattern
package mypkg;
public interface IMachineState {
// Fields are public, static and final.
int BUSY = 1;
int IDLE = 0;
int BLOCKED = -1;
}

import static mypkg.IMachineState.*;

// (1) Static import interface constants

public class MyFactory {
public static void main(String[] args) {
int[] states = { IDLE, BUSY, IDLE, BLOCKED };
for (int s : states)
System.out.print(s + " ");
}
}

111

4.2: PACKAGES

Output from the program:
0 1 0 -1

Static import is ideal for importing enum constants from packages, as such constants are static members of an enum type. Example 4.4 combines type and static
import. The enum constants can be accessed at (4) using their simple names
because of the static import statement at (2). The type import at (1) is required to
access the enum type State by its simple name at (5).
Example 4.4

Importing Enum Constants
package mypkg;
public enum State { BUSY, IDLE, BLOCKED }

import mypkg.State;

// (1) Single type import

import static mypkg.State.*;
import static java.lang.System.out;

// (2) Static import on demand
// (3) Single static import

public class Factory {
public static void main(String[] args) {
State[] states = {
IDLE, BUSY, IDLE, BLOCKED
// (4) Using static import implied by (2).
};
for (State s : states)
// (5) Using type import implied by (1).
out.print(s + " ");
// (6) Using static import implied by (3).
}
}

Output from the program:
IDLE BUSY IDLE BLOCKED

Identifiers in a class can shadow static members that are imported. Example 4.5
illustrates the case where the parameter out of the method writeInfo() has the same
name as the statically imported field java.lang.System.out. The type of the parameter is PrintWriter, and that of the statically imported field is PrintStream. Both
classes PrintStream and PrintWriter define the method println() that is called in the
program. The only way to access the imported field in the method writeInfo() is to
use its fully qualified name.

112

Example 4.5

CHAPTER 4: ACCESS CONTROL

Shadowing by Importing
import static java.lang.System.out;

// (1) Static import

import java.io.FileNotFoundException;
import java.io.PrintWriter;

// (2) Single type import

public class ShadowingByImporting {
public static void main(String[] args) throws FileNotFoundException {
out.println("Calling println() in java.lang.System.out");
PrintWriter pw = new PrintWriter("log.txt");
writeInfo(pw);
pw.flush();
pw.close();
}
public static void writeInfo(PrintWriter out) { // Shadows java.lang.System.out
out.println("Calling println() in the parameter out");
System.out.println("Calling println() in java.lang.System.out"); // Qualify
}
}

Output from the program:
Calling println() in java.lang.System.out
Calling println() in java.lang.System.out

Contents of the file log.txt:
Calling println() in the parameter out

Conflicts can also occur when a static method with the same signature is imported
by several static import statements. In Example 4.6, a method named binarySearch
is imported 21 times by the static import statements. This method is overloaded
twice in the java.util.Collections class and 18 times in the java.util.Arrays class,
in addition to one declaration in the mypkg.Auxiliary class. The classes
java.util.Arrays and mypkg.Auxiliary have a declaration of this method with the
same signature that matches the method call at (2), resulting in a signature conflict.
The conflict can again be resolved by specifying the fully qualified name of the
method.
If the static import statement at (1) is removed, there is no conflict, as only the class
java.util.Arrays has a method that matches the method call at (2). If the declaration of the method binarySearch() at (3) is allowed, there is also no conflict, as this

method declaration will shadow the imported method whose signature it matches.

113

4.2: PACKAGES

Example 4.6

Conflict in Importing Static Method with the Same Signature
package mypkg;
public class Auxiliary {
public static int binarySearch(int[] a, int key) { // Same in java.util.Arrays.
// Implementation is omitted.
return -1;
}
}

import static java.util.Collections.binarySearch; //
2 overloaded methods
import static java.util.Arrays.binarySearch;
// + 18 overloaded methods
import static mypkg.Auxiliary.binarySearch; // (1) Causes signature conflict.
class MultipleStaticImport {
public static void main(String[] args) {
int index = binarySearch(new int[] {10, 50, 100}, 50); // (2) Not ok!
System.out.println(index);
}
//
//
//
}

public static int binarySearch(int[] a, int key) {
return -1;
}

// (3)

Example 4.6 illustrates importing nested static types (Section 8.2, p. 355). The class
yap.Machine declares three static members, which all are types. Since these nested
members are types that are static, they can be imported both as types and as static
members. The class MachineClient uses the static types declared in the yap.Machine
class. The program shows how the import statements influence which types and
members are accessible. The following statement in the main() method declared at
(10) does not compile:
String s1 = IDLE;

// Ambiguous because of (3) and (6)

because the constant IDLE is imported from both the static class StateConstant and
the enum type MachineState by the following import statements:
import static yap.Machine.StateConstant.*;
...
import static yap.Machine.MachineState.*;

// (3)
// (6)

Similarly, the following statement in the main() method is also not permitted:
MachineState ms1 = BLOCKED;

// Ambiguous because of (3) and (6)

The conflicts are resolved by qualifying the member just enough to make the
names unambiguous.

114

Example 4.7

CHAPTER 4: ACCESS CONTROL

Importing Nested Static Types
package yap;

// yet another package

public class Machine {

// Class with 3 nested types

public static class StateConstant {
// A static member class
public static final String BUSY = "Busy";
public static final String IDLE = "Idle";
public static final String BLOCKED = "Blocked";
}
public enum MachineState {
BUSY, IDLE, BLOCKED
}

// A nested enum is static.

public enum AuxMachineState {
UNDER_REPAIR, WRITE_OFF, HIRED, AVAILABLE;
}

// Another static enum

}

import yap.Machine;

// (0)

import yap.Machine.StateConstant;
import static yap.Machine.StateConstant;
import static yap.Machine.StateConstant.*;

// (1)
// (2) Superfluous because of (1)
// (3)

import yap.Machine.MachineState;
import static yap.Machine.MachineState;
import static yap.Machine.MachineState.*;

// (4)
// (5) Superfluous because of (4)
// (6)

import
import
import
import

yap.Machine.AuxMachineState;
// (7)
static yap.Machine.AuxMachineState;
// (8) Superfluous because of (7)
static yap.Machine.AuxMachineState.*; // (9)
static yap.Machine.AuxMachineState.WRITE_OFF; // (10)

public class MachineClient {
public static void main(String[] args) {

// (10)

StateConstant msc = new StateConstant(); // Requires (1) or (2)
//String s1 = IDLE;
// Ambiguous because of (3) and (6)
String s2 = StateConstant.IDLE;
// Explicit disambiguation necessary.
//MachineState ms1 = BLOCKED;
// Ambiguous because of (3) and (6)
MachineState ms2 = MachineState.BLOCKED; // Requires (4) or (5)
MachineState ms3 = MachineState.IDLE;
// Explicit disambiguation necessary.
AuxMachineState[] states = {
AVAILABLE, HIRED, UNDER_REPAIR,
WRITE_OFF,
AuxMachineState.WRITE_OFF,
Machine.AuxMachineState.WRITE_OFF,
yap.Machine.AuxMachineState.WRITE_OFF
};

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

Requires
Requires
Requires
Requires
Requires
Does not

(7) or (8)
(9)
(9) or (10)
(7) or (8)
(0)
require any import

115

4.2: PACKAGES
for (AuxMachineState s : states)
System.out.print(s + " ");
}
}

Output from the program:
AVAILABLE HIRED UNDER_REPAIR WRITE_OFF WRITE_OFF WRITE_OFF WRITE_OFF

Compiling Code into Packages
In this chapter, we will use pathname conventions used on a Unix platform. See
Section 11.2, p. 468, for a discussion on pathnames and conventions for specifying
pathnames on different platforms. While trying out the examples in this section,
attention should be paid to platform-dependencies in this regard. Particularly, the
fact that the separator character in a file path for the Unix and Windows platform is
'/' and '\', respectively.
As mentioned earlier, a package can be mapped on a hierarchical file system. We
can think of a package name as a pathname in the file system. Referring to Example
4.1, the package name wizard.pandorasBox corresponds to the pathname wizard/pandorasBox. The Java byte code for all types declared in the source files Clown.java and
LovePotion.java will be placed in the package directory with the pathname wizard/
pandorasBox, as these source files have the following package declaration:
package wizard.pandorasBox;

The location in the file system where the package directory should be created is
specified using the -d option (d for destination) of the javac command. The term destination directory is a synonym for this location in the file system. The compiler will
create the package directory with the pathname wizard/pandorasBox (including any
subdirectories required) under the specified location, and place the Java byte code
for the types declared in the source files Clown.java and LovePotion.java inside the
package directory.
Assuming that the current directory (.) is the directory /pgjc/work, and the four
source files in Example 4.1 are to be found in this directory, the command
>javac -d . Clown.java LovePotion.java Ailment.java Baldness.java

issued in the current directory will create a file hierarchy under this directory,
that mirrors the package hierarchy in Figure 4.2 (see also Figure 4.3). Note the
subdirectories that are created for a fully qualified package name, and where the
class files are located. In the command line above, space between the -d option
and its argument is mandatory.
We can specify any relative pathname that designates the destination directory, or
its absolute pathname:
>javac -d /pgjc/work Clown.java LovePotion.java Ailment.java Baldness.java

116

CHAPTER 4: ACCESS CONTROL

We can, of course, specify other destinations than the current directory where the
class files with the byte code should be stored. The following command
>javac -d ../myapp Clown.java LovePotion.java Ailment.java Baldness.java

in the current directory /pgjc/work will create the necessary packages with the class
files under the destination directory /pgjc/myapp.
Without the -d option, the default behavior of the javac compiler is to place all class
files directly under the current directory (where the source files are located), rather
than in the appropriate subdirectories corresponding to the packages.
Figure 4.3

File Hierarchy
/pgjc
work
Clown.java
LovePotion.java
Ailment.java
Baldness.java
wizard
pandorasBox
Magic.class
Clown.class
LovePotion.class
artifacts
Ailment.class
spells
Baldness.class
LovePotion.class

The compiler will report an error if there is any problem with the destination directory specified with the -d option (e.g., if it does not exist or does not have the right
file permissions).

4.3: SEARCHING FOR CLASSES

117

Running Code from Packages
Referring to Example 4.1, if the current directory has the absolute pathname /pgjc/
work and we want to run Clown.class in the directory with the pathname ./wizard/
pandorasBox, the fully qualified name of the Clown class must be specified in the java
command
>java wizard.pandorasBox.Clown

This will load the class Clown from the byte code in the file with the pathname ./
wizard/pandorasBox/Clown.class, and start the execution of its main() method.

4.3 Searching for Classes
The documentation for the JDK tools explains how to organize packages in more
elaborate schemes. In particular, the CLASSPATH environment variable can be used to
specify the class search path (usually abbreviated to just class path), which are
pathnames or locations in the file system where JDK tools should look when searching for classes and other resource files. Alternatively, the -classpath option (often
abbreviated to -cp) of the JDK tool commands can be used for the same purpose.
The CLASSPATH environment variable is not recommended for this purpose, as its
class path value affects all Java applications on the host platform, and any application can modify it. However, the -cp option can be used to set the class path for
each application individually. This way, an application cannot modify the class
path for other applications. The class path specified in the -cp option supersedes
the path or paths set by the CLASSPATH environment variable while the JDK tool
command is running. We will not discuss the CLASSPATH environment variable here,
and assume it to be undefined.
Basically, the JDK tools first look in the directories where the Java standard libraries are installed. If the class is not found in the standard libraries, the tool searches
in the class path. When no class path is defined, the default value of the class path
is assumed to be the current directory. If the -cp option is used and the current
directory should be searched by the JDK tool, the current directory must be specified as an entry in the class path, just like any other directory that should be
searched. This is most conveniently done by including '.' as one of the entries in
the class path.
We will use the file hierarchies shown in Figure 4.4 to illustrate some of the intricacies involved when searching for classes. The current directory has the absolute
pathname /top/src, where the source files are stored. The package pkg is stored
under the directory with the absolute pathname /top/bin. The source code in the
two source files A.java and B.java is also shown in Figure 4.4.
The file hierarchy before any files are compiled is shown in Figure 4.4a. Since the
class B does not use any other classes, we compile it first with the following command, resulting in the file hierarchy shown in Figure 4.4b:

118
Figure 4.4

CHAPTER 4: ACCESS CONTROL

Searching for Classes
/top

/top

/top
src *

src *

src *

A.java

A.java

A.java

B.java

B.java

B.java

bin

bin

bin
pkg

pkg
A.class

B.class

* current directory

B.class
(a)

(b)

// File name: A.java
package pkg;
class A { B b; } // A uses B

(c)

// File name: B.java
package pkg;
class B { }

>javac -d ../bin B.java

Next, we try to compile the file A.java, and get the following results:
>javac -d ../bin A.java
A.java:3: cannot find symbol
symbol : class B
location: class pkg.A
public class A { B b; }
^
1 error

The compiler cannot find the class B, i.e., the file B.class containing the Java byte
code for the class B. From Figure 4.4b we can see that it is in the package pkg under
the directory bin, but the compiler cannot find it. This is hardly surprising, as there
is no byte code file for the class B in the current directory, which is the default value
of the class path. The command below sets the value of the class path to be /top/
bin, and compilation is successful (see Figure 4.4c):
>javac -cp /top/bin -d ../bin A.java

It is very important to understand that when we want the JDK tool to search in a
named package, it is the location of the package that is specified, i.e., the class path
indicates the directory that contains the first element of the fully qualified package
name. In Figure 4.4c, the package pkg is contained under the directory whose absolute path is /top/bin. The following command will not work, as the directory /top/
bin/pkg does not contain a package with the name pkg that has a class B:

4.3: SEARCHING FOR CLASSES

119

>javac -cp /top/bin/pkg -d ../bin A.java

Also, the compiler is not using the class path to find the source file(s) that are specified in the command line. In the command above, the source file has the relative
pathname ./A.java. So the compiler looks for the source file in the current directory. The class path is used to find classes used by the class A.
Given the file hierarchy in Figure 4.3, the following -cp option sets the class path so
that all packages (wizard.pandorasBox, wizard.pandorasBox.artifacts, wizard.spells)
in Figure 4.3 will be searched, as all packages are located under the specified
directory:
-cp /pgjc/work

However, the following -cp option will not help in finding any of the packages in
Figure 4.3, as none of the packages are located under the specified directory:
>java -cp /pgjc/work/wizard pandorasBox.Clown

The command above also illustrates an important point about package names:
the fully qualified package name should not be split. The package name for the class
wizard.pandorasBox.Clown is wizard.pandorasBox, and must be specified fully. The
following command will search all packages in Figure 4.3 for classes that are
used by the class wizard.pandorasBox.Clown:
>java -cp /pgjc/work wizard.pandorasBox.Clown

The class path can specify several entries, i.e., several locations, and the JDK tool
searches them in the order they are specified, from left to right.
-cp /pgjc/work:/top/bin/pkg:.

We have used the path-separator character ':' for Unix platforms to separate the
entries, and also included the current directory (.) as an entry. There should be no
white space on either side of the path-separator character.
The search in the class path entries stops once the required class file is found.
Therefore, the order in which entries are specified can be significant. If a class B is
found in a package pkg located under the directory /ext/lib1, and also in a package
pkg located under the directory /ext/lib2, the order in which the entries are specified in the two -cp options shown below is significant. They will result in the class
pkg.B being found under /ext/lib1 and /ext/lib2, respectively.
-cp /ext/lib1:/ext/lib2
-cp /ext/lib2:/ext/lib1

The examples so far have used absolute pathnames for class path entries. We can
of course use relative pathnames as well. If the current directory has the absolute
pathname /pgjc/work in Figure 4.3, the following command will search the packages under the current directory:
>java -cp . wizard.pandorasBox.Clown

120

CHAPTER 4: ACCESS CONTROL

If the current directory has the absolute pathname /top/src in Figure 4.4, the following command will compile the file ./A.java:
>javac -cp ../bin/pkg -d ../bin A.java

If the name of an entry in the class path includes white space, the name should be
double quoted in order to be interpreted correctly:
-cp "../new bin/large pkg"

4.4 The JAR Utility
The JAR (Java ARchive) utility provides a convenient way of bundling and deploying Java programs. A JAR file is created by using the jar tool. A typical JAR file
for an application will contain the class files and any other resources needed by
the application (for example image and audio files). In addition, a special manifest
file is also created and included in the archive. The manifest file can contain pertinent information, such as which class contains the main() method for starting the
application.
The jar command has many options (akin to the Unix tar command). A typical
command for making a JAR file for an application (for example, Example 4.10) has
the following syntax:
>jar cmf whereismain.txt bundledApp.jar wizard

Option c tells the jar tool to create an archive. Option m is used to create and include
a manifest file. Information to be included in the manifest file comes from a text file
specified on the command line (whereismain.txt). Option f specifies the name of the
archive to be created (bundledApp.jar). The JAR file name can be any valid file
name. Files to be included in the archive are listed on the command line after the
JAR file name. In the command line above, the contents under the wizard directory
will be archived. If the order of the options m and f is switched in the command line,
the order of the respective file names for these options must also be switched.
Information to be included in the manifest file is specified as name-value pairs.
In Example 4.10, program execution should start in the main() method of the
wizard.pandorasBox.Clown class. The file whereismain.txt has the following single
text line:
Main-Class: wizard.pandorasBox.Clown

The value of the predefined header named Main-Class specifies the execution entry
point of the application. The last text line in the file must be terminated by a
newline as well, in order to be processed by the jar tool. This is also true even if the
file only has a single line.
The application in an archive can be run by issuing the following command:
>java -jar bundledApp.jar

121

4.4: THE JAR UTILITY

Program arguments can be specified after the JAR file name.
Another typical use of a JAR file is bundling packages as libraries so that other Java
programs can use them. Such JAR files can be made available centrally, e.g., in the
jre/lib/ext directory under Unix, where the jre directory contains the Java runtime environment. The pathname of such a JAR file can also be specified in the CLASSPATH environment variable. Clients can also use the -cp option to specify the
pathname of the JAR file in order to utilize its contents. In all cases, the Java tools
will be able to find the packages contained in the JAR file. The compiler can search
the JAR file for classes when compiling the program, and the JVM can search the
JAR file for classes to load in order to run the program.
As an example, we consider the file organization in Figure 4.5, where the class
MyApp uses the class org.graphics.draw4d.Menu, and also classes from packages in the
JAR file gui.jar in the directory /top/lib. We can compile the file MyApp.java in the
current directory /top/src with the following command:
>javac -cp /top/lib/gui.jar:/top/lib -d /top/bin MyApp.java

Note that we need to specify pathnames of JAR files, but we specify locations where
to search for particular packages.
We can also use the class path wildcard * to include all JAR files contained in a
directory. Referring to Figure 4.5, the following -cp option will set the class path to
include both the JAR files gui.jar and db.jar:
>javac -cp /top/lib/*:/top/lib -d /top/bin MyApp.java
Figure 4.5

Searching in JAR files
/top
src
MyApp.java
bin
MyApp.class
lib
org
graphics
draw3d
Menu.class
gui.jar
db.jar

122

CHAPTER 4: ACCESS CONTROL

It may be necessary to quote the wildcard, depending on the configuration of the
command line environment:
>javac -cp "/top/lib/*":/top/lib -d /top/bin MyApp.java

The wildcard * only expands to JAR files under the directory designated by the
class path entry. It does not expand to any class files. Neither does it expand recursively to any JAR files contained in any subdirectories under the directory designated by the class path entry. The order in which the JAR files are searched
depends on how the wildcard is expanded, and should not be relied upon when
using the JDK tools.

4.5 System Properties
The Java runtime environment maintains persistent information like the operating
system (OS) name, the JDK version, and various platform-dependent conventions
(e.g., file separator, path separator, line terminator). This information is stored as a
collection of properties on the platform on which the Java runtime environment is
installed. Each property is defined as a name-value pair. For example, the name of
the OS is stored as a property with the name "os.name" and the value "Windows
Vista" on a platform running this OS. Properties are stored in a hash table, and
applications can access them through the class java.util.Properties, which is a
subclass of the java.util.Hashtable class (Section 15.8, p. 821).
Example 4.8 provides a basic introduction to using system properties. The System.
getProperties() method returns a Properties hashtable containing all the properties
stored on the host platform, (1). An application-defined property can be added to the
Properties hashtable by calling the setProperty() method, with the appropriate
name and value of the property. At (2), a property with the name "appName" and the
value "BigKahuna" is put into the Properties hashtable. A property with a particular
name can be retrieved from the Properties hashtable by calling the getProperties()
method with the property name as argument, (3). Note that the type of both property
name and value is String.
The program in Example 4.8 is run with the following command line:
>java SysProp os.name java.version appName FontSize

The program arguments are property names. The program looks them up in the Properties hashtable, and prints their values. We see that the value of the applicationdefined property with the name "appNam" is retrieved correctly. However, no property
with the name "FontSize" is found, there null is printed as its value.
Another way of adding a property is by specifying it with the -D option (D for
Define) in the java command. Running the program with the following command
line
>java SysProp -DFontSize=18 os.name java.version appName FontSize

123

4.5: SYSTEM PROPERTIES

produces the following result:
os.name=Windows Vista
java.version=1.6.0_05
appName=BigKahuna
FontSize=18

The name and the value of the property are separated by the character = when
specified using the -D option. The property is added by the JVM, and made available to the application.
There is also no white space on either side of the separator = in the -D option syntax,
and the value can be double quoted, if necessary.
Example 4.8

Using Properties
import java.util.Properties;
public class SysProp {
public static void main(String[] args) {
Properties props = System.getProperties();
props.setProperty("appName", "BigKahuna");
for (String prop : args) {
String value = props.getProperty(prop);
System.out.printf("%s=%s%n", prop, value);
}
}
}

// (1)
// (2)
// (3)

Output from the program:
>javac SysProp.java
>java SysProp os.name java.version appName FontSize
os.name=Windows Vista
java.version=1.6.0_05
appName=BigKahuna
FontSize=null

Review Questions
4.1

What will be the result of attempting to compile this code?
import java.util.*;
package com.acme.toolkit;
public class AClass {
public Other anInstance;
}
class Other {
int value;
}

124

CHAPTER 4: ACCESS CONTROL

Select the one correct answer.
(a) The code will fail to compile, since the class Other has not yet been declared
when referenced in the class AClass.
(b) The code will fail to compile, since an import statement cannot occur as the
first statement in a source file.
(c) The code will fail to compile, since the package declaration cannot occur after
an import statement.
(d) The code will fail to compile, since the class Other must be defined in a file
called Other.java.
(e) The code will fail to compile, since the class Other must be declared public.
(f) The class will compile without errors.
4.2

Given the following code:
// (1) INSERT ONE IMPORT STATEMENT HERE
public class RQ700_20 {
public static void main(String[] args) {
System.out.println(sqrt(49));
}
}

Which statements, when inserted at (1), will result in a program that prints 7, when
compiled and run?
Select the two correct answers.
(a) import static Math.*;
(b) import static Math.sqrt;
(c) import static java.lang.Math.sqrt;
(d) import static java.lang.Math.sqrt();
(e) import static java.lang.Math.*;
4.3

Given the following code:
// (1) INSERT ONE IMPORT STATEMENT HERE
public class RQ700_10 {
public static void main(String[] args) {
System.out.println(Locale.UK);
// Locale string for UK is "en_GB".
}
}

Which statements, when inserted at (1), will result in a program that prints en_GB,
when compiled and run?
Select the two correct answers.
(a) import java.util.*;
(b) import java.util.Locale;
(c) import java.util.Locale.UK;
(d) import java.util.Locale.*;
(e) import static java.util.*;
(f) import static java.util.Locale;
(g) import static java.util.Locale.UK;
(h) import static java.util.Locale.*;

4.5: SYSTEM PROPERTIES

4.4

125

Given the following code:
package p1;
enum Signal {
GET_SET, ON_YOUR_MARKS, GO;
}
-----------------------------------------------------------package p2;
// (1) INSERT IMPORT STATEMENT(S) HERE
public class RQ700_50 {
public static void main(String[] args) {
for(Signal sign : Signal.values()) {
System.out.println(sign);
}
}
}

Which import statement(s), when inserted at (1), will result in a program that
prints the constants of the enum type Signal, when compiled and run?
Select the one correct answer.
(a) import static p1.Signal.*;
(b) import p1.Signal;
(c) import p1.*;
(d) import p1.Signal;
import static p1.Signal.*;

(e) import p1.*;
import static p1.*;

(f) None of the above.
4.5

Given the following code:
package p3;
public class Util {
public enum Format {
JPEG { public String toString() {return "Jpeggy"; }},
GIF { public String toString() {return "Giffy"; }},
TIFF { public String toString() {return "Tiffy"; }};
}
public static  void print(T t) {
System.out.print("|" + t + "|");
}
}
-----------------------------------------------------------// (1) INSERT IMPORT STATEMENTS HERE
public class NestedImportsA {
public static void main(String[] args) {
Util u = new Util();
Format[] formats = {
GIF, TIFF,
JPEG,
Format.JPEG,
Util.Format.JPEG,
p3.Util.Format.JPEG

126

CHAPTER 4: ACCESS CONTROL
};
for (Format fmt : formats)
print(fmt);
}
}

Which sequence of import statements, when inserted at (1), will result in the code
compiling, and the execution of the main() method printing:
|Giffy||Tiffy||Jpeggy||Jpeggy||Jpeggy||Jpeggy|

Select the three correct answers.
(a) import p3.Util;
import
import
import
(b) import
import
import
import
(c) import
import
import
(d) import
import
import

4.6

p3.Util.Format;
static p3.Util.print;
static p3.Util.Format.*;
p3.Util;
static p3.Util.Format;
static p3.Util.print;
static p3.Util.Format.*;
p3.*;
static p3.Util.*;
static p3.Util.Format.*;
p3.*;
p3.Util.*;
static p3.Util.Format.*;

Which statements are true about the import statement?
Select the two correct answers.
(a) Static import from a class automatically imports names of static members of
any nested types declared in that class.
(b) Static members of the default package cannot be imported.
(c) Static import statements must be specified after any type import statements.
(d) In the case of a name conflict, the name in the last static import statement is
chosen.
(e) A declaration of a name in a compilation unit can shadow a name that is
imported.

4.7

Given the source file A.java:
package top.sub;
public class A {}

And the following directory hierarchy:
/proj
|--- src
|
|--- top
|
|--- sub
|
|--- A.java
|--- bin

4.5: SYSTEM PROPERTIES

127

Assuming that the current directory is /proj/src, which of the following statements
are true?
Select the three correct answers.
(a) The following command will compile, and place the file A.class under /proj/
bin:
javac -d . top/sub/A.java

(b) The following command will compile, and place the file A.class under /proj/
bin:
javac -d /proj/bin top/sub/A.java

(c) The following command will compile, and place the file A.class under /proj/
bin:
javac -D /proj/bin ./top/sub/A.java

(d) The following command will compile, and place the file A.class under /proj/
bin:
javac -d ../bin top/sub/A.java

(e) After successful compilation, the absolute pathname of the file A.class will be:
/proj/bin/A.class

(f) After successful compilation, the absolute pathname of the file A.class will be:
/proj/bin/top/sub/A.class

4.8

Given the following directory structure:
/top
|--- wrk
|--- pkg
|--- A.java
|--- B.java

Assume that the two files A.java and B.java contain the following code, respectively:
// Filename: A.java
package pkg;
class A { B b; }
// Filename: B.java
package pkg;
class B {...}

For which combinations of current directory and command is the compilation successful?
Select the two correct answers.
(a) Current directory: /top/wrk
Command: javac -cp .:pkg A.java

(b) Current directory: /top/wrk
Command: javac -cp . pkg/A.java

(c) Current directory: /top/wrk
Command: javac -cp pkg A.java

128

CHAPTER 4: ACCESS CONTROL

(d) Current directory: /top/wrk
Command: javac -cp .:pkg pkg/A.java

(e) Current directory: /top/wrk/pkg
Command: javac A.java

(f) Current directory: /top/wrk/pkg
Command: javac -cp . A.java

4.9

Given the following directory structure:
/proj
|--- src
|
|--- A.class
|
|
|--- bin
|--- top
|--- sub
|--- A.class

Assume that the current directory is /proj/src. Which classpath specifications will
find the file A.class for the class top.sub.A?
Select the two correct answers.
(a) -cp /top/bin/top
(b) -cp /top/bin/top/sub
(c) -cp /top/bin/top/sub/A.class
(d) -cp ../bin;.
(e) -cp /top
(f) -cp /top/bin
4.10

Given that the name of the class MyClass is specified correctly, which commands are
syntactically valid:
Select the two correct answers.
(a) java -Ddebug=true MyClass
(b) java -ddebug=true MyClass
(c) java -Ddebug="true" MyClass
(d) java -D debug=true MyClass

4.11

Which statement is true?
Select the one correct answer.
(a) A JAR file can only contain one package.
(b) A JAR file can only be specified for use with the java command, in order to
run a program.
(c) The classpath definition of the platform overrides any entries specified in the

129

4.6: SCOPE RULES

classpath option.
(d) The -d option is used with the java command, and the -D is used with the
javac command.
(e) None of the above statements are true.

4.6 Scope Rules
Java provides explicit accessibility modifiers to control the accessibility of members in a class by external clients (see Section 4.9, p. 138), but in two areas access is
governed by specific scope rules:
• Class scope for members: how member declarations are accessed within the
class.
• Block scope for local variables: how local variable declarations are accessed
within a block.

Class Scope for Members
Class scope concerns accessing members (including inherited ones) from code
within a class. Table 4.1 gives an overview of how static and non-static code in a
class can access members of the class, including those that are inherited. Table 4.1
assumes the following declarations:
class SuperName {
int instanceVarInSuper;
static int staticVarInSuper;
void instanceMethodInSuper()
{ /* ... */ }
static void staticMethodInSuper() { /* ... */ }
// ...
}
class ClassName extends SuperName {
int instanceVar;
static int staticVar;
void instanceMethod()
{ /* ... */ }
static void staticMethod() { /* ... */ }
// ...
}

The golden rule is that static code can only access other static members by their
simple names. Static code is not executed in the context of an object, therefore the
references this and super are not available. An object has knowledge of its class,
therefore, static members are always accessible in a non-static context.
Note that using the class name to access static members within the class is no different from how external clients access these static members.

130

CHAPTER 4: ACCESS CONTROL

Some factors that can influence the scope of a member declaration are:
• shadowing of a field declaration, either by local variables (see Section 4.6,
p. 131) or by declarations in the subclass (see Section 7.3, p. 294)
• initializers preceding the field declaration (see Section 9.7, p. 406)
• overriding an instance method from a superclass (see Section 7.2, p. 288)
• hiding a static method declared in a superclass (see Section 7.3, p. 294)
Accessing members within nested classes is discussed in Chapter 8.
Table 4.1

Accessing Members within a Class
Non-static Code in the Class
ClassName Can Refer to the
Member as

Static Code in the Class
ClassName Can Refer to the
Member as

Instance variables

instanceVar
this.instanceVar
instanceVarInSuper
this.instanceVarInSuper
super.instanceVarInSuper

Not possible

Instance methods

instanceMethod()
this.instanceMethod()
instanceMethodInSuper()
this.instanceMethodInSuper()
super.instanceMethodInSuper()

Not possible

Static variables

staticVar
this.staticVar
ClassName.staticVar
staticVarInSuper
this.staticVarInSuper
super.staticVarInSuper
ClassName.staticVarInSuper
SuperName.staticVarInSuper

staticVar

staticMethod()
this.staticMethod()
ClassName.staticMethod()
staticMethodInSuper()
this.staticMethodInSuper()
super.staticMethodInSuper()
ClassName.staticMethodInSuper()
SuperName.staticMethodInSuper()

staticMethod()

Member
declarations

Static methods

ClassName.staticVar
staticVarInSuper

ClassName.staticVarInSuper
SuperName.staticVarInSuper

ClassName.staticMethod()
staticMethodInSuper()

ClassName.staticMethodInSuper()
SuperName.staticMethodInSuper()

Within a class C, references of type C can be used to access all members in the class
C, regardless of their accessibility modifiers. In Example 4.9, the method duplicate-

131

4.6: SCOPE RULES

Light at (1) in the class Light has the parameter oldLight and the local variable newLight that are references of the class Light. Even though the fields of the class are
private, they are accessible through the two references (oldLight and newLight) in
the method duplicateLight() as shown at (2), (3), and (4).

Example 4.9

Class Scope
class Light {
// Instance variables:
private int
noOfWatts;
private boolean indicator;
private String location;

// wattage
// on or off
// placement

// Instance methods:
public void switchOn() { indicator = true; }
public void switchOff() { indicator = false; }
public boolean isOn()
{ return indicator; }
public static Light duplicateLight(Light oldLight) {
Light newLight = new Light();
newLight.noOfWatts = oldLight.noOfWatts;
newLight.indicator = oldLight.indicator;
newLight.location = oldLight.location;
return newLight;
}

// (1)
// (2)
// (3)
// (4)

}

Block Scope for Local Variables
Declarations and statements can be grouped into a block using braces, {}. Blocks
can be nested, and scope rules apply to local variable declarations in such blocks.
A local declaration can appear anywhere in a block. The general rule is that a variable declared in a block is in scope inside the block in which it is declared, but it is
not accessible outside of this block. It is not possible to redeclare a variable if a local
variable of the same name is already declared in the current scope.
Local variables of a method include the formal parameters of the method and variables that are declared in the method body. The local variables in a method are created each time the method is invoked, and are therefore distinct from local
variables in other invocations of the same method that might be executing (see Section 6.5, p. 235).
Figure 4.6 illustrates block scope for local variables. A method body is a block.
Parameters cannot be redeclared in the method body, as shown at (1) in Block 1.
A local variable—already declared in an enclosing block and, therefore, visible in
a nested block—cannot be redeclared in the nested block. These cases are shown at
(3), (5), and (6).

132

CHAPTER 4: ACCESS CONTROL

A local variable in a block can be redeclared in another block if the blocks are
disjoint, that is, they do not overlap. This is the case for variable i at (2) in Block 3
and at (4) in Block 4, as these two blocks are disjoint.
The scope of a local variable declaration begins from where it is declared in the
block and ends where this block terminates. The scope of the loop variable index is
the entire Block 2. Even though Block 2 is nested in Block 1, the declaration of the
variable index at (7) in Block 1 is valid. The scope of the variable index at (7) spans
from its declaration to the end of Block 1, and it does not overlap with that of the
loop variable index in Block 2.
Figure 4.6

Block Scope

public static void main(String args[]) {
//

String args = "";
char digit = 'z';

// (1) Cannot redeclare parameters.

for (int index = 0; index < 10; ++index) {
switch(digit) {
case 'a':
int i;
default:
// int i;
} // switch
if (true) {
int i;
// int digit;
// int index;
} //if

// Block 1

// Block 2
// Block 3

// (2)
// (3) Already declared in the same block.
// Block 4
// (4) OK
// (5) Already declared in enclosing block 1.
// (6) Already declared in enclosing block 2.

} // for
int index;

// (7) OK

} // main

4.7 Accessibility Modifiers for Top-Level Type Declarations
The accessibility modifier public can be used to declare top-level types (that is,
classes, enums, and interfaces) in a package to be accessible from everywhere, both
inside their own package and other packages. If the accessibility modifier is omitted, they are only accessible in their own package and not in any other packages or
subpackages. This is called package or default accessibility.
Accessibility modifiers for nested reference types are discussed in Section 8.1 on
page 352.

133

4.7: ACCESSIBILITY MODIFIERS FOR TOP-LEVEL TYPE DECLARATIONS

Example 4.10 Accessibility Modifiers for Classes and Interfaces
//File: Clown.java
package wizard.pandorasBox;

// (1) Package declaration

import wizard.pandorasBox.artifacts.Ailment;

// (2) Importing class

public class Clown implements Magic {
LovePotion tlc;
// (3) Class in same package
wizard.pandorasBox.artifacts.Ailment problem; // (4) Fully qualified class name
Clown() {
tlc = new LovePotion("passion");
problem = new Ailment("flu");
// (5) Simple class name
}
public void levitate() { System.out.println("Levitating"); }
public void mixPotion() { System.out.println("Mixing " + tlc); }
public void healAilment() { System.out.println("Healing " + problem); }
public static void main(String[] args) {
Clown joker = new Clown();
joker.levitate();
joker.mixPotion();
joker.healAilment();
}

// (6)

}
interface Magic { void levitate(); }

//File: LovePotion.java
package wizard.pandorasBox;

// (7)

// (1) Package declaration

public class LovePotion {
// (2) Accessible outside package
String potionName;
public LovePotion(String name) { potionName = name; }
public String toString() { return potionName; }
}

//File: Ailment.java
package wizard.pandorasBox.artifacts;

// (1) Package declaration

public class Ailment {
// (2) Accessible outside package
String ailmentName;
public Ailment(String name) { ailmentName = name; }
public String toString() { return ailmentName; }
}

//File: Baldness.java
package wizard.spells;

// (1)Package declaration

import wizard.pandorasBox.*;
import wizard.pandorasBox.artifacts.*;

// (2) Type import on demand
// (3) Import of subpackage

public class Baldness extends Ailment {

// (4) Simple name for Ailment

134

CHAPTER 4: ACCESS CONTROL
wizard.pandorasBox.LovePotion tlcOne;
LovePotion tlcTwo;
Baldness(String name) {
super(name);
tlcOne = new wizard.pandorasBox.
LovePotion("romance");
tlcTwo = new LovePotion();
}

// (5) Fully qualified name
// (6) Class in same package

// (7) Fully qualified name
// (8) Class in same package

}
class LovePotion // implements Magic
{ public void levitate(){} }

// (9) Not accessible

Compiling and running the program from the current directory gives the following results:
>javac -d . Clown.java LovePotion.java Ailment.java Baldness.java
>java wizard.pandorasBox.Clown
Levitating
Mixing passion
Healing flu

In Example 4.10, the class Clown and the interface Magic are placed in a package
called wizard.pandorasBox. The public class Clown is accessible from everywhere. The
Magic interface has default accessibility, and can only be accessed within the package wizard.pandorasBox. It is not accessible from other packages, not even from its
subpackages.
The class LovePotion is also placed in the package called wizard.pandorasBox. The
class has public accessibility and is, therefore, accessible from other packages.
The two files Clown.java and LovePotion.java demonstrate how several compilation
units can be used to group classes in the same package.
The class Clown, from the file Clown.java, uses the class Ailment. The example shows
two ways in which a class can access classes from other packages:
1.

Denote the class by its fully qualified class name, as shown at (4) (wizard.
pandorasBox.artifacts.Ailment).

2.

Import the class explicitly from the package wizard.pandorasBox.artifacts as
shown at (2), and use the simple class name Ailment, as shown at (5).

In the file Baldness.java at (9), the class LovePotion wishes to implement the interface Magic from the package wizard.pandorasBox, but cannot do so, although the
source file imports from this package. The reason is that the interface Magic has
default accessibility and can, therefore, only be accessed within the package
wizard.pandorasBox.

135

4.8: OTHER MODIFIERS FOR CLASSES

Just because a type is accessible does not necessarily mean that members of the
type are also accessible. Member accessibility is governed separately from type
accessibility, as explained in Section 4.6.
Table 4.2

Summary of Accessibility Modifiers for Top-Level Types
Modifiers

Top-Level Types

default (no modifier)

Accessible in its own package (package accessibility)

public

Accessible anywhere

4.8 Other Modifiers for Classes
The modifiers abstract and final can be applied to top-level and nested classes.

abstract Classes
A class can be declared with the keyword abstract to indicate that it cannot be
instantiated. A class might choose to do this if the abstraction it represents is so
general that it needs to be specialized in order to be of practical use. The class Vehicle might be specified as abstract to represent the general abstraction of a vehicle,
as creating instances of the class would not make much sense. Creating instances
of non-abstract subclasses, like Car and Bus, would make more sense, as this would
make the abstraction more concrete.
Any normal class (that is, a class declared with the keyword class) can be declared
abstract. However, if such a class that has one or more abstract methods (see Section 4.10, p. 150), it must be declared abstract. Obviously such classes cannot be
instantiated, as their implementation might only be partial. A class might choose
this strategy to dictate certain behavior, but allow its subclasses the freedom to provide the relevant implementation. In other words, subclasses of the abstract class
have to take a stand and provide implementations of any inherited abstract methods before instances can be created. A subclass that does not provide an implementation of its inherited abstract methods, must also be declared abstract.
In Example 4.11, the declaration of the abstract class Light has an abstract method
named kwhPrice at (1). This forces its subclasses to provide an implementation for
this method. The subclass TubeLight provides an implementation for the method
kwhPrice() at (2). The class Factory creates an instance of the class TubeLight at (3).
References of an abstract class can be declared, as shown at (4), but an abstract
class cannot be instantiated, as shown at (5). References of an abstract class can
refer to objects of the subclasses, as shown at (6).
Interfaces just specify abstract methods and not any implementation; they are, by
their nature, implicitly abstract (that is, they cannot be instantiated). Though it is

136

CHAPTER 4: ACCESS CONTROL

legal, it is redundant to declare an interface with the keyword abstract (see Section
7.6, p. 309).
Enum types cannot be declared abstract, because of the way they are implemented
in Java (see Section 3.5, p. 54).
Example 4.11

Abstract Classes
abstract class Light {
// Fields:
int
noOfWatts;
boolean indicator;
String location;

// wattage
// on or off
// placement

// Instance methods:
public void switchOn() { indicator = true; }
public void switchOff() { indicator = false; }
public boolean isOn()
{ return indicator; }
// Abstract instance method
abstract public double kwhPrice();

// (1) No method body
}
//______________________________________________________________________________
class TubeLight extends Light {
// Field
int tubeLength;
// Implementation of inherited abstract method.
public double kwhPrice() { return 2.75; }

// (2)
}
//______________________________________________________________________________
public class Factory {
public static void main(String[] args) {
TubeLight cellarLight = new TubeLight();
// (3) OK
Light nightLight;
// (4) OK
// Light tableLight = new Light();
// (5) Compile time error
nightLight = cellarLight;
// (6) OK
System.out.println("KWH price: " + nightLight.kwhPrice());
}
}

Output from the program:
KWH price: 2.75

final Classes
A class can be declared final to indicate that it cannot be extended; that is, one cannot declare subclasses of a final class. This implies that one cannot override any
methods declared in such a class. In other words, the class behavior cannot be

137

4.8: OTHER MODIFIERS FOR CLASSES

changed by extending the class. A final class marks the lower boundary of its
implementation inheritance hierarchy (see Section 7.1, p. 284). Only a class whose definition is complete (that is, provides implementations of all its methods) can be
declared final.
A final class must be complete, whereas an abstract class is considered incomplete. Classes, therefore, cannot be both final and abstract at the same time. Interfaces are inherently abstract, as they can only specify methods that are abstract,
and therefore cannot be declared final. A final class and an interface represent two
extremes when it comes to providing an implementation. An abstract class represents a compromise between these two extremes. An enum type is also implicitly
final, and cannot be explicitly declared with the keyword final.
The Java standard library includes many final classes; for example, the
java.lang.String class and the wrapper classes for primitive values.
If it is decided that the class TubeLight in Example 4.11 may not be extended, it can
be declared final:
final class TubeLight extends Light {
// ...
}

Discussion of final methods, fields, and local variables can be found in
Section 4.10, p. 148.
Table 4.3

Summary of Other Modifiers for Types
Modifiers

Classes

Interfaces

Enum types

abstract

A non-final class can be declared
abstract.
A class with an abstract method
must be declared abstract.
An abstract class cannot be
instantiated.

Permitted, but
redundant.

Not permitted.

final

A non-abstract class can be declared
final.
A class with a final method need not
be declared final.
A final class cannot be extended.

Not permitted.

Not permitted.

138

CHAPTER 4: ACCESS CONTROL

Review Questions
4.12

Given the following class, which of these alternatives are valid ways of referring
to the class from outside of the package net.basemaster?
package net.basemaster;
public class Base {
// ...
}

Select the two correct answers.
(a)
(b)
(c)
(d)
(e)
4.13

By simply referring to the class as Base.
By simply referring to the class as basemaster.Base.
By simply referring to the class as net.basemaster.Base.
By importing with net.basemaster.*, and referring to the class as Base.
By importing with net.*, and referring to the class as basemaster.Base.

Which one of the following class declarations is a valid declaration of a class that
cannot be instantiated?
Select the one correct answer.
(a) class Ghost
{ abstract void haunt(); }
(b) abstract class Ghost { void haunt(); }
(c) abstract class Ghost { void haunt() {}; }
(d) abstract Ghost
{ abstract void haunt(); }
(e) static class Ghost { abstract haunt(); }

4.14

Which one of the following class declarations is a valid declaration of a class that
cannot be extended?
Select the one correct answer.
(a) class Link { }
(b) abstract class Link { }
(c) native class Link { }
(d) static class Link { }
(e) final class Link { }
(f) private class Link { }
(g) abstract final class Link { }

4.9 Member Accessibility Modifiers
By specifying member accessibility modifiers, a class can control what information
is accessible to clients (that is, other classes). These modifiers help a class to define
a contract so that clients know exactly what services are offered by the class.

139

4.9: MEMBER ACCESSIBILITY MODIFIERS

The accessibility of members can be one of the following:
❍

public
protected

❍

default (also called package accessibility)

❍

private

❍

If an accessibility modifier is not specified, the member has package or default
accessibility.
In the following discussion on accessibility modifiers for members of a class, keep
in mind that the member accessibility modifier only has meaning if the class (or
one of its subclasses) is accessible to the client. Also, note that only one accessibility
modifier can be specified for a member. The discussion in this section applies to
both instance and static members of top-level classes. It applies equally to constructors as well. Discussion of member accessibility for nested classes is deferred to
Chapter 8.
In UML notation, the prefixes + , #, and -, when applied to a member name, indicate
public, protected, and private member accessibility, respectively. No prefix indicates default or package accessibility.

public Members
Public accessibility is the least restrictive of all the accessibility modifiers. A public
member is accessible from anywhere, both in the package containing its class and
in other packages where this class is visible. This is true for both instance and static
members.
Example 4.12 contains two source files, shown at (1) and (6). The package hierarchy
defined by the source files is depicted in Figure 4.7, showing the two packages,
packageA and packageB, containing their respective classes. The classes in packageB
use classes from packageA. The class SuperclassA in packageA has two subclasses: SubclassA in packageA and SubclassB in packageB.
Example 4.12 Public Accessibility of Members
//Filename: SuperclassA.java
package packageA;

(1)

public class SuperclassA {
public int superclassVarA;
public void superclassMethodA() {/*...*/}
}

// (2)
// (3)

class SubclassA extends SuperclassA {
void subclassMethodA() { superclassVarA = 10; }
}

// (4) OK.

140

CHAPTER 4: ACCESS CONTROL
class AnyClassA {
SuperclassA obj = new SuperclassA();
void anyClassMethodA() {
obj.superclassMethodA();
}
}

//Filename: SubclassB.java
package packageB;
import packageA.*;
public class SubclassB extends SuperclassA {
void subclassMethodB() { superclassMethodA(); }
}
class AnyClassB {
SuperclassA obj = new SuperclassA();
void anyClassMethodB() {
obj.superclassVarA = 20;
}
}

// (5) OK.

(6)

// (7) OK.

// (8) OK.

Accessibility is illustrated in Example 4.12 by the accessibility modifiers for the
field superclassVarA and the method superclassMethodA() at (2) and (3), respectively,
defined in the class SuperclassA. These members are accessed from four different
clients in Example 4.12.
• Client 1: From a subclass in the same package, which accesses an inherited
field. SubclassA is such a client, and does this at (4).
• Client 2: From a non-subclass in the same package, which invokes a method on
an instance of the class. AnyClassA is such a client, and does this at (5).
• Client 3: From a subclass in another package, which invokes an inherited
method. SubclassB is such a client, and does this at (7).
• Client 4: From a non-subclass in another package, which accesses a field in an
instance of the class. AnyClassB is such a client, and does this at (8).
In Example 4.12, the field superclassVarA and the method superclassMethodA() have
public accessibility, and are accessible by all four clients listed above. Subclasses
can access their inherited public members by their simple name, and all clients can
access public members through an instance of the class. Public accessibility is
depicted in Figure 4.7.

141

4.9: MEMBER ACCESSIBILITY MODIFIERS
Figure 4.7

Public Accessibility

packageA

packageB
Client 3
SuperclassA

Client 4

SubclassB

+superclassVarA:int
+superclassMethodA
Client 2

obj:SuperClassA
subclassMethodB

anyClassMethodB

Client 1

AnyClassA

SubclassA

Inheritance relationship
Access is permitted.

obj:SuperClassA
anyClassMethodA

AnyClassB

subclassMethodA

protected Members
A protected member is accessible in all classes in the same package, and by all subclasses of its class in any package where this class is visible. In other words, nonsubclasses in other packages cannot access protected members from other packages. It is more restrictive than public member accessibility.
In Example 4.12, if the field superclassVarA and the method superclassMethodA()
have protected accessibility, they are accessible within packageA, and only accessible by subclasses in any other packages.
public class SuperclassA {
protected int superclassVarA;
protected void superclassMethodA() {/*...*/}
}

// (2) Protected member
// (3) Protected member

Client 4 in packageB cannot access these members, as shown in Figure 4.8.
A subclass in another package can only access protected members in the superclass
via references of its own type or its subtypes. The following new declaration of
SubclassB in packageB from Example 4.12 illustrates the point:
// Filename: SubclassB.java
package packageB;
import packageA.*;
public class SubclassB extends SuperclassA {
SuperclassA objRefA = new SuperclassA();
void subclassMethodB(SubclassB objRefB) {
objRefB.superclassMethodA();
objRefB.superclassVarA = 5;
objRefA.superclassMethodA();
objRefA.superclassVarA = 10;
}
}

// In packageB.
// (1)
//
//
//
//

(2)
(3)
(4)
(5)

OK.
OK.
Not OK.
Not OK.

142
Figure 4.8

CHAPTER 4: ACCESS CONTROL

Protected Accessibility
packageA

packageB
Client 3
SuperclassA

Client 4

SubclassB

#superclassVarA:int
#superclassMethodA
Client 2
AnyClassA

obj:SuperClassA
subclassMethodB

anyClassMethodB

Client 1
SubclassA

obj:SuperClassA
anyClassMethodA

AnyClassB

Inheritance relationship
Access is permitted.

subclassMethodA

Access is denied.

The class SubclassB declares the field objRefA of type SuperclassA at (1). The method
subclassMethodB() has the formal parameter objRefB of type SubclassB. Access is permitted to a protected member of SuperclassA in packageA by a reference of the subclass, as shown at (2) and (3), but not by a reference of its superclass, as shown at
(4) and (5). Access to the field superclassVarA and the call to the method superclassMethodA() occur in SubclassB. These members are declared in SuperclassA. SubclassB
is not involved in the implementation of SuperclassA, which is the type of the reference objRefA. Hence, access to protected members at (4) and (5) is not permitted
as these are not members of an object that can be guaranteed to be implemented by
the code accessing them.
Accessibility to protected members of the superclass would also be permitted via
any reference whose type is a subclass of SubclassB. The above restriction helps to
ensure that subclasses in packages different from their superclass can only access
protected members of the superclass in their part of the implementation inheritance hierarchy. In other words, a protected member of a superclass is only accessible in a subclass that is in another package if the member is inherited by an object
of the subclass (or by an object of a subclass of this subclass).

Default Accessibility for Members
When no member accessibility modifier is specified, the member is only accessible
by other classes in its own class’s package. Even if its class is visible in another
(possibly nested) package, the member is not accessible elsewhere. Default member accessibility is more restrictive than protected member accessibility.

143

4.9: MEMBER ACCESSIBILITY MODIFIERS

In Example 4.12, if the field superclassVarA and the method superclassMethodA() are
defined with no accessibility modifier, they are only accessible within packageA, but
not in any other packages.
public class SuperclassA {
int superclassVarA;
void superclassMethodA() {/*...*/}
}

// (2)
// (3)

The clients in packageB (that is, Clients 3 and 4) cannot access these members. This
situation is depicted in Figure 4.9.
Figure 4.9

Default Accessibility
packageA

packageB
Client 3
SuperclassA

Client 4

SubclassB

superclassVarA:int
superclassMethodA
Client 2

AnyClassB
obj:SuperClassA

subclassMethodB

anyClassMethodB

Client 1

AnyClassA

SubclassA

obj:SuperClassA
anyClassMethodA

Inheritance relationship
Access is permitted.

subclassMethodA

Access is denied.

private Members
This is the most restrictive of all the accessibility modifiers. Private members are
not accessible from any other classes. This also applies to subclasses, whether they
are in the same package or not. Since they are not accessible by their simple name
in a subclass, they are also not inherited by the subclass. This is not to be confused
with the existence of such a member in the state of an object of the subclass (see
Section 9.11, p. 416). A standard design strategy for JavaBeans is to make all fields
private and provide public accessor methods for them. Auxiliary methods are
often declared private, as they do not concern any client.
In Example 4.12, if the field superclassVarA and the method superclassMethodA()
have private accessibility, they are not accessible by any other clients.
public class SuperclassA {
private int superclassVarA;
private void superclassMethodA() {/*...*/}
}

// (2) Private member
// (3) Private member

144

CHAPTER 4: ACCESS CONTROL

None of the clients in Figure 4.10 can access these members.
Figure 4.10

Private Accessibility
packageA

packageB
Client 3
SuperclassA

Client 4

SubclassB

–superclassVarA:int
–superclassMethodA
Client 2

obj:SuperClassA
subclassMethodB

SubclassA

obj:SuperClassA

Table 4.4

anyClassMethodB

Client 1

AnyClassA

anyClassMethodA

AnyClassB

Inheritance relationship
Access is denied.

subclassMethodA

Summary of Accessibility Modifiers for Members
Modifiers

Members

public

Accessible everywhere.

protected

Accessible by any class in the same package as its class, and
accessible only by subclasses of its class in other packages.

default (no modifier)

Only accessible by classes, including subclasses, in the
same package as its class (package accessibility).

private

Only accessible in its own class and not anywhere else.

Review Questions
4.15

Given the following declaration of a class, which fields are accessible from outside
the package com.corporation.project?
package com.corporation.project;
public class MyClass {
int i;
public
int j;
protected int k;
private
int l;
}

4.9: MEMBER ACCESSIBILITY MODIFIERS

145

Select the two correct answers.
(a)
(b)
(c)
(d)
(e)
(f)
4.16

Field i is accessible in all classes in other packages.
Field j is accessible in all classes in other packages.
Field k is accessible in all classes in other packages.
Field k is accessible in subclasses only in other packages.
Field l is accessible in all classes in other packages.
Field l is accessible in subclasses only in other packages.

How restrictive is the default accessibility compared to public, protected, and
private accessibility?

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

4.17

Less restrictive than public.
More restrictive than public, but less restrictive than protected.
More restrictive than protected, but less restrictive than private.
More restrictive than private.
Less restrictive than protected from within a package, and more restrictive
than protected from outside a package.

Which statement is true about the accessibility of members?
Select the one correct answer.
(a) A private member is always accessible within the same package.
(b) A private member can only be accessed within the class of the member.
(c) A member with default accessibility can be accessed by any subclass of the
class in which it is declared.
(d) A private member cannot be accessed at all.
(e) Package/default accessibility for a member can be declared using the keyword default.

4.18

Which lines that are marked will compile in the following code?
//Filename: A.java
package packageA;
public class A {
protected int pf;
}

//Filename: B.java
package packageB;
import packageA.A;
public class B extends A {
void action(A obj1, B obj2, C obj3) {
pf = 10;
// (1)
obj1.pf = 10;
// (2)
obj2.pf = 10;
// (3)
obj3.pf = 10;
// (4)

146

CHAPTER 4: ACCESS CONTROL
}
}
class C extends B {
void action(A obj1, B obj2, C obj3) {
pf = 10;
// (5)
obj1.pf = 10;
// (6)
obj2.pf = 10;
// (7)
obj3.pf = 10;
// (8)
}
}
class D {
void action(A obj1, B obj2, C obj3) {
pf = 10;
// (9)
obj1.pf = 10;
// (10)
obj2.pf = 10;
// (11)
obj3.pf = 10;
// (12)
}
}

Select the five correct answers.
(a) (1)
(b) (2)
(c) (3)
(d) (4)
(e) (5)
(f) (6)
(g) (7)
(h) (8)
(i) (9)
(j) (10)
(k) (11)
(l) (12)

4.10 Other Modifiers for Members
The following keywords can be used to specify certain characteristics of members
in a type declaration:
❍
❍
❍
❍
❍
❍
❍

static
final
abstract
synchronized
native
transient
volatile

147

4.10: OTHER MODIFIERS FOR MEMBERS

static Members
Static members belong to the class in which they are declared and are not part of
any instance of the class. The declaration of static members is prefixed by the keyword static to distinguish them from instance members. Depending on the accessibility modifiers of the static members in a class, clients can access these by using
the class name or through object references of the class. The class need not be
instantiated to access its static members.
Static variables (also called class variables) exist in the class they are defined in only.
They are not instantiated when an instance of the class is created. In other words,
the values of these variables are not a part of the state of any object. When the class
is loaded, static variables are initialized to their default values if no explicit initialization expression is specified (see Section 9.9, p. 410).
Static methods are also known as class methods. A static method in a class can
directly access other static members in the class. It cannot access instance (i.e., nonstatic) members of the class, as there is no notion of an object associated with a
static method.
A typical static method might perform some task on behalf of the whole class and/
or for objects of the class. In Example 4.13, the static variable counter keeps track of
the number of instances of the Light class that have been created. The example shows
that the static method writeCount can only access static members directly, as shown
at (2), but not non-static members, as shown at (3). The static variable counter will be
initialized to the value 0 when the class is loaded at runtime. The main() method at
(4) in the class Warehouse shows how static members of the class Light can be accessed
using the class name and via object references of the type Light.
A summary of how static members are accessed in static and non-static code is
given in Table 4.1, p. 130.
Example 4.13 Accessing Static Members
class Light {
// Fields:
int
noOfWatts;
boolean indicator;
String location;
// Static variable
static int counter;

// wattage
// on or off
// placement

// No. of Light objects created.

// Explicit default constructor
Light(int noOfWatts, boolean indicator, String location) {
this.noOfWatts = noOfWatts;
this.indicator = indicator;
this.location = location;
++counter;
// Increment counter.
}

(1)

148

CHAPTER 4: ACCESS CONTROL
// Static method
public static void writeCount() {
System.out.println("Number of lights: " + counter);
// Compile-time error. Field noOfWatts is not accessible:
// System.out.println("Number of Watts: " + noOfWatts);
}

// (2)
// (3)

}
//______________________________________________________________________________
public class Warehouse {
public static void main(String[] args) {
// (4)
Light.writeCount();
Light light1 = new Light(100, true, "basement");
System.out.println(
"Value of counter: " + Light.counter
);
Light light2 = new Light(200, false, "garage");
light2.writeCount();
Light light3 = new Light(300, true, "kitchen");
System.out.println(
"Value of counter: " + light3.counter
);
final int i;

// Invoked using class name
// Create an object
// Accessed via class name
// Create another object
// Invoked using reference
// Create another object
// Accessed via reference

}
}

Output from the program:
Number of lights:
Value of counter:
Number of lights:
Value of counter:

0
1
2
3

final Members
A final variable is a constant despite being called a variable. Its value cannot be
changed once it has been initialized. Instance and static variables can be declared
final. Note that the keyword final can also be applied to local variables, including
method parameters. Declaring a variable final has the following implications:
• A final variable of a primitive data type cannot change its value once it has
been initialized.
• A final variable of a reference type cannot change its reference value once it
has been initialized. This effectively means that a final reference will always
refer to the same object. However, the keyword final has no bearing on
whether the state of the object denoted by the reference can be changed or not.
Final static variables are commonly used to define manifest constants (also called
named constants), e.g., Integer.MAX_VALUE, which is the maximum int value. Variables defined in an interface are implicitly final (see Section 7.6, p. 309). Note that a
final variable need not be initialized in its declaration, but it must be initialized in

149

4.10: OTHER MODIFIERS FOR MEMBERS

the code once before it is used. These variables are also known as blank final variables. For a discussion on final parameters, see Section 3.7, p. 89.
A final method in a class is complete (that is, has an implementation) and cannot be
overridden in any subclass (see Section 7.2, p. 288).
Final variables ensure that values cannot be changed and final methods ensure
that behavior cannot be changed. Final classes are discussed in Section 4.8, p. 136.
The compiler may be able to perform code optimizations for final members,
because certain assumptions can be made about such members.
In Example 4.14, the class Light defines a final static variable at (1) and a final
method at (2). An attempt to change the value of the final variable at (3) results in
a compile-time error. The subclass TubeLight attempts to override the final method
setWatts() from the superclass Light at (4), which is not permitted. The class Warehouse defines a final local reference aLight at (5). The state of the object denoted by
the reference tableLight is changed at (6), but its reference value cannot be changed
as attempted at (7). Another final local reference streetLight is declared at (8), but
it is not initialized. The compiler reports an error when an attempt is made to use
this reference at (9).
Example 4.14 Accessing Final Members
class Light {
// Final static variable
(1)
final public static double KWH_PRICE = 3.25;
int noOfWatts;
// Final instance method
final public void setWatts(int watt) {
noOfWatts = watt;
}
public void setKWH() {
// KWH_PRICE = 4.10;
}

(2)

// (3) Not OK. Cannot be changed.

}
//______________________________________________________________________________
class TubeLight extends Light {
// Final method in superclass cannot be overridden.
// This method will not compile.
/*
public void setWatts(int watt) {
// (4) Attempt to override.
noOfWatts = 2*watt;
}
*/
}
//______________________________________________________________________________

150

CHAPTER 4: ACCESS CONTROL
public class Warehouse {
public static void main(String[] args) {

//

final Light tableLight = new Light();// (5) Final local variable.
tableLight.noOfWatts = 100;
// (6) OK. Changing object state.
tableLight = new Light();
// (7) Not OK. Changing final reference.

final Light streetLight;
// streetLight.noOfWatts = 2000;
}
}

// (8) Not initialized.
// (9) Not OK.

abstract Methods
An abstract method has the following syntax:
abstract    ()
;

An abstract method does not have an implementation; i.e., no method body is
defined for an abstract method, only the method header is provided in the class declaration. The keyword abstract is mandatory in the header of an abstract method
declared in a class. Its class is then incomplete and must be explicitly declared
abstract (see Section 4.8, p. 135). Subclasses of an abstract class must then provide
the method implementation; otherwise, they must also be declared abstract. The
accessibility of an abstract method declared in a class cannot be private, as subclasses would not be able to override the method and provide an implementation.
See Section 4.8, where Example 4.11 also illustrates the use of abstract methods.
Only an instance method can be declared abstract. Since static methods cannot be
overridden, declaring an abstract static method makes no sense. A final method
cannot be abstract (i.e., cannot be incomplete) and vice versa. The keyword
abstract can only be combined with accessibility modifiers public or private.
Methods specified in an interface are implicitly abstract (see Section 7.6, p. 309),
and the keyword abstract is seldom specified in their method headers. These
methods can only have public or package accessibility.

synchronized Methods
Several threads can be executing in a program (see Section 13.5, p. 626). They might
try to execute several methods on the same object simultaneously. Methods can be
declared synchronized if it is desirable that only one thread at a time can execute a
method of the object. Their execution is then mutually exclusive among all threads.
At any given time, at most one thread can be executing a synchronized method on
an object. This discussion also applies to static synchronized methods of a class.
In Example 4.15, both the push() method, declared at (1), and the pop() method,
declared at (2), are synchronized in the class StackImpl. Only one thread at a time can

151

4.10: OTHER MODIFIERS FOR MEMBERS

execute a synchronized method in an object of the class StackImpl. This means that
it is not possible for the state of an object of the class StackImpl to be corrupted, for
example, while one thread is pushing an element and another is attempting to pop
the stack.
Example 4.15 Synchronized Methods
class StackImpl {
// Non-generic partial implementation
private Object[] stackArray;
private int topOfStack;
// ...
synchronized public void push(Object elem) { // (1)
stackArray[++topOfStack] = elem;
}
synchronized public Object pop() {
Object obj = stackArray[topOfStack];
stackArray[topOfStack] = null;
topOfStack--;
return obj;
}

// (2)

// Other methods, etc.
public Object peek() { return stackArray[topOfStack]; }
}

native Methods
Native methods are methods whose implementation is not defined in Java but in
another programming language, for example, C or C++. Such a method can be
declared as a member in a Java class declaration. Since its implementation appears
elsewhere, only the method header is specified in the class declaration. The keyword native is mandatory in the method header. A native method can also specify
checked exceptions in a throws clause (Section 6.9, p. 257), but the compiler cannot
check them, since the method is not implemented in Java.
In the following example, a native method in the class Native is declared at (2). The
class also uses a static initializer block (see Section 9.9, p. 410) at (1) to load the
native library when the class is loaded. Clients of the Native class can call the native
method like any another method, as at (3).
class Native {
/*
* The static block ensures that the native method library
* is loaded before the native method is called.
*/
static {
System.loadLibrary("NativeMethodLib"); // (1) Load native library.
}

152

CHAPTER 4: ACCESS CONTROL

native void nativeMethod();
// ...

// (2) Native method header.

}
class Client {
//...
public static void main(String[] args) {
Native trueNative = new Native();
trueNative.nativeMethod();
}
//...
}

// (3) Native method call.

The Java Native Interface (JNI) is a special API that allows Java methods to invoke
native functions implemented in C.

transient Fields
Often it is desirable to save the state of an object. Such objects are said to be persistent. In Java, the state of an object can be stored using serialization (see Section 11.6,
p. 510). Serialization transforms objects into an output format that is conducive for
storing objects. Objects can later be retrieved in the same state as when they were
serialized, meaning that all fields included in the serialization will have the same
values as at the time of serialization.
Sometimes the value of a field in an object should not be saved, in which case, the
field can be specified as transient in the class declaration. This implies that its
value should not be saved when objects of the class are written to persistent storage. In the following example, the field currentTemperature is declared transient at
(1), because the current temperature is most likely to have changed when the object
is restored at a later date. However, the value of the field mass, declared at (2), is
likely to remain unchanged. When objects of the class Experiment are serialized, the
value of the field currentTemperature will not be saved, but that of the field mass will
be, as part of the state of the serialized object.
class Experiment implements Serializable {
// ...
// The value of currentTemperature will not persist.
transient int currentTemperature;
// (1) Transient value.
double mass;

// (2) Persistent value.

}

Specifying the transient modifier for static variables is redundant and, therefore,
discouraged. Static variables are not part of the persistent state of a serialized
object.

153

4.10: OTHER MODIFIERS FOR MEMBERS

volatile Fields
During execution, compiled code might cache the values of fields for efficiency reasons. Since multiple threads can access the same field, it is vital that caching is not
allowed to cause inconsistencies when reading and writing the value in the field.
The volatile modifier can be used to inform the compiler that it should not attempt
to perform optimizations on the field, which could cause unpredictable results
when the field is accessed by multiple threads (see also Example 13.5, p. 644).
In the simple example below, the value of the field clockReading might be changed
unexpectedly by another thread while one thread is performing a task that
involves always using the current value of the field clockReading. Declaring the
field as volatile ensures that a write operation will always be performed on the
master field variable, and a read operation will always return the correct current
value.
class VitalControl {
// ...
volatile long clockReading;
// Two successive reads might give different results.
}
Table 4.5

Summary of Other Modifiers for Members
Modifiers

Fields

Methods

static

Defines a class variable.

Defines a class method.

final

Defines a constant.

The method cannot be overridden.

abstract

Not applicable.

No method body is defined. Its
class must also be designated
abstract.

synchronized

Not applicable.

Only one thread at a time can
execute the method.

native

Not applicable.

Declares that the method is
implemented in another language.

transient

The value in the field will
not be included when the
object is serialized.

Not applicable.

volatile

The compiler will not
attempt to optimize access
to the value in the field.

Not applicable.

154

CHAPTER 4: ACCESS CONTROL

Review Questions
4.19

Which statements about the use of modifiers are true?
Select the two correct answers.
(a) If no accessibility modifier (public, protected, or private) is specified for a
member declaration, the member is only accessible by classes in the package
of its class and by subclasses of its class in any package.
(b) You cannot specify accessibility of local variables. They are only accessible
within the block in which they are declared.
(c) Subclasses of a class must reside in the same package as the class they extend.
(d) Local variables can be declared static.
(e) The objects themselves do not have any accessibility modifiers, only the
object references do.

4.20

Given the following source code, which comment line can be uncommented without introducing errors?
abstract class MyClass {
abstract void f();
final
void g() {}
//final
void h() {}

// (1)

protected static int i;
private
int j;
}
final class MyOtherClass extends MyClass {
//MyOtherClass(int n) { m = n; }

// (2)

public static void main(String[] args) {
MyClass mc = new MyOtherClass();
}
void
void
//void
//void

f()
h()
k()
l()

{}
{}
{ i++; }
{ j++; }

int m;
}

Select the one correct answer.
(a)
(b)
(c)
(d)

(1)
(2)
(3)
(4)

// (3)
// (4)

155

4.10: OTHER MODIFIERS FOR MEMBERS

4.21

What would be the result of compiling and running the following program?
class MyClass {
static MyClass ref;
String[] arguments;
public static void main(String[] args) {
ref = new MyClass();
ref.func(args);
}
public void func(String[] args) {
ref.arguments = args;
}
}

Select the one correct answer.
(a) The program will fail to compile, since the static method main() cannot have a
call to the non-static method func().
(b) The program will fail to compile, since the non-static method func() cannot
access the static variable ref.
(c) The program will fail to compile, since the argument args passed to the static
method main() cannot be passed to the non-static method func().
(d) The program will compile, but will throw an exception when run.
(e) The program will compile and run successfully.
4.22

Given the following member declarations, which statement is true?
int a;
static int a;
int f() { return a; }
static int f() { return a; }

//
//
//
//

(1)
(2)
(3)
(4)

Select the one correct answer.
(a)
(b)
(c)
(d)
4.23

Declarations (1) and (3) cannot occur in the same class declaration.
Declarations (2) and (4) cannot occur in the same class declaration.
Declarations (1) and (4) cannot occur in the same class declaration.
Declarations (2) and (3) cannot occur in the same class declaration.

Which statement is true?
Select the one correct answer.
(a) A static method can call other non-static methods in the same class by using
the this keyword.
(b) A class may contain both static and non-static variables, and both static and
non-static methods.
(c) Each object of a class has its own instance of the static variables declared in
the class.
(d) Instance methods may access local variables of static methods.
(e) All methods in a class are implicitly passed the this reference as argument,
when invoked.

156

CHAPTER 4: ACCESS CONTROL

4.24

What, if anything, is wrong with the following code?
abstract class MyClass {
transient int j;
synchronized int k;
final void MyClass() {}
static void f() {}
}

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
(f)
4.25

The class MyClass cannot be declared abstract.
The field j cannot be declared transient.
The field k cannot be declared synchronized.
The method MyClass() cannot be declared final.
The method f() cannot be declared static.
Nothing is wrong with the code; it will compile successfully.

Which one of these is not a legal member declaration within a class?
Select the one correct answer.
(a) static int a;
(b) final Object[] fudge = { null };
(c) abstract int t;
(d) native void sneeze();
(e) final static private double PI = 3.14159265358979323846;

4.26

Which statements about modifiers are true?
Select the two correct answers.
(a)
(b)
(c)
(d)
(e)

4.27

Abstract classes can declare final methods.
Fields can be declared native.
Non-abstract methods can be declared in abstract classes.
Classes can be declared native.
Abstract classes can be declared final.

Which statement is true?
Select the one correct answer.
(a) The values of transient fields will not be saved during serialization.
(b) Constructors can be declared abstract.
(c) The initial state of an array object constructed with the statement int[] a =
new int[10] will depend on whether the array variable a is a local variable or a
field.
(d) A subclass of a class with an abstract method must provide an implementation for the abstract method.
(e) Only static methods can access static members.

PROGRAMMING EXERCISE

157

Chapter Summary
The following information was included in this chapter:
• the structure of a Java source file
• defining, using, and deploying packages
• explanation of class scope for members, and block scope for local variables
• discussion of accessibility (default, public) and other modifiers (abstract, final)
for reference types
• applicability of member accessibility (default, public, protected, private) and
other member modifiers (static, final, abstract, synchronized, native, transient,
volatile)

Programming Exercise
4.1

Design a class for a bank database. The database should support the following
operations:
❍

deposit a certain amount into an account

❍

withdraw a certain amount from an account

❍

get the balance (i.e., the current amount) in an account

❍

transfer an amount from one account to another

The amount in the transactions is a value of type double. The accounts are identified
by instances of the class Account that is in the package com.megabankcorp.records.
The database class should be placed in a package called com.megabankcorp.system.
The deposit, withdraw, and balance operations should not have any implementation, but allow subclasses to provide the implementation. The transfer operation should use the deposit and withdraw operations to implement the transfer.
It should not be possible to alter this operation in any subclass, and only classes
within the package com.megabankcorp.system should be allowed to use this operation. The deposit and withdraw operations should be accessible in all packages.
The balance operation should only be accessible in subclasses and classes within
the package com.megabankcorp.system.

This page intentionally left blank

Operators and Expressions

5

Exam Objectives
3.1 Develop code that uses the primitive wrapper classes (such as Boolean,
Character, Double, Integer, etc.), and/or autoboxing & unboxing.
Discuss the differences between the String, StringBuilder, and
StringBuffer classes.
❍

Boxing/unboxing are covered in this chapter.

For primitive wrapper classes and string handling classes, see Chapter 10, p.
423.
7.6 Write code that correctly applies the appropriate operators including
assignment operators (limited to: =, +=, -=), arithmetic operators (limited
to: +, -, *, /, %, ++, --), relational operators (limited to: <, <=, >, >=, ==, !=),
the instanceof operator, logical operators (limited to: &, |, ^, !, &&, ||),
and the conditional operator ( ? : ), to produce a desired result. Write code
that determines the equality of two objects or two primitives.
❍

❍

For the instanceof operator, see Section 7.11, p. 328.

❍

For object equality, see also Section 15.1, p. 751.

Supplementary Objectives
• Distinguish between conversion categories and conversion contexts, and
understand which conversions are permissible in each conversion context.
• Understand the order in which operands and operators are evaluated,
including the precedence and associativity rules.

159

160

CHAPTER 5: OPERATORS AND EXPRESSIONS

5.1 Conversions
In this section we first discuss the different kinds of type conversions that can be
applied to values, and in the next section we discuss the contexts in which these
conversions are permitted. Some type conversions must be explicitly stated in the
program, while others are performed implicitly. Some type conversions can be
checked at compile time to guarantee their validity at runtime, while others will
require an extra check at runtime.

Widening and Narrowing Primitive Conversions
For the primitive data types, the value of a narrower data type can be converted to
a value of a wider data type. This is called a widening primitive conversion. Widening
conversions from one primitive type to the next wider primitive type are summarized in Figure 5.1. The conversions shown are transitive. For example, an int can
be directly converted to a double without first having to convert it to a long and a
float.
Note that the target type of a widening primitive conversion has a wider range of
values than the source type, e.g., the range of the long type subsumes the range of
the int type. In widening conversions between integral types, the source value
remains intact, with no loss of magnitude information. However, a widening conversion from an int or a long value to a float value, or from a long value to a double
value, may result in a loss of precision. The floating-point value in the target type is
then a correctly rounded approximation of the integer value. Note that precision
relates to the number of significant bits in the value, and must not be confused with
magnitude, which relates how big a value can be represented.
Figure 5.1

Widening Primitive Conversions
byte

short
int

long

float

double

char

Converting from a wider primitive type to a narrower primitive type is called a
narrowing primitive conversion, which can result in loss of magnitude information,
and possibly in precision as well. Any conversion which is not a widening primitive conversion according to Figure 5.1 is a narrowing primitive conversion. The
target type of a narrowing primitive conversion has a narrower range of values than
the source type, for example, the range of the int type does not include all the values in the range of the long type.
Note that all conversions between char and the two integer types byte and short are
considered narrowing primitive conversions: the reason being that the conversions

161

5.1: CONVERSIONS

between the unsigned type char and the signed types byte or short can result in loss
of information. These narrowing conversions are done in two steps, first converting the source value to the int type, and then converting the int value to the target
type.
Widening primitive conversions are usually done implicitly, whereas narrowing
primitive conversions usually require a cast (Section 5.2, p. 164). It is not illegal to
use a cast for a widening conversion. However, the compiler will flag any conversion that require a cast if none has been specified. Regardless of any loss of magnitude or precision, widening and narrowing primitive conversions never result in a
runtime exception.
Ample examples of widening and narrowing primitive conversions can be found
in this chapter and also in Section 3.7, p. 81.

Widening and Narrowing Reference Conversions
The subtype-supertype relationship between reference types determines which conversions are permissible between them. Conversions up the type hierarchy are called
widening reference conversions (also called upcasting), i.e., such a conversion converts
from a subtype to a supertype.
Object obj = "upcast me";

// Widening: Object <----- String

Conversions down the type hierarchy represent narrowing reference conversions (also
called downcasting).
String str = (String) obj; // Narrowing requires cast: String <----- Object

A subtype is a narrower type than its supertype in the sense that it has no relationship with other subtypes of its supertype, i.e., a supertype can be the supertype of
types that a subtype is not supertype of. For example, given that A is the supertype
of immediate subtypes B, C, and D, each subtype is narrower than the supertype A,
as any one of the subtypes cannot represent the other two subtypes. Contexts
under which reference conversions can occur are discussed in Section 7.8, p. 319.
Widening reference conversions are usually done implicitly, whereas narrowing
reference conversions usually require a cast (Section 5.2, p. 164). The compiler will
reject casts that are not legal or issue an unchecked warning under certain circumstances if type safety cannot be guaranteed (Section 14.2, p. 670).
Widening reference conversions do not require any runtime checks and never
result in an exception during execution. This is not the case for narrowing reference
conversions, which require a runtime check and can throw a ClassCastException if
the conversion is not legal.

162

CHAPTER 5: OPERATORS AND EXPRESSIONS

Boxing and Unboxing Conversions
For an overview of the primitive types and their wrapper types, see Table 2.13,
p. 30. For an overview of the methods provided by the wrapper types, see Section
10.3, p. 428.
A boxing conversion converts the value of a primitive type to a corresponding value
of its wrapper type. If p is a value of a primitiveType, boxing conversion converts p
into a reference r of corresponding WrapperType, such that r.primitiveTypeValue()
== p. In the code below, the int value 10 results in an object of the type Integer
implicitly being created that contains the int value 10. We say that the int value 10
has been boxed in an object of the wrapper type Integer.
Integer iRef = 10;
System.out.println(iRef.intValue() == 10);

// Boxing: Integer <----- int
// true

An unboxing conversion converts the value of a wrapper type to a value of its corresponding primitive type. If r is a reference of a WrapperType, unboxing conversion
converts the reference r into r.primitiveTypeValue(), where primitiveType is the
primitive type corresponding to the WrapperType. In the code below, the value in
the Integer object referenced by iRef is implicitly converted to the int type. We say
that the wrapper object has been unboxed to its corresponding primitive type.
int i = iRef;
System.out.println(iRef.intValue() == i);

// Unboxing: int <----- Integer
// true

Note that both boxing and unboxing are done implicitly in the right context. Boxing allows primitive values to be used where an object of their wrapper type is
expected, and unboxing allows the converse. Unboxing makes it possible to use a
Boolean wrapper object in a boolean expression and as an integral wrapper object
in an arithmetic expression. Unboxing a wrapper reference that has the null value
results in a NullPointerException. Ample examples of boxing and unboxing can be
found in this chapter and in Section 7.8, p. 319.

Other Conversions
We briefly mention some other conversions and where they are covered in this
book.
• Identity conversions are always permitted, as they allow conversions from a type
to that same type. An identity conversion is always permitted.
int i = (int) 10;
String str = (String) "Hi";

// int <---- int
// String <---- String

• String conversions allow a value of any other type to be converted to a String
type in the context of the string concatenation operator + (Section 5.7, p. 185).
• Unchecked conversions are permitted to facilitate operability between legacy and
generic code (Section 14.2, p. 670).

163

5.2: TYPE CONVERSION CONTEXTS

• Capture conversions aid in increasing the usefulness of wildcards in generic code
(Section 14.9, p. 703).

5.2 Type Conversion Contexts
Selected conversion contexts and the conversions that are applicable in these
contexts are summarized in Table 5.1. The conversions shown in each context
occur implicitly, without the program having to take any special action. For other
conversion contexts, see the sections mentioned in the subsection Other Conversions, p. 162.
Table 5.1

Selected Conversion Contexts and Conversion Categories
Conversion Contexts
Conversion
Categories

Assignment

Method Invocation

Casting

Numeric
Promotion

Widening/
Narrowing

widening

widening

both

widening

Primitive

narrowing for

Conversions

constant
expressions of non-

widening

widening

both,
followed
by
optional
unchecked
conversion

Not
applicable

unboxing, followed
by optional
widening primitive
conversion

unboxing, followed
by optional
widening primitive
conversion

both

unboxing,
followed
by
optional
widening

boxing, followed by
optional widening

boxing, followed by
optional widening

reference

reference

conversion

conversion

long integer type,
with optional
boxing

Widening/
Narrowing

Reference
Conversions

Boxing/
Unboxing
Conversions

primitive
conversion

164

CHAPTER 5: OPERATORS AND EXPRESSIONS

Assignment Context
Assignment conversions that can occur in an assignment context are shown in the
second column of Table 5.1. An assignment conversion converts the type of an
expression to the type of a target variable.
An expression (or its value) is assignable to the target variable, if the type of the
expression can be converted to the type of the target variable by an assignment
conversion. Equivalently, the type of the expression is assignment compatible with
the type of the target variable.
For assignment conversion involving primitive data types, see Section 5.5, p. 169.
Note the special case where a narrowing conversion occurs when assigning a nonlong integer constant expression:
byte b = 10;

// Narrowing conversion: byte <--- int

For assignment conversions involving reference types, see Section 7.8, p. 319.

Method Invocation Context
Method invocation conversions that can occur in a method invocation context are
shown in the third column of Table 5.1. Note that method invocation and assignment
conversions differ in one respect: method invocation conversions do not include the
implicit narrowing conversion performed for integer constant expressions.
A method invocation conversion involves converting each argument value in a
method or constructor call to the type of the corresponding formal parameter in
the method or constructor declaration.
Method invocation conversions involving parameters of primitive data types are
discussed in Section 3.7, p. 82, and those involving reference types are discussed in
Section 7.8, p. 319.

Casting Context of the Unary Type Cast Operator: (type)
Java, being a strongly typed language, checks for type compatibility (i.e., checks if a
type can substitute for another type in a given context) at compile time. However,
some checks are only possible at runtime (for example, which type of object a reference actually denotes during execution). In cases where an operator would have
incompatible operands (e.g., assigning a double to an int), Java demands that a type
cast be used to explicitly indicate the type conversion. The type cast construct has
the following syntax:
() 

The cast operator () is applied to the value of the . At runtime,
a cast results in a new value of , which best represents the value of the
 in the old type. We use the term casting to mean applying the cast
operator for explicit type conversion.

5.2: TYPE CONVERSION CONTEXTS

165

However, in the context of casting, implicit casting conversions can take place.
These casting conversions are shown in the fourth column of Table 5.1. Casting
conversions include more conversion categories than the assignment or the
method invocation conversions. In the code below, the comments indicate the category of the conversion that takes place because of the cast operator on the righthand side of each assignment—although some casts are not necessary for the sake
of the assignment.
long l = (long) 10;
// Widening primitive conversion: long <--- int
int i = (int) l;
// Narrowing primitive conversion: int <--- long
Object obj = (Object) "Upcast me"; // Widening ref conversion: Object <--- String
String str = (String) obj;
// Narrowing ref conversion: String <--- Object
Integer iRef = (Integer) i;
// Boxing: Integer <--- int
i = (int) iRef;
// Unboxing: int <--- Integer

A casting conversion is applied to the value of the operand  of a cast
operator. Casting can be applied to primitive values as well as references. Casting
between primitive data types and reference types is not permitted, except where
boxing and unboxing is applicable. Boolean values cannot be cast to other data values, and vice versa. The reference literal null can be cast to any reference type.
Examples of casting between primitive data types are provided in this chapter.
Casting reference values is discussed in Section 7.11, p. 327. Implications that
generics have on casting are discussed in Section 14.13, p. 724.

Numeric Promotion Context
Numeric operators only allow operands of certain types. Numeric promotion
results in conversions being applied to the operands to convert them to permissible
types. Numeric promotion conversions that can occur in a numeric promotion context
are shown in the fifth column of Table 5.1. Permissible conversion categories are:
widening primitive conversions and unboxing conversions. A distinction is made
between unary and binary numeric promotion.

Unary Numeric Promotion
Unary numeric promotion proceeds as follows:
• If the single operand is of type Byte, Short, Character, or Integer, it is unboxed. If
the resulting value is narrower than int, it is promoted to a value of type int by
a widening conversion.
• Otherwise, if the single operand is of type Long, Float, or Double, it is unboxed.
• Otherwise, if the single operand is of a type narrower than int, its value is promoted to a value of type int by a widening conversion.
• Otherwise, the operand remains unchanged.
In other words, unary numeric promotion results in an operand value that is either
int or wider.

166

CHAPTER 5: OPERATORS AND EXPRESSIONS

Unary numeric promotion is applied in the following expressions:
• operand of the unary arithmetic operators + and - (see Section 5.6, p. 174)
• array creation expression; e.g., new int[20], where the dimension expression (in
this case 20) must evaluate to an int value (see Section 3.6, p. 70)
• indexing array elements; e.g., objArray['a'], where the index expression (in this
case 'a') must evaluate to an int value (see Section 3.6, p. 72)

Binary Numeric Promotion
Binary numeric promotion implicitly applies appropriate widening primitive conversions so that a pair of operands have the widest numeric type of the two, which
is always at least int. Given T to be the widest numeric type of the two operands
after any unboxing conversions have been performed, the operands are promoted
as follows during binary numeric promotion:
If T is wider than int, both operands are converted to T; otherwise, both
operands are converted to int.
This means that the resulting type of the operands is at least int.
Binary numeric promotion is applied in the following expressions:
• operands of the arithmetic operators *, /, %, +, and - (see Section 5.6, p. 174)
• operands of the relational operators <, <=, >, and >= (see Section 5.10, p. 190)
• operands of the numerical equality operators == and != (see Section 5.11, p. 191)
• operands of the conditional operator ? :, under certain circumstances (see Section 5.14, p. 201)

5.3 Precedence and Associativity Rules for Operators
Precedence and associativity rules are necessary for deterministic evaluation of
expressions. The operators are summarized in Table 5.2. The majority of them are
discussed in subsequent sections in this chapter.
The following remarks apply to Table 5.2:
• The operators are shown with decreasing precedence from the top of the table.
• Operators within the same row have the same precedence.
• Parentheses, ( ), can be used to override precedence and associativity.
• The unary operators, which require one operand, include the following: the
postfix increment (++) and decrement (--) operators from the first row, all the
prefix operators (+, -, ++, --, ~, !) in the second row, and the prefix operators
(object creation operator new, cast operator (type)) in the third row.
• The conditional operator (? :) is ternary, that is, requires three operands.

5.3: PRECEDENCE AND ASSOCIATIVITY RULES FOR OPERATORS

167

• All operators not listed above as unary or ternary, are binary, that is, require two
operands.
• All binary operators, except for the relational and assignment operators, associate from left to right. The relational operators are nonassociative.
• Except for unary postfix increment and decrement operators, all unary operators, all assignment operators, and the ternary conditional operator associate
from right to left.
Table 5.2

Operator Summary
Postfix operators

[] . (parameters) expression++ expression--

Unary prefix operators

++expression --expression +expression -expression ~ !

Unary prefix creation and cast

new (type)

Multiplicative

* / %

Additive

+ -

Shift

<< >> >>>

Relational

< <= > >= instanceof

Equality

== !=

Bitwise/logical AND

&

Bitwise/logical XOR

^

Bitwise/logical OR

|

Conditional AND

&&

Conditional OR

||

Conditional

?:

Assignment

= += -= *= /= %= <<= >>= >>>= &= ^= |=

Precedence rules are used to determine which operator should be applied first if
there are two operators with different precedence, and these follow each other in
the expression. In such a case, the operator with the highest precedence is applied
first.
2 + 3 * 4 is evaluated as 2 + (3 * 4) (with the result 14) since * has higher precedence

than +.
Associativity rules are used to determine which operator should be applied first if
there are two operators with the same precedence, and these follow each other in
the expression.
Left associativity implies grouping from left to right:
1 + 2 - 3 is interpreted as ((1 + 2) - 3), since the binary operators + and - both

have same precedence and left associativity.

168

CHAPTER 5: OPERATORS AND EXPRESSIONS

Right associativity implies grouping from right to left:
- - 4 is interpreted as (- (- 4)) (with the result 4), since the unary operator - has
right associativity.

The precedence and associativity rules together determine the evaluation order of the
operators.

5.4 Evaluation Order of Operands
In order to understand the result returned by an operator, it is important to understand the evaluation order of its operands. In general, the operands of operators are
evaluated from left to right.
The evaluation order also respects any parentheses, and the precedence and associativity rules of operators.
Examples illustrating how the operand evaluation order influences the result
returned by an operator, can be found in Sections 5.5 and 5.8.

Left-Hand Operand Evaluation First
The left-hand operand of a binary operator is fully evaluated before the right-hand
operand is evaluated.
The evaluation of the left-hand operand can have side effects that can influence the
value of the right-hand operand. For example, in the following code:
int b = 10;
System.out.println((b=3) + b);

the value printed will be 6 and not 13. The evaluation proceeds as follows:
(b=3) + b
3

+ b

3

+ 3

b is assigned the value 3

6

If evaluation of the left-hand operand of a binary operator raises an exception (see
Section 6.5, p. 235), we cannot rely on the presumption that the right-hand operand
has been evaluated.

Operand Evaluation before Operation Execution
Java guarantees that all operands of an operator are fully evaluated before the actual
operation is performed. This rule does not apply to the short-circuit conditional
operators &&, ||, and ?:.

5.5: THE SIMPLE ASSIGNMENT OPERATOR =

169

This rule also applies to operators that throw an exception (the integer division
operator / and the integer remainder operate %). The operation is only performed
if the operands evaluate normally. Any side-effects of the right-hand operand will
have been effectuated before the operator throws an exception.

Left to Right Evaluation of Argument Lists
In a method or constructor invocation, each argument expression in the argument
list is fully evaluated before any argument expression to its right.
If evaluation of an argument expression does not complete normally, we cannot
presume that any argument expression to its right has been evaluated.

5.5 The Simple Assignment Operator =
The assignment statement has the following syntax:
 = 
which can be read as “the target, , gets the value of the source, ”.
The previous value of the target variable is overwritten by the assignment operator =.
The target  and the source  must be assignment compatible.
The target variable must also have been declared. Since variables can store either
primitive values or reference values,  evaluates to either a primitive
value or a reference value.

Assigning Primitive Values
The following examples illustrate assignment of primitive values:
int
j =
j =
k =

j, k;
10;
5;
j;

// j gets the value 10.
// j gets the value 5. Previous value is overwritten.
// k gets the value 5.

The assignment operator has the lowest precedence allowing the expression on the
right-hand side to be evaluated before assignment.
int
i =
i =
i =

i;
5;
i + 1;
20 - i * 2;

// i gets the value 5.
// i gets the value 6. + has higher precedence than =.
// i gets the value 8: (20 - (i * 2))

Assigning References
Copying reference values by assignment creates aliases, which is discussed in Section 1.3, p. 6. The following example recapitulates that discussion:

170

CHAPTER 5: OPERATORS AND EXPRESSIONS
Pizza pizza1 = new Pizza("Hot&Spicy");
Pizza pizza2 = new Pizza("Sweet&Sour");
pizza2 = pizza1;

Variable pizza1 is a reference to a pizza that is hot and spicy, and pizza2 is a
reference to a pizza which is sweet and sour. Assigning pizza1 to pizza2 means that
pizza2 now refers to the same pizza as pizza1, i.e., the hot and spicy one. After the
assignment, these variables are aliases and either one can be used to manipulate
the hot and spicy Pizza object.
Assigning a reference value does not create a copy of the source object denoted by
the reference variable on the right-hand side. It merely assigns the reference value
to the variable on the right-hand side to the variable on the left-hand side, so that
they denote the same object. Reference assignment also does not copy the state of
the source object to any object denoted by the reference variable on the left-hand
side.
A more detailed discussion of reference assignment can be found in Section 7.8, p.
319.

Multiple Assignments
The assignment statement is an expression statement, which means that application
of the binary assignment operator returns the value of the expression on the righthand side.
int j, k;
j = 10;
k = j;

// j gets the value 10 which is returned
// k gets the value of j, which is 10, and this value is returned

The last two assignments can be written as multiple assignments, illustrating the
right associativity of the assignment operator.
k = j = 10;

// (k = (j = 10))

Multiple assignments are equally valid with references.
Pizza pizzaOne, pizzaTwo;
pizzaOne = pizzaTwo = new Pizza("Supreme"); // Aliases.

The following example shows the effect of operand evaluation order:
int[] a = {10, 20, 30, 40, 50}; // an array of int
int index = 4;
a[index] = index = 2;
// (1)

What is the value of index, and which array element a[index] is assigned a value in
the multiple assignment statement at (1)? The evaluation proceeds as follows:
a[index]
a[4]
a[4]
a[4]

= index = 2;
= index = 2;
= (index = 2);
=
2;

// index gets the value 2. = is right associative.
// The value of a[4] is changed from 50 to 2.

5.5: THE SIMPLE ASSIGNMENT OPERATOR =

171

Type Conversions in Assignment Context
If the target and source have the same type in an assignment, then, obviously, the
source and the target are assignment compatible and the source value need not be
converted. Otherwise, if a widening primitive conversion is permissible, then the
widening conversion is applied implicitly, i.e., the source type is converted to the
target type in an assignment context.
// Widening Primitive Conversions
int
smallOne = 1234;
long
bigOne
= 2000;
double largeOne = bigOne;
double hugeOne = (double) bigOne;

// Widening: int to long.
// Widening: long to double.
// Cast redundant but allowed.

A widening primitive conversion can result in loss of precision. In the next example,
the precision of the least significant bits of the long value may be lost when converting to a float value.
long bigInteger = 98765432112345678L;
float fpNum = bigInteger; // Widening but loss of precision: 9.8765436E16

Additionally, implicit narrowing primitive conversions on assignment can occur in
cases where all of the following conditions are fulfilled:
• the source is a constant expression of either byte, short, char, or int type
• the target type is either byte, short, or char type
• the value of the source is determined to be in the range of the target type at
compile time
Here are some examples to illustrate how these conditions effect narrowing primitive conversions:
// Above conditions fulfilled for implicit narrowing primitive conversions.
short s1 = 10;
// int value in range.
short s2 = 'a';
// char value in range.
char c1 = 32;
// int value in range.
char c2 = (byte)35;
// byte value in range. (int value in range, without cast.)
byte b1 = 40;
// int value in range.
byte b2 = (short)40; // short value in range. (int value in range, without cast.)
final int i1 = 20;
byte b3 = i1;
// final value of i1 in range.

All other narrowing primitive conversions will produce a compile-time error on
assignment and will explicitly require a cast. Here are some examples:
// Above conditions not fulfilled for implicit narrowing primitive conversions.
// A cast is required.
int i2 = -20;
final int i3 = i2;
final int i4 = 200;
short s3 = (short) i2; // Not constant expression.
char c3 = (char) i3;
// final value of i3 not determinable.
char c4 = (char) i2;
// Not constant expression.

172

CHAPTER 5: OPERATORS AND EXPRESSIONS
byte b4 = (byte) 128;
byte b5 = (byte) i4;

// int value not in range.
// final value of i4 not in range.

Floating-point values are truncated when cast to integral values.
// The value
float huge
long giant
int
big
short small
byte minute
char symbol

is truncated to fit the size of the target type.
= (float) 1.7976931348623157d; // double to float.
= (long) 4415961481999.03D;
// (1) double to long.
= (int) giant;
// (2) long to int.
= (short) big;
// (3) int to short.
= (byte) small;
// (4) short to byte.
= (char) 112.5F;
// (5) float to char.

Table 5.3 shows how the values are truncated for assignments from (1) to (5).
Table 5.3

Examples of Truncated Values
Binary

Decimal

0000000000000000000001000000010000101011110100001100001100001111

4415961481999

(1)

00101011110100001100001100001111

735101711

(2)

1100001100001111

-15601

(3)

00001111

15

(4)

0000000001110000

'p'

(5)

The discussion on numeric assignment conversions also applies to numeric
parameter values at method invocation (see Section 3.7, p. 82), except for the narrowing conversions, which always require a cast.
The following examples illustrate boxing and unboxing in assignment context:
Boolean
boolRef = true;
Byte
bRef
= 2;
// Byte bRef2
= 257;
Integer iRef3 = (short)10;

//
//
//
//
//

boxing
constant
constant
constant
widening

short s = 10;
// Integer
iRef1 = s;

// narrowing
// short not assignable to Integer

boolean bv1 = boolRef;
byte b1 = bRef;

// unboxing
// unboxing

in range: narrowing to byte, then boxing
not in range: cast required
in range: casting by narrowing to short,
to int, then boxing

Integer iRefVal = null;
// Always allowed.
int
j = iRefVal;
// NullPointerException!
if (iRefVal != null) j = iRefVal; // Avoids the exception

5.5: THE SIMPLE ASSIGNMENT OPERATOR =

173

Review Questions
5.1

Given the following declaration:
char c = 'A';

What is the simplest way to convert the character value in c into an int?
Select the one correct answer.
(a) int i = c;
(b) int i = (int) c;
(c) int i = Character.getNumericValue(c);
5.2

What will be the result of compiling and running the following program?
public class Assignment {
public static void main(String[] args) {
int a, b, c;
b = 10;
a = b = c = 20;
System.out.println(a);
}
}

Select the one correct answer.
(a) The program will fail to compile since the compiler will report that the variable c in the multiple assignment statement a = b = c = 20; has not been initialized.
(b) The program will fail to compile, because the multiple assignment statement
a = b = c = 20; is illegal.
(c) The code will compile and print 10, when run.
(d) The code will compile and print 20, when run.
5.3

What will be the result of compiling and running the following program?
public class MyClass {
public static void main(String[] args) {
String a, b, c;
c = new String("mouse");
a = new String("cat");
b = a;
a = new String("dog");
c = b;
System.out.println(c);
}
}

Select the one correct answer.
(a) The program will fail to compile.
(b) The program will print mouse, when run.

174

CHAPTER 5: OPERATORS AND EXPRESSIONS

(c) The program will print cat, when run.
(d) The program will print dog, when run.
(e) The program will randomly print either cat or dog, when run.

5.6 Arithmetic Operators: *, /, %, +, Arithmetic operators are used to construct mathematical expressions as in algebra.
Their operands are of numeric type (which includes the char type).

Arithmetic Operator Precedence and Associativity
In Table 5.4, the precedence of the operators is in decreasing order, starting from
the top row, which has the highest precedence. Unary subtraction has higher precedence than multiplication. The operators in the same row have the same precedence.
Binary multiplication, division, and remainder operators have the same precedence. The unary operators have right associativity, and the binary operators have
left associativity.
Table 5.4

Arithmetic Operators
Unary

+ Addition

-

Subtraction

Binary

*

Multiplication

/

Division

+

Addition

-

Subtraction

% Remainder

Evaluation Order in Arithmetic Expressions
Java guarantees that the operands are fully evaluated from left to right before an
arithmetic binary operator is applied. If evaluation of an operand results in an
error, the subsequent operands will not be evaluated.
In the expression a + b * c, the operand a will always be fully evaluated before the
operand b, which will always be fully evaluated before the operand c. However,
the multiplication operator * will be applied before the addition operator +,
respecting the precedence rules. Note that a, b, and c are arbitrary arithmetic
expressions that have been determined to be the operands of the operators.
The evaluation order and precedence rules for arithmetic expressions are illustrated in Example 5.1. The evaluation of each operand in the expression at (1)
results in a call of the operandEval() method declared at (2). The first argument to
this method is a number to identify the operand and the second argument is the
operand value which is returned by the method. The output from the program
shows that all three operands were evaluated from left to right and the value of the
variable i shows that the precedence rules were applied in the evaluation.

5.6: ARITHMETIC OPERATORS: *, /, %, +, -

Example 5.1

175

Operand Evaluation Order
public class OperandEvaluationOrder {
public static void main(String[] args) {
// Evaluate: 4 + 5 * 6
int i = operandEval(1, 4) + operandEval(2, 5) * operandEval(3, 6);
System.out.println();
System.out.println("Value of i: " + i);
}
static int operandEval(int opNum, int operand) {
System.out.print(opNum);
return operand;
}

// (1)

// (2)

}

Output from the program:
123
Value of i: 34

Range of Numeric Values
As we have seen, all numeric types have a range of valid values (Section 2.2, p. 28).
This range is given by the constants named MAX_VALUE and MIN_VALUE, which are
defined in each numeric wrapper class.
The arithmetic operators are overloaded, meaning that the operation of an operator varies depending on the type of its operands. Floating-point arithmetic is performed if any operand of an operator is of floating-point type, otherwise, integer
arithmetic is performed.
Values that are out-of-range or are the results of invalid expressions are handled differently depending on whether integer or floating-point arithmetic is performed.

Integer Arithmetic
Integer arithmetic always returns a value that is in range, except in the case of integer division by zero and remainder by zero, which causes an ArithmeticException
(see the division operator / and the remainder operator % below). A valid value
does not necessarily mean that the result is correct, as demonstrated by the following examples:
int tooBig = Integer.MAX_VALUE + 1;
int tooSmall = Integer.MIN_VALUE - 1;

// -2147483648 which is Integer.MIN_VALUE.
// 2147483647 which is Integer.MAX_VALUE.

The results above should be values that are out-of-range. However, integer arithmetic wraps if the result is out-of-range, i.e., the result is reduced modulo in the
range of the result type. In order to avoid wrapping of out-of-range values, pro-

176

CHAPTER 5: OPERATORS AND EXPRESSIONS

grams should either use explicit checks or a wider type. If the type long is used in
the examples above, the results would be correct in the long range:
long notTooBig
= Integer.MAX_VALUE + 1L;
long notTooSmall = Integer.MIN_VALUE - 1L;

// 2147483648L in range.
// -2147483649L in range.

Floating-Point Arithmetic
Certain floating-point operations result in values that are out-of-range. Typically,
adding or multiplying two very large floating-point numbers can result in an
out-of-range value which is represented by Infinity (see Figure 5.2). Attempting
floating-point division by zero also returns infinity. The examples below show how
this value is printed as signed infinity.
System.out.println( 4.0 / 0.0);
System.out.println(-4.0 / 0.0);

// Prints: Infinity
// Prints: -Infinity

Both positive and negative infinity represent overflow to infinity, that is, the value
is too large to be represented as a double or float (see Figure 5.2). Signed infinity is
represented by named constants POSITIVE_INFINITY and NEGATIVE_INFINITY in the
wrapper classes java.lang.Float and java.lang.Double. A value can be compared
with these constants to detect overflow.

...

Double.MAX_VALUE

Infinity

]

Double.POSITIVE_INFINITY

...

Overflow and Underflow in Floating-point Arithmetic

Double.MIN_VALUE

0.0
-0.0

positive zero
negative zero

Out-of-range

Underflow

[

-Double.MIN_VALUE

Overflow

[

Figure 5.2

...
...

Double.NEGATIVE_INFINITY

]

-Double.MAX_VALUE

-Infinity

(Not drawn to scale)

Floating-point arithmetic can also result in underflow to zero, i.e., the value is too
small to be represented as a double or float (see Figure 5.2). Underflow occurs in
the following situations:

5.6: ARITHMETIC OPERATORS: *, /, %, +, -

177

• the result is between Double.MIN_VALUE (or Float.MIN_VALUE) and zero; e.g., the
result of (5.1E-324 - 4.9E-324). Underflow then returns positive zero 0.0 (or
0.0F).
• the result is between -Double.MIN_VALUE (or -Float.MIN_VALUE) and zero; e.g., the
result of (-Double.MIN_VALUE * 1E-1). Underflow then returns negative zero -0.0
(or -0.0F).
Negative zero compares equal to positive zero, i.e., (-0.0 == 0.0) is true.
Certain operations have no mathematical result, and are represented by NaN (Not
a Number). For example, calculating the square root of -1. Another example is
(floating-point) dividing zero by zero:
System.out.println(0.0 / 0.0);

// Prints: NaN

NaN is represented by the constant named NaN in the wrapper classes
java.lang.Float and java.lang.Double. Any operation involving NaN produces
NaN. Any comparison (except inequality !=) involving NaN and any other value
(including NaN) returns false. An inequality comparison of NaN with another
value (including NaN) always returns true. However, the recommended way of
checking a value for NaN is to use the static method isNaN() defined in both wrapper classes, java.lang.Float and java.lang.Double.

Strict Floating-Point Arithmetic: strictfp
Although floating-point arithmetic in Java is defined in accordance with the
IEEE-754 32-bit (float) and 64-bit (double) standard formats, the language does
allow JVM implementations to use other extended formats for intermediate
results. This means that floating-point arithmetic can give different results on
such JVMs, with possible loss of precision. Such a behavior is termed non-strict,
in contrast to being strict and adhering to the standard formats.
To ensure that identical results are produced on all JVMs, the keyword strictfp can
be used to enforce strict behavior for floating-point arithmetic. The modifier
strictfp can be applied to classes, interfaces, and methods. A strictfp method
ensures that all code in the method is executed strictly. If a class or interface is
declared to be strictfp, then all code (in methods, initializers, and nested classes
and interfaces) is executed strictly. If the expression is determined to be in a
strictfp construct, it is executed strictly. However, note that strictness is not inherited by the subclasses or subinterfaces. Constant expressions are always evaluated
strictly at compile time.

Unary Arithmetic Operators: -, +
The unary operators have the highest precedence of all the arithmetic operators.
The unary operator - negates the numeric value of its operand. The following
example illustrates the right associativity of the unary operators:
int value = - -10;

// (-(-10)) is 10

178

CHAPTER 5: OPERATORS AND EXPRESSIONS

Notice the blank needed to separate the unary operators; otherwise, these would
be interpreted as the decrement operator -- (see Section 5.8, p. 186). The unary
operator + has no effect on the evaluation of the operand value.
Section G.4 on page 1010 discusses how negative integers are represented using 2’s
complement.

Multiplicative Binary Operators: *, /, %
Multiplication Operator: *
The multiplication operator * multiplies two numbers.
int
sameSigns
= -4
double oppositeSigns = 4.0
int
zero
= 0

* -8;
* -8.0;
* -0;

// result: 32
// result: -32.0
// result:
0

Division Operator: /
The division operator / is overloaded. If its operands are integral, the operation
results in integer division.
int
i1 = 4 / 5;
int
i2 = 8 / 8;
double d1 = 12 / 8;

// result: 0
// result: 1
// result: 1.0, integer division, then widening conversion.

Integer division always returns the quotient as an integer value, i.e., the result is
truncated toward zero. Note that the division performed is integer division if the
operands have integral values, even if the result will be stored in a floating-point
type. The integer value is subjected to a widening conversion in the assignment
context.
An ArithmeticException is thrown when attempting integer division with zero,
meaning that integer division by zero is an illegal operation.
If any of the operands is a floating-point type, the operation performs floating-point
division, where relevant operand values undergo binary numeric promotion:
double d2 = 4.0 / 8;
double d3 = 8 / 8.0;
double d4 = 12.0F / 8;

// result: 0.5
// result: 1.0
// result: 1.5F

double result1 = 12.0 / 4.0 * 3.0;
double result2 = 12.0 * 3.0 / 4.0;

// ((12.0 / 4.0) * 3.0) which is 9.0
// ((12.0 * 3.0) / 4.0) which is 9.0

Remainder Operator: %
In mathematics, when we divide a number (the dividend) by another number (the
divisor), the result can be expressed in terms of a quotient and a remainder. For example, dividing 7 by 5, the quotient is 1 and the remainder is 2. The remainder operator % returns the remainder of the division performed on the operands.

5.6: ARITHMETIC OPERATORS: *, /, %, +, int quotient = 7 / 5;
int remainder = 7 % 5;

179
// Integer division operation: 1
// Integer remainder operation: 2

For integer remainder operation, where only integer operands are involved, evaluation of the expression (x % y) always satisfies the following relation:
x == (x / y) * y + (x % y)
In other words, the right-hand side yields a value that is always equal to the value
of the dividend. The following examples show how we can calculate the remainder
so that the above relation is satisfied:
Calculating (7 % 5):
7 == (7 / 5) * 5 + (7 % 5)
== ( 1 ) * 5 + (7 % 5)
==
5 + (7 % 5)
2 ==
(7 % 5)

i.e., (7 % 5) is equal to 2

Calculating (7 % -5):
7 == (7 / -5) * -5 + (7
== ( -1 ) * -5 + (7
==
5 + (7
2 ==
(7

%
%
%
%

-5)
-5)
-5)
-5)

i.e., (7 % -5) is equal to 2

Calculating (-7 % 5):
-7 == (-7 / 5) * 5 + (-7 % 5)
== ( -1 ) * 5 + (-7 % 5)
==
-5 + (-7 % 5)
-2 ==
(-7 % 5)

i.e., (-7 % 5) is equal to -2

Calculating (-7 % -5):
-7 == (-7 / -5) * -5 + (-7 % -5)
== (
1
) * -5 + (-7 % -5)
==
-5 + (-7 % -5)
-2 ==
(-7 % -5)

i.e., (-7 % -5) is equal to -2

The above relation shows that the remainder can only be negative if the dividend
is negative, and the sign of the divisor is irrelevant. A shortcut to evaluating the
remainder involving negative operands is the following: ignore the signs of the
operands, calculate the remainder, and negate the remainder if the dividend is
negative.
int r0
int r1
long r2
int r3
long r4
boolean

= 7 % 7;
// 0
= 7 % 5;
// 2
= 7L % -5L;
// 2L
= -7 % 5;
// -2
= -7L % -5L;
// -2L
relation = -7L == (-7L / -5L) * -5L + r4;

// true

An ArithmeticException is thrown if the divisor evaluates to zero.
Note that the remainder operator not only accepts integral operands, but floatingpoint operands as well. The floating-point remainder r is defined by the relation:
r == a - (b * q)

180

CHAPTER 5: OPERATORS AND EXPRESSIONS

where a and b are the dividend and the divisor, respectively, and q is the integer
quotient of (a/b). The following examples illustrate a floating-point remainder
operation:
double
float
double
float
double
boolean
float

dr0 = 7.0 % 7.0;
// 0.0
fr1 = 7.0F % 5.0F;
// 2.0F
dr1 = 7.0 % -5.0;
// 2.0
fr2 = -7.0F % 5.0F;
// -2.0F
dr2 = -7.0 % -5.0;
// -2.0
fpRelation = dr2 == (-7.0) - (-5.0) * (long)(-7.0 / -5.0);
fr3 = -7.0F % 0.0F;
// NaN

// true

Additive Binary Operators: +, The addition operator + and the subtraction operator - behave as their names
imply: add or subtract values. The binary operator + also acts as string concatenation
if any of its operands is a string (see Section 5.7, p. 185).
Additive operators have lower precedence than all the other arithmetic operators.
Table 5.5 includes examples that show how precedence and associativity are used
in arithmetic expression evaluation.
Table 5.5

Examples of Arithmetic Expression Evaluation
Arithmetic Expression

Evaluation

Result When Printed

3 + 2 - 1

((3 + 2) - 1)

4

2 + 6 * 7

(2 + (6 * 7))

44

-5+7- -6

(((-5)+7)-(-6))

8

2+4/5

(2+(4/5))

2

13 % 5

(13 % 5)

3

11.5 % 2.5

(11.5 % 2.5)

1.5

10 / 0

ArithmeticException

2+4.0/5

(2.0+(4.0/5.0))

2.8

4.0 / 0.0

(4.0 / 0.0)

Infinity

-4.0 / 0.0

((-4.0) / 0.0)

-Infinity

0.0 / 0.0

(0.0 / 0.0)

NaN

Numeric Promotions in Arithmetic Expressions
Unary numeric promotion is applied to the single operand of the unary arithmetic
operators - and +. When a unary arithmetic operator is applied to an operand
whose type is narrower than int, the operand is promoted to a value of type int,
with the operation resulting in an int value. If the conditions for implicit narrowing conversion are not fulfilled (p. 171), assigning the int result to a variable of a
narrower type will require a cast. This is demonstrated by the following example,
where the byte operand b is promoted to an int in the expression (-b):
byte b = 3;
b = (byte) -b;

// int literal in range. Narrowing conversion.
// Cast required on assignment.

5.6: ARITHMETIC OPERATORS: *, /, %, +, -

181

Binary numeric promotion is applied to operands of binary arithmetic operators. Its
application leads to type promotion for the operands, as explained in Section 5.2, p.
165. The result is of the promoted type, which is always type int or wider. For the
expression at (1) in Example 5.2, numeric promotions proceed as shown in Figure
5.3. Note the integer division performed in evaluating the subexpression (c / s).
Example 5.2

Numeric Promotion in Arithmetic Expressions
public class NumPromotion {
public static void main(String[] args) {
byte
b = 32;
char
c = ’z’;
// Unicode value 122 (\u007a)
short s = 256;
int
i = 10000;
float f = 3.5F;
double d = 0.5;
double v = (d * i) + (f * - b) - (c / s);
// (1) 4888.0D
System.out.println("Value of v: " + v);
}
}

Output from the program:
Value of v: 4888.0

Figure 5.3

Numeric Promotion in Arithmetic Expressions
( d

i ) + ( f

*

double

int

*

float

-b ) - ( c

/

s )

byte

char

short

int

int

int

double

float

double

float

int

double

Unary Numeric

double

double

Binary Numeric

double

182

CHAPTER 5: OPERATORS AND EXPRESSIONS

In addition to the binary numeric promotions in arithmetic expression evaluation,
the resulting value can undergo an implicit widening conversion if assigned to a
variable. In the first two declaration statements below, only assignment conversions take place. Numeric promotions take place in the evaluation of the righthand expression in the other declaration statements.
Byte
Short
char
int
long
float

b
s
c
i
n
r

=
=
=
=
=
=

10;
20;
'z';
s * b;
20L + s;
s + c;

double d = r + i;

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

constant in range: narrowing and boxing on assignment.
constant in range: narrowing and boxing on assignment.
122 (\u007a)
Values in s and b promoted to int: unboxing, widening
Value in s promoted to long: unboxing, widening
Values in s and c promoted to int, followed by implicit
widening conversion of int to float on assignment.
value in i promoted to float, followed by implicit
widening conversion of float to double on assignment.

Binary numeric promotion for operands of binary operators implies that each
operand of a binary operator is promoted to type int or a broader numeric type, if
necessary. As with unary operators, care must be exercised in assigning the value
resulting from applying a binary operator to operands of these types.
short h = 40;
h = h + 2;

// OK: int converted to short. Implicit narrowing.
// Error: cannot assign an int to short.

The value of expression h + 2 is of type int. Although the result of the expression
is in the range of short, this cannot be determined at compile time. The assignment
requires a cast.
h = (short) (h + 2);

// OK

Notice that applying the cast operator (short) to the individual operands does not
work:
h = (short) h + (short) 2;

// The resulting value should be cast.

In this case, binary numeric promotion leads to an int value as the result of evaluating the expression on the right-hand side and, therefore, requires an additional
cast to narrow it to a short value.

Arithmetic Compound Assignment Operators: *=, /=, %=, +=, -=
A compound assignment operator has the following syntax:
 = 
and the following semantics:
 = () (()  ())
The type of the  is  and the  is evaluated only once. Note
the cast and the parentheses implied in the semantics. Here = can be any of the
compound assignment operators specified in Table 5.2. The compound assignment
operators have the lowest precedence of all the operators in Java, allowing the

5.6: ARITHMETIC OPERATORS: *, /, %, +, -

183

expression on the right-hand side to be evaluated before the assignment. Table 5.4
defines the arithmetic compound assignment operators.
Table 5.6

Arithmetic Compound Assignment Operators
Expression:

Given T as the Numeric Type of x, the Expression Is Evaluated as:

x *= a

x = (T) ((x) * (a))

x /= a

x = (T) ((x) / (a))

x %= a

x = (T) ((x) % (a))

x += a

x = (T) ((x) + (a))

x -= a

x = (T) ((x) - (a))

The implied cast operator, (T), in the compound assignments becomes necessary
when the result must be narrowed to the target type. This is illustrated by the following examples:
int i = 2;
i *= i + 4;
Integer iRef = 2;
iRef *= iRef + 4;
byte b = 2;
b += 10;
b = b + 10;

// (1) Evaluated as i = (int) ((i) * (i + 4)).

// (2) Evaluated as iRef = (Integer) ((iRef) * (iRef + 4)).
// (3) Evaluated as b = (byte) (b + 10).
// (4) Will not compile. Cast is required.

At (1) the source int value is assigned to the target int variable, and the cast operator (int) in this case is an identity conversion (i.e., conversion from a type to the
same type). Such casts are permitted. The assignment at (2) entails unboxing to
evaluate the expression on the right-hand side, followed by boxing to assign the
int value. However, at (3), as the source value is an int value because the byte value
in b is promoted to int to carry out the addition, assigning it to a target byte variable
requires an implicit narrowing conversion. The situation at (4) with simple assignment will not compile, because implicit narrowing conversion is not applicable.
The  is only evaluated once in the expression, not twice, as one might
infer from the definition of the compound assignment operator. In the following
assignment, a[i] is only evaluated once:
int[] a = new int[] { 2008, 2009, 2010 };
int i = 2;
a[i] += 1;
// evaluates as a[2] = a[2] + 1, and a[2] gets the value 2011.

Implicit narrowing conversions are also applied for increment and decrement
operators (see Section 5.8, p. 186).
Other compound assignment operators include boolean logical, bitwise, and shift
operators—of which, only the boolean logical operators are discussed in this book
(see Section 5.12, p. 194).

184

CHAPTER 5: OPERATORS AND EXPRESSIONS

Review Questions
5.4

Which of the following expressions will be evaluated using floating-point arithmetic?
Select the three correct answers.
(a) 2.0 * 3.0
(b) 2 * 3
(c) 2/3 + 5/7
(d) 2.4 + 1.6
(e) 0x10 * 1L * 300.0

5.5

What is the value of the expression (1 / 2 + 3 / 2 + 0.1)?
Select the one correct answer.
(a)
(b)
(c)
(d)
(e)

5.6

1
1.1
1.6
2
2.1

What will be the result of compiling and running the following program?
public class Integers {
public static void main(String[] args) {
System.out.println(0x10 + 10 + 010);
}
}

Select the one correct answer.
(a) The program will not compile because of errors in the expression 0x10 + 10 +
010.
(b) When run, the program will print 28.
(c) When run, the program will print 30.
(d) When run, the program will print 34.
(e) When run, the program will print 36.
(f) When run, the program will print 101010.
5.7

Which of the following expressions are valid?
Select the three correct answers.
(a) (- 1 -)
(b) (+ + 1)
(c) (+-+-+-1)
(d) (--1)
(e) (1 * * 1)
(f) (- -1)

5.7: THE BINARY STRING CONCATENATION OPERATOR +

5.8

185

What is the value of evaluating the following expression (- -1-3 * 10 / 5-1)?
Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
(f)

5.9

–8
–6
7
8
10
None of the above.

Which of these assignments are valid?
Select the four correct answers.
(a) short s = 12;
(b) long l = 012;
(c) int other = (int) true;
(d) float f = -123;
(e) double d = 0x12345678;

5.7 The Binary String Concatenation Operator +
The binary operator + is overloaded in the sense that the operation performed is
determined by the type of the operands. When one of the operands is a String
object, a string conversion is performed on the other operand, implicitly converting it to its string representation, before the string concatenation is performed.
Non-String operands are converted as follows:
• For an operand of a primitive data type, its value is first converted to a reference value using the object creation expression. A string representation of the
reference value is obtained as explained below for reference types.
• Values like true, false, and null are represented by string representations of
these literals. A reference variable with the value null also has the string representation "null" in this context.
• For all reference value operands, a string representation is constructed by calling the toString() method on the referred object. Most classes override this
method from the Object class in order to provide a more meaningful string representation of their objects. Discussion of the toString() method can be found
in Section 10.2, p. 424.
The string concatenation operator + is left associative, and the result of the concatenation is always a new String object. The String class is discussed in Section 10.4,
p. 439.
String theName = " Uranium";
theName = " Pure" + theName;
String trademark1 = 100 + "%" + theName;

// " Pure Uranium"
// "100% Pure Uranium"

(1)

186

CHAPTER 5: OPERATORS AND EXPRESSIONS

The integer literal 100 is implicitly converted to the string "100" before concatenation. This conversion corresponds to first creating an object of the wrapper class
Integer, which boxes the integer 100, and then creating a string from this object by
using the toString() method supplied by this class:
new Integer(100).toString();

Note that using the character literal '%', instead of the string literal "%" in line (1)
above, does not give the same result:
String trademark2 = 100 + '%' + theName;

// "137 Pure Uranium"

Integer addition is performed by the first + operator: 100 + '%', that is, (100 + 37).
Caution should be exercised as the + operator might not be applied as intended, as
shown by the following example:
System.out.println("We put two and two together and get " + 2 + 2);

The above statement prints "We put two and two together and get 22" and not "We put
two and two together and get 4". The first integer literal 2 is promoted to a String literal "2" for the first concatenation, resulting in the String literal "We put two and two
together and get 2". This result is then concatenated with the String literal "2". The
whole process proceeds as follows:
"We
"We
"We
"We
"We

put
put
put
put
put

two
two
two
two
two

and
and
and
and
and

two
two
two
two
two

together
together
together
together
together

and
and
and
and
and

get
get
get
get
get

" + 2 + 2
" + "2" + 2
2"
+ 2
2"
+ "2"
22"

Both occurrences of the + operator are treated as string concatenation. To convey
the intended meaning of the sentence, parentheses are highly recommended:
System.out.println("We put two and two together and get " + (2 + 2));

The compiler uses a string builder to avoid the overhead of temporary String objects
when applying the string concatenation operator (+), as explained in Section 10.5,
p. 460.

5.8 Variable Increment and Decrement Operators: ++, -Variable increment (++) and decrement (--) operators come in two flavors: prefix
and postfix. These unary operators have the side effect of changing the value of the
arithmetic operand which must evaluate to a variable. Depending on the operator
used, the variable is either incremented or decremented by 1.
These operators cannot be applied to a variable that is declared final and which
has been initialized, as the side effect would change the value in such a variable.
These operators are very useful for updating variables in loops where only the side
effect of the operator is of interest.

5.8: VARIABLE INCREMENT AND DECREMENT OPERATORS: ++, --

187

The Increment Operator ++
Prefix increment operator has the following semantics:
++i adds 1 to the value in i, and stores the new value in i. It returns the new

value as the value of the expression. It is equivalent to the following statements:
i += 1;
result = i;
return result;

Postfix increment operator has the following semantics:
j++ adds 1 to the value in j, and stores the new value in j. It returns the old value
in j before the new value is stored in j, as the value of the expression. It is
equivalent to the following statements:
result = j;
j += 1;
return result;

The Decrement Operator -Prefix decrement operator has the following semantics:
--i subtracts 1 from the value of i, and stores the new value in i. It returns the
new value as the value of the expression.

Postfix decrement operator has the following semantics:
j-- subtracts 1 from the value of j, and stores the new value in j. It returns the
old value in j before the new value is stored in j as the value of the expression.

The above discussion on decrement and increment operators applies to any variable whose type is a numeric primitive type or its corresponding numeric wrapper
type. Necessary numeric promotions are performed on the value 1 and the value
of the variable. Before assigning the new value to the variable, it is subjected to any
narrowing primitive conversion and/or boxing that might be necessary.
Here are some examples to illustrate the behavior of increment and decrement
operators:
// (1) Prefix order: increment/decrement operand before use.
int i = 10;
int k = ++i + --i; // ((++i) + (--i)). k gets the value 21 and i becomes 10.
--i;
// Only side effect utilized. i is 9. (expression statement)
Integer iRef = 10; //
k = ++iRef + --iRef;//
//
--iRef;
//
//

Boxing on assignment
((++iRef) + (--iRef)). k gets the value 21 and
iRef refers to an Integer object with the value 10.
Only side effect utilized. iRef refers to an Integer
object with the value 9. (expression statement)

// (2) Postfix order: increment/decrement operand after use.
long i = 10;

188

CHAPTER 5: OPERATORS AND EXPRESSIONS
long k = i++ + i--; // ((i++) + (i--)). k gets the value 21L and i becomes 10L.
i++;
// Only side effect utilized. i is 11L. (expression statement)

An increment or decrement operator, together with its operand, can be used as an
expression statement (see Section 3.3, p. 45).
Execution of the assignment in the second declaration statement under (1) proceeds as follows:
Operands are evaluated from left to right.
Side-effect: i += 1, i gets the value 11.
Side-effect: i -= 1, i gets the value 10.

k = ((++i) + (--i))
k = ( 11

+ (--i))

k = ( 11
k = 21

+

10)

Expressions where variables are modified multiple times during the evaluation
should be avoided, because the order of evaluation is not always immediately
apparent.
We cannot associate increment and decrement operators. Given that a is a variable,
we cannot write (++(++a)). The reason is that any operand to ++ must evaluate to a
variable, but the evaluation of (++a) results in a value.
In the example below, both binary numeric promotion and an implicit narrowing
conversion are performed to achieve the side effect of modifying the value of the
operand. The int value of the expression (++b) (that is, 11), is assigned to the int
variable i. The side effect of incrementing the value of the byte variable b requires
binary numeric promotion to perform int addition, followed by an implicit narrowing conversion of the int value to byte to perform the assignment.
byte b = 10;
int i = ++b;

// i is 11, and so is b.

The example below illustrates applying the increment operator to a floating-point
operand. The side effect of the ++ operator is overwritten by the assignment.
double x = 4.5;
x = x + ++x;

// x gets the value 10.0.

Review Questions
5.10

Which statements are true?
Select the three correct answers.
(a)
(b)
(c)
(d)
(e)

The expression (1 + 2 + "3") evaluates to the string "33".
The expression ("1" + 2 + 3) evaluates to the string "15".
The expression (4 + 1.0f) evaluates to the float value 5.0f.
The expression (10/9) evaluates to the int value 1.
The expression ('a' + 1) evaluates to the char value 'b'.

5.8: VARIABLE INCREMENT AND DECREMENT OPERATORS: ++, --

5.11

189

What happens when you try to compile and run the following program?
public class Prog1 {
public static void main(String[] args) {
int k = 1;
int i = ++k + k++ + + k;
// (1)
System.out.println(i);
}
}

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
5.12

The program will not compile, because of errors in the expression at (1).
The program will compile and print the value 3, when run.
The program will compile and print the value 4, when run.
The program will compile and print the value 7, when run.
The program will compile and print the value 8, when run.

What is the label of the first line that will cause a compile time error in the following program?
public class MyClass {
public static void main(String[] args) {
char c;
int i;
c = 'a'; // (1)
i = c;
// (2)
i++;
// (3)
c = i;
// (4)
c++;
// (5)
}
}

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
(f)
5.13

(1)
(2)
(3)
(4)
(5)
None of the above. The compiler will not report any errors.

What is the result of compiling and running the following program?
public class Cast {
public static void main(String[] args) {
byte b = 128;
int i = b;
System.out.println(i);
}
}

190

CHAPTER 5: OPERATORS AND EXPRESSIONS

Select the one correct answer.
(a) The program will not compile because a byte value cannot be assigned to an
int variable without using a cast.
(b) The program will compile and print 128, when run.
(c) The program will not compile because the value 128 is not in the range of values for the byte type.
(d) The program will compile but will throw a ClassCastException when run.
(e) The program will compile and print 255, when run.
5.14

What will be the result of compiling and running the following program?
public class EvaluationOrder {
public static void main(String[] args) {
int[] array = { 4, 8, 16 };
int i=1;
array[++i] = --i;
System.out.println(array[0] + array[1] + array[2]);
}
}

Select the one correct answer.
(a) 13
(b) 14
(c) 20
(d) 21
(e) 24

5.9 Boolean Expressions
As the name implies, a boolean expression has the boolean data type and can only
evaluate to the values true or false.
Boolean expressions, when used as conditionals in control statements, allow the
program flow to be controlled during execution.
Boolean expressions can be formed using relational operators (Section 5.10, p. 190),
equality operators (Section 5.11, p. 191), bitwise operators (which are not covered in
this book), boolean logical operators (Section 5.12, p. 194), conditional operators (Section 5.13, p. 196), the assignment operator (Section 5.5, p. 169), and the instanceof
operator (Section 7.11, p. 328).

5.10 Relational Operators: <, <=, >, >=
Given that a and b represent numeric expressions, the relational (also called
comparison) operators are defined as shown in Table 5.7.

191

5.11: EQUALITY
Table 5.7

Relational Operators
a < b

a less than b?

a <= b

a less than or equal to b?

a > b

a greater than b?

a >= b

a greater than or equal to b?

All relational operators are binary operators and their operands are numeric
expressions. Binary numeric promotion is applied to the operands of these
operators. The evaluation results in a boolean value. Relational operators have
precedence lower than arithmetic operators, but higher than that of the assignment
operators.
double hours = 45.5;
boolean overtime = hours >= 35.0;
// true.
boolean order = 'A' < 'a';
// true. Binary numeric promotion applied.
Double time = 18.0;
boolean beforeMidnight = time < 24.0;// true. Binary numeric promotion applied.

Relational operators are nonassociative. Mathematical expressions like a d b
must be written using relational and boolean logical/conditional operators.
int a = 1, b = 7, c = 10;
boolean valid1 = a <= b <= c;
boolean valid2 = a <= b && b <= c;

d

c

// (1) Illegal.
// (2) OK.

Since relational operators have left associativity, the evaluation of the expression
a <= b <= c at (1) in the examples above would proceed as follows: ((a <= b) <= c).
Evaluation of (a <= b) would yield a boolean value that is not permitted as an operand of a relational operator, i.e., ( <= c) would be illegal.

5.11 Equality
We distinguish between primitive data equality, object reference equality, and
object value equality.
The equality operators have lower precedence than the relational operators, but
higher than that of the assignment operators.

Primitive Data Value Equality: ==, !=
Given that a and b represent operands of primitive data types, the primitive data
value equality operators are defined as shown in Table 5.8.
The equality operator == and the inequality operator != can be used to compare
primitive data values, including boolean values. Binary numeric promotion is
applied to the nonboolean operands of these equality operators.

192

CHAPTER 5: OPERATORS AND EXPRESSIONS
Table 5.8

Primitive Data Value Equality Operators
a == b

Determines whether a and b are equal, i.e., have the same primitive value.
(Equality)

a != b

Determines whether a and b are not equal, i.e., do not have the same
primitive value. (Inequality)

int year = 2002;
boolean isEven = year % 2 == 0;
boolean compare = '1' == 1;
boolean test
= compare == false;

// true.
// false. Binary numeric promotion applied.
// true.

Care must be exercised in comparing floating-point numbers for equality, as an
infinite number of floating-point values can be stored in a finite number of bits
only as approximations. For example, the expression (1.0 - 2.0/3.0 == 1.0/3.0)
returns false, although mathematically the result should be true.
Analogous to the discussion for relational operators, mathematical expressions
like a = b = c must be written using relational and logical/conditional operators.
Since equality operators have left associativity, the evaluation of the expression
a == b == c would proceed as follows: ((a == b) == c). Evaluation of (a == b)
would yield a boolean value that is permitted as an operand of a data value equality
operator, but ( == c) would be illegal if c had a numeric type. This
problem is illustrated in the examples below. The expression at (1) is illegal, but
those at (2) and (3) are legal.
int a, b, c;
a = b = c = 5;
boolean valid1 = a == b == c;
boolean valid2 = a == b && b == c;
boolean valid3 = a == b == true;

// (1) Illegal.
// (2) Legal.
// (3) Legal.

Object Reference Equality: ==, !=
The equality operator == and the inequality operator != can be applied to reference
variables to test whether they refer to the same object. Given that r and s are reference variables, the reference equality operators are defined as shown in Table 5.9.
Table 5.9

Reference Equality Operators
r == s

Determines whether r and s are equal, i.e., have the same reference value
and therefore refer to the same object (also called aliases). (Equality)

r != s

Determines whether r and s are not equal, i.e., do not have the same
reference value and therefore refer to different objects. (Inequality)

The operands must be cast compatible: it must be possible to cast the reference
value of the one into the other’s type; otherwise, it is a compile-time error. Casting
of references is discussed in Section 7.8, p. 319.

193

5.11: EQUALITY
Pizza pizza_A = new Pizza("Sweet&Sour");
Pizza pizza_B = new Pizza("Sweet&Sour");
Pizza pizza_C = new Pizza("Hot&Spicy");

// new object
// new object
// new object

String banner = "Come and get it!";

// new object

boolean test = banner == pizza_A;
boolean test1 = pizza_A == pizza_B;
boolean test2 = pizza_A == pizza_C;

// (1) Compile-time error.
// false
// false

pizza_A = pizza_B;
boolean test3 = pizza_A == pizza_B;

// Denote the same object, are aliases.
// true

The comparison banner == pizza_A in (1) is illegal, because the String and Pizza
types are not related and therefore the reference value of one type cannot be cast to
the other type. The values of test1 and test2 are false because the three references
denote different objects, regardless of the fact that pizza_A and pizza_B are both
sweet and sour pizzas. The value of test3 is true because now both pizza_A and
pizza_B denote the same object.
The equality and inequality operators are applied to object references to check
whether two references denote the same object or not. The state of the objects that
the references denote is not compared. This is the same as testing whether the
references are aliases, i.e., denoting the same object.
The null literal can be assigned to any reference variable, and the reference value
in a reference variable can be compared for equality with the null literal. The comparison can be used to avoid inadvertent use of a reference variable that does not
denote any object.
if (objRef != null) {
// ... use objRef ...
}

Note that only when the type of both operands is either a reference type or the null
type, do these operators test for object reference equality. Otherwise, they test for
primitive data equality (see also Section 10.3, p. 432). In (2) below, binary numeric
promotion involving unboxing is performed.
Integer iRef = 10;
boolean b1 = iRef == null;
boolean b2 = iRef == 10;

// (1) object reference equality
// (2) primitive data equality

Object Value Equality
The Object class provides the method public boolean equals(Object obj), which can
be overridden (see Section 7.2, p. 288) to give the right semantics of object value equality. The default implementation of this method in the Object class returns true only
if the object is compared with itself, i.e., as if the equality operator == had been used
to compare aliases of an object. This means that if a class does not override the
semantics of the equals() method from the Object class, object value equality is the
same as object reference equality. For a detailed discussion on implementing the
equals() method, see Section 15.1, p. 751.

194

CHAPTER 5: OPERATORS AND EXPRESSIONS

Certain classes in the standard API override the equals() method, e.g.,
java.lang.String, java.util.Date, java.io.File and the wrapper classes for the
primitive data types. For two String objects, value equality means they contain
identical character sequences. For the wrapper classes, value equality means that
the primitive values in the two wrapper objects are equal (see also Section 10.3, p.
432).
// Equality for
String movie1 =
String movie2 =
String movie3 =
boolean test0 =
boolean test1 =

String objects means identical character sequences.
new String("The Revenge of the Exception Handler");
new String("High Noon at the Java Corral");
new String("The Revenge of the Exception Handler");
movie1.equals(movie2);
// false
movie1.equals(movie3);
// true

// Equality for
Boolean flag1 =
Boolean flag2 =
boolean test2 =

Boolean objects means same primitive value
true;
false;
flag1.equals(flag2);
// false

// The Pizza class does not override the equals() method,
// can use either equals() inherited from Object or ==.
Pizza pizza1 = new Pizza("VeggiesDelight");
Pizza pizza2 = new Pizza("VeggiesDelight");
Pizza pizza3 = new Pizza("CheeseDelight");
boolean test3 = pizza1.equals(pizza2);
// false
boolean test4 = pizza1.equals(pizza3);
// false
boolean test5 = pizza1 == pizza2;
// false
pizza1 = pizza2;
// Creates aliases
boolean test7 = pizza1.equals(pizza2);
// true
boolean test6 = pizza1 == pizza2;
// true

5.12 Boolean Logical Operators: !, ^, &, |
Boolean logical operators include the unary operator ! (logical complement) and the
binary operators & (logical AND), | (logical inclusive OR), and ^ (logical exclusive OR,
also called logical XOR). Boolean logical operators can be applied to boolean or
Boolean operands, returning a boolean value. The operators &, |, and ^ can also be
applied to integral operands to perform bitwise logical operations (which are not
covered in this book).
Given that x and y represent boolean expressions, the boolean logical operators are
defined in Table 5.10.
These operators always evaluate both the operands, unlike their counterpart conditional operators && and || (see Section 5.13, p. 196). Unboxing is applied to the
operand values, if necessary. Truth-values for boolean logical operators are shown
in Table 5.10.

5.12: BOOLEAN LOGICAL OPERATORS: !, ^, &, |
Table 5.10

195

Truth-Values for Boolean Logical Operators
x

y

!x

x & y

x | y

x ^ y

true

true

false

true

true

false

true

false

false

false

true

true

false

true

true

false

true

true

false

false

true

false

false

false

Operand Evaluation for Boolean Logical Operators
In the evaluation of boolean expressions involving boolean logical AND, XOR, and
OR operators, both the operands are evaluated. The order of operand evaluation is
always from left to right.
if (i > 0 & i++ < 10) {/*...*/} // i will be incremented, regardless of value in i.

The binary boolean logical operators have precedence lower than arithmetic and
relational operators, but higher than assignment, conditional AND, and OR operators (see Section 5.13, p. 196). This is illustrated in the following examples:
boolean b1, b2, b3 = false, b4 = false;
Boolean b5 = true;
b1 = 4 == 2 & 1 < 4;
// false, evaluated as (b1 = ((4 == 2) & (1 < 4)))
b2 = b1 | !(2.5 >= 8);
// true
b3 = b3 ^ b5;
// true, unboxing conversion on b5
b4 = b4 | b1 & b2;
// false

Order of evaluation is illustrated for the last example:
(b4 = (b4 | (b1 & b2)))

Ÿ
Ÿ
Ÿ
Ÿ
Ÿ

(b4 = (false | (b1 & b2)))
(b4 = (false | (false & b2)))
(b4 = (false | (false & true)))
(b4 = (false | false))
(b4 = false)

Note that b2 was evaluated although, strictly speaking, it was not necessary. This
behavior is guaranteed for boolean logical operators.

Boolean Logical Compound Assignment Operators: &=, ^=, |=
Compound assignment operators for the boolean logical operators are defined in
Table 5.11. The left-hand operand must be a boolean variable, and the right-hand
operand must be a boolean expression. An identity conversion is applied implicitly on assignment.

196

CHAPTER 5: OPERATORS AND EXPRESSIONS

Table 5.11

Boolean Logical Compound Assignment Operators
Expression:

Given b and a Are of Type Boolean, the Expression Is Evaluated as:

b &= a

b = (b & (a))

b ^= a

b = (b ^ (a))

b |= a

b = (b | (a))

See also the discussion on arithmetic compound assignment operators in Section
5.6, p. 182. Here are some examples to illustrate the behavior of boolean logical
compound assignment operators:
boolean b1 = false, b2 = false, b3 = false;
Boolean b4 = false;
b1 |= true;
// true
b4 ^= b1;
// (1) true, unboxing in (b4 ^ b1), boxing on assignment
b3 &= b1 | b2;
// (2) false. b3 = (b3 & (b1 | b2)).
b3 = b3 & b1 | b2;
// (3) true. b3 = ((b3 & b1) | b2).

The assignment at (1) entails unboxing to evaluate the expression on the righthand side, followed by boxing to assign the boolean result. It is also instructive to
compare how the assignments at (2) and (3) above are performed, giving different
results for the same value of the operands, showing how the precedence affects the
evaluation.

5.13 Conditional Operators: &&, ||
The conditional operators && and || are similar to their counterpart logical operators & and |, except that their evaluation is short-circuited. Given that x and y represent values of boolean or Boolean expressions, the conditional operators are
defined in Table 5.12. In the table, the operators are listed in decreasing precedence
order.
Table 5.12

Conditional Operators
Conditional AND

x && y

true if both operands are true; otherwise, false.

Conditional OR

x || y

true if either or both operands are true; otherwise,
false.

Unlike their logical counterparts & and |, which can also be applied to integral
operands for bitwise operations, the conditional operators && and || can only be
applied to boolean operands. Their evaluation results in a boolean value. Truthvalues for conditional operators are shown in Table 5.13. Not surprisingly, they
have the same truth-values as their counterpart logical operators.

5.13: CONDITIONAL OPERATORS: &&, ||

197

Note that, unlike their logical counterparts, there are no compound assignment
operators for the conditional operators.
Table 5.13

Truth-values for Conditional Operators
x

y

x && y

x || y

true

true

true

true

true

false

false

true

false

true

false

true

false

false

false

false

Short-Circuit Evaluation
In evaluation of boolean expressions involving conditional AND and OR, the lefthand operand is evaluated before the right one, and the evaluation is shortcircuited (i.e., if the result of the boolean expression can be determined from the
left-hand operand, the right-hand operand is not evaluated). In other words, the
right-hand operand is evaluated conditionally.
The binary conditional operators have precedence lower than either arithmetic,
relational, or logical operators, but higher than assignment operators. Unboxing of
the operand value takes place when necessary, before the operation is performed.
The following examples illustrate usage of conditional operators:
Boolean b1 = 4 == 2 && 1 < 4;
boolean b2 = !b1 || 2.5 > 8;
Boolean b3 = !(b1 && b2);
boolean b4 = b1 || !b3 && b2;

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

false, short-circuit evaluated as
(b1 = ((4 == 2) && (1 < 4)))
true, short-circuit evaluated as
(b2 = ((!b1) || (2.5 > 8)))
true
false, short-circuit evaluated as
(b4 = (b1 || ((!b3) && b2)))

The order of evaluation for computing the value of boolean variable b4 proceeds as
follows:
(b4 = (b1 || ((!b3) && b2)))

Ÿ
Ÿ
Ÿ
Ÿ
Ÿ

(b4 = (false || ((!b3) && b2)))
(b4 = (false || ((!true) && b2)))
(b4 = (false || ((false) && b2)))
(b4 = (false || false))
(b4 = false)

Note that b2 is not evaluated, short-circuiting the evaluation. Example 5.3 illustrates the short-circuit evaluation of the initialization expressions in the declaration
statements above. In addition, it shows an evaluation (see the declaration of b5)
involving boolean logical operators that always evaluate both operands. See also
Example 5.1 that uses a similar approach to illustrate the order of operand evaluation in arithmetic expressions.

198

Example 5.3

CHAPTER 5: OPERATORS AND EXPRESSIONS

Short-Circuit Evaluation Involving Conditional Operators
public class ShortCircuit {
public static void main(String[] args) {
// Boolean b1 = 4 == 2 && 1 < 4;
Boolean b1 = operandEval(1, 4 == 2) && operandEval(2, 1 < 4);
System.out.println();
System.out.println("Value of b1: " + b1);
// boolean b2 = !b1 || 2.5 > 8;
boolean b2 = !operandEval(1, b1) || operandEval(2, 2.5 > 8);
System.out.println();
System.out.println("Value of b2: " + b2);
// Boolean b3 = !(b1 && b2);
Boolean b3 = !(operandEval(1, b1) && operandEval(2, b2));
System.out.println();
System.out.println("Value of b3: " + b3);
// boolean b4 = b1 || !b3 && b2;
boolean b4 = operandEval(1, b1) || !operandEval(2, b3) && operandEval(3, b2);
System.out.println();
System.out.println("Value of b4: " + b4);
// boolean b5 = b1 | !b3 & b2;
// Using boolean logical operators
boolean b5 = operandEval(1, b1) | !operandEval(2, b3) & operandEval(3, b2);
System.out.println();
System.out.println("Value of b5: " + b5);
}
static boolean operandEval(int opNum, boolean operand) {
System.out.print(opNum);
return operand;
}

// (1)

}

Output from the program:
1
Value
1
Value
1
Value
12
Value
123
Value

of b1: false
of b2: true
of b3: true
of b4: false
of b5: false

Short-circuit evaluation can be used to ensure that a reference variable denotes an
object before it is used.
if (objRef != null && objRef.doIt()) { /*...*/ }

5.13: CONDITIONAL OPERATORS: &&, ||

199

The method call is now conditionally dependent on the left-hand operand and will
not be executed if the variable objRef has the null reference. If we use the logical &
operator and the variable objRef has the null reference, evaluation of the righthand operand will result in a NullPointerException.
In summary, we employ the conditional operators && and || if the evaluation of the
right-hand operand is conditionally dependent on the left-hand operand. We use the
boolean logical operators & and | if both operands must be evaluated. The subtlety
of conditional operators is illustrated by the following examples:
if (i > 0 && i++ < 10) {/*...*/}
if (i > 0 || i++ < 10) {/*...*/}

// i is not incremented if i > 0 is false.
// i is not incremented if i > 0 is true.

Review Questions
5.15

Which of the following expressions evaluate to true?
Select the two correct answers.
(a) (false | true)
(b) (null != null)
(c) (4 <= 4)
(d) (!true)
(e) (true & false)

5.16

Which statements are true?
Select the two correct answers.
(a) The remainder operator % can only be used with integral operands.
(b) Short-circuit evaluation occurs with boolean logical operators.
(c) The arithmetic operators *, /, and % have the same level of precedence.
(d) A short value ranges from -128 to +127, inclusive.
(e) (+15) is a legal expression.

5.17

Which statements are true about the lines of output printed by the following
program?
public class BoolOp {
static void op(boolean a, boolean b) {
boolean c = a != b;
boolean d = a ^ b;
boolean e = c == d;
System.out.println(e);
}
public static void main(String[] args) {
op(false, false);
op(true, false);
op(false, true);
op(true, true);
}
}

200

CHAPTER 5: OPERATORS AND EXPRESSIONS

Select the three correct answers.
(a)
(b)
(c)
(d)
(e)
5.18

All lines printed are the same.
At least one line contains false.
At least one line contains true.
The first line contains false.
The last line contains true.

What is the result of running the following program?
public class OperandOrder {
public static void main(String[] args) {
int i = 0;
int[] a = {3,6};
a[i] = i = 9;
System.out.println(i + " " + a[0] + " " + a[1]);
}
}

Select the one correct answer.
(a) When run, the program throws an exception of type ArrayIndexOutOfBoundsException.
(b) When run, the program will print "9 9 6".
(c) When run, the program will print "9 0 6".
(d) When run, the program will print "9 3 6".
(e) When run, the program will print "9 3 9".
5.19

Which statements are true about the output from the following program?
public class Logic {
public static void main(String[] args) {
int i = 0;
int j = 0;
boolean t = true;
boolean r;
r = (t & 0 < (i+=1));
r = (t && 0 < (i+=2));
r = (t | 0 < (j+=1));
r = (t || 0 < (j+=2));
System.out.println(i + " " + j);
}
}

Select the two correct answers.
(a)
(b)
(c)
(d)
(e)
(f)

The first digit printed is 1.
The first digit printed is 2.
The first digit printed is 3.
The second digit printed is 1.
The second digit printed is 2.
The second digit printed is 3.

5.15 OTHER OPERATORS: new, [], instanceof

201

5.14 The Conditional Operator: ?:
The ternary conditional operator allows conditional expressions to be defined. The
operator has the following syntax:
 ?  : 
If the boolean expression  is true then  is evaluated; otherwise,  is evaluated. Of course,  and  must
evaluate to values of compatible types. The value of the expression evaluated is
returned by the conditional expression.
boolean leapYear = false;
int daysInFebruary = leapYear ? 29 : 28;

// 28

The conditional operator is the expression equivalent of the if-else statement (Section 6.2, p. 205). The conditional expression can be nested and the conditional operator associates from right to left:
(a?b?c?d:e:f:g) evaluates as (a?(b?(c?d:e):f):g)

5.15 Other Operators: new, [], instanceof
The new operator is used to create objects, i.e., instances of classes and arrays. It is
used with a constructor call to instantiate classes (see Section 3.4, p. 48), and with
the [] notation to create arrays (see Section 3.6, p. 70). It is also used to instantiate
anonymous arrays (see Section 3.6, p. 74), and anonymous classes (see Section 8.5,
p. 377).
Pizza onePizza = new Pizza();

// Create an instance of the Pizza class.

The [] notation is used to declare and construct arrays and also to access array elements (see Section 3.6, p. 69).
int[] anArray = new int[5];// Declare and construct an int array of 5 elements.
anArray[4] = anArray[3];
// Element at index 4 gets value of element at index 3.

The boolean, binary, and infix operator instanceof is used to test the type of an
object (see Section 7.11, p. 327).
Pizza myPizza
boolean test1
boolean test2
boolean test3

=
=
=
=

new Pizza();
myPizza instanceof Pizza; // True.
"Pizza" instanceof Pizza; // Compile error. String is not Pizza.
null instanceof Pizza;
// Always false. null is not an instance.

202

CHAPTER 5: OPERATORS AND EXPRESSIONS

Chapter Summary
The following information was included in this chapter:
• type conversion categories and conversion contexts, and which conversions are
permissible in each conversion context.
• operators in Java, including precedence and associativity rules.
• defining and evaluating arithmetic and boolean expressions, and the order in
which operands and operators are evaluated.

Programming Exercise
5.1

The program below is supposed to calculate and print the time it takes for light
to travel from the sun to the earth. It contains some logical errors. Fix the program so that it will compile and print the correct result when run.
//Filename: Sunlight.java
public class Sunlight {
public static void main(String[] args) {
// Distance from sun (150 million kilometers)
int kmFromSun = 150000000;
int lightSpeed = 299792458; // meters per second
// Convert distance to meters.
int mFromSun = kmFromSun * 1000;
int seconds = mFromSun / lightSpeed;
System.out.print("Light will use ");
printTime(seconds);
System.out.println(" to travel from the sun to the earth.");
}
public static void printTime(int sec) {
int min = sec / 60;
sec = sec - (min * 60);
System.out.print(min + " minute(s) and " + sec + " second(s)");
}
}

Control Flow

6

Exam Objectives
2.1 Develop code that implements an if or switch statement; and identify
legal argument types for these statements.
2.2 Develop code that implements all forms of loops and iterators, including
the use of for, the enhanced for loop (for-each), do, while, labels, break,
and continue; and explain the values taken by loop counter variables
during and after loop execution.
2.3 Develop code that makes use of assertions, and distinguish appropriate
from inappropriate uses of assertions.
2.4 Develop code that makes use of exceptions and exception handling clauses
(try, catch, finally), and declares methods and overriding methods that
throw exceptions.
2.5 Recognize the effect of an exception arising at a specified point in a code
fragment. Note that the exception may be a runtime exception, a checked
exception, or an error.
2.6 Recognize situations that will result in any of the following being thrown:
ArrayIndexOutOfBoundsException, ClassCastException,
IllegalArgumentException, IllegalStateException,
NullPointerException, NumberFormatException, AssertionError,
ExceptionInInitializerError, StackOverflowError, or
NoClassDefFoundError. Understand which of these are thrown by the
virtual machine and recognize situations in which others should be
thrown programmatically.
Supplementary Objectives
• Understand method execution.
• Understand exception propagation through the runtime stack.

203

204

CHAPTER 6: CONTROL FLOW

6.1 Overview of Control Flow Statements
Control flow statements govern the flow of control in a program during execution,
i.e., the order in which statements are executed in a running program. There are
three main categories of control flow statements:
• Selection statements: if, if-else, and switch.
• Iteration statements: while, do-while, basic for, and enhanced for.
• Transfer statements: break, continue, return, try-catch-finally, throw, and assert.

6.2 Selection Statements
Java provides selection statements that allow the program to choose between
alternative actions during execution. The choice is based on criteria specified in the
selection statement. These selection statements are
• simple if statement
• if-else statement
• switch statement

The Simple if Statement
The simple if statement has the following syntax:
if ()


It is used to decide whether an action is to be performed or not, based on a condition. The condition is specified by  and the action to be performed is specified by , which can be a single statement or a code block.
The  must evaluate to a boolean or a Boolean value. In the latter case, the Boolean value is unboxed to the corresponding boolean value.
The semantics of the simple if statement are straightforward. The  is evaluated first. If its value is true,  (called the if block) is
executed and execution continues with the rest of the program. If the value is false,
the if block is skipped and execution continues with the rest of the program. The
semantics are illustrated by the activity diagram in Figure 6.1a.
In the following examples of the if statement, it is assumed that the variables and
the methods have been appropriately defined:
if (emergency)
operate();

// emergency is a boolean variable

if (temperature > critical)
soundAlarm();

205

6.2: SELECTION STATEMENTS
Figure 6.1

Activity Diagram for if Statements

Evaluate boolean
expression

Evaluate boolean
expression

[false]

[true]

[true]

Execute
if block

Execute
if block

(a) Simple if Statement

[false]
Execute
else block

(b) if-else Statement

if (isLeapYear() && endOfCentury())
celebrate();
if (catIsAway()) {
getFishingRod();
goFishing();
}

// Block

Note that  can be a block, and the block notation is necessary if more
than one statement is to be executed when the  is true.
Since the  evaluates to a boolean value, it avoids a common
programming error: using an expression of the form (a=b) as the condition, where
inadvertently an assignment operator is used instead of a relational operator. The
compiler will flag this as an error, unless both a and b are boolean.
Note that the if block can be any valid statement. In particular, it can be the empty
statement (;) or the empty block ({}). A common programming error is an inadvertent use of the empty statement.
if (emergency); // Empty if block
operate();
// Executed regardless of whether it was an emergency or not.

The if-else Statement
The if-else statement is used to decide between two actions, based on a condition.
It has the following syntax:
if ()


else


The  is evaluated first. If its value is true (or unboxed to
true),  (the if block) is executed and execution continues with the rest
of the program. If the value is false (or unboxed to false),  (the else
block) is executed and execution continues with the rest of the program. In other

206

CHAPTER 6: CONTROL FLOW

words, one of two mutually exclusive actions is performed. The else clause is
optional; if omitted, the construct is equivalent to the simple if statement. The
semantics are illustrated by the activity diagram in Figure 6.1b.
In the following examples of the if-else statement, it is assumed that all variables
and methods have been appropriately defined:
if (emergency)
operate();
else
joinQueue();
if (temperature > critical)
soundAlarm();
else
businessAsUsual();
if (catIsAway()) {
getFishingRod();
goFishing();
} else
playWithCat();

Since actions can be arbitrary statements, the if statements can be nested.
if (temperature >= upperLimit) {
if (danger)
soundAlarm();
if (critical)
evacuate();
else
turnHeaterOff();
} else
turnHeaterOn();

// (1)
// (2) Simple if.
// (3)
// Goes with if at (3).
// Goes with if at (1).

The use of the block notation, {}, can be critical to the execution of if statements.
The if statements (A) and (B) in the following examples do not have the same
meaning. The if statements (B) and (C) are the same, with extra indentation used
in (C) to make the meaning evident. Leaving out the block notation in this case
could have catastrophic consequences: the heater could be turned on when the
temperature is above the upper limit.
// (A):
if (temperature > upperLimit) {
if (danger) soundAlarm();
} else
turnHeaterOn();
// (B):
if (temperature > upperLimit)
if (danger) soundAlarm();
else turnHeaterOn();
// (C):
if (temperature > upperLimit)
if (danger)
soundAlarm();
else
turnHeaterOn();

// (1) Block notation.
// (2)
// Goes with if at (1).

// (1) Without block notation.
// (2)
// Goes with if at (2).
// (1)
// (2)
// Goes with if at (2).

207

6.2: SELECTION STATEMENTS

The rule for matching an else clause is that an else clause always refers to the
nearest if that is not already associated with another else clause. Block notation
and proper indentation can be used to make the meaning obvious.
Cascading if-else statements are a sequence of nested if-else statements where
the if of the next if-else statement is joined to the else clause of the previous one.
The decision to execute a block is then based on all the conditions evaluated so far.
if (temperature >= upperLimit) {
soundAlarm();
turnHeaterOff();
} else if (temperature < lowerLimit) {
soundAlarm();
turnHeaterOn();
} else if (temperature == (upperLimit-lowerLimit)/2) {
doingFine();
} else
noCauseToWorry();

// (1)

// (2)

// (3)
// (4)

The block corresponding to the first if condition that evaluates to true is executed,
and the remaining ifs are skipped. In the example given above, the block at (3) will
execute only if the conditions at (1) and (2) are false and the condition at (3) is true.
If none of the conditions are true, the block associated with the last else clause is
executed. If there is no last else clause, no actions are performed.

The switch Statement
Conceptually, the switch statement can be used to choose one among many alternative actions, based on the value of an expression. Its general form is as follows:
switch ()
label1: 
label2: 

{

labeln: 

default:

} // end switch

The syntax of the switch statement comprises a switch expression followed by the
switch body, which is a block of statements. The type of the switch expression is
either an enumerated type or one of the following types: char, byte, short, int, or
the corresponding wrapper type for these primitive types. The statements in the
switch body can be labeled, this defines entry points in the switch body where control can be transferred depending on the value of the switch expression. The execution of the switch statement is as follows:
• The switch expression is evaluated first. If the value is a wrapper type, an
unboxing conversion is performed.
• The value of the switch expression is compared with the case labels. Control is
transferred to the  associated with the case label that is equal to the

208

CHAPTER 6: CONTROL FLOW

value of the switch expression. After execution of the associated statement, control falls through to the next statement unless appropriate action is taken.
• If no case label is equal to the value of the switch expression, the statement
associated with the default label is executed.
Figure 6.2 illustrates the flow of control through a switch statement where the
default label is declared last.
All labels (including the default label) are optional, and can be defined in any order
in the switch body. There can be at the most one default label in a switch statement.
If no valid case labels are found and the default label is left out, the whole switch
statement is skipped.
The case labels are constant expressions whose values must be unique, meaning no
duplicate values are allowed. As a matter of fact, a case label must be a compiletime constant expression whose value must be assignable to the type of the switch
expression (see Section 5.2, p. 163). In particular, all the case label values must be
in the range of the type of the switch expression. Note that the type of the case label
cannot be boolean, long, or floating-point.
Figure 6.2

Activity Diagram for a switch Statement

Evaluate switch expression
Find matching label.
...

[case label1]
Execute
associated statement1

Example 6.1

[case labeln]
...

Execute
associated statementn

Fall Through in a switch Statement
public class Advice {
public final static int LITTLE_ADVICE = 0;
public final static int MORE_ADVICE
= 1;
public final static int LOTS_OF_ADVICE = 2;
public static void main(String[] args) {
dispenseAdvice(LOTS_OF_ADVICE);
}

[default label]
Execute
associated statement

6.2: SELECTION STATEMENTS

209

public static void dispenseAdvice(int howMuchAdvice) {
switch(howMuchAdvice) {
// (1)
case LOTS_OF_ADVICE:
System.out.println("See no evil.");
// (2)
case MORE_ADVICE:
System.out.println("Speak no evil.");
// (3)
case LITTLE_ADVICE:
System.out.println("Hear no evil.");
// (4)
break;
// (5)
default:
System.out.println("No advice.");
// (6)
}
}
}

Output from the program:
See no evil.
Speak no evil.
Hear no evil.

In Example 6.1, depending on the value of the howMuchAdvice parameter, different
advice is printed in the switch statement at (1) in the method dispenseAdvice(). The
example shows the output when the value of the howMuchAdvice parameter is
LOTS_OF_ADVICE. In the switch statement, the associated statement at (2) is executed,
giving one advice. Control then falls through to the statement at (3), giving the
second advice. Control falls through to (4), dispensing the third advice, and finally,
executing the break statement at (5) causes control to exit the switch statement.
Without the break statement at (5), control would continue to fall through the
remaining statements, if there were any. Execution of the break statement in a
switch body transfers control out of the switch statement (see Section 6.4, p. 224). If
the parameter howMuchAdvice has the value MORE_ADVICE, then the advice at (3) and
(4) are given. The value LITTLE_ADVICE results in only one advice at (4) being given.
Any other value results in the default action, which announces that there is no
advice.
The associated statement of a case label can be a list of statements (which need not
be a statement block). The case label is prefixed to the first statement in each case.
This is illustrated by the associated statement for the case label LITTLE_ADVICE in
Example 6.1, which comprises statements (4) and (5).
Example 6.2 makes use of a break statement inside a switch statement to convert a
char value representing a digit to its corresponding word in English. Note that the
break statement is the last statement in the list of statements associated with each
case label. It is easy to think that the break statement is a part of the switch statement
syntax, but technically it is not.

210

Example 6.2

CHAPTER 6: CONTROL FLOW

Using break in a switch Statement
public class Digits {
public static void main(String[] args) {
System.out.println(digitToString(’7’) + " " + digitToString(’8’) + " " +
digitToString(’6’));
}
public static String digitToString(char digit) {
String str = "";
switch(digit) {
case ’1’: str = "one";
break;
case ’2’: str = "two";
break;
case ’3’: str = "three"; break;
case ’4’: str = "four"; break;
case ’5’: str = "five"; break;
case ’6’: str = "six";
break;
case ’7’: str = "seven"; break;
case ’8’: str = "eight"; break;
case ’9’: str = "nine"; break;
case ’0’: str = "zero"; break;
default: System.out.println(digit + " is not a digit!");
}
return str;
}
}

Output from the program:
seven eight six

Several case labels can prefix the same statement. They will all result in the associated statement being executed. This is illustrated in Example 6.3 for the switch
statement at (1).
The first statement in the switch body must have a case or default label, otherwise
it is unreachable. This statement will never be executed, since control can never be
transferred to it. The compiler will flag this as an error.
Since each action associated with a case label can be an arbitrary statement, it can
be another switch statement. In other words, switch statements can be nested. Since
a switch statement defines its own local block, the case labels in an inner block do
not conflict with any case labels in an outer block. Labels can be redefined in nested
blocks, unlike variables which cannot be redeclared in nested blocks (see Section
4.6, p. 131). In Example 6.3, an inner switch statement is defined at (2). This allows
further refinement of the action to take on the value of the switch expression, in
cases where multiple labels are used in the outer switch statement. A break statement terminates the innermost switch statement in which it is executed.

6.2: SELECTION STATEMENTS

Example 6.3

211

Nested switch Statement
public class Seasons {
public static void main(String[] args) {
int monthNumber = 11;
switch(monthNumber) {
// (1) Outer
case 12: case 1: case 2:
System.out.println("Snow in the winter.");
break;
case 3: case 4: case 5:
System.out.println("Green grass in the spring.");
break;
case 6: case 7: case 8:
System.out.println("Sunshine in the summer.");
break;
case 9: case 10: case 11:
// (2)
switch(monthNumber) { // Nested switch
(3) Inner
case 10:
System.out.println("Halloween.");
break;
case 11:
System.out.println("Thanksgiving.");
break;
} // end nested switch
// Always printed for case labels 9, 10, 11
System.out.println("Yellow leaves in the fall.");
// (4)
break;
default:
System.out.println(monthNumber + " is not a valid month.");
}
}
}

Output from the program:
Thanksgiving.
Yellow leaves in the fall.

Example 6.4 illustrates using enum types in a switch statement. The enum type
SPICE_DEGREE is defined at (1). The type of the switch expression is SPICE_DEGREE.
Note that the enum constants are not specified with their fully qualified name (see
(2a). Using the fully qualified name results in a compile-time error, as shown at
(2b). Only enum constants that have the same enum type as the switch expression can
be specified as case label values. The semantics of the switch statement are the same
as described earlier.

212

CHAPTER 6: CONTROL FLOW

Example 6.4

Enums in switch Statement
public class SwitchingFun {
enum SPICE_DEGREE {
MILD, MEDIUM, HOT, SUICIDE;
}

// (1)

public static void main(String[] args) {
SPICE_DEGREE spiceDegree = SPICE_DEGREE.HOT;
switch (spiceDegree) {
case HOT:
// (2a) OK!
//
case SPICE_LEVEL.HOT:
// (2b) COMPILE-TIME ERROR!
System.out.println("Have fun!");
break;
case SUICIDE:
System.out.println("Good luck!");
break;
default:
System.out.println("Enjoy!");
}
}
}

Output from the program:
Have fun!

Review Questions
6.1

What will be the result of attempting to compile and run the following class?
public class IfTest {
public static void main(String[] args) {
if (true)
if (false)
System.out.println("a");
else
System.out.println("b");
}
}

Select the one correct answer.
(a) The code will fail to compile because the syntax of the if statement is
incorrect.
(b) The code will fail to compile because the compiler will not be able to determine which if statement the else clause belongs to.
(c) The code will compile correctly and display the letter a, when run.
(d) The code will compile correctly and display the letter b, when run.
(e) The code will compile correctly, but will not display any output.

6.2: SELECTION STATEMENTS

6.2

213

Which statements are true?
Select the three correct answers.
(a) The conditional expression in an if statement can have method calls.
(b) If a and b are of type boolean, the expression (a = b) can be the conditional
expression of an if statement.
(c) An if statement can have either an if clause or an else clause.
(d) The statement if (false) ; else ; is illegal.
(e) Only expressions which evaluate to a boolean value can be used as the condition in an if statement.

6.3

What, if anything, is wrong with the following code?
void test(int x) {
switch (x) {
case 1:
case 2:
case 0:
default:
case 4:
}
}

Select the one correct answer.
(a)
(b)
(c)
(d)
(e)
(f)
6.4

The variable x does not have the right type for a switch expression.
The case label 0 must precede case label 1.
Each case section must end with a break statement.
The default label must be the last label in the switch statement.
The body of the switch statement must contain at least one statement.
There is nothing wrong with the code.

Which of these combinations of switch expression types and case label value types
are legal within a switch statement?
Select the two correct answers.
(a)
(b)
(c)
(d)
(e)
(f)
(g)

6.5

switch expression of type int and case label value of type char.
switch expression of type float and case label value of type int.
switch expression of type byte and case label value of type float.
switch expression of type char and case label value of type long.
switch expression of type boolean and case label value of type boolean.
switch expression of type Byte and case label value of type byte.
switch expression of type byte and case label value of type Byte.

What will be the result of attempting to compile and run the following program?
public class Switching {
public static void main(String[] args) {
final int iLoc = 3;
switch (6) {
case 1:

214

CHAPTER 6: CONTROL FLOW
case iLoc:
case 2 * iLoc:
System.out.println("I am not OK.");
default:
System.out.println("You are OK.");
case 4:
System.out.println("It's OK.");
}
}
}

Select the one correct answer.
(a) The code will fail to compile because of the case label value 2 * iLoc.
(b) The code will fail to compile because the default label is not specified last in
the switch statement.
(c) The code will compile correctly and will only print the following, when run:
I am not OK.
You are OK.
It's OK.

(d) The code will compile correctly and will only print the following, when run:
You are OK.
It's OK.

(e) The code will compile correctly and will only print the following, when run:
It's OK.

6.6

What will be the result of attempting to compile and run the following program?
public class MoreSwitching {
public static void main(String[] args) {
final int iLoc = 3;
Integer iRef = 5;
switch (iRef) {
default:
System.out.println("You are OK.");
case 1:
case iLoc:
case 2 * iLoc:
System.out.println("I am not OK.");
break;
case 4:
System.out.println("It's OK.");
}
}
}

Select the one correct answer.
(a) The code will fail to compile because the type of the switch expression is not
valid.
(b) The code will compile correctly and will only print the following, when run:
You are OK.
I am not OK.

6.2: SELECTION STATEMENTS

215

(c) The code will compile correctly and will only print the following, when run:
You are OK.
I am not OK.
It's OK.

(d) The code will compile correctly and will only print the following, when run:
It's OK.

6.7

What will be the result of attempting to compile and run the following program?
public class KeepOnSwitching {
public static void main(String[] args) {
final int iLoc = 3;
final Integer iFour = 4;
Integer iRef = 4;
switch (iRef) {
case 1:
case iLoc:
case 2 * iLoc:
System.out.println("I am not OK.");
default:
System.out.println("You are OK.");
case iFour:
System.out.println("It’s OK.");
}
}
}

Select the one correct answer.
(a) The code will fail to compile because of the value of one of the case labels.
(b) The code will fail to compile because of the type of the switch expression.
(c) The code will compile correctly and will only print the following, when run:
You are OK.
It's OK.

(d) The code will compile correctly and will only print the following, when run:
It's OK.

6.8

What will be the result of attempting to compile and run the following code?
public enum Scale5 {
GOOD, BETTER, BEST;
public char getGrade() {
char grade = '\u0000';
switch(this){
case GOOD:
grade = 'C';
break;
case BETTER:
grade = 'B';
break;
case BEST:
grade = 'A';

216

CHAPTER 6: CONTROL FLOW
break;
}
return grade;
}
public static void main (String[] args) {
System.out.println(GOOD.getGrade());
}
}

Select the one correct answer.
(a) The program will not compile because of the switch expression.
(b) The program will not compile, as enum constants cannot be used as case
labels.
(c) The case labels must be qualified with the enum type name.
(d) The program compiles and only prints the following, when run:
C

(e) The program compiles and only prints the following, when run:
GOOD

(f) None of the above.

6.3 Iteration Statements
Loops allow a block of statements to be executed repeatedly (that is, iterated). A
boolean condition (called the loop condition) is commonly used to determine when
to terminate the loop. The statements executed in the loop constitute the loop body.
The loop body can be a single statement or a block.
Java provides three language constructs for loop construction:
• the while statement
• the do-while statement
• the basic for statement
These loops differ in the order in which they execute the loop body and test the
loop condition. The while loop and the basic for loop test the loop condition before
executing the loop body, while the do-while loop tests the loop condition after execution of the loop body.
In addition to the basic for loop, there is a specialized one called the enhanced for
loop (also called the for-each loop) that simplifies iterating over arrays and collections. We will use the notations for(;;) and for(:) to designate the basic for loop
and the enhanced for loop, respectively.

217

6.3: ITERATION STATEMENTS

The while Statement
The syntax of the while loop is
while ()


The  is evaluated before executing the . The while statement executes the  as long as the  is true. When the  becomes false, the loop is terminated and execution continues with the
statement immediately following the loop. If the  is false to begin
with, the  is not executed at all. In other words, a while loop can execute
zero or more times. The  must evaluate to a boolean or a Boolean
value. In the latter case, the reference value is unboxed to a boolean value. The flow
of control in a while statement is shown in Figure 6.3.
Figure 6.3

Activity Diagram for the while Statement

Evaluate boolean
expression

[true]

Execute
while body

[false]

The while statement is normally used when the number of iterations is not known.
while (noSignOfLife())
keepLooking();

Since the  can be any valid statement, inadvertently terminating each
line with the empty statement (;) can give unintended results. Always using a
block statement, { ... }, as the  helps to avoid such problems.
while (noSignOfLife());
keepLooking();

// Empty statement as loop body!
// Statement not in the loop body.

The do-while Statement
The syntax of the do-while loop is
do


while ();

The  is evaluated after executing the . The value of the
 is subjected to unboxing if it is of the type Boolean. The do-while
statement executes the  until the  becomes false. When
the  becomes false, the loop is terminated and execution continues
with the statement immediately following the loop. Note that the  is executed at least once. Figure 6.4 illustrates the flow of control in a do-while statement.

218
Figure 6.4

CHAPTER 6: CONTROL FLOW

Activity Diagram for the do-while Statement

Execute
do-while body

Evaluate boolean
expression
[true]
[false]

The  in a do-while loop is invariably a statement block. It is instructive
to compare the while and the do-while loops. In the examples below, the mice might
never get to play if the cat is not away, as in the loop at (1). The mice do get to play
at least once (at the peril of losing their life) in the loop at (2).
while (cat.isAway()) {
mice.play();
}

// (1)

do {
mice.play();
} while (cat.isAway());

// (2)

The for(;;) Statement
The for(;;) loop is the most general of all the loops. It is mostly used for countercontrolled loops, i.e., when the number of iterations is known beforehand.
The syntax of the loop is as follows:
for (; ; )


The  usually declares and initializes a loop variable that controls the
execution of the . The  must evaluate to a boolean or a
Boolean value. In the latter case, the reference value is converted to a boolean value
by unboxing. The  usually involves the loop variable, and if the
loop condition is true, the loop body is executed; otherwise, execution continues
with the statement following the for(;;) loop. After each iteration (that is, execution of the loop body), the  is executed. This usually modifies
the value of the loop variable to ensure eventual loop termination. The loop condition is then tested to determine whether the loop body should be executed again.
Note that the  is only executed once on entry to the loop. The semantics of the for(;;) loop are illustrated in Figure 6.5, and can be summarized by the
following equivalent while loop code template:

condition>) {



while ( section of
the loop. It is incremented in the  section.
The for(;;) loop defines a local block so that the scope of this declaration is the
for(;;) block, which comprises the , the , the  and the  sections. Any variable declared in the for(;;)
block is thus not accessible after the for(;;) loop terminates. The loop at (1)
showed how a declaration statement can be specified in the  section.
Such a declaration statement can also specify a comma-separated list of variables.
for (int i = 0, j = 1, k = 2; ... ; ...) ...;

// (2)

The variables i, j, and k in the declaration statement all have type int. All variables
declared in the  section are local variables in the for(;;) block and
obey the scope rules for local blocks. However, note that the following code will
not compile, as variable declarations of different types (in this case, int and String)
require declaration statements that are terminated by semicolons:
for (int i = 0, String str = "@"; ... ; ...) ...;

// (3) Compile time error.

The  section can also be a comma-separated list of expression statements (see Section 3.3, p. 45). For example, the loop at (2) can be rewritten by factoring out the variable declaration.
int i, j, k; // Variable declaration
for (i = 0, j = 1, k = 2; ... ; ...) ...;

// (4) Only initialization

The  section is now a comma-separated list of three expressions. The
expressions in such a list are always evaluated from left to right. Note that the variables i, j, and k at (4) are not local to the loop.
Declaration statements cannot be mixed with expression statements in the  section, as is the case at (5) in the following example. Factoring out the

220

CHAPTER 6: CONTROL FLOW

variable declaration, as at (6), leaves a legal comma-separated list of expression
statements only.
// (5) Not legal and ugly:
for (int i = 0, System.out.println("This won't do!"); flag; i++) { // Error!
// loop body
}
// (6) Legal, but still ugly:
int i;
// declaration factored out.
for (i = 0, System.out.println("This is legal!"); flag; i++) {
// OK.
// loop body
}

The  can also be a comma-separated list of expression statements. The following code specifies a for(;;) loop that has a comma-separated list
of three variables in the  section, and a comma-separated list of two
expressions in the  section:
// Legal usage but not recommended.
int[][] sqMatrix = { {3, 4, 6}, {5, 7, 4}, {5, 8, 9} };
for (int i = 0, j = sqMatrix[0].length - 1, asymDiagonal = 0; // initialization
i < sqMatrix.length;
// loop condition
i++, j--)
// increment expression
asymDiagonal += sqMatrix[i][j];
// loop body

All sections in the for(;;) header are optional. Any or all of them can be left empty,
but the two semicolons are mandatory. In particular, leaving out the  signifies that the loop condition is true. The “crab”, (;;), is commonly used
to construct an infinite loop, where termination is presumably achieved through
code in the loop body (see next section on transfer statements):
for (;;) Java.programming();

// Infinite loop

The for(:) Statement
The enhanced for loop is convenient when we need to iterate over an array or a collection, especially when some operation needs to be performed on each element of
the array or collection. In this section we discuss iterating over arrays, and in Chapter 15 we take a closer look at the for(:) loop for iterating over collections.
Earlier in this chapter we used a for(;;) loop to sum the values of elements in an
int array:
int sum = 0;
int[] intArray = {12, 23, 5, 7, 19};
for (int index = 0; index < intArray.length; index++) {
sum += intArray[index];
}

// (1) using for(;;) loop

The for(;;) loop at (1) above is rewritten using the for(:) loop in Figure 6.6. The
body of the loop is executed for each element in the array, where the variable element
successively denotes the current element in the array intArray. When the loop terminates, the variable sum will contain the sum of all elements in the array. We do not care

221

6.3: ITERATION STATEMENTS
Figure 6.6

Enhanced for Statement
element declaration

loop body

expression

for (int element : intArray)
{
sum += element;
}

about the position of the elements in the array, just that the loop iterates over all elements of the array.
From Figure 6.6 we see that the for(:) loop header has two parts. The expression
must evaluate to a reference value that refers to an array, i.e., the array we want to
iterate over. The array can be an array of primitive values or objects, or even an
array of arrays. The expression is only evaluated once. The element declaration specifies a local variable that can be assigned a value of the element type of the array. This
assignment might require either a boxing or an unboxing conversion. The type of
the array in the code snippet is int[], and the element type is int. Therefore, the element variable is declared to be of type int. The element variable is local to the loop
block and is not accessible after the loop terminates. Also, changing the value of the
current variable does not change any value in the array. The loop body, which can be
a simple statement or a statement block, is executed for each element in the array
and there is no danger of any out-of-bounds errors.
The for(:) loop has its limitations. We cannot change element values and it does
not provide any provision for positional access using an index. The for(:) loop
only increments by one and always in a forward direction. It does not allow iterations over several arrays simultaneously. Under such circumstances the for(;;)
loop can be more convenient.
Here are some code examples of for(:) loops that are legal:
// Some 1-dim arrays:
int[] intArray = {10, 20, 30};
Integer[] intObjArray = {10, 20, 30};
String[] strArray = {"one", "two"};
// Some 2-dim arrays:
Object[][] objArrayOfArrays = {intObjArray, strArray};
Number[][] numArrayOfArrays = {{1.5, 2.5}, intObjArray, {100L, 200L}};
int[][] intArrayOfArrays = {{20}, intArray, {40}};
// Iterate over an array of Strings.
// Expression type is String[], and element type is String.
// String is assignable to Object.
for (Object obj : strArray) {}
// Iterate over an array of ints.
// Expression type is int[], and element type is int.
// int is assignable to Integer (boxing conversion)
for (Integer iRef : intArrayOfArrays[0]){}

222

CHAPTER 6: CONTROL FLOW

// Iterate over an array of Integers.
// Expression type is Integer[], and element type is Integer.
// Integer is assignable to int (unboxing conversion)
for (int i : intObjArray){}
// Iterate over a 2-dim array of ints.
// Outer loop: expression type is int[][], and element type is int[].
// Inner loop: expression type is int[], element type is int.
for (int[] row : intArrayOfArrays)
for (int val : row) {}
// Iterate over a 2-dim array of Numbers.
// Outer loop: expression type is Number[][], and element type is Number[].
// Outer loop: Number[] is assignable to Object[].
// Inner loop: expression type is Object[], element type is Object.
for (Object[] row : numArrayOfArrays)
for (Object obj : row) {}
// Outer loop: expression type is Integer[][], and element type is Integer[].
// Outer loop: Integer[] is assignable to Number[].
// Inner loop: expression type is int[], and element type is int.
// Inner loop: int is assignable to double.
for (Number[] row : new Integer[][] {intObjArray, intObjArray, intObjArray})
for (double num : new int[] {}) {}

Here are some code examples of for(:) loops that are not legal:
// Expression type is Number[][], element type is Number[].
// Number[] is not assignable to Number.
for (Number num : numArrayOfArrays) {}
// Compile-time error.
// Expression type is Number[], element type is Number.
// Number is not assignable to int.
for (int row : numArrayOfArrays[0]) {}
// Compile-time error.
// Outer loop: expression type is int[][], and element type is int[].
// int[] is not assignable to Integer[].
for (Integer[] row : intArrayOfArrays)
// Compile-time error.
for (int val : row) {}
// Expression type is Object[][], and element type is Object[].
// Object[] is not assignable to Integer[].
for (Integer[] row : objArrayOfArrays) {}
// Compile-time error.
// Outer loop: expression type is String[], and element type is String.
// Inner loop: expression type is String which is not legal here.
for (String str : strArray)
for (char val : str) {}
// Compile-time error.

When using the for(:) loop to iterate over an array, the two main causes of errors
are: the expression in the loop header does not represent an array and/or the element type of the array is not assignable to the local variable declared in the loop
header.

223

6.4: TRANSFER STATEMENTS

6.4 Transfer Statements
Java provides six language constructs for transferring control in a program:
• break
• continue
• return
• try-catch-finally
• throw
• assert
This section discusses the first three statements, and the remaining statements are
discussed in subsequent sections.
Note that Java does not have a goto statement, although goto is a reserved word.

Labeled Statements
A statement may have a label.