OCP Java SE 7 Programmer II Certification Guide Prepare For The 1ZO 804 Exam

User Manual: Pdf

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

DownloadOCP Java SE 7 Programmer II Certification Guide- Prepare For The 1ZO-804 Exam
Open PDF In BrowserView PDF
Prepare for the 1ZO-804 exam

Mala Gupta

MANNING
www.allitebooks.com

OCP Java SE 7 Programmer II
Certification Guide

Licensed to Mark
Watson 
www.allitebooks.com

Licensed to Mark
Watson 
www.allitebooks.com

OCP Java SE 7
Programmer II
Certification Guide
PREPARE FOR THE 1ZO-804 EXAM
MALA GUPTA

MANNING
SHELTER ISLAND

Licensed to Mark
Watson 
www.allitebooks.com

For online information and ordering of this and other Manning books, please visit
www.manning.com. The publisher offers discounts on this book when ordered in quantity.
For more information, please contact
Special Sales Department
Manning Publications Co.
20 Baldwin Road
PO Box 761
Shelter Island, NY 11964
Email: orders@manning.com
©2015 by Manning Publications Co. All rights reserved.

No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in
any form or by means electronic, mechanical, photocopying, or otherwise, without prior written
permission of the publisher.

Many of the designations used by manufacturers and sellers to distinguish their products are
claimed as trademarks. Where those designations appear in the book, and Manning
Publications was aware of a trademark claim, the designations have been printed in initial caps
or all caps.

Recognizing the importance of preserving what has been written, it is Manning’s policy to have
the books we publish printed on acid-free paper, and we exert our best efforts to that end.
Recognizing also our responsibility to conserve the resources of our planet, Manning books
are printed on paper that is at least 15 percent recycled and processed without the use of
elemental chlorine.

Manning Publications Co.
20 Baldwin Road
PO Box 761
Shelter Island, NY 11964

Development editor:
Technical editor:
Copyeditor:
Proofreader:
Technical proofreaders:
Typesetter:
Cover designer:

Cynthia Kane
George Zurowski
Jodie Allen
Alyson Brener
Roel De Nijs, Jean-François Morin
Dennis Dalinnik
Marija Tudor

ISBN: 9781617291487
Printed in the United States of America
1 2 3 4 5 6 7 8 9 10 – MAL – 20 19 18 17 16 15

Licensed to Mark
Watson 
www.allitebooks.com

To Dheeraj, my pillar of strength

Licensed to Mark
Watson 
www.allitebooks.com

Licensed to Mark
Watson 
www.allitebooks.com

brief contents
Introduction 1
1

■

Java class design 13

2

■

Advanced class design 95

3

■

Object-oriented design principles 172

4

■

Generics and collections 242

5

■

String processing 348

6

■

Exceptions and assertions 396

7

■

Java I/O fundamentals 463

8

■

Java file I/O (NIO.2) 512

9

■

Building database applications with JDBC 577

10

■

Threads 627

11

■

Concurrency 679

12

■

Localization 719

vii

Licensed to Mark
Watson 
www.allitebooks.com

Licensed to Mark
Watson 
www.allitebooks.com

contents
preface xxi
acknowledgments xxiii
about this book xxv

Introduction 1
Disclaimer 2
Introduction to OCP Java SE 7 Programmer II certification
(1Z0-804) 2
The importance of the OCP Java SE 7 Programmer II certification 2
Comparing the OCA Java SE 7 Programmer I (1Z0-803) and OCP
Java SE 7 Programmer II (1Z0-804) exams 4 Complete exam
objectives, mapped to book chapters, and readiness checklist 4
■

FAQ

7

FAQ on exam preparation

8

■

FAQ on taking the exam 9

The testing engine used in the exam 11

ix

Licensed to Mark
Watson 
www.allitebooks.com

CONTENTS

x

1

Java class design 13
1.1

Java access modifiers 15
Public access modifier 16 Protected access modifier 17
Default access (package access) 19 The private access
modifier 23 Access modifiers and Java entities 25
Effects of changing access modifiers for existing entities 26
■

■

■

1.2

Overloaded methods and constructors 28
Argument list 30
overloaded methods

1.3

■

When methods can’t be defined as
34 Overloaded constructors 35
■

Method overriding and virtual method invocation 40
Need of overridden methods 41 Correct syntax of overriding
methods 44 Can you override all methods from the base
class or invoke them virtually? 48 Identifying method
overriding, overloading, and hiding 49 Can you override
base class constructors or invoke them virtually? 50
■

■

■

■

1.4

Overriding methods of class Object 51
Overriding method toString() 51 Overriding method
equals() 54 Overriding method hashCode() 60
■

■

1.5

Casting and the instanceof operator

66

Implicit and explicit casting 67 Combinations of casting
Using the instanceof operator 73
■

1.6

70

Packages 75
The need for packages 75 Defining classes in a package using
the package statement 76 Using simple names with import
statements 78 Using packages without using the import
statement 79 Importing a single member versus all members
of a package 80 The import statement doesn’t import the
whole package tree 80 Importing classes from the default
package 81 Static imports 81
■

■

■

■

■

■

■

1.7
1.8

Summary 82
Review notes 83
Java access modifiers 83 Overloaded methods and
constructors 84 Method overriding and virtual
method invocation 84 Java packages 85
■

■

■

1.9
1.10

Sample exam questions 85
Answers to sample exam questions 90

Licensed to Mark Watson 

CONTENTS

2

xi

Advanced class design 95
2.1

Abstract classes and their application 97
Identify abstract classes 97 Construct abstract classes and
subclasses 100 Understand the need for abstract classes 103
Follow the do’s and don’t’s of creating and using abstract
classes 103 Compare abstract classes and concrete classes 105
■

■

■

2.2

Static and final keywords 106
Static modifier

2.3

107

■

Nonaccess modifier—final 115

Enumerated types 122
Understanding the need for and creating an enum 123
Adding implicit code to an enum 124 Extending
java.lang.Enum 125 Adding variables, constructors,
and methods to your enum 127 Where can you define
an enum? 130
■

■

■

2.4

Static nested and inner classes 132
Advantages of inner classes 133 Static nested class (also
called static inner class) 134 Inner class (also called
member class) 139 Anonymous inner classes 147
Method local inner classes 152 Disadvantages of
inner classes 153
■

■

■

■

2.5
2.6

Summary 154
Review notes 154
Abstract classes 154 Nonaccess modifier—static 155
Nonaccess modifier—final 155 Enumerated types 156
Static nested classes 157 Inner classes 158 Anonymous
inner classes 158 Method local inner classes 159
■

■

■

■

■

2.7
2.8

3

Sample exam questions 159
Answers to sample exam questions 166

Object-oriented design principles 172
3.1

Interfaces 174
Understanding interfaces 175 Declaring interfaces 176
Implementing interfaces 179 Extending interfaces 183
■

■

3.2

Class inheritance versus interface inheritance 184
Comparing class inheritance and interface inheritance 184
Preferring class inheritance over interface inheritance 185
Preferring interface inheritance over class inheritance 186

Licensed to Mark Watson 

CONTENTS

xii

3.3

IS-A and HAS-A relationships in code 190
Identifying and implementing an IS-A relationship 191
Identifying and implementing a HAS-A relationship 196

3.4

Cohesion and low coupling
Cohesion

3.5
3.6

197

■

Coupling

197
198

Object composition principles 200
Introduction to design patterns 201
What is a design pattern? 201
design pattern? 201

3.7

■

Why do you need a

Singleton pattern 202
Why do you need this pattern? 202 Implementing the
Singleton pattern 202 Ensuring creation of only one object
in the Singleton pattern 203 Comparing Singleton with
global data 207
■

■

■

3.8

Factory pattern 208
Simple Factory pattern (or Static Factory pattern) 208
Factory Method pattern 210 Abstract Factory pattern 211
Benefits of the Factory pattern 213 Using the Factory pattern
from the Java API 214
■

■

3.9

DAO pattern 215
What is the DAO pattern? 215 Implementing the
DAO pattern 215 Using the Simple Factory pattern
with the DAO pattern 217 Using the Factory Method
or Abstract Factory pattern with the DAO pattern 218
Benefits of the DAO pattern 220
■

■

■

3.10
3.11

Summary 220
Review notes 222
Interfaces 222 Class inheritance versus interface
inheritance 223 IS-A and HAS-A relationships
in code 223 Cohesion and low coupling 223
Object composition principles 224 Singleton pattern
Factory pattern 225 DAO pattern 226
■

■

■

■

224

■

3.12
3.13

4

Sample exam questions 226
Answers to sample exam questions 234

Generics and collections 242
4.1

Introducing generics: WARM-UP 244
Need for introducing generics 244
of using generics 245

■

Benefits and complexities

Licensed to Mark Watson 

CONTENTS

4.2

xiii

Creating generic entities 246
Creating a generic class 246 Working with generic
interfaces 250 Using generic methods 252
Bounded type parameters 253 Using wildcards 255
Using bounded wildcards 257 Type erasure 260
Refreshing the commonly used terms 262
■

■

■

■

4.3

Using type inference 263
Using type inference to instantiate a generic class 264
Using type inference to invoke generic methods 265

4.4

Understanding interoperability of collections using raw
types and generic types 265
Mixing reference variables and objects of raw and generic types
Subtyping with generics 270

4.5
4.6

Introducing the collections framework: WARM-UP
Working with the Collection interface 273
The core Collection interface 274
Collection interface 275

4.7

■

266

271

Methods of the

Creating and using List, Set, and Deque
implementations 276
List interface and its implementations 276 Deque interface
and its implementations 282 Set interface and its
implementations 290 Set implementation classes 291
■

■

■

4.8

Map and its implementations 296
Map interface 296
TreeMap 305

4.9

■

HashMap 297

LinkedHashMap 304

Using java.util.Comparator and
java.lang.Comparable 308
Comparable interface 308

4.10

■

■

Comparator interface 310

Sorting and searching arrays and lists 313
Sorting arrays 313 Sorting List using Collections
Searching arrays and List using collections 318
■

4.11

317

Using wrapper classes 320
Class hierarchy of wrapper classes 320 Creating objects of
the wrapper classes 321 Retrieving primitive values from
the wrapper classes 322 Parsing a string value to a
primitive type 322 Difference between using method
valueOf() and constructors of wrapper classes 323
Comparing objects of wrapper classes 323
■

■
■

■

Licensed to Mark Watson 

CONTENTS

xiv

4.12
4.13
4.14

Autoboxing and unboxing
Summary 327
Review notes 328

325

Creating generic entities 328 Using type inference 330
Understanding interoperability of collections using raw types and
generic types 330 Working with the Collection interface 330
Creating and using List, Set, and Deque implementations 331
Map and its implementations 334 Using java.util.Comparator
and java.lang.Comparable 336 Sorting and searching
arrays and lists 336 Using wrapper classes 337
Autoboxing and Unboxing 337
■

■

■

■

■

4.15
4.16

5

Sample exam questions 338
Answers to sample exam questions 343

String processing 348
5.1

Regular expressions 349
What is a regular expression? 351 Character classes 351
Predefined character classes 354 Matching boundaries 356
Quantifiers 358 Java’s regex support 362
■

■

■

5.2

Searching, parsing, and building strings 364
Searching strings 364 Replacing strings 368
Parsing and tokenizing strings with Scanner and
StringTokenizer 372
■

5.3

Formatting strings 376
Formatting classes 376 Formatting methods 376
Defining format strings 377 Formatting parameter %b 379
Formatting parameter %c 380 Formatting parameters %d
and %f 380 Formatting parameter %s 381
■

■

■

■

5.4
5.5

Summary 382
Review notes 382
Regular expressions 382
Formatting strings 385

5.6
5.7

6

■

Search, parse, and build strings 384

Sample exam questions 387
Answers to sample exam questions 391

Exceptions and assertions 396
6.1

Using the throw statement and the throws clause 398
Creating a method that throws a checked exception 400
Using a method that throws a checked exception 400

Licensed to Mark Watson 

CONTENTS

xv

Creating and using a method that throws runtime exceptions
or errors 403 Points to note while using the throw statement
and the throws clause 404
■

6.2

Creating custom exceptions 409
Creating a custom checked exception 410
unchecked exception 412

6.3
6.4

■

Creating a custom

Overriding methods that throw exceptions 413
Using the try statement with multi-catch and
finally clauses 415
Comparing single-catch handlers and multi-catch handlers 415
Handling multiple exceptions in the same exception handler 416

6.5

Auto-closing resources with a try-with-resources
statement 422
How to use a try-with-resources statement 422
Suppressed exceptions 424 The right ingredients 426
■

6.6

Using assertions 431
Exploring the forms of assertions 432 Testing invariants in
your code 435 Understanding appropriate and inappropriate
uses of assertions 439
■

■

6.7
6.8

Summary 442
Review notes 443
Using the throw statement and the throws clause 443
Custom exceptions 444 Overriding methods that throw
exceptions 444 try statement with multi-catch and
finally clauses 445 Auto-close resources with
try-with-resources statement 445 Assertions 446
■

■

■

■

6.9
6.10

7

Sample exam questions 447
Answers to sample exam questions 456

Java I/O fundamentals 463
7.1

Introducing Java I/O: WARM-UP 464
Understanding streams
flavors of data 465

7.2

464

■

Understanding multiple

Working with class java.io.File 469
Instantiating and querying File instances 470
Creating new files and directories on your physical device

Licensed to Mark Watson 

472

CONTENTS

xvi

7.3

Using byte stream I/O 473
Input streams 473 Output streams 475 File I/O with
byte streams 477 Buffered I/O with byte streams 481
Primitive values and strings I/O with byte streams 482
Object I/O with byte streams: reading and writing objects 484
■

■

■

7.4

Using character I/O with readers and writers 489
Abstract class java.io.Reader 491 Abstract class
java.io.Writer 491 File I/O with character streams 492
Buffered I/O with character streams 493 Data streams
with character streams: using PrintWriter to write to a file 494
Constructor chaining with I/O classes 496
■

■

■

7.5
7.6
7.7

Working with the console 497
Summary 499
Review notes 500
Working with class java.io.File 500 Using byte
stream I/O 500 Using character I/O with readers
and writers 502 Working with the console 503
■

■

■

7.8
7.9

8

Sample exam questions 504
Answers to sample exam questions 509

Java file I/O (NIO.2) 512
8.1

Path objects

516

Multiple ways to create Path objects 518 Methods to access
Path components 521 Comparing paths 522
Converting relative paths to absolute paths 523
Resolving paths using methods resolve and resolveSibling 525
Method relativize() 526
■

■

8.2

Class Files 527
Create files and directories 527 Check for the existence of files
and directories 529 Copy files 530 Move files and
directories 534 Delete files and directories 534
Commonly thrown exceptions 535
■

■

■

■

8.3

Files and directory attributes 535
Individual attributes 535 Group of attributes 537
Basic attributes 540 DOS attributes 541 POSIX
attributes 542 AclFileAttributeView interface 543
FileOwnerAttributeView interface 543
UserDefinedAttributeView interface 543
■

■

■

■

Licensed to Mark Watson 

CONTENTS

8.4

xvii

Recursively access a directory tree

545

FileVisitor interface 546 Class SimpleFileVisitor 549
Initiate traversal for FileVisitor and SimpleFileVisitor 550
DirectoryStream interface 553
■

8.5
8.6

Using PathMatcher 555
Watch a directory for changes 557
Create WatchService object
WatchService object 557
WatchKey interface 558

8.7
8.8

557 Register with
Access watched events using
Processing events 558
■

■
■

Summary 561
Review notes 561
Path objects 561 Class Files 563 Files and directory
attributes 564 Recursively access a directory tree 566
Using PathMatcher 566 Watch a directory for changes 567
■

■

■

■

8.9
8.10

9

Sample exam questions 568
Answers to sample exam questions 573

Building database applications with JDBC 577
9.1

Introduction 578
JDBC API overview 579
JDBC drivers 580

9.2

■

JDBC architecture

580

Interfaces that make up the JDBC API core

581

Interface java.sql.Driver 582
Interface java.sql.Connection 583
Interface java.sql.Statement 583
Interface java.sql.ResultSet 583

9.3

Connecting to a database 584
Loading JDBC drivers 585 Use DriverManager to
connect to a database 586 Exceptions thrown by
database connections 588
■

■

9.4

CRUD (create, retrieve, update, and delete)
operations 589
Read table definition and create table 590 Mapping SQL data
types to Java data types 591 Insert rows in a table 592
Update data in a table 594 Delete data in a table 595
Querying database 595
■

■

■

Licensed to Mark Watson 

CONTENTS

xviii

9.5

JDBC transactions 599
A transaction example 599 Create savepoints and
roll back partial transactions 601 Commit modes and
JDBC transactions 603
■

■

9.6

RowSet objects 603
Interface RowSetFactory 605 Class RowSetProvider 605
An example of working with JdbcRowSet 605
■

9.7

Precompiled statements 607
Prepared statements 607 Interface CallableStatement
Database-stored procedures with parameters 612
■

9.8
9.9

610

Summary 613
Review notes 614
Introduction 614 Interfaces that make up the
JDBC API core 615 Connecting to a database 615
CRUD (create, retrieve, update, and delete) operations 616
JDBC transactions 617 RowSet objects 617
Precompiled statements 618
■

■

■

9.10
9.11

10

Sample exam questions 619
Answers to sample exam questions 624

Threads 627
10.1

Create and use threads 629
Extending class Thread
Runnable 632

10.2

630

■

Implement interface

Thread lifecycle 634
Lifecycle of a thread 634 Methods of class Thread 637
Start thread execution 637 Pause thread execution 639
End thread execution 645
■

■

10.3

Protect shared data 645
Identifying shared data: WARM-UP 645 Thread
interference 646 Thread-safe access to shared data 649
Immutable objects are thread safe 654 Volatile variables 655
■

■

■

10.4

Identify and fix code in a multithreaded
environment 657
Variables you should care about 657 Operations you should
care about 658 Waiting for notification of events: using wait,
notify, and notifyAll 659 Deadlock 662 Starvation 663
Livelock 664 Happens-before relationship 664
■

■

■

■

■

Licensed to Mark Watson 

CONTENTS

10.5
10.6

xix

Summary 665
Review notes 665
Create and use threads 665 Thread lifecycle 666
Methods of class Thread 667 Protect shared data 668
Identify and fix code in a multithreaded environment 669
■

■

10.7
10.8

11

Sample exam questions 670
Answers to sample exam questions 675

Concurrency 679
11.1

Concurrent collection classes 680
Interface BlockingQueue 681 Interface ConcurrentMap 682
Class ConcurrentHashMap 682
■

11.2

Locks 684
Acquire lock 685 Acquire lock and return immediately 686
Interruptible locks 688 Nonblock-structured locking 690
Interface ReadWriteLock 692 Class ReentrantReadWriteLock 692
Atomic variables 693
■

■

■

11.3

Executors 695
Interface Executor 696 Interface Callable 698
Interface ExecutorService 699 Thread pools 700
Interface ScheduledExecutorService 701
■

■

11.4
11.5
11.6

Parallel fork/join framework
Summary 708
Review notes 709

703

Concurrent collection classes 709 Locks 709
Executors 710 Parallel fork/join framework 711
■

■

11.7
11.8

12

Sample exam questions 712
Answers to sample exam questions 716

Localization 719
12.1

Internationalization and localization

720

Advantages of localization 722 Class java.util.Locale 722
Creating and accessing Locale objects 723 Building locale-aware
applications 727
■

■

12.2

Resource bundles 728
Implementing resource bundles using .properties files 728
Implementing resource bundles using ListResourceBundle 733
Loading resource bundles for invalid values 735

Licensed to Mark
Watson 
www.allitebooks.com

CONTENTS

xx

12.3

Formatting dates, numbers, and currencies for
locales 737
Format numbers 738 Format currencies 740
Format dates 743 Formatting and parsing time for a
specific locale 745 Formatting and parsing date and time
together for a specific locale 746 Using custom date and time
patterns with SimpleDateFormat 747 Creating class Date object
using class Calendar 749
■

■

■

■

■

12.4
12.5

Summary 749
Review Notes 750
Internationalization and localization 750
Resource bundles 752 Formatting dates, numbers,
and currencies for locales 753
■

12.6
12.7
appendix

Sample exam questions 754
Answers to sample exam questions 758
Answers to “Twist in the Tale” exercises
index

13

761

781

Full mock exam
Available online only at www.manning.com/gupta2

Licensed to Mark Watson 

preface
The OCP Java SE Programmer II certification is designed to tell would-be employers
that you really know your basic and advanced Java stuff. It certifies that you understand and can work with design patterns and advanced Java concepts like concurrency,
multithreading, localization, string processing, and JDBC. The exam preparation helps
you to understand the finer details of the Java language and its implementation and
usage, which is crucial to writing quality code.
Cracking this exam is not an easy task. Thorough preparation is crucial if you want
to pass the exam the first time with a score that you can be proud of. You need to
know Java inside and out, and you need to understand the certification process so that
you’re ready for the challenging questions you’ll face on the exam.
This book is a comprehensive guide to the 1Z0-804 exam. You’ll explore a wide
range of important Java topics as you systematically learn how to pass the certification
exam. Each chapter starts with a list of the exam objectives covered in that chapter.
Throughout the book you’ll find sample questions and exercises designed to reinforce key concepts and prepare you for what you’ll see on the real exam, along with
numerous tips, notes, and visual aids.
Unlike many other exam guides, this book provides multiple ways to digest important techniques and concepts, including comic conversations, analogies, pictorial representations, flowcharts, UML diagrams, and, naturally, lots of well-commented code.

xxi

Licensed to Mark Watson 

PREFACE

xxii

The book also gives insight into typical exam question mistakes and guides you in
avoiding traps and pitfalls. It provides
■
■

■

■

Complete coverage of exam topics, all mapped to chapter and section numbers
Hands-on coding exercises, including particularly challenging ones that throw
in a twist
Instruction on what’s happening behind the scenes using the actual code from
the Java API source
Everything you need to master both the concepts and the exam

This book is written for developers with a working knowledge of Java. My hope is that
the book will deepen your knowledge, prepare you well for the exam, and that you
will pass it with flying colors!

Licensed to Mark Watson 

acknowledgments
First and foremost, I thank Dheeraj. He helped me to get started with this book, and
his guidance, encouragement, and love enabled me to get over the goal line.
My sincere gratitude to Marjan Bace, publisher at Manning, for giving me the
opportunity to author this book.
An extremely talented individual, Cynthia Kane, my development editor at Manning, was a pleasure to work with. She not only helped me improve the organization
of the chapters, she also pulled me up whenever the task of completing the book
became overwhelming for me.
The contributions of Roel De Nijs, technical proofreader on this book, are unparalleled. His feedback helped me to improve all sections and chapters. Jean-François
Morin, technical proofreader for a few chapters, also helped me to improve the book
just before it went into production.
Gregor Zurowski, my technical editor, provided great insight and helped iron out
technical glitches as the book was being written.
Apart from applying her magic to sentence and language constructions, Jodie Allen,
my copyeditor, was very supportive and patient in applying changes across all chapters.
I’d also like to thank Ozren Harlovic, review editor, for managing the review process and meticulously funneling the feedback to me to make this book better.
Mary Piergies, Alyson Brener, and Kevin Sullivan were awesome in their expertise
at turning all text, code, and images into publishable form. I am also grateful to Candace Gillhoolley and Ana Radic for managing the promotion of this book.

xxiii

Licensed to Mark Watson 

xxiv

ACKNOWLEDGMENTS

Next, I’d like to thank all the MEAP readers for trusting me by buying the book
while it was being written. I thank them for their patience, suggestions, corrections,
and encouragement.
Technical reviewers helped in validating the chapters’ contents at various stages of
their development. The reviewers’ detailed and helpful feedback helped me to improve
the book throughout the writing process: Alexander Schwartz, Ashutosh Sharma, Bill
Weiland, Colin Hastie, Dylan Scott, Jamie Atkinson, Kevin Vig, Kyle Smith, Manish
Verma, Mikael Strand, Mikalai Zaikin, Robin Coe, Simon Joseph Aquilina, Steve
Etherington, and Witold Bolt. Special shout-out to Mikalai for his detailed feedback—
it helped me to improve the contents enormously.
I thank my former colleagues Harry Mantheakis, Paul Rosenthal, and Selvan
Rajan, whose names I have used in coding examples throughout the book. I have
always looked up to them.
Finally, I thank my parents and my daughters, Shreya and Pavni. This book
would have been not been possible without their unconditional support, love, and
encouragement.

Licensed to Mark Watson 

about this book
This book is written for developers with a working knowledge of Java who want to earn the
OCP Java SE 7 Programmer II certification (exam 1Z0-804). It uses powerful tools and features to make reaching your goal of certification a quick, smooth, and enjoyable experience. This section will explain the features used in the book and tell you how to use the
book to get the most out of it as you prepare for the certification exam. More information
on the exam and on how the book is organized is available in the Introduction.

Start your preparation with the chapter-based exam
objective map
I strongly recommend a structured approach to preparing for this exam. To help you
with this task, I’ve developed a chapter-based exam objective map, as shown in figure 1.
The full version is in the Introduction (table 2).
Exam objective as per Oracle’s website

Covered in chapter/
section

1

Java Class Design

Chapter 1

1.1

Use access modifiers: private, protected, and public

Section 1.1

1.2

Override methods

Section 1.3

1.3

Overload constructors and methods

Section 1.2

Figure 1 The Introduction to this book provides a list of all exam objectives and the corresponding
chapter and section numbers where they are covered.

xxv

Licensed to Mark Watson 

ABOUT THIS BOOK

xxvi

The map in the Introduction shows the complete exam objective list mapped to the
relevant chapter and section numbers. You can jump to the relevant section number
to work on a particular exam topic.

Chapter-based objectives
Each chapter starts with a list of the exam objectives covered in that chapter, as shown
in figure 2. This list is followed by a quick comparison of the major concepts and topics covered in the chapter with real-world objects and scenarios.

Exam objectives covered in this chapter

What you need to know

[3.1] Write code that declares, implements, and/or extends interfaces

The need for interfaces. How to declare, implement,
and extend interfaces. Implications of implicit modifiers
that are added to an interface and its members.

[3.2] Choose between interface inheritance and class inheritance

The differences and similarities between implementing
inheritance by using interfaces and by using abstract or
concrete classes. Factors that favor using interface
inheritance over class inheritance, and vice versa.

Figure 2 An example of the list of exam objectives and brief explanations at the beginning of each
chapter

Section-based objectives
Each main section in a chapter starts by identifying the exam objective(s) that it covers. Each listed exam topic starts with the exam objective and its subobjective number.
In figure 3, the number 4.2 refers to section 4.2 in chapter 4 (the complete list of
chapters and sections can be found in the contents). The 4.1 preceding the exam
objective refers to the objective’s numbering in the list of exam objectives on Oracle’s website (the complete numbered list of exam objectives is given in table 2 in
the Introduction).

4.2

Creating generic entities
[4.1] Create a generic class
On the exam, you’ll be tested on how to create generic classes, interfaces, and methods—within generic and nongeneric classes or interfaces.

Figure 3 An example of the beginning of a section, identifying the exam objective that it covers

Licensed to Mark Watson 

ABOUT THIS BOOK

xxvii

Exam tips
Each chapter provides multiple exam tips to re-emphasize the points that are the most
confusing, overlooked, or frequently answered incorrectly by candidates and that
therefore require special attention for the exam. Figure 4 shows an example.

EXAM TIP A type argument must be passed to the type parameter of a base
class. You can do so while extending the base class or while instantiating
the derived class.

Figure 4 Example of an exam tip; they occur multiple times in a chapter

Notes
All chapters also include multiple notes, which draw your attention to points that
should be noted while you’re preparing for the exam. Figure 5 shows an example.

NOTE Though the exam might not include explicit questions on the
contents of a class file after type erasure, it will help you to understand
generics better and answer all questions on generics.

Figure 5 Example note

Sidebars
Sidebars contain information that may not be directly relevant to the exam but that is
related to it. Figure 6 shows an example.

Using instanceof versus getClass in method equals()
Using instanceof versus getClass is a common subject of debate about proper use
and object orientation in general (including performance aspects, design patterns,
and so on). Though important, this discussion is beyond the scope of this book. If
you’re interested in further details, refer to Josh Bloch’s book Effective Java.

Figure 6 Example sidebar

Licensed to Mark Watson 

ABOUT THIS BOOK

xxviii

Images
I’ve used a lot of images in the chapters for an immersive learning experience. I
believe that a simple image can help you understand a concept quickly, and a little
humor can help you to retain information longer.
Simple images are used to draw your attention to a particular line of code (as
shown in figure 7).
Calling
method 1

Calling
method 2
public static Singleton getInstance() {
if (anInstance == null)
anInstance = new Singleton() ;
return anInstance;
}
Create new
Singleton object

Figure 7 An example image that draws your attention to a particular line of code

As shown in figure 8, I’ve used pictorial representation to aid better understanding of
how Java concepts work.

class Outer {
class Inner{}

Code added
before byte code
generation

}
1

private final Outer this$0;
Inner (Outer outer){
this$0 = outer;
}

In
2

Out

Java
compiler

}
3

4

class Outer {
class Inner{

In

Out

Outer.class
Outer$Inner.class
Figure 8 An example of pictorial representation of how the compiler handles data in an array

Licensed to Mark Watson 

ABOUT THIS BOOK

xxix

To reinforce important points and help you retain them longer, a little humor has
been added using comic strips (as in figure 9).

If we are in
love… we must
must reside
at the same address.

if x.equals(y)==true
x.hashCode()==
y.hashCode()
must be true

Before
marriage

y

x

If we are not
not
in love… we might
might
or might not reside
at the same address.

if x.equals(y)==false
x.hashCode() and
y.hashCode() can be
same or different

Post
breakup....

y

x

Figure 9 An example of a little humor to help you remember that the finally block always executes

I’ve also used images to group and represent information for quick reference. Figure 10 shows an example of a rather raw
form of the UML diagram that you may
draw on an erasable board while taking
your exam to represent an IS-A relationship between classes and interfaces. I
strongly recommend that you try to create
a few of your own figures like these.
An image can also add more meaning
to a sequence of steps explained in the
text. For example, figure 11 seems to bring
the process of adding and removing items
to an ArrayList to life by showing placement of the existing items at each step.
Again, try a few of your own. It’ll be fun!

Movable
Animal
Cow

Hunter

Herbivore

Goat
Carnivore
Lion

Tiger

Figure 10 An example of grouping and
representing information for quick reference

Licensed to Mark
Watson 
www.allitebooks.com

ABOUT THIS BOOK

xxx

Figure 11 An example image showing how existing elements are placed when items are added to or
removed from an ArrayList

The exam requires that you know multiple methods from collection classes, File I/O,
NIO.2, concurrency, and others. The number of these methods can be overwhelming,
but grouping these methods according to their functionality can make this task a lot
more manageable. Figure 12 shows an example of an image that groups methods of
the Queue class used to work with Deque as FIFO.
0

1

2

3

4

5

6

7

head

tail

Remove elements
remove
Query elements

Add elements
add(E)
offer(E)

element
peer
Query and remove
poll

Figure 12 An example
image showing Queue
methods used to work
with Deque as a FIFO
data structure

Licensed to Mark Watson 

ABOUT THIS BOOK

xxxi

String processing expressions can be hard to comprehend. Figure 13 is an example of
an image that can help you understand the strings that match a regular expression.
Target string
t h e

l e a t h e r

i n

t h e i r

c o a t

m a d e

h e r

s e e t h e

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

Regex
\Bthe

Match a
nonword boundary
followed by “the”

Matching result
t h e

l e a t h e r

i n

t h e i r

c o a t

m a d e

h e r

s e e t h e

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

Figure 13 Example of image showing the strings that match a regex pattern

In multithreading, the same code can be executed by multiple threads. Such code can
be difficult to comprehend. Figure 14 is an example of an image that clearly shows
how the variable values of book:Book might be modified by multiple threads.

book:Book
task1

title = "Java"
copiesSold = 0

book:Book
copiesSold = 0+1

title = "Java"
copiesSold = 1

book:Book
task2

title = "Java"
copiesSold = 0

book:Book
copiesSold = 0+1

title = "Java"
copiesSold = 1

book:Book
task3

title = "Java"
copiesSold = 0

book:Book
copiesSold = 0–1

Time

Figure 14 An example of how interleaving threads can lead to incorrect results

Licensed to Mark Watson 

title = "Java"
copiesSold = –1

ABOUT THIS BOOK

xxxii

Twist in the Tale exercises
Each chapter includes a few Twist in the Tale exercises. For these exercises, I’ve tried
to use modified code from the examples already covered in a chapter, and the “Twist
in the Tale” title refers to modified or tweaked code. These exercises highlight how
even small code modifications can change the behavior of your code. They should
encourage you to carefully examine all of the code on the exam.
My main reason for including these exercises is that on the real exam you may be
asked to answer more than one question that seems exactly the same as another. But
upon closer inspection, you’ll realize that these questions differ slightly, and that these
differences change the behavior of the code and the correct answer option.
The answers to all of the Twist in the Tale exercises are given in the appendix.

Review notes
When you’re ready to take your exam, don’t forget to reread the review notes a day
before or on the morning of the exam. These notes contain important points from
each chapter as a quick refresher.

Exam questions
Each chapter concludes with a set of sample exam questions. These follow the same
pattern as the real exam questions. Attempt these exam questions after completing
a chapter.

Answers to exam questions
The answers to all exam questions provide detailed explanations, including why
options are correct or incorrect. Mark your incorrect answers and identify the sections
that you need to reread. If possible, draw a few diagrams—you’ll be amazed at how
much they can help you retain the concepts. Give it a try—it’ll be fun!

This book online
More information and a bonus chapter consisting of a mock exam can be found online
at www.manning.com/gupta2.

Author Online
The purchase of this book includes free access to a private web forum run by Manning
Publications, where you can make comments, ask technical questions, and receive
help from the author and from other users. To access the forum and subscribe to it,
point your web browser to www.manning.com/gupta2. This page provides information on how to get on the forum once you are registered, what kind of help is available, and the rules of conduct on the forum.

Licensed to Mark Watson 

ABOUT THIS BOOK

xxxiii

Manning’s commitment to our readers is to provide a venue where a meaningful
dialogue between individual readers and between readers and the author can take
place. It is not a commitment to any specific amount of participation on the part of
the author, whose contribution to the forum remains voluntary (and unpaid). We suggest you try asking the author some challenging questions lest her interest stray!
The Author Online forum and the archives of previous discussions will be accessible from the publisher’s website as long as the book is in print.

About the author
Mala Gupta is passionate about making people employable by bridging the gap
between their existing and required skills. In her quest to fulfill this mission, she is
authoring books to help IT professionals and students on industry-recognized Oracle
Java certifications.
Mala has a master’s degree in computer applications along with multiple other certifications from Oracle. With over a decade and a half of experience working in IT as a
developer, architect, trainer, and mentor, she has worked with international training
and software services organizations on various Java projects. She is experienced in
mentoring teams on technical and process skills.
She is the founder and lead mentor of a portal (www.ejavaguru.com) that has
offered Java courses for Oracle certification since 2006.
Mala is a firm believer in creativity as an essential life skill. To popularize the
importance of creativity, innovation, and design in life, she started “KaagZevar”
(www.facebook.com/KaagZevar)—a platform to nurture design and creativity in life.

About the cover illustration
The figure on the cover this book is captioned “The habit of a French merchant in
1700.” The illustration is taken from Thomas Jefferys’ A Collection of the Dresses of Different Nations, Ancient and Modern (four volumes), London, published between 1757 and
1772. The title page states that these are hand-colored copperplate engravings,
heightened with gum arabic. Thomas Jefferys (1719–1771) was called “geographer to
King George III.” An English cartographer who was the leading map supplier of his
day, Jefferys engraved and printed maps for government and other official bodies and
produced a wide range of commercial maps and atlases, especially of North America.
His work as a mapmaker sparked an interest in local dress customs of the lands he surveyed and mapped, which are brilliantly displayed in this four-volume collection.
Fascination with faraway lands and travel for pleasure were relatively new phenomena in the late eighteenth century, and collections such as this one were popular,
introducing both the tourist as well as the armchair traveler to the inhabitants of
other countries. The diversity of the drawings in Jefferys’ volumes speaks vividly of the
uniqueness and individuality of the world’s nations some 200 years ago. Dress codes
have changed since then, and the diversity by region and country, so rich at the time,
has faded away. It is now hard to tell the inhabitants of one continent apart from

Licensed to Mark Watson 

xxxiv

ABOUT THIS BOOK

another. Perhaps, trying to view it optimistically, we have traded cultural and visual
diversity for a more varied personal life. Or a more varied and interesting intellectual
and technical life.
At a time when it is hard to tell one computer book from another, Manning celebrates the inventiveness and initiative of the computer business with book covers
based on the rich diversity of regional life of two centuries ago, brought back to life by
Jefferys’ pictures.

Licensed to Mark Watson 

Introduction

This introduction covers
■

Introduction to the Oracle Certified Professional
(OCP) Java SE 7 Programmer II certification
(exam number 1Z0-804)

■

Importance of OCP Java SE 7 Programmer II
certification

■

Detailed exam objectives, mapped to book
chapters

■

FAQ on exam preparation and on taking
the exam

■

Introduction to the testing engine used for
the exam

This book is intended specifically for individuals who wish to earn the Oracle Certified Professional (OCP) Java SE 7 Programmer II certification (exam number
1Z0-804). It assumes that you have practical experience of working with Java. If you
are completely new to Java or to object-oriented languages, I suggest that you start
your journey with an entry-level book and then come back to this one.

1

Licensed to Mark Watson 

2

Introduction

Disclaimer
The information in this chapter is sourced from Oracle.com, public websites, and user
forums. Input has been taken from real people who have earned Java certification,
including the author. All efforts have been made to maintain the accuracy of the content, but the details of the exam—including its objectives, pricing, pass score, total
number of questions, and maximum duration—are subject to change per Oracle’s
policies. The author and publisher of the book shall not be held responsible for any
loss or damage accrued due to any information contained in this book or due to any
direct or indirect use of this information.

Introduction to OCP Java SE 7 Programmer II
certification (1Z0-804)
The Oracle Certified Professional Java SE 7 Programmer II certification exam (1Z0804) covers intermediate and advanced concepts of Java programming, such as the
importance of threads, concurrency, localization, JDBC, String processing, and design
patterns.
This exam is the second of the two steps in earning the title of OCP Java SE 7 Programmer. The first step is to earn the OCA Java SE 7 Programmer I certification (1Z0-803).
Though you can write the exams 1Z0-803 and 1Z0-804 in any order
to earn the title of OCP Java SE 7 Programmer, it is highly recommended
that you write exam 1Z0-803 before exam 1Z0-804. Exam 1Z0-803 covers
basics of Java and exam 1Z0-804 covers advanced Java topics.

NOTE

This exam certifies that an individual possesses strong practical skills in intermediate and advanced Java programming language concepts. Table 1 lists the details of
this exam.
Table 1 Details for OCP Java SE 7 Programmer II exam (1Z0-804)
Exam number

1Z0-804

Java version

Based on Java version 7

Number of questions

90

Passing score

65%

Time duration

150 minutes

Pricing

US $245

Type of questions

Multiple-choice

The importance of the OCP Java SE 7 Programmer II certification
Real, on-the-job projects need you to understand and work with multiple basic and
advanced concepts. Apart from covering the finer details of basic Java-like class design, it
covers advanced Java topics like threading, concurrency, localization, File I/O, string
processing, exception handling, assertions, collections API, and design patterns. This

Licensed to Mark Watson 

3

Introduction to OCP Java SE 7 Programmer II certification (1Z0-804)

certification establishes your expertise with these topics, increasing your prospects for
better projects, jobs, remuneration, responsibilities, and designations.
The OCP Java SE 7 Programmer II exam (1Z0-804) is an entry-level exam in your
Java certification roadmap, as shown in figure 1. This exam is a prerequisite for most
of the other Professional and Expert Oracle certifications in Java. The dashed lines
and arrows in the figure depict the prerequisites for certifications.
Associate

Professional

Java SE 7

Java SE 7

Expert

Master

Java SE 6
Developer
Java SE 5/6

Java SE 6/5

Exam
covered by
book

Java EE 5 Web
Component
Developer

Java EE 6 Web
Component
Developer

Java EE 5
Business
Component
Developer

Java EE 6
Enterprise
JavaBeans
Developer

Java SE

Java EE 5
Enterprise
Architect
Java EE 6
Enterprise
Architect

Java EE
Java EE 5 Web
Services
Developer

Java EE 6 Web
Services
Developer

Java EE 6 Web
JavaServer
Faces Developer

Java EE 6 Java
Persistence API
Developer

Java ME Mobile
Application
Developer

Java ME

Increasing difficulty level

Figure 1 The OCP Java SE 7 Programmer II certification (1Z0-804) is an entry-level certification in the
Java certification roadmap. It’s a prerequisite for writing most of the other Professional and Expert
certifications in Java.

Licensed to Mark Watson 

4

Introduction

As shown in figure 1, the Java certification tracks are offered under the categories
Associate, Professional, Expert, and Master.

Comparing the OCA Java SE 7 Programmer I (1Z0-803) and OCP
Java SE 7 Programmer II (1Z0-804) exams
The confusion about these two exams is due to the similarity in their names, but these
are separate exams. Starting with Java 7, Oracle has raised the bar to earn the title of
Oracle Certified Professional Java SE 7 Programmer, which now requires successfully
completing the following two exams:
■

OCA Java SE 7 Programmer I (exam number: 1Z0-803)

■

OCP Java SE 7 Programmer II (exam number: 1Z0-804)

The OCA Java SE 7 Programmer certification is designed for individuals who possess
basic skills in the Java programming language. Exam 1Z0-803 covers comparatively
basic Java language features, such as data types, operators, decision constructs, arrays,
methods, inheritance, and exception handling.

Complete exam objectives, mapped to book chapters, and
readiness checklist
Table 2 shows the complete list of exam objectives for the OCP Java SE 7 Programmer II
exam, which was taken from Oracle’s website. All the objectives are mapped to the
book’s chapters and the section numbers that cover them.
Table 2 Exam objectives and subobjectives mapped to chapter and section numbers
Exam objective as per Oracle’s website

Covered in chapter/
section

11

Java class design

Chapter 1

11.1

Use access modifiers: private, protected, and public

Section 1.1

11.2

Override methods

Section 1.3

11.3

Overload constructors and methods

Section 1.2

11.4

Use the instanceof operator and casting

Section 1.5

11.5

Use virtual method invocation

Section 1.3

11.6

Override the hashCode, equals, and toString methods from
the Object class to improve the functionality of your class

Section 1.4

11.7

Use package and import statements

Section 1.6

12

Advanced class design

Chapter 2

12.1

Identify when and how to apply abstract classes

Section 2.1

12.2

Construct abstract Java classes and subclasses

Section 2.1

12.3

Use the static and final keywords

Section 2.2

Licensed to Mark Watson 

Introduction to OCP Java SE 7 Programmer II certification (1Z0-804)
Table 2 Exam objectives and subobjectives mapped to chapter and section numbers (continued)
Exam objective as per Oracle’s website

Covered in chapter/
section

12.4

Create top-level and nested classes

Section 2.4

12.5

Use enumerated types

Section 2.3

13

Object-oriented design principles

Chapter 3

13.1

Write code that declares, implements, and/or extends interfaces

Section 3.1

13.2

Choose between interface inheritance and class inheritance

Section 3.2

13.3

Apply cohesion, low-coupling, IS-A, and HAS-A principles

Sections 3.3, 3.4

13.4

Apply object composition principles (including HAS-A relationships)

Section 3.5

13.5

Design a class using the Singleton design pattern

Section 3.7

13.6

Write code to implement the Data Access Object (DAO) pattern

Section 3.9

13.7

Design and create objects using a Factory pattern

Section 3.8

14

Generics and collections

Chapter 4

14.1

Create a generic class

Section 4.2

14.2

Use the diamond for type inference

Section 4.3

14.3

Analyze the interoperability of collections that use raw types and
generic types

Section 4.4

14.4

Use wrapper classes, autoboxing, and unboxing

Sections 4.11, 4.12

14.5

Create and use List, Set, and Deque implementations

Section 4.7

14.6

Create and use Map implementations

Section 4.8

14.7

Use java.util.Comparator and java.lang.Comparable

Section 4.9

14.8

Sort and search arrays and lists

Section 4.10

15

String processing

Chapter 5

15.1

Search, parse, and build strings (including Scanner, StringTokenizer, StringBuilder, String, and Formatter)

Section 5.1

15.2

Search, parse, and replace strings by using regular expressions,
using expression patterns for matching limited to: . (dot), * (star),
+ (plus), ?, \d, \D, \s, \S, \w, \W, \b, \B, [], ()

Sections 5.1, 5.2

15.3

Format strings using the formatting parameters: %b, %c, %d, %f,
and %s in format strings

Section 5.3

16

Exceptions and assertions

Chapter 6

16.1

Use throw and throws statements

Section 6.1

16.2

Use the try statement with multi-catch and finally clauses

Section 6.4

Licensed to Mark
Watson 
www.allitebooks.com

5

6

Introduction
Table 2 Exam objectives and subobjectives mapped to chapter and section numbers (continued)
Exam objective as per Oracle’s website

Covered in chapter/
section

16.3

Develop code that uses try-with-resources statements

Section 6.5

16.4

Create custom exceptions

Section 6.2

16.5

Test invariants by using assertions

Section 6.6

17

Java I/O fundamentals

Chapter 7

17.1

Read and write data from the console

Section 7.5

17.2

Use streams to read from and write to files by using classes in
the java.io package including BufferedReader,
BufferedWriter, File, FileReader, FileWriter,
DataInputStream, DataOutputStream, ObjectOutputStream, ObjectInputStream, and PrintWriter

Sections 7.2, 7.3, 7.4

18

Java file I/O (NIO.2)

Chapter 8

18.1

Operate on file and directory paths with the Path class

Section 8.1

18.2

Check, delete, copy, or move a file or directory with the Files class

Section 8.2

Read and change file and directory attributes, focusing on the

Section 8.3

18.3

BasicFileAttributes, DosFileAttributes, and
PosixFileAttributes interfaces
18.4

Recursively access a directory tree using the DirectoryStream
and FileVisitor interfaces

Section 8.4

18.5

Find a file with the PathMatcher interface

Section 8.5

18.6

Watch a directory for changes with the WatchService interface

Section 8.6

19

Building database applications with JDBC

Chapter 9

19.1

Describe the interfaces that make up the core of the JDBC
API (including Driver, Connection, Statement, and
ResultSet) and their relationships to provider implementations

Section 9.2

19.2

Identify the components required to connect to a database using
the DriverManager class (including the JDBC URL)

Section 9.3

19.3

Submit queries and read results from the database (including
creating statements, returning result sets, iterating through
the results, and properly closing result sets, statements,
and connections)

Section 9.4

19.4

Use JDBC transactions (including disabling auto-commit mode,
committing and rolling back transactions, and setting and rolling
back to savepoints)

Section 9.5

19.5

Construct and use RowSet objects using the RowSetProvider
class and the RowSetFactory interface

Section 9.6

Licensed to Mark Watson 

7

FAQ
Table 2

Exam objectives and subobjectives mapped to chapter and section numbers (continued)
Exam objective as per Oracle’s website

Covered in chapter/
section

19.6

Create and use PreparedStatement and
CallableStatement objects

Section 9.7

10

Threads

Chapter 10

10.1

Create and use the Thread class and the Runnable interface

Section 10.1

10.2

Manage and control thread lifecycle

Section 10.2

10.3

Synchronize thread access to shared data

Section 10.3

10.4

Identify code that may not execute correctly in a multi-threaded
environment

Section 10.4

11

Concurrency

Chapter 11

11.1

Use collections from the java.util.concurrent package
with a focus on the advantages over and differences from the traditional java.util collections

Section 11.1

11.2

Use Lock, ReadWriteLock, and ReentrantLock classes in
the java.util.concurrent.locks package to support lockfree, thread-safe programming on single variables

Section 11.2

11.3

Use Executor, ExecutorService, Executors,
Callable, and Future to execute tasks using thread pools

Section 11.3

11.4

Use the parallel fork/join framework

Section 11.4

12

Localization

Chapter 12

12.1

Read and set the locale by using the Locale object

Section 12.2

12.2

Build a resource bundle for each locale

Section 12.2

12.3

Call a resource bundle from an application

Section 12.2

12.4

Format dates, numbers, and currency values for localization with
the NumberFormat and DateFormat classes (including number format patterns)

Section 12.3

12.5

Describe the advantages of localizing an application

Section 12.1

12.6

Define a locale using language and country codes

Section 12.1

FAQ
You might be anxious when you start your exam preparation or even think about getting certified. This section can help calm your nerves by answering frequently asked
questions on exam preparation and on writing the exam.

Licensed to Mark Watson 

8

Introduction

FAQ on exam preparation
This sections answers frequently asked questions on how to prepare for the exam,
including the best approach, study material, preparation duration, and how to test
self-readiness.
WILL THE EXAM DETAILS EVER CHANGE FOR THE OCP JAVA SE 7 PROGRAMMER II EXAM?

Oracle can change the exam details for a certification even after the certification is
made live. The changes can be made to any of its details, like exam objectives, pricing,
exam duration, exam questions, and others. In the past, Oracle has made similar
changes to certification exams. Such changes may not be major, but it is always advisable to check Oracle’s website for the latest exam information when you start your
exam preparation.
WHAT IS THE BEST WAY TO PREPARE FOR THIS EXAM?

Generally, candidates use a combination of resources, such as books, online study
materials, articles on the exam, free and paid mock exams, and training to prepare for
the exam. Different combinations work best for different people, and there is no one
perfect formula for preparation. Select the method—training or self-study—that
works best for you. Combine it with a lot of code practice and mock exams.
HOW DO I KNOW WHEN I AM READY FOR THE EXAM?

You can be sure about your exam readiness by consistently getting a good score on the
mock exams. Generally, a score of 80% and above on approximately 7 mock exams
(the more the better) attempted consecutively will assure you of a similar score on the
real exam.
HOW MANY MOCK TESTS SHOULD I ATTEMPT BEFORE THE REAL EXAM?

Ideally, you should attempt at least five complete mock exams before you attempt the
real exam. The more the better!
I HAVE TWO–FOURS YEARS’ EXPERIENCE WORKING WITH JAVA. DO I STILL NEED TO PREPARE FOR
THIS CERTIFICATION?

There is a difference between the practical knowledge of having worked with Java and
the knowledge required to pass this certification exam. The authors of the Java certification exams employ multiple tricks to test your knowledge. Hence, you need a structured preparation and approach to succeed on the certification exam.
WHAT IS THE IDEAL TIME REQUIRED TO PREPARE FOR THE EXAM?

The preparation time frame mainly depends on your experience with Java and the
amount of time that you can spend to prepare yourself. On average, you will require
approximately 200 hours of study over two or three months to prepare for this exam.
Again, the number of study hours required depends on individual learning curves
and backgrounds.
It’s important to be consistent with your exam preparation. You cannot study for a
month and then restart after, say, a gap of a month or more.

Licensed to Mark Watson 

FAQ

9

DO I NEED TO COMPLETE ANY TRAINING FROM ORACLE?

Though Oracle requires candidates to complete specific Oracle training programs for
a few of its certification courses, it isn’t mandatory to complete any training from Oracle for this certification.
DOES THIS EXAM INCLUDE ANY UNSCORED QUESTIONS?

A few of the questions that you write on any Oracle exam may be marked unscored.
Oracle’s policy states that while writing an exam, you won’t be informed whether a
question will be scored. You may be surprised to learn that as many as 10 out of the 90
questions on the OCP Java SE 7 Programmer II exam may be unscored. Even if you
answer a few questions incorrectly, you stand a chance of scoring 100%.
Oracle regularly updates its question bank for all its certification exams. These
unscored questions may be used for research and to evaluate new questions that can
be added to an exam.
CAN I START MY EXAM PREPARATION WITH THE MOCK EXAMS?

If you are quite comfortable with the advanced Java language features, then yes, you
can start your exam preparation with the mock exams. This will also help you to
understand the types of questions to expect on the real certification exam. But if you
have little or no experience working with advanced Java concepts, I don’t advise you
to start with the mock exams. The exam authors often use a lot of tricks to evaluate a
candidate on the real certification exam. Starting your exam preparation with mock
exams will only leave you confused about the Java concepts.
SHOULD I REALLY BOTHER GETTING CERTIFIED?

Yes, you should, for the simple reason that employers bother about the certification of
employees. Organizations prefer a certified Java developer over a noncertified Java
developer with similar IT skills and experience. The certification can also get you a
higher paycheck than uncertified peers with comparable skills.

FAQ on taking the exam
This section contains a list of frequently asked questions related to exam registration,
the exam coupon, do’s and don’t’s while taking the exam, and exam retakes.
WHERE AND HOW DO I WRITE THIS EXAM?

You can write this exam at an Oracle Testing Center or Pearson VUE Authorized Testing Center. To sit for the exam, you must register and purchase an exam voucher. The
following options are available:
■
■

■

Register for the exam and pay Pearson VUE directly.
Purchase an exam voucher from Oracle and register at Pearson VUE to take
the exam.
Register at an Oracle Testing Center.

Look for the nearest testing centers in your area, register yourself, and schedule an
exam date and time. Most of the popular computer training institutes also have a

Licensed to Mark Watson 

10

Introduction

testing center on their premises. You can locate a Pearson VUE testing site at
www.pearsonvue.com/oracle/, which contains detailed information on locating testing centers and scheduling or rescheduling an exam. At the time of registration,
you’ll need to provide the following details along with your name, address, and contact numbers:
■
■
■

■

Exam title and number (OCP Java SE 7 Programmer II, 1Z0-804)
Any discount code that should be applied during registration
Oracle Testing ID/Candidate ID, if you have written any other Oracle/Sun certification exam(s)
Your OPN Company ID (If your employer is in the Oracle Partner Network, you
can find out the company ID and use any available discounts on the exam fee.)

HOW LONG IS THE EXAM COUPON VALID FOR?

Each exam coupon is printed with an expiration date. Beware of any discounted coupons that come with an assurance that they can be used past the expiration date.
CAN I REFER TO NOTES OR BOOKS WHILE WRITING THIS EXAM?

You can’t refer to any books or notes while writing this exam. You are not allowed
to carry any blank paper for rough work or even your mobile phone inside the testing cubicle.
WHAT IS THE PURPOSE OF MARKING A QUESTION WHILE WRITING THE EXAM?

By marking a question, you can manage your time efficiently. Don’t spend a lot of
time on a single question. You can mark a difficult question to defer answering it while
writing your exam. You have an option to review answers to the marked questions at
the end of the exam. Also, navigating from one question to another using Back and
Next buttons is usually time-consuming. If you are unsure of an answer, mark it and
review it at the end.
CAN I WRITE DOWN THE EXAM QUESTIONS AND BRING THEM BACK WITH ME?

No. The exam centers no longer provide sheets of paper for the rough work that you
may need to do while taking the exam. The testing center will provide you with either
erasable or nonerasable boards. If you’re provided with a nonerasable board, you may
request another one if you need it.
Oracle is quite particular about certification candidates distributing or circulating
the memorized questions in any form. If Oracle finds out that this is happening, it
may cancel a candidate’s certificate, bar that candidate forever from writing any Oracle certification, inform the employer, or take legal action.
WHAT HAPPENS IF I COMPLETE THE EXAM BEFORE OR AFTER THE TOTAL TIME?

If you complete the exam before the total exam time has elapsed, review your answers
and click the Submit or finish button.
If you have not clicked the Submit button and you use up all the exam time, the
exam engine will no longer allow you to modify any of the exam answers and will present the screen with the Submit button.

Licensed to Mark Watson 

The testing engine used in the exam

11

WILL I RECEIVE MY SCORE IMMEDIATELY AFTER THE EXAM?

No, you won’t. When you click the Submit exam button, the screen will inform you that
your exam results will be available in an hour. Usually Oracle sends you an email when
the results can be accessed online. Even if you don’t receive an email from Oracle, you
could log in and check your result. The result includes your score on each exam
objective. The certificate itself will arrive via mail within six to eight weeks.
WHAT HAPPENS IF I FAIL? CAN I RETAKE THE EXAM?

It’s not the end of the world. Don’t worry if you fail. You can retake the exam after 14
days (and the world will not know it’s a retake).
However, you cannot retake a passed exam to improve your score. Also, you cannot
retake a beta exam.

The testing engine used in the exam
The UI of the testing engine used for the certification exam is quite simple. (You could
even call it primitive, compared to today’s web, desktop, and smartphone applications.)
Before you can start the exam, you will be required to accept the terms and conditions of the Oracle Certification Candidate Agreement. Your computer screen will display all these conditions and give you an option to accept the conditions. You can
proceed with writing the exam only if you accept these conditions.
Here are the features of the testing engine used by Oracle:
■

■

■

■

■

The engine UI is divided into three sections. The UI of the testing engine is divided
into the following three segments:
– Static upper section—Displays question number, time remaining, and a checkbox to mark a question for review.
– Scrollable middle section—Displays the question text and the answer options.
– Static bottom section—Displays buttons to display the previous question, display
the next question, end the exam, and review marked questions.
Each question is displayed on a separate screen. The exam engine displays one question on the screen at a time. It does not display multiple questions on a single
screen, like a scrollable web page. All effort is made to display the complete
question and answer options without scrolling, or with little scrolling.
Code exhibit button. Many questions include code. Such questions, together with
their answers, may require significant scrolling to be viewed. As this can be
quite inconvenient, such questions include a Code Exhibit button that displays
the code in a separate window.
Mark questions to be reviewed. The question screen displays a check box with the
text “Mark for review” at the top-left corner. A question can be marked using this
option. The marked questions can be reviewed at the end of the exam.
Buttons to display previous and next questions. The test includes buttons to display
previous and next questions within the bottom section of the testing engine.

Licensed to Mark Watson 

12

Introduction
■

■

■
■

Buttons to end the exam and review marked questions. The engine displays buttons to
end the exam and to review the marked questions in the bottom section of the
testing engine.
Remaining time. The engine displays the time remaining for the exam at the top
right of the screen.
Question number. Each question displays its serial number.
Correct number of answer options. Each question displays the correct number of
options that should be selected from multiple options.

On behalf of all at Manning Publications, I wish you good luck and hope that you
score very well on your exam.

Licensed to Mark Watson 

Java class design

Exam objectives covered in this chapter

What you need to know

[1.1] Use access modifiers: private, protected, and public

How to use appropriate access modifiers to design
classes
How to limit accessibility of classes, interfaces,
enums, methods, and variables by using the appropriate access modifiers
The correct combination of access modifiers and the
entities (classes, interfaces, enums, methods, and
variables) to which they can be applied
The implications of modifying the access modifier of a
Java entity

[1.2] Override methods

The conditions and requirements that make a subclass override a base class method
How to differentiate among overloaded, overridden,
and hidden methods

[1.3] Overload constructors and methods

The need and right rules to overload constructors and
methods

[1.4] Use the instanceof operator
and casting

Understand the right use of the instanceof
operator, and implicit and explicit object casting
and their implications
Compilation errors and runtime exceptions
associated with the use of the instanceof
operator and casting

[1.5] Use virtual method invocation

The methods that can and can’t be invoked virtually

13

Licensed to Mark Watson 

14

CHAPTER 1 Java class design

Exam objectives covered in this chapter

What you need to know

[1.6] Override methods from the Object
class to improve the functionality of
your class

The need to override methods from class Object—differentiate correct, incorrect, appropriate, and inappropriate overriding

[1.7] Use package and import statements

How to package classes and use package, import,
and static import statements

Classes and interfaces are building blocks of an application. Efficient and effective
class design makes a significant impact on the overall application design. Imagine
if, while designing your classes, you didn’t consider effective packaging, correct overloaded or overridden methods, or access protection—you might lose on extensibility, flexibility, and usability of your classes. For example, if you didn’t override methods
hashCode() and equals() correctly in your classes, your seemingly “equal” objects
might not be considered equal by collection classes like HashSet or HashMap. Or,
say, imagine if you didn’t use the right access modifiers to protect your classes and
their members, they could be subject to unwanted manipulation by other classes
from the same or different packages. The creation of overloaded methods is another
domain, which is an important class design decision. It eases instance creation and
use of methods.
Class design decisions require an insight into understanding correct and appropriate implementation practices. When armed with adequate information you’ll be able
to select the best practices and approach to designing your classes. The topics covered
in this chapter will help you design better classes by taking you through multiple
examples. This chapter covers
■
■
■
■
■
■
■

Access modifiers
Method overloading
Method overriding
Virtual method invocation
Use of the instanceof operator and casting
Override methods from class Object to improve the functionality of your class
How to create packages and use classes from other packages

Let’s get started with how to control access to your classes and their members, using
access modifiers.

Licensed to Mark Watson 

15

Java access modifiers

1.1

Java access modifiers
[1.1] Use access modifiers: private, protected, and public
When you design applications and create classes, you need to answer multiple questions:
■
■

How do I restrict other classes from accessing certain members of a class?
How do I prevent classes from modifying the state of objects of a class, both
within the same and separate packages?

Java access modifiers answer all these questions. Access modifiers control the accessibility of a class or an interface, including its members (methods and variables), by other
classes and interfaces within the same or separate packages. By using the appropriate
access modifiers, you can limit access to your class or interface, and its members.
Access modifiers can be applied to classes, interfaces, and their members (instance
and class variables and methods). Local variables and method parameters can’t be
defined using access modifiers. An attempt to do so will prevent the code from compiling.
In this section, we’ll cover all of the access modifiers—public, protected, and
private—as well as default access, which is the result when you don’t use an access
modifier. You’ll also discover the effects of changing the access levels of existing types
on other code.
Access modifiers are also covered in the OCA Java SE 7 Programmer I exam (1Z0-803). If you’ve written this exam recently, then perhaps
you might like to skip sections 1.1.1–1.1.4.

NOTE

To understand all of these access modifiers, we’ll use the same set of classes: Book,
CourseBook, Librarian, StoryBook, and House. Figure 1.1 depicts these classes using
UML notation.

Classes Book, CourseBook, and Librarian are defined in the package library.
Classes StoryBook and House are defined in the package building. Classes StoryBook
and CourseBook (defined in separate packages) extend class Book. Using these classes,

library

building
<>

Book

StoryBook
Librarian

House

<>
CourseBook

Figure 1.1 A set of classes and their relationships to help understand access modifiers

Licensed to Mark
Watson 
www.allitebooks.com

16

CHAPTER 1 Java class design

library

building
<>

Book

StoryBook

+isbn:String
+printBook()

House
Librarian

<>
CourseBook

Figure 1.2 Understanding the public access modifier

you’ll see how the accessibility of a class and its members varies with different access
modifiers, from unrelated to derived classes, across packages.
As we cover each of the access modifiers, we’ll add a set of instance variables and a
method to class Book with the relevant access modifier.

1.1.1

Public access modifier
This is the least restrictive access modifier. Classes and interfaces defined using the
public access modifier are accessible across all packages, from derived to unrelated classes.
To understand the public access modifier, let’s define class Book as a public class
and add a public instance variable (isbn) and a public method (printBook()) to it.
Figure 1.2 shows the UML notation.
Examine the following definition of class Book:
package library;
public class Book {
public String isbn;
public void printBook() {}
}

public
class Book

public
variable
isbn

public method
printBook()

The public access modifier is said to be the least restrictive, so let’s try to access the
public class Book and its public members from class House. We’ll use class House
because House and Book are defined in separate packages and they’re unrelated. Class
House doesn’t enjoy any advantages of being defined in the same package or being a
derived class.
Here’s the code for class House:
package building;
import library.Book;
public class House {

Licensed to Mark Watson 

17

Java access modifiers
public House() {
Book book = new Book();
String value = book.isbn;
book.printBook();
}

Book is
accessible
to House
}

isbn is accessible
to House
printBook() is
accessible to House

In the preceding example, class Book and its public members—instance variable isbn
and method printBook()—are accessible to class House. They’re also accessible to the
other classes: StoryBook, Librarian, and CourseBook. Figure 1.3 shows the classes
that can access a public class and its members.

1.1.2

Protected access modifier
The members of a class defined using the protected access modifier are accessible to
■
■

Classes and interfaces defined in the same package
All derived classes, even if they’re defined in separate packages
EXAM TIP Members of an interface are implicitly public. If you define
interface members as protected, the interface won’t compile.

Let’s add a protected instance variable author and method modifyTemplate() to class
Book. Figure 1.4 shows the class representation.

Same package

Separate package

Derived classes
Unrelated classes

Figure 1.3 Classes that can access a public class and
its members

library

building
<>
Book

StoryBook

#author:String
#modifyTemplate()

House
Librarian

<>
CourseBook

Figure 1.4 Understanding the protected access modifier

Licensed to Mark Watson 

18

CHAPTER 1 Java class design

Here’s the code for class Book (I’ve deliberately left out its public members because they
aren’t required in this section):
package library;
public class Book {
protected String author;
protected void modifyTemplate(){}
}

protected
variable author
protected method
modifyTemplate()

Figure 1.5 illustrates how classes from the same and separate packages, derived classes,
and unrelated classes access class Book and its protected members.
building

library
package library;
public class Book {
protected string author;

Can access
<>

protected void modifyTemplate(){}
}

Can access
<>

package library;

package building;

public class CourseBook extends Book {

import library.Book;
public class StoryBook extends Book{

public CourseBook(){

modifyTemplate();
}
}

Cannot access

author="ABC";

public StoryBook(){
author="ABC";
modifyTemplate();
}
}

package library;

package building;

public class Librarian {

import library.Book;

public Librarian(){

public class House{

Book book = new Book();
book.author = "ABC";

public House(){
Can access

Book book=new Book();

book.modifyTemplate();

book.author="ABC";

}

book.modifyTemplate();

}

}
}

Figure 1.5 Access of protected members of class Book in unrelated and derived classes, from the
same and separate packages

Licensed to Mark Watson 

19

Java access modifiers

Class House fails compilation for trying to access method modifyTemplate()and variable author, as follows:
House.java:8: modifyTemplate()has protected access in library.Book
book.modifyTemplate();
^

A derived class inherits the protected members of its base class, irrespective of the
packages in which they are defined.
Notice that the derived classes CourseBook and StoryBook inherit class Book’s protected member variable author and method modifyTemplate(). If class StoryBook
tries to instantiate Book using a reference variable and then tries to access its protected variable author and method modifyTemplate(), it won’t compile:
package building;
import library.Book;
class StoryBook extends Book {
StoryBook () {
Book book = new Book();
String v = book.author;
book.modifyTemplate();
}
}

Book and StoryBook
defined in separate
packages
Protected members of Book aren’t
accessible in StoryBook if accessed
using an instance of Book.

A concise but not too simple way of stating the previous rule
is this: A derived class can inherit and access protected members of its
base class, regardless of the package in which it’s defined. A derived
class in a separate package can’t access protected members of its base
class using reference variables.

EXAM TIP

Figure 1.6 shows the classes that can access protected members of a class or an interface.

1.1.3

Default access (package access)
The members of a class defined without using any explicit access modifier are defined
with package accessibility (also called default accessibility). The members with package
access are only accessible to classes and interfaces defined in the same package. The
default access is also referred to as package-private. Think of a package as your home,
classes as rooms, and things in rooms as variables with default access. These things
Using
inheritance

Same package

Using
reference variable

Separate package

Derived classes
Unrelated classes

Figure 1.6 Classes that can
access protected members

Licensed to Mark Watson 

20

CHAPTER 1 Java class design

aren’t limited to one room—they can be accessed across all the rooms in your home.
But they’re still private to your home—you wouldn’t want them to be accessed outside
your home. Similarly, when you define a package, you might want to make accessible
members of classes to all the other classes across the same package.
While the package-private access is as valid as the other access levels, in real projects, it often appears as the result of inexperienced developers forgetting to specify the access modifier of Java components.

NOTE

Let’s define an instance variable issueCount and a method issueHistory()with default
access in class Book. Figure 1.7 shows the class representation with these new members.
Here’s the code for class Book (I’ve deliberately left out its public and protected
members because they aren’t required in this section):
package library;
public class Book {
int issueCount;
void issueHistory () {}
}

public class
Book

issueCount with
default access

issueHistory() with
default access

You can see how classes from the same package and separate packages, derived classes,
and unrelated classes access class Book and its members (instance variable issueCount
and method issueHistory()) in figure 1.8.
Because classes CourseBook and Librarian are defined in the same package as
class Book, they can access the members issueCount and issueHistory(). Because
classes House and StoryBook aren’t defined in the same package as class Book, they
can’t access the members issueCount and issueHistory(). Class StoryBook fails
compilation with the following error message:
StoryBook.java:6: issueHistory () is not public in library.Book; cannot be
accessed from outside package
book.issueHistory ();
^

library

building
<>
Book

StoryBook

~issueCount:int
~issueHistory()

House
Librarian

<>
CourseBook

Figure 1.7 Understanding class representations for the default access

Licensed to Mark Watson 

21

Java access modifiers

building

library
package library;
public class Book {
Can access

int issueCount;

Cannot access
<>

void issueHistory(){}
Can access
}
<>

package building;

package library;

import library.Book;

public class CourseBook extends Book {

public class StoryBook extends Book{

int c = issueCount;
issueHistory();
}

Cannot access

public CourseBook(){

public StoryBook(){
int c = issueCount;
issueHistory();
}

}

}

package library;

package building;

public class Librarian {

import library.Book;

public Librarian(){

public class House{

Book b = new Book();

public House(){

int c = b.issueCount;

Book b = new Book();

b.issueHistory();

int c = b.issueCount;

}

b.issueHistory();

}

}
}

Figure 1.8 Access of members with default access to class Book in unrelated and derived classes
from the same and separate packages

Class House is unaware of the existence of issueHistory()—it fails compilation with
the following error message:
House.java:9: cannot find symbol
symbol : method issueHistory ()
location: class building.House
issueHistory ();

DEFINING A CLASS BOOK WITH DEFAULT ACCESS

What happens if you define a class with default access? What will happen to the accessibility of its members if the class itself has default (package) accessibility?

Licensed to Mark Watson 

22

CHAPTER 1 Java class design

Can be accessed
only by inhabitants
of the island

Far-away island
inaccessble by
air and water

Figure 1.9 This Superfast Burgers cannot be accessed from outside the island because
the island is inaccessible by air and water.

Consider this situation: Assume that Superfast Burgers opens a new outlet on a beautiful island and offers free meals to people from all over the world, which obviously
includes inhabitants of the island. But the island is inaccessible by all means (air and
water). Would the existence of this particular Superfast Burger outlet make any sense
to people who don’t inhabit the island? An illustration of this example is shown in figure 1.9.
The island is like a package in Java, and the Superfast Burger outlet is like a class
defined with default access. In the same way that the Superfast Burger outlet can’t be
accessed from outside the island on which it exists, a class defined with default (package) access is visible and accessible only within the package in which it’s defined. It
can’t be accessed from outside its package.
Let’s redefine class Book with default (package) access as follows:
package library;
class Book {
//.. class members
}

Book now has
default access

The behavior of class Book remains the same for classes CourseBook and Librarian,
which are defined in the same package. But class Book can’t be accessed by classes
House and StoryBook, which reside in a separate package.
Let’s start with class House. Examine the following code:
package building;
import library.Book;
public class House {}

Book isn’t
accessible in House

Licensed to Mark Watson 

23

Java access modifiers
Same package

Separate package

Derived classes
Unrelated classes

Figure 1.10 Classes that can
access members with default
(package) access

Class House fails compilation with the following error message:
House.java:2: library.Book is not public in library; cannot be accessed from
outside package
import library.Book;

Here’s the code of class StoryBook:
package building;
import library.Book;
class StoryBook extends Book {}

Book isn’t accessible
in StoryBook
StoryBook cannot
extend Book

Figure 1.10 shows which classes can access members of a class or an interface with
default (package) access.
Because a lot of programmers are confused about which members are made
accessible by using the protected access modifier and no modifier (default), the
following exam tip offers a simple and interesting rule to help you remember their
differences.
Default access can be compared to package-private (accessible
only within a package) and protected access can be compared to package-private + kids (kids refers to derived classes). Kids can access protected members only by inheritance and not by reference (accessing
members by using the dot operator on an object).

EXAM TIP

1.1.4

The private access modifier
The private access modifier is the most restrictive access modifier. The members of a
class defined using the private access modifier are accessible only to them. For example, the internal organs of your body (heart, lungs, etc.) are private to your body. No
one else can access them. It doesn’t matter whether the class or interface in question
is from another package or has extended the class—private members aren’t accessible
outside the class in which they’re defined.
EXAM TIP Members of an interface are implicitly public. If you define
interface members as private, the interface won’t compile.

Licensed to Mark Watson 

24

CHAPTER 1 Java class design

library

building
<>
Book

StoryBook

-countPages()
#modifyTemplate()

House
Librarian

<>
CourseBook

Figure 1.11

Understanding the private access modifier

Let’s see the private members in action by adding a private method countPages() to
class Book. Figure 1.11 depicts the class representation using UML.
Examine the following definition of class Book:
package library;
class Book {
private void countPages () {}
protected void modifyTemplate(){
countPages ();
}
}

Private method
Only Book can access
its own private
method countPages()

None of the classes defined in any of the packages (whether derived or not) can
access the private method countPages(). But let’s try to access it from class CourseBook. I chose class CourseBook because both of these classes are defined in the same
package, and class CourseBook extends class Book. Here’s the code of CourseBook:
package library;
class CourseBook extends Book {
CourseBook () {
countPages ();
}
}

CourseBook
extends Book
CourseBook cannot
access private method
countPages()

Because class CourseBook tries to access private members of class Book, it will not compile. Similarly, if any of the other classes (StoryBook, Librarian, or House) try to
access private method countPages() of class Book, it will not compile. Figure 1.12
shows the classes that can access the private members of a class.
For your real projects, it is possible to access private members of a
class outside them, using Java Reflection. But Java Reflection isn’t on the
exam. So don’t consider it when answering questions on the accessibility
of private members.

NOTE

Licensed to Mark Watson 

25

Java access modifiers
Same package

Separate package

Derived classes

Figure 1.12 No classes can
access private members of
another class.

Unrelated classes

1.1.5

Access modifiers and Java entities
Can every access modifier be applied to all the Java entities? The simple answer is no.
Table 1.1 lists the Java entities and the access modifiers that can be used with them.
Table 1.1 Java entities and the access modifiers that can be applied to them
Entity name

public

protected

private

Top-level class, interface, enum

✓

✗

✗

Nested class, interface, enum

✓

✓

✓

Class variables and methods

✓

✓

✓

Instance variables and methods

✓

✓

✓

Method parameters and local variables

✗

✗

✗

What happens if you try to code the combinations for an X above? None of these combinations will compile. Here’s the code:
protected class MyTopLevelClass {}
private class MyTopLevelClass {}
protected interface TopLevelInterface {}
protected enum TopLevelEnum {}
void myMethod(private int param) {}
void myMethod(int param) {
public int localVariable = 10;
}

Won’t compile—top-level
class, interface, and enums
can’t be defined with protected
and private access.
Won’t compile—method parameters
and local variables can’t be defined
using any explicit access modifiers.

Watch out for these combinations on the exam. It’s simple to insert these small and
invalid combinations in any code snippet and still make you believe that you’re being
tested on a rather complex topic like threads or concurrency.
Watch out for invalid combinations of a Java entity and an
access modifier. Such code won’t compile.

EXAM TIP

Licensed to Mark
Watson 
www.allitebooks.com

26

CHAPTER 1 Java class design

Project status: code by Harry fails compilation

I promise!
I didn’t change
any code!

Harry

Then how did
this happen?

Paul

Figure 1.13 A change in the access modifier of a member of a class can
break the code of other classes.

1.1.6

Effects of changing access modifiers for existing entities
Shreya, a programmer, changed the access modifier of a member in her class, Book,
and see what Harry (another programmer) had to go through the next morning (figure 1.13).
Let’s analyze what happened. Why did Harry’s code break when Shreya changed
her own code? As shown in figure 1.14, Harry’s class StoryBook extends class Book created by Shreya. Before the modifications, Harry’s class StoryBook accessed the protected member author from its parent class Book. But when Shreya modified the
access modifier of the member author from protected to default access, it could no
longer be accessed by class StoryBook because they reside in separate packages. So,
even though Harry didn’t change his code, it didn’t compile.
You can change the access modifier of a member in two ways:
■
■

Accessibility is decreased—for example, a public member is made private
Accessibility is increased—for example, a private member is made public

WHEN ACCESSIBILITY OF AN ENTITY IS DECREASED (MORE RESTRICTIVE)

As shown in figure 1.14, when an entity is made more restrictive, there are chances
that other code that uses that entity might break.

Impact of decreasing accessibility in real-life projects
Decreasing the accessibility of entities can affect the overall application in a big way.
This is especially important for designing APIs and maintaining software. Many Java
developers make the mistake of carelessly decreasing the accessibility of methods
or fields, which can result in access issues with other components in a system.

WHEN ACCESSIBILITY OF AN ENTITY IS INCREASED (LESS RESTRICTIVE)

There are no issues when an entity is made less restrictive, say, when access of an
entity is changed from default to protected or public. With increased access, an

Licensed to Mark Watson 

27

Java access modifiers

Before modification

library

building

package library;
public class Book {

<>
Can
access

protected String author;

package building;
import library.Book;
Class StoryBook extends Book{

}

{author = "Selvan";}
}

Shreya’s code

Harry’s code

After modification

building

library
package library;
public class Book {

<>
Can’t
access

String author;

package building;
import library.Book;
Class StoryBook extends Book{

}

{author = "Selvan";}
}

Shreya’s code

Harry’s code

Figure 1.14 Code before and after modification showing why Harry’s code failed to compile, even
though he didn’t change a bit of it.

entity may become visible to other classes, interfaces, and enums to which it wasn’t
visible earlier.
Apart from being an important exam topic, you’re sure to encounter issues related
to access modifiers at your workplace in real projects. Let’s see whether you can spot a
similar issue in the first “Twist in the Tale” exercise.

About the “Twist in the Tale” exercises
For these exercises, I’ve tried to use modified code from the examples already covered in the chapter. The “Twist in the Tale” title refers to modified or tweaked code.
These exercises will help you understand how even small code modifications can
change the behavior of your code. They should also encourage you to carefully examine all of the code on the exam. The reason for these exercises is that on the exam,
you may be asked more than one question that seems to require the same answer.
But on closer inspection, you’ll realize that the questions differ slightly, and this will
change the behavior of the code and the correct answer option. All answers to “Twist
in the Tale” exercises are in the appendix.

Licensed to Mark Watson 

28

CHAPTER 1 Java class design

Twist in the Tale 1.1

Here are the classes written by Shreya and Harry (residing in separate source code
files) that work without any issues:
package library;
public class Book {
protected String author;
}

// Class written by Shreya

package building;
import library.Book;
class StoryBook extends Book {
{ author = "Selvan"; }

// Class written by Harry

}

On Friday evening, Shreya modified her code and checked it in to the organization’s
version control system. Do you think Harry would be able to run his code without any
errors when he checks out the modified code on Monday morning, and why? Here’s
the modified code:
package library;
class Book {
protected String author;
}

// Class written by Shreya

package building;
import library.Book;
class StoryBook extends Book {
{ author = "Selvan"; }
}

// Class written by Harry

In the next section, we’ll cover the need and semantics of defining overloaded methods. You can compare overloaded methods with any action that you might specify with
multiple, different, or additional details. Let’s get started with understanding the
need of defining overloaded methods.

1.2

Overloaded methods and constructors
[1.3] Overload constructors and methods
Overloaded methods are methods with the same name but different method parameter
lists. In this section, you’ll learn how to create and use overloaded methods.
Imagine that you’re delivering a lecture and need to instruct the audience to take
notes using paper, a Smartphone, or a laptop—whichever is available to them for the
day. One way to do this is to give the audience a list of instructions like
■
■
■

Take notes using paper.
Take notes using Smartphones.
Take notes using laptops.

Licensed to Mark Watson 

Overloaded methods and constructors
Unrelated methods
Different names

Figure 1.15

29

Overloaded methods
Same names

takeNotesUsingPaper

takeNotes(Paper)

takeNotesUsingSmartphone

takeNotes(Smartphone)

takeNotesUsingLaptop

takeNotes(Laptop)

Real-life examples of overloaded methods

Another method is to instruct them to “take notes” and then provide them with the
paper, a Smartphone, or a laptop they’re supposed to use. Apart from the simplicity of
the latter method, it also gives you the flexibility to add other media on which to take
notes (such as one’s hand, some cloth, or the wall) without needing to remember the
list of all the instructions.
This second approach—providing one set of instructions (with the same name)
but a different set of input values—can be compared to overloaded methods in Java,
as shown in figure 1.15.
The implementation of the example shown in figure 1.15 in code is as follows:
class Paper {}
class Smartphone {}
class Laptop {}
class Lecture {
void takeNotes(Paper paper) {}
void takeNotes(Smartphone phone) {}
void takeNotes(Laptop laptop) {}
}

Overloaded
method—
takeNotes()

Overloaded methods are usually referred to as methods that are defined in the same
class, with the same name, but with a different method argument list. A derived class
can also overload the methods inherited from its base class as follows:
class Paper {}
class Smartphone {}
class Laptop {}
class Lecture {
void takeNotes(Paper paper) {}
void takeNotes(Smartphone phone) {}
void takeNotes(Laptop laptop) {}
}
class Canvas {}
class FineArtLecture extends Lecture {
void takeNotes(Canvas canvas) {}
}

takeNotes() in FineArtLecture
overloads takeNotes() from
Lecture by specifying a
different parameter list.

Licensed to Mark Watson 

30

CHAPTER 1 Java class design

Overloaded methods make it easier to add methods with similar functionality that
work with a different set of input values. Let’s work with an example from the Java API
classes that we all use frequently: System.out.println(). Method println() accepts
multiple types of method parameters:
int intVal = 10;
boolean boolVal = false;
String name = "eJava";

Prints an
int value

System.out.println(intVal);
System.out.println(boolVal);
System.out.println(name);

Prints a
boolean value

Prints a
string value

When you use method println(), you know that whatever you pass to it as a method
argument will be printed to the console. Wouldn’t it be crazy to use methods like
printlnInt(), printlnBool(), and printlnString() for the same functionality? I
think so, too.
Let’s examine in detail the method parameters passed to overloaded methods,
their return types, and their access and nonaccess modifiers.
The exam will test you on how you can define correct overloaded
methods, which overloaded methods get invoked when you use a set of
arguments, and also whether a compiler is unable to resolve the call.

NOTE

1.2.1

Argument list
Overloaded methods accept different lists of arguments. The argument lists can differ
in terms of
■
■
■

The change in the number of parameters that are accepted
The change in the type of the method parameters that are accepted
The change in the positions of the parameters that are accepted (based on
parameter type, not variable names)

Let’s work with some examples to verify these points.
CHANGE IN THE NUMBER OF METHOD PARAMETERS

Overloaded methods that define a different number of method parameters are the
simplest among all the method types. Let’s work with an example of an overloaded
method, calcAverage(), which accepts a different count of method parameters:
class Result {
Two method
double calcAverage(int marks1, int marks2) {
arguments
return (marks1 + marks2)/2;
}
double calcAverage(int marks1, int marks2, int marks3) {
Three
return (marks1 + marks2 + marks3)/3;
method
}
arguments
}

Licensed to Mark Watson 

31

Overloaded methods and constructors

CHANGE IN THE TYPE OF METHOD PARAMETERS

In the following example, the difference is in the argument list—due to the change
in the type of parameters it accepts—to calculate the average of integer and decimal numbers:
class Result {
double calcAverage(int marks1, double marks2) {
return (marks1 + marks2)/2;
}
double calcAverage(double marks1, double marks2) {
return (marks1 + marks2)/2;
}
}

Arguments—
int and double
Arguments—
double and
double

When you define overloaded methods with object references as parameters, their
classes might or might not share an inheritance relationship. When the classes don’t
share an inheritance relationship, there isn’t any confusion with the version of the
method that will be called:
class Employee {}
class Engineer extends Employee {}
class CEO extends Employee {}
class Travel {
static String bookTicket(Engineer val) {
return "economy class";
}
static String bookTicket(CEO val) {
return "business class";
}
}

Engineer and CEO
aren’t in the same
inheritance tree

For the preceding code, if you call method bookTicket() by passing it a CEO object, it
will call the method that accepts a parameter of type CEO—no confusion here. Now,
what happens if you define overloaded methods that accept object references of
classes which share an inheritance relationship? For example (modifications in code
are in bold)
class Employee {}
class CEO extends Employee {}
class Travel {
static String bookTicket(Employee val) {
return "economy class";
}
static String bookTicket(CEO val) {
return "business class";
}
}

Method
parameters—CEO
extends Employee

Licensed to Mark Watson 

32

CHAPTER 1 Java class design

Which of these methods do you think would be called if you pass a CEO object to
method bookTicket()? Can a CEO object be assigned to both CEO and Employee?
class TravelAgent {
public static void main(String... args) {
System.out.println(Travel.bookTicket(new CEO()));
}
}

Prints
“business
class”

The preceding code calls overloaded method bookTicket()that accepts a CEO, because
without any explicit reference variable, new CEO() is referred to using a CEO variable.
Now, try to determine the output of the following code:
class TravelAgent {
public static void main(String... args) {
Employee emp = new CEO();
System.out.println(Travel.bookTicket(emp));
}
}

Prints
“economy
class”

The preceding code prints “economy class” and not “business class” because the type
of the reference variable emp is Employee. The overloaded methods are bound at
compile time and not runtime. To resolve the call to the overloaded methods, the
compiler considers the type of variable that’s used to refer to an object.
Calls to the overloaded methods are resolved during
compilation.

EXAM TIP

Using the preceding Employee and CEO example, figure 1.16 shows a fun way to remember calls to the overloaded methods are resolved during compilation.

Please book a
ticket to Paris for
our employee.

Group
secretary
Figure 1.16

Sure!

Travel
desk

Why didn’t
you book business
class?

Because you
were referred to
as an employee.

CEO

Overloaded methods are resolved during compilation.

Licensed to Mark Watson 

Travel
desk

33

Overloaded methods and constructors

For the overloaded method bookTicket() that defines the method parameter of
either Engineer or CEO, watch out for exam questions that try to call it using a reference variable of Employee:
class Employee {}
class Engineer extends Employee {}
class CEO extends Employee {}
class Travel {
static String bookTicket(Engineer val) {
return "economy class";
}
static String bookTicket(CEO val) {
return "business class";
}
public static void main(String args[]) {
Employee emp = new CEO();
System.out.println(bookTicket(emp));
}
}

Accepts
Engineer
Accepts CEO
Won’t compile—Travel
doesn’t define method
that accepts Employee

CHANGE IN THE POSITIONS OF METHOD PARAMETERS

The methods are correctly overloaded if they only change the positions of the parameters that are passed to them, as follows:
double calcAverage(double marks1, int marks2) {
return (marks1 + marks2)/2;
}
double calcAverage(int marks1, double marks2) {
return (marks1 + marks2)/2;
}

Arguments—
double and int
Arguments—
int and double

Although you might argue that the arguments being accepted are the same, with only
a difference in their positions, the Java compiler treats them as different argument
lists. Therefore, the previous code is a valid example of overloaded methods. But an
issue arises when you try to execute this method using values that can be passed to
both versions of the overloaded method. In this case, the code in method main() will
fail to compile:

b

Method

parameters—
class MyClass {
double and int
static double calcAverage(double marks1, int marks2) {
return(marks1 + marks2)/2;
Method
}
parameters—
static double calcAverage(int marks1, double marks2) {
int and double
return(marks1 + marks2)/2;
}
Compiler can’t determine
public static void main(String[] args) {
overloaded calcAverage()
calcAverage(2, 3);
that should be called
}
}

c

d

In the previous code, B defines the calcAverage() method, which accepts two
method parameters: a double and an int. The code at c defines overloaded method

Licensed to Mark Watson 

34

CHAPTER 1 Java class design

calcAverage(), which accepts two method parameters: first an int and then a double.
Because an int literal value can be passed to a variable of type double, literal values 2
and 3 can be passed to both overloaded methods, declared at B and c. Because this

method call is dubious, the code at d fails to compile, with the following message:
MyClass.java:10: error: reference to calcAverage is ambiguous, both method
calcAverage(double,int) in MyClass and method calcAverage(int,double) in
MyClass match
calcAverage(2, 3);
^
1 error

For primitive method arguments, if a call to an overloaded
method is dubious, the code won’t compile.

EXAM TIP

Here’s an interesting question: Would an overloaded method with the following signature solve this specific problem?
static double calcAverage(int marks1, int marks2)

Yes, it will. Because the type of literal integer value is int, the compiler will be able to
resolve the call calcAverage(2, 3) to calcAverage(int marks1, int marks2) and
compile successfully.

1.2.2

When methods can’t be defined as overloaded methods
The overloaded methods give you the flexibility of defining methods with the same
name that can be passed a different set of arguments. But it doesn’t make sense to
define overloaded methods with a difference in only their return types or access or
nonaccess modifiers.
RETURN TYPE

Methods can’t be defined as overloaded methods if they only differ in their return
types, as follows:
class Result {
double calcAverage(int marks1, int marks2) {
return (marks1 + marks2)/2;
}
int calcAverage(int marks1, int marks2) {
return (marks1 + marks2)/2;
}
}

Return type of
calcAverage() is double
Return type of
calcAverage() is int

The methods defined in the preceding code aren’t correctly overloaded methods—
they won’t compile.
When the Java compiler differentiates methods, it doesn’t consider their return types. So you can’t define overloaded methods with the
same parameter list and different return types.

EXAM TIP

Licensed to Mark Watson 

35

Overloaded methods and constructors

ACCESS MODIFIER

Methods can’t be defined as overloaded methods if they only differ in their access
modifiers, as follows:
class Result {
public double calcAverage(int marks1, int marks2) {
return (marks1 + marks2)/2;
}
protected double calcAverage(int marks1, int marks2) {
return (marks1 + marks2)/2;
}
}

Access—public
Access—protected

NONACCESS MODIFIER

Methods can’t be defined as overloaded methods if they only differ in their nonaccess
modifiers, as follows:
class Result {
Nonaccess
public synchronized double calcAverage(int marks1, int marks2) {
modifier—
return (marks1 + marks2)/2;
synchronized
}
public final double calcAverage(int marks1, int marks2) {
Nonaccess
return (marks1 + marks2)/2;
modifier—
}
final
}

Let’s revisit the rules for defining overloaded methods.

Rules to remember for defining overloaded methods
Here’s a quick list of rules to remember for the exam for defining and using overloaded methods:
■
■
■
■

A class can overload its own methods and methods inherited from its base class.
Overloaded methods must be defined with the same name.
Overloaded methods must be defined with different parameter lists.
Overloaded methods might define a different return type or access or nonaccess
modifier, but they can’t be defined with only a change in their return types or
access or nonaccess modifiers.

In the next section, we’ll create overloaded versions of special methods, called constructors, which are used to create objects of a class.

1.2.3

Overloaded constructors
While creating instances of a class, you might need to assign default values to some of
its variables and assign explicit values to the rest. You can do so by overloading the

Licensed to Mark
Watson 
www.allitebooks.com

36

CHAPTER 1 Java class design

constructors. Overloaded constructors follow the same rules as discussed in the previous
section on overloaded methods:
■
■

Overloaded constructors must be defined using a different argument list.
Overloaded constructors can’t be defined by a mere change in their access
modifiers.
EXAM TIP Watch out for exam questions that use nonaccess modifiers
with constructors.

Using nonaccess modifiers with constructors is illegal—the code won’t compile. Here’s
an example of class Employee, which defines four overloaded constructors:
class Employee {
String name;
No-argument
int age;
constructor
Employee() {
name = "John";
age = 25;
Constructor with one
}
String argument
Employee(String newName) {
name = newName;
Constructor with
age = 25;
two arguments—
}
int and String
Employee(int newAge, String newName) {
name = newName;
Constructor with
age = newAge;
two arguments—
}
String and int
Employee(String newName, int newAge) {
name = newName;
age = newAge;
}
}

b

c

d
e

In the previous code, the code at B defines a constructor that doesn’t accept any
arguments, and the code at c defines another constructor that accepts a single argument. Note the constructors defined at d and e. Both of these accept two arguments,
String and int. But the placement of these two arguments is different in d and e,
which is acceptable and valid for overloaded constructors and methods.
INVOKING AN OVERLOADED CONSTRUCTOR FROM ANOTHER CONSTRUCTOR

It’s common to define multiple constructors in a class. Unlike overloaded methods,
which can be invoked using the name of a method, overloaded constructors are
invoked by using the keyword this—an implicit reference, accessible to an object, to
refer to itself. For instance
class Employee {
String name;
int age;
Employee() {
this(null, 0);
}

b

No-argument
constructor

c

Invokes constructor that
accepts two arguments

Licensed to Mark Watson 

37

Overloaded methods and constructors
Employee(String newName, int newAge) {
name = newName;
age = newAge;
}

d

Constructor
that accepts
two arguments

}

The code at B defines a no-argument constructor. At c, this constructor calls the
overloaded constructor by passing to it values null and 0. d defines an overloaded
constructor that accepts two arguments.
Because a constructor is defined using the name of its class, it’s a common mistake
to try to invoke a constructor from another constructor using the class’s name:
class Employee {
String name;
int age;
Employee() {
Employee(null, 0);
}
Employee(String newName, int newAge) {
name = newName;
age = newAge;
}
}

Won’t compile—you can’t
invoke a constructor within a
class by using the class’s name.

Also, when you invoke an overloaded constructor using the keyword this, it must be
the first statement in your constructor:
class Employee {
String name;
int age;
Employee() {
System.out.println("No-argument constructor");
this(null, 0);
}
Employee(String newName, int newAge) {
name = newName;
age = newAge;
}
}

Won’t compile—call to
overloaded constructor
must be first statement
in constructor.

That’s not all: you can’t call a constructor from any other method in your class. None
of the other methods of class Employee can invoke its constructor.

Rules to remember for defining overloaded constructors
Here’s a quick list of rules to remember for the exam for defining and using overloaded constructors:
■
■
■

Overloaded constructors must be defined using different argument lists.
Overloaded constructors can’t be defined by just a change in the access modifiers.
Overloaded constructors can be defined using different access modifiers.

Licensed to Mark Watson 

38

CHAPTER 1 Java class design

(continued)
■ A constructor can call another overloaded constructor by using the keyword this.
■ A constructor can’t invoke a constructor by using its class’s name.
■ If present, the call to another constructor must be the first statement in a
constructor.
INSTANCE INITIALIZERS

Apart from constructors, you can also define an instance initializer to initialize the
instance variables of your class. An instance initializer is a code block defined within a
class, using a pair of {}. You can define multiple instance initializers in your class. Each
instance initializer is invoked when an instance is created, in the order they’re defined
in a class. They’re invoked before a class constructor is invoked.
Why do you think you need an instance initializer if you can initialize your
instances using constructors? Multiple reasons exist:
■

■

■

For a big class, it makes sense to place the variable initialization just after its
declaration.
All the initializers are invoked, irrespective of the constructor that’s used to
instantiate an object.
Initializers can be used to initialize variables of anonymous classes that can’t
define constructors. (You’ll work with anonymous classes in the next chapter.)

Here’s a simple example:
class Pencil {
public Pencil() {
System.out.println("Pencil:constructor");
}
public Pencil(String a) {
System.out.println("Pencil:constructor2");
}
{
System.out.println("Pencil:init1");
}
{
System.out.println("Pencil:init2");
}

Added to both
overloaded
constructors

public static void main(String[] args) {
new Pencil();
new Pencil("aValue");
}
}

The output of the preceding code is
Pencil:init1
Pencil:init2
Pencil:constructor

Licensed to Mark Watson 

Overloaded methods and constructors

39

Pencil:init1
Pencil:init2
Pencil:constructor2

The next “Twist in the Tale” exercise hides an important concept within its code,
which you can get to know only if you try to compile and execute the modified code.
Twist in the Tale 1.2

Let’s modify the definition of class Employee used in the section on overloaded constructors as follows:
class Employee {
String name;
int age;
Employee() {
this("Shreya", 10);
}
Employee (String newName, int newAge) {
this();
name = newName;
age = newAge;
}
void print(){
print(age);
}
void print(int age) {
print();
}
}

What is the output of this modified code, and why?

The instance initializer blocks are executed after an implicit or explicit call to the parent class’s constructor:
class Instrument {
Instrument() {
System.out.println("Instrument:constructor");
}
}
class Pencil extends Instrument {
public Pencil() {
System.out.println("Pencil:constructor");
}
{
System.out.println("Pencil:instance initializer");
}
public static void main(String[] args) {
new Pencil();
}
}

Licensed to Mark Watson 

40

CHAPTER 1 Java class design

Alphabetical order

Child constructor
Child initialization block

Execution order

Got
the execution
order.

4
3

Parent constructor

2

Parent initialization block

1

Figure 1.17 The order of execution of constructors and instance initializers in parent and
child classes

The output of the preceding code is
Instrument:constructor
Pencil:instance initializer
Pencil:constructor

Figure 1.17 shows a fun way of remembering the order of execution of a parent class
constructor, instance initializers, and a class constructor. Paul, our programmer, was
having a very hard time remembering the order of execution of all these code blocks.
He literally had to stand upside down to get the order right.
If a parent or child class defines static initializer block(s), they
execute before all parent and child class constructors and instance initializers—first for the parent and then for the child class.

EXAM TIP

Now that you’ve seen how to create the overloaded variants of methods and constructors, let’s dive deep into method overriding. These two concepts, overloading and
overriding, seem to be confusing for a lot of programmers. Let’s get started by clearing the cobwebs.

1.3

Method overriding and virtual method invocation
[1.2] Override methods
[1.5] Use virtual method invocation
Do you celebrate a festival or an event in exactly the same manner as celebrated by
your parents? Or have your modified it? Perhaps you celebrate the same festivals
and events, but in your own unique manner. In a similar manner, classes can inherit

Licensed to Mark Watson 

Method overriding and virtual method invocation

41

behavior from other classes. But they can redefine the behavior that they inherit—this
is also referred to as method overriding.
Method overriding is an object-oriented programming (OOP) language feature
that enables a derived class to define a specific implementation of an existing base
class method to extend its own behavior. A derived class can override an instance
method defined in a base class by defining an instance method with the same method
signature/method name and number and types of method parameters. Overridden
methods are also synonymous with polymorphic methods. The static methods of a base
can’t be overridden, but they can be hidden by defining methods with the same signature in the derived class.
A method that can be overridden by a derived class is called a virtual method. But
beware: Java has always shied away from using the term virtual methods and you will not
find a mention of this term in Java’s vocabulary. This term is used in other OO languages like C and C++. Virtual method invocation is the invocation of the correct overridden method, which is based on the type of the object referred to by an object
reference and not by the object reference itself. It’s determined at runtime, not at
compilation time.
The exam will question you on the need for overridden methods; the correct syntax of overridden methods; the differences between overloaded, overridden, and hidden methods; common mistakes while overriding methods; and virtual method
invocation. Let’s get started with the need for overridden methods.
A base class method is referred to as the overridden method and the
derived class method is referred to as the overriding method.

NOTE

1.3.1

Need of overridden methods
In the same way we inherit our parents’ behaviors but redefine some of the inherited
behavior to suit our own needs, a derived class can inherit the behavior and properties
of its base class but still be different in its own manner—by defining new variables and
methods. A derived class can also choose to define a different course of action for its
base class method by overriding it. Here’s an example of class Book, which defines a
method issueBook() that accepts days as a method parameter:
class Book {
void issueBook(int days) {
if (days > 0)
System.out.println("Book issued");
else
System.out.println("Cannot issue for 0 or less days");
}
}

Following is another class, CourseBook, which inherits class Book. This class needs to
override method issueBook() because a CourseBook can’t be issued if it’s only for

Licensed to Mark Watson 

42

CHAPTER 1 Java class design

reference. Also, a CourseBook can’t be issued for 14 or more days. Let’s see how this is
accomplished by overriding method issueBook():
class CourseBook extends Book {
boolean onlyForReference;
CourseBook(boolean val) {
onlyForReference = val;
}
@Override
void issueBook(int days) {
if (onlyForReference)
System.out.println("Reference book");
else
if (days < 14)
super.issueBook(days);
else
System.out.println("days >= 14");
}
}

b

Annotation—
@Override

c

Overrides issueBook()
in base class Book

d

Calls issueBook()
defined in Book

The code at B uses the annotation @Override, which notifies the compiler that this
method overrides a base class method. Though optional, this annotation can come in
very handy if you try to override a method incorrectly. The code at c defines method
issueBook() with the same name and method parameters as defined in class Book.
The code at d calls method issueBook() defined in class Book; however, it isn’t mandatory to do so. It depends on whether the derived class wants to execute the same
code as defined by the base class.
Whenever you intend to override methods in a derived class, use
the annotation @Override. It will warn you if a method can’t be overridden or if you’re actually overloading a method rather than overriding it.

NOTE

The following example can be used to test the preceding code:
class BookExample {
public static void main(String[] args) {
Book b = new CourseBook(true);
b.issueBook(100);
b = new CourseBook(false);
b.issueBook(100);
b = new Book();
b.issueBook(100);
Prints “Book
}
issued”
}

Prints “Reference
book”
Prints “days
>= 14”
b now refers to
a Book instance

Figure 1.18 represents the compilation and execution process of class BookExample, as
Step 1 and Step 2:
■
■

Step 1: The compile time uses the reference type for the method check.
Step 2: The runtime uses the instance type for the method invocation.

Now let’s move on to how to correctly override a base class method in a derived class.

Licensed to Mark Watson 

43

Method overriding and virtual method invocation
Step 1

Type of reference variable “b” is
I must consult class Book to
verify existence of method issueBook().

Book b = new CourseBook(true);
b.issueBook(100);

In

Book.

Java
compiler

Consult

Compilation
successful

class Book {
void issueBook(int days){
.....
}
}
BookExample.class
– –
– –

Step 2
BookExample.class

Type of object referred by “b” is
CourseBook. I must consult CourseBook
for description of method issueBook().

Book b = new CourseBook(true);
b.issueBook(100);

In

1
Java
runtime
2
Consult

4

Result
3
Confusion

Because method signatures
are exactly the same, call
issueBook() from CourseBook
(type of object is CourseBook).

class CourseBook extends Book {
void issueBook(int days){
.....
}
}

Classes Book and
CourseBook define
method issueBook()
with identical
signatures.

Book
issueBook(int)

CourseBook
onlyForReference
issueBook(int)

Figure 1.18 To compile b.issueBook(), the compiler refers only to the definition of class Book.
To execute b.issueBook(), the Java Runtime Environment (JRE) uses the actual method
implementation of issueBook() from class CourseBook.

Licensed to Mark Watson 

44

CHAPTER 1 Java class design

Method
review()
in Book
Method
review() in
CourseBook

Nonaccess
modifiers

Access
modifiers

synchronized

protected

List

review

(int id,
throws
List names) Exception

final

public

ArrayList

review

(int id,
throws
List names) IOException

Return
type

Same or different
(conditions apply)

Same or
covariant

Method
name

Parameter
list

Exact match

Exception
list

None, same,
or subclass

Figure 1.19 Comparing parts of a method declaration for a base class method and overriding method

1.3.2

Correct syntax of overriding methods
Let’s start with an example of overridden method review(), as follows:

Method
review() in
base class
Book

class Book {
synchronized protected List review(int id,
List names) throws Exception {
return null;
}
CourseBook
}
extends Book
class CourseBook extends Book {
@Override
final public ArrayList review(int id,
List names) throws IOException {
return null;
}
Overridden method review()
}
in derived class CourseBook

Figure 1.19 shows the components of a method declaration: access modifiers, nonaccess modifiers, return type, method name, parameter list, and a list of exceptions that
can be thrown (method declaration isn’t the same as method signature). The figure
also compares the review method defined in base class Book with overriding method
review() defined in class CourseBook with respect to these identified parts.
Table 1.2 compares the method components shown in figure 1.19.
Table 1.2 Comparison of method components and their acceptable values for an overriding method
Value in
class Book

Method
component
Access modifier

protected

Value in class
CourseBook

public

Overriding method review()
in class CourseBook
Define same access or less restrictive
access than method review() in
the base class.

Licensed to Mark Watson 

Method overriding and virtual method invocation

45

Table 1.2 Comparison of method components and their acceptable values for an overriding method
Method
component

Value in
class Book

Value in class
CourseBook

Overriding method review()
in class CourseBook

Nonaccess
modifier

synchronized

final

Overriding method can use any
nonaccess modifier for an overridden
method. A nonabstract method can
also be overridden to an abstract
method. But a final method in the
base class cannot be overridden. A
static method cannot be overridden to
be nonstatic.

Return type

List

ArrayList

Define the same or a subtype of the
return type used in the base class
method (covariant return types).

Method name

review

review

Exact match.

Parameter list

(int id, List
names)

(int id, List
names)

Exact match.

Exceptions thrown

throws
Exception

throws
IOException

Throw none, same, or a subclass of
the exception thrown by the base
class method.

The rule listed in table 1.2 on exceptions in overriding methods only applies to checked exceptions. An overriding method can throw
any unchecked exception (RuntimeException or Error) even if the overridden method doesn’t. The unchecked exceptions aren’t part of the
method signature and aren’t checked by the compiler.
EXAM TIP

Chapter 6 includes a detailed explanation on overridden and overriding methods that
throw exceptions. Let’s walk through a couple of invalid combinations that are important and very likely to be on the exam.
Though a best practice, I’ve deliberately not preceded the definition of the overriding methods with the annotation @Override because
you might not see it on the exam.
NOTE

ACCESS MODIFIERS

A derived class can assign the same or more access but not a weaker access to the overriding method in the derived class:
class Book {
protected void review(int id, List names) {}
}
class CourseBook extends Book {
void review(int id, List names) {}
}

Won’t compile;
overriding methods in
derived classes can’t
use a weaker access.

Licensed to Mark Watson 

46

CHAPTER 1 Java class design

NONACCESS MODIFIERS

A derived class can’t override a base class method marked final:
class Book {
final void review(int id, List names) {}
}
class CourseBook extends Book {
void review(int id, List names) {}
}

Won’t compile; final
methods can’t be
overridden.

ARGUMENT LIST AND COVARIANT RETURN TYPES

When the overriding method returns a subclass of the return type of the overridden
method, it’s known as a covariant return type. To override a method, the parameter list
of the methods in the base and derived classes must be exactly the same. It you try to
use covariant types in the argument list, you’ll end up overloading the methods and
not overriding them. For example
class Book {
void review(int id, List names) throws Exception {
System.out.println("Base:review");
}
}
class CourseBook extends Book {
void review(int id, ArrayList names) throws IOException {
System.out.println("Derived:review");
}
}

b

At

Argument list—
int and List

Argument
list—int and
ArrayList

B method review() in base class Book accepts an object of type List. Method

review() in derived class CourseBook accepts a subtype ArrayList (ArrayList implements List). These methods aren’t overridden—they’re overloaded:
class Verify {
public static void main(String[] args)throws Exception {
Book book = new CourseBook();
book.review(1, null);
Calls review in
}
Book; prints
}
“Base:review”

b

Reference variable
of type Book used
to refer to object
CourseBook.

The code at B uses a reference variable of type Book to refer to an object of type
CourseBook. The compilation process assigns execution of method review() from
base class Book to the reference variable book. Because method review() in class
CourseBook doesn’t override the review method in class Book, the JRE doesn’t have any
confusion regarding whether to call method review() from class Book or from class
CourseBook. It moves forward with calling review() from Book.
EXAM TIP It’s the reference variable type that dictates which overloaded
method will be chosen. This choice is made at compilation time.

Licensed to Mark Watson 

47

Method overriding and virtual method invocation

EXCEPTIONS THROWN

An overriding method must either declare to throw no exception, the same exception, or a subtype of the exception declared to be thrown by the base class method, or
else it will fail to compile. This rule, however, doesn’t apply to error classes or runtime
exceptions. For example

Compiles; an
overriding
method can
declare to
throw any
RuntimeException.

class Book {
void review() throws Exception {}
void read() throws Exception {}
void close() throws Exception {}
void write() throws NullPointerException {}
void skip() throws IOException {}
void modify() {}
}
class CourseBook extends Book {
void review() {}
void read() throws IOException {}
void close() throws Error {}
void write() throws RuntimeException {}
void skip() throws Exception {}
void modify() throws IOException {}
}

Compiles;
declares to throw
no exception.

Doesn’t compile; declares to throw IOException.
Overriding method can’t declare to throw a checked
exception if overridden method doesn’t.
EXAM TIP

Compiles; declares to
throw IOException, a
subclass of Exception.
Compiles; an overriding
method can declare to
throw any Error.
Doesn’t compile; declares to
throw Exception, a superclass of
IOException. Overriding method
can’t declare to throw broader
exceptions than declared to be
thrown by overridden method.

An overriding method can declare to throw any Runtime-

Exception or Error, even if the overridden method doesn’t.

To remember this preceding point, let’s compare exceptions with monsters. Figure 1.20
shows a fun way to remember the exceptions (monsters) that can be on the list of an

Exception list
overridden method
(in base class)

Exception list
overriding method
(in derived class)
None

Same

Narrower

Error

RuntimeException

None

Error

RuntimeException

Figure 1.20 Comparing exceptions to monsters. When an overridden method declares to throw a
checked exception (monster), the overriding method can declare to throw none, the same, or a
narrower checked exception. An overriding method can declare to throw any Error or
RuntimeException.

Licensed to Mark Watson 

48

CHAPTER 1 Java class design

overriding method, when the overridden method doesn’t declare to throw a checked
exception and when it declares to throw a checked exception.

1.3.3

Can you override all methods from the base class or
invoke them virtually?
The simple answer is no. You can override only the following methods from the
base class:
■
■

Methods accessible to a derived class
Nonstatic base class methods

METHODS ACCESSIBLE TO A BASE CLASS

The accessibility of a method in a derived class depends on its access modifier. For
example, a private method defined in a base class isn’t available to any of its derived
classes. Also, a method with default access in a base class isn’t available to a derived
class in another package. A class can’t override the methods that it can’t access.
ONLY NONSTATIC METHODS CAN BE OVERRIDDEN

If a derived class defines a static method with the same name and signature as the one
defined in its base class, it hides its base class method and doesn’t override it. You can’t
override static methods. For example
class Book {
static void printName() {
System.out.println("Book");
}
}
class CourseBook extends Book {
static void printName() {
System.out.println("CourseBook");
}
}

Static method
in base class

Static method
in derived class

Method printName() in class CourseBook hides printName() in class Book. It doesn’t
override it. Because the static methods are bound at compile time, the method printName() that’s called depends on the type of the reference variable:
class BookExampleStaticMethod {
public static void main(String[] args) {
Book base = new Book();
base.printName();
Book derived = new CourseBook();
derived.printName();
}

Prints
“Book”

Prints
“Book”

}

Licensed to Mark Watson 

49

Method overriding and virtual method invocation

class Book{
public static void printName(){
....
}
public int issueBook(int days){
....
}
public int returnBook(int days){
....
}
}

Book
+ printName()
+ issueBook(int)
+ returnBook(int)
Method
hiding
CourseBook

class CourseBook extends Book{
public static void printName(){
....
Method
}
overloading
public int issueBook(int days){
....
}
public int issueBook(){
....
}
public int returnBook(int a, int b){
....
}
}

+
+
+
+

printName()
issueBook(int)
issueBook()
returnBook(int, int)

Method
overriding

Method
overloading

Figure 1.21 Identifying method overriding, method overloading, and method hiding in a base and
derived class

1.3.4

Identifying method overriding, overloading, and hiding
It’s easy to get confused with method overriding, overloading, and hiding. Figure 1.21
identifies these methods in classes Book and CourseBook. On the left are the class definitions, and on the right their UML representations.
When a class extends another class, it can overload, override,
or hide its base class methods. A class can’t override or hide its own methods—it can only overload its own methods.

EXAM TIP

Let’s check out the correct code for defining a static or nonstatic method in a derived
class that overrides or hides a static or nonstatic method in a base class using the next
“Twist in the Tale” exercise.
Twist in the Tale 1.3

Let’s modify the code of classes Book and CourseBook and define multiple combinations of static and nonstatic method print() in both these classes as follows:
a

class Book{
static void print(){}
}

Licensed to Mark Watson 

50

CHAPTER 1 Java class design
class CourseBook extends Book{
static void print(){}
}
b

c

d

class Book{
static void print(){}
}
class CourseBook extends Book{
void print(){}
}
class Book{
void print(){}
}
class CourseBook extends Book{
static void print(){}
}
class Book{
void print(){}
}
class CourseBook extends Book{
void print(){}
}

Your task is to first tag them with one of the options and then compile them on your
system to see if they’re correct. On the actual exam, you’ll need to verify (without a
compiler) if a code snippet compiles or not:
■
■
■

1.3.5

Overridden print() method
Hidden print() method
Compilation error

Can you override base class constructors or invoke
them virtually?
The simple answer is no. Constructors aren’t inherited by a derived class. Because
only inherited methods can be overridden, constructors cannot be overridden by a
derived class. If you attempt an exam question that queries you on overriding a base
class constructor, you know that it’s trying to trick you.
EXAM TIP Constructors can’t be overridden because a base class constructor isn’t inherited by a derived class.

Now that you know why and how to override methods in your own classes, let’s see in the
next section why it’s important to override the methods of class java.lang.Object.

Licensed to Mark Watson 

51

Overriding methods of class Object

1.4

Overriding methods of class Object
[1.6] Override methods from the Object class to improve the functionality
of your class
All the classes in java—classes from the Java API, user-defined classes, or classes from
any other API—extend class java.lang.Object, either implicitly or explicitly. Because
this section talks about overriding the methods from class Object, let’s take a look at
its nonfinal and final methods in figure 1.22.
You might write a Java class to be used in your small in-house project or a commercial project, or it could be a part of a library that may be released to be used by other
programmers. As you have less control over who uses your class and how it’s used, the
importance of correctly overriding methods from class Object rises. It’s important to
override the nonfinal Object class methods so that these classes can be used efficiently by other users. Apart from being able to be used as desired, incorrect overriding of these methods can also result in increased debug time.
Because the final methods can’t be overridden, I’ll discuss the nonfinal methods
of class Object in this section. These methods—clone(), equals(), hashCode(),
toString(), and finalize()—define a contract, a set of rules on how to override
these methods, specified by the Java API documentation.

1.4.1

Overriding method toString()
Method toString() is called when you try to print out the value of a reference variable or use a reference variable in a concatenation operator. The default implementation of method toString() returns the name of the class, followed by @ and the hash

java.lang.Object

Nonfinal methods

clone()

Final methods

finalize()

equals()

toString()

getClass()
notifyAll()

notify()
wait()

hashCode()
Figure 1.22

Categorization of final and nonfinal methods of class java.lang.Object

Licensed to Mark Watson 

52

CHAPTER 1 Java class design

code of the object it represents. Following is the code of method toString(), as
defined in class Object in the Java API:
toString() as
defined in
java.lang.Object

public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

Following is an example of class Book, which doesn’t override method toString().
In this case, a request to print the reference variable of this class will call method
toString() defined in class Object:
class Book {
String title;
}
class PrintBook {
public static void main(String[] args) {
Book b = new Book();
System.out.println(b);
}
}

Prints a value similar
to Book@45a877

Let’s override method toString() in class Book. The contract of method toString()
specifies that it should return a concise but informative textual representation of the
object that it represents. This is usually accomplished by using the value of the instance
variables of an object:
class Book {
String title;
toString() uses title
@Override
to represent Book
public String toString() {
return title;
}
}
class Test {
public static void main(String[] args) {
Book b = new Book();
Prints book title,
b.title = "Java Certification";
“Java Certification”
System.out.println(b);
}
}

If a class defines a lot of instance variables, method toString()might include only the
important ones—that is, the ones that provide its concise description. In the following
example, class Book defines multiple instance variables and uses a few of them in
method toString():
class Book {
String title;
String isbn;
String[] author;
java.util.Date publishDate;

Instance
variables to
store a Book’s
state

Licensed to Mark Watson 

53

Overriding methods of class Object

toString
uses title,
isbn, and the
first element
of array
author to
describe
a Book.

double price;
int version;
String publisher;
boolean eBookReady;
@Override
public String toString() {
return title + ", ISBN:"+isbn +
}

Instance variables
to store a Book’s
state

", Lead Author:"+author[0];

}
class Test {
public static void main(String[] args) {
Book b = new Book();
b.title = "Java Smart Apps";
b.author = new String[]{"Paul", "Larry"};
b.isbn = "9810-9643-987";
System.out.println(b);
}
}

Prints “Java Smart
Apps, ISBN:9810-9643987, Lead Author:Paul”

You have overridden method toString() inappropriately if it returns any text that’s specific to a particular class, for example, the name of a class or a value of a static variable:
class Book {
String title;
static int bookCopies = 1000;
@Override
public String toString() {
Overridden
return title + ", Copies:" + bookCopies;
toString() uses static
}
variable of Book.
}
class CourseBook extends Book {
static int bookCopies = 99999;
Static variable
}
bookCopies also
class BookOverrideToString {
defined in CourseBook
public static void main(String[] args) {
CourseBook b = new CourseBook();
b.title = "Java Smart Apps";
System.out.println(b);
Prints “Java Smart
}
Apps, Copies:1000”
}

b

c

d

In this code, B shows inappropriate overriding of method toString() because it uses
a static variable. The code at c defines a static variable bookCopies in class CourseBook. Because static members are bound at compile time, method toString() will refer
to the variable bookCopies defined in class Book, even if the object it refers to is of the
type CourseBook. d prints the value of the static variable defined in class Book.
Overriding methods of class Object is an important concept. Let it sink in. The
next “Twist in the Tale” exercise will ensure that you get the hang of correct overriding of method toString(), before moving on to the next section.

Licensed to Mark Watson 

54

CHAPTER 1 Java class design

Twist in the Tale 1.4

Which of the following classes—Book1, Book2, Book3, or Book4—shows an appropriate
overridden method toString()?
class Book1 {
String title;
int copies = 1000;
public String toString() {
return "Class Book, Title: " + title;
}
}
class Book2 {
String title;
int copies = 1000;
public String toString() {
return ""+copies * 11;
}
}
class Book3 {
String title;
int copies = 1000;
public String toString() {
return title;
}
}
class Book4 {
String title;
int copies = 1000;
public String toString() {
return getClass().getName() + ":" + title;
}
}

1.4.2

Overriding method equals()
Method equals() is used to determine whether two objects of a class should be considered equal or not. Figure 1.23 shows a conversation between two objects, wondering whether they’re equal or not.

Are we
equal?

equals()

knows it
better!

Figure 1.23 Applying a twist on
Shakespeare’s quote: “Equal or not
equal, that is the question.”
Method equals() returns a
boolean value that determines
whether two objects should be
considered equal or not.

Licensed to Mark Watson 

Overriding methods of class Object

55

The default implementation of method equals() in class Object compares the object
references and returns true if both reference variables refer to the same object, or
false otherwise. In essence, it only returns true if an object is compared to itself. Following is the default implementation of method equals() in class java.lang.Object:
public boolean equals(Object obj) {
return (this == obj);
}

The exam will question you on the following points:
■
■
■

The need to override method equals()
Overriding method equals() correctly
Overriding method equals() incorrectly

THE NEED TO OVERRIDE METHOD EQUALS()
You need to override method equals() for objects that you wish to equate logically,

which normally depends on the state of an object (that is, the value of its instance variables). The goal of overriding method equals() is to check for equality of the objects,
not to check for the same variable references. For two objects of the same class, say,
object1 and object2, equals() checks whether object1 is logically equal to object2,
but object1 isn’t necessarily pointing to the exact same object as object2.
For example, class String overrides method equals() to check whether two
String objects define the exact same sequence of characters:
String name1 = "Harry";
String name2 = new String ("Harry");
System.out.println(name1.equals(name2));

Prints
“true”

In the preceding code, name1 and name2 refer to separate String objects but define the
exact same sequence of characters—"Harry". So name1.equals(name2) returns true.
AN EXAMPLE

You might need to find out whether the same undergraduate course is or is not offered
by multiple universities. In an application, you can represent a university using a class,
say, University, and each course being offered using a class, say, Course. Assuming that
each university offers a list of courses, you can override method equals() in class
Course to determine if two Course objects can be considered equal, as follows:
class Course {
String title;
int duration;
public boolean equals(Object o) {
if (o != null && o instanceof Course) {
Course c = (Course)o;
return (title.equals(c.title) && duration==c.duration);
}
else
return false;
}
}

Licensed to Mark Watson 

56

CHAPTER 1 Java class design

RULES FOR OVERRIDING METHOD EQUALS()
Method equals() defines an elaborate contract (set of rules), as follows (straight

from the Java API documentation):
1

2

3

4

5

It’s reflexive—For any non-null reference value x, x.equals(x) should return true.
This rule states that an object should be equal to itself, which is reasonable.
It’s symmetric—For any non-null reference values x and y, x.equals(y) should
return true if and only if y.equals(x) returns true. This rule states that two
objects should be comparable to each other in the same way.
It’s transitive—For any non-null reference values x, y, and z, if x.equals(y)
returns true and y.equals(z) returns true, then x.equals(z) should return
true. This rule states that while comparing objects, you shouldn’t selectively
compare the values based on the type of an object.
It’s consistent—For any non-null reference values x and y, multiple invocations of
x.equals(y) consistently return true or consistently return false, provided no
information used in equals comparisons on the objects is modified. This rule states
that method equals() should rely on the value of instance variables that can be
accessed from the memory and shouldn’t try to rely on values like the IP address of
a system, which may be assigned a separate value upon reconnection to a network.
For any non-null reference value x, x.equals(null) should return false. This
rule states that a non-null object can never be equal to null.

Quite a lot of rules to remember! Let’s use an interesting way to remember all these
rules, by comparing equals() to love. So when you see “x.equals(x),” read it as
“x.loves(x).” Read “if x.equals(y) returns true, y.equals(x) must return true”
as “if x loves y, y loves x.” All these rules are shown in figure 1.24. They’ll make more
sense when you cover them using these examples.
CORRECT AND INCORRECT OVERRIDING OF METHOD EQUALS()
To override method toString() correctly, follow the method overriding rules defined
in section 1.3. Note that the type of parameter passed to equals() is Object. Watch
out for exam questions that seem to override equals(), passing it to a parameter type
of the class in which it’s defined. In the following example, class Course doesn’t override method equals(), it overloads it:
class Course {
String title;
Course(String title) {
this.title = title;
}
public boolean equals(Course o) {
return title.equals(o.title);
}
public static void main(String args[]) {
Object c1 = new Course("eJava");
Object c2 = new Course("eJava");
System.out.println(c1.equals(c2));
}
}

Course doesn’t override
toString(), it overloads it.

Prints
“false”

Licensed to Mark Watson 

57

Overriding methods of class Object

I love myself!
x.equals(x) is true

x

If
If I love you...

I love you!

if x.equals(y)==true
then y.equals(x)
should be true

x

y

IfIf I love y...

If
If I love z ...

x loves me!

if x.equals(y)==true
and if y.equals(z)==true
then x.equals(z)
should be true
x

y

z

If we love each other now...
we’ll always love each other.
if x.equals(y)==true
then it is always true

x

y

I cannot love null!
if (x != null)
then x.equals(null) is false

x

Figure 1.24
with love.

A fun way to remember all the rules of the equals() contract by comparing equals()

Licensed to Mark Watson 

58

CHAPTER 1 Java class design

Use Object as the parameter type to equals(). Using any other
type will overload equals().

EXAM TIP

APPROPRIATE AND INAPPROPRIATE OVERRIDING OF METHOD EQUALS()
If you don’t follow the contract of method equals() while overriding it in your

classes, you’ll be overriding it inappropriately. An inappropriately overridden method
equals() doesn’t mean compilation failure.

An inappropriately overridden method equals() doesn’t mean
compilation failure.

EXAM TIP

In the following code, class Course doesn’t comply with the symmetric and reflexive
rules while overriding method equals(). Class University shows how these rules
aren’t adhered to:
class Course {
String title;
Course(String title) {
this.title = title;
}
public boolean equals(Object o) {
return (title.equals(o));
}
}
class University {
public static void main(String[] args) {
Course c1 = new Course("level1");
String s1 = "level1";
System.out.println(c1.equals(s1));
System.out.println(s1.equals(c1));

Compares title of
course with object
passed to equals

b

System.out.println(c1.equals(c1));
}
}

c

Shows violation of
symmetric rule—
c1.equals(s1) prints
“true” but s1.equals(c1)
prints “false”

Shows violation of reflexive rule—
c1.equals(c1) prints “false”

The code at B prints true for c1.equals(s1) and false for s1.equals(c1), which is
a clear violation of equals()’s symmetric contract, which states that for any non-null
reference values x and y, x.equals(y) should print true if and only if y.equals(x)
returns true. The Course object will not evaluate to true in String’s method
equals() because equals() in String first verifies if the object being compared to it
is a String object before checking to see if their character sequence is the same. At c,
c1.equals(c1) prints false, violating the reflexive rule that states that an object
should be equal to itself.
Let’s work with another example, where class JavaCourse violates the transitive
rule while overriding method equals(). Class JavaCourse extends class Course and
defines method equals(), which compares its object to both an object of Course
and JavaCourse. Method equals() compares the common attributes, if the object
being compared is that of the base class Course. It also compares all the attributes, if
the object being compared is of class JavaCourse:

Licensed to Mark Watson 

Overriding methods of class Object

59

class Course {
String title;
Course(String title) {
this.title = title;
}
public boolean equals(Object o) {
if (o instanceof Course) {
Course c = (Course)o;
return (title.equals(c.title));
}
else
return false;
}
}
class JavaCourse extends Course {
int duration = 0;
JavaCourse(String title, int duration) {
super(title);
this.duration = duration;
}
public boolean equals(Object o) {
if (o instanceof JavaCourse) {
return (super.equals(o) &&
((JavaCourse)o).duration == duration);
}
else if(o instanceof Course) {
return (super.equals(o));
}
else
return false;
}
}

In the following code for class University2, c1 is equal to c2 and c2 is equal to c3
because these comparisons only check the course title. But c1 isn’t equal to c3
because the course durations aren’t the same. Therefore, the overridden method
equals() in class JavaCourse fails the transitive rule:
class University2
public static
Course c1
Course c2
Course c3

{
void main(String[] args) {
= new JavaCourse("level1", 2);
= new Course("level1");
= new JavaCourse("level1", 12);

System.out.println(c1.equals(c2));
System.out.println(c2.equals(c3));
System.out.println(c1.equals(c3));
}
}

Inappropriate overriding of method equals() can result in bizarre behavior. Use of
equals() by collection classes is explained in detail in chapter 4.

Licensed to Mark Watson 

60

CHAPTER 1 Java class design

The programmers
claim you’re in a
relationship and often call
each other.

Interviewer
Figure 1.25

1.4.3

No!! We don’t even
have each other’s
phone number.

equals()

hashCode()

Methods hashCode() and equals() don’t call each other.

Overriding method hashCode()
First, method hashCode() isn’t called by method equals() or vice versa. The contract
of methods equals() and hashCode() mentions that both these methods should be
overridden if one of them is overridden. This makes a lot of programmers believe that
perhaps these methods are called by each other, which isn’t the case. Figure 1.25
shows a fun way to remember that methods equals() and hashCode() deny being in a
relationship and calling each other.
THE NEED TO OVERRIDE METHOD HASHCODE()
Method hashCode() returns a hash-code value for an object, which is used to effi-

ciently store and retrieve values in collection classes that use hashing algorithms, such
as HashMap. Hashing algorithms identify the buckets in which they would store the
objects and from which they would retrieve them. A well-written method hashCode()
ensures that objects are evenly distributed in these buckets. Objects with the same
hash-code values are stored in the same bucket. To retrieve an object, its bucket is
identified using its hash-code value. If the bucket contains multiple objects, method
equals() is used to find the target object.
To understand how this works, let’s create a class called MyNumber, which contains a
primitive long as its field. It returns a sum of all the individual digits of its field as its
method hashCode(), as follows:
class MyNumber {
long number;
MyNumber(long number) {this.number = number;}
public int hashCode() {
int sum = 0;
long num = number;
do {
sum += num % 10; num /= 10;
}
while( num != 0 );
return sum;
}
}

Licensed to Mark Watson 

Overriding methods of class Object

61

Let’s assume you add the following keys and values in a HashMap:
Map map = new HashMap<>();
value 3
= new MyNumber(1200);
= new MyNumber(2500);
Hash-code
= new MyNumber(57123);
value 7
"John");
"Mary");
Hash-code
"Sam");
value 18

With the preceding keys, each bucket contains only one entry. When you request the
HashMap to retrieve a value, it would find the corresponding bucket using the key’s
hash-code value and then it retrieves the value. Now let’s add another key-value pair:
MyNumber num4 = new MyNumber(57123);
map.put(num4, "Kim");

Hash-code
value 18

Now the bucket with the hash-code value 18 has two String values. In this case, HashMap would use the hashCode() value to identify the bucket and then call method
equals() to find the correct object. This explains why distinct hash-code values for
distinct values are preferred.
Chapter 4 explains in detail how the hashing algorithms in collection classes use methods hashCode() and equals().

NOTE

OVERRIDING METHOD HASHCODE() CORRECTLY
Here’s the signature of method hashCode() as defined in class Object:
public native int hashCode();

To correctly override method hashCode(), you must follow the rules already discussed
in section 1.3. Watch out for exam questions that use the incorrect case for hashCode()—the correct name uses uppercase C. Figure 1.26 shows a fun way to remember this simple, but important, exam point.

I take all coding
seriously! That’s why
Code starts with capital
C in hashCode().

hashCode()

Figure 1.26 The correct case
of hashCode() includes a
capital C.

Licensed to Mark Watson 

62

CHAPTER 1 Java class design

If we are in
love… we must
must reside
at the same address.

if x.equals(y)==true
x.hashCode()==
y.hashCode()
must be true

Before
marriage

y

x

If we are not
not
in love… we might
might
or might not reside
at the same address.

if x.equals(y)==false
x.hashCode() and
y.hashCode() can be
same or different

x

Post
breakup....

y

Figure 1.27 Comparing equals() with being in love and hashCode() with an address. If two
objects are equal, they must return the same hashCode(). But if two objects return the same
hashCode(), they might not be equal.

To override method hashCode() correctly, you must also abide by its contract, as mentioned in the Java documentation. For the exam, the following rules are important:
1

2

If two objects are equal according to method equals(Object), then calling
method hashCode() on each of the two objects must produce the same integer result.
It’s not required that if two objects are unequal according to method equals
(java.lang.Object), that calling method hashCode() on each of the two objects
must produce distinct integer results.

Let’s use a fun analogy to remember these rules, as shown in figure 1.27. Let’s compare method equals() to being in love and method hashCode() to a physical address.
When two objects are in love with each other, they must reside at the same address
(this is what they think before they marry). Later, if they fall out of love, they might or
might not continue to reside at the same address.
Figure 1.27 will make more sense as you work with the code examples in this section.
Let’s revisit the previous example, including equals() and verifying the rules:
class MyNumber {
long number;
MyNumber(long number) {this.number = number;}

Licensed to Mark Watson 

Overriding methods of class Object
public int hashCode() {
int sum = 0;
long num = number;
do {
sum += num % 10; num /= 10;
}
while( num != 0 );
return sum;
}
public boolean equals(Object o) {
if (o != null && o instanceof MyNumber)
return (number == ((MyNumber)o).number);
else
return false;
}
public static void main(String args[]) {
MyNumber n1 = new MyNumber(9);
MyNumber n2 = new MyNumber(18);
MyNumber n3 = new MyNumber(18);
System.out.println
(n1.equals(n2)+":"+n1.hashCode()+":"+n2.hashCode());
System.out.println
(n2.equals(n3)+":"+n2.hashCode()+":"+n3.hashCode());
}
}

63

Prints
“false:9:9”
Prints
“true:9:9”

The preceding code abides by both the rules of the hashCode() contract, when
n2.equals(n3) returns true, n2.hashCode() and n3.hashCode() return the same
value. But when n1.equals(n2) returns false, n1.hashCode() and n2.hashCode()
might not return distinct values.
Let’s modify the preceding code, so that hashCode() returns distinct values when
equals() returns false:
class MyNumber {
long number;
MyNumber(long number) {this.number = number;}
public int hashCode() {
return (int)number;
}
public boolean equals(Object o) {
if (o != null && o instanceof MyNumber)
return (number == ((MyNumber)o).number);
else
return false;
}
public static void main(String args[]) {
MyNumber n1 = new MyNumber(9);
MyNumber n2 = new MyNumber(18);
MyNumber n3 = new MyNumber(18);
System.out.println
(n1.equals(n2)+":"+n1.hashCode()+":"+n2.hashCode());
System.out.println
(n2.equals(n3)+":"+n2.hashCode()+":"+n3.hashCode());
}
}

Licensed to Mark Watson 

Prints
“false:9:18”
Prints
“true:18:18”

64

CHAPTER 1 Java class design

NOTE Using a system-dependent value (like a memory address) is allowed

in hashCode(). But objects of such classes can’t be used as keys in distributed systems because equal objects (across systems) will return different hash-code values.
OVERRIDING METHOD HASHCODE() INAPPROPRIATELY

Inappropriate overriding isn’t the same as incorrect overriding—the former won’t fail
compilation but can have issues with object retrieval. On the exam, watch out for
questions that will show code for hashCode(), equals(), or both, and query what happens when the class instances are used as keys in collection classes, like HashMap. In
this section, you’ll work with examples that override hashCode() correctly—syntactically, but not appropriately.
In the previous section you learned why it’s important for method hashCode() that
two objects return the same value, if they’re equal as per method equals(). Failing
this condition, an object value will never be able to be retrieved from a HashMap. Let’s
see what happens when class MyNumber doesn’t return the same hashCode() values for
its equal objects:
class MyNumber {
int primary, secondary;
MyNumber(int primary, int secondary) {
this.primary = primary;
Doesn’t print
this.secondary = secondary;
same hashCode()
}
value for equal
public int hashCode() {
objects
return secondary;
}
public boolean equals(Object o) {
if (o != null && o instanceof MyNumber)
return (primary == ((MyNumber)o).primary);
else
return false;
}
public static void main(String args[]) {
Prints “true”—
Map map = new HashMap<>();
objects num1
MyNumber num1 = new MyNumber(2500, 100);
and num2 are
MyNumber num2 = new MyNumber(2500, 200);
considered equal.
System.out.println(num1.equals(num2));
map.put(num1, "Shreya");
System.out.println(map.get(num2));
Prints
}
“null”
}

b

c

d

In the preceding code, even though the code at c prints true, confirming that
objects num1 and num2 are considered equal by equals(), the code at d prints null.
The reason for this? The hashCode() in MyNumber doesn’t return the same values for
its equal objects. In method hashCode(), the code at B uses secondary to calculate its
value, which isn’t used by equals().

Licensed to Mark Watson 

65

Overriding methods of class Object

Another rule of method hashCode() is that when it’s invoked on the same object
more than once during the execution of a Java application, hashCode() must consistently return the same integer, provided no information used in the equals() comparisons on the object is modified. This integer doesn’t need to remain consistent
from one execution of an application to another execution of the same application.
Let’s see what happens when hashCode() doesn’t return the same integer value
when it’s invoked on the same instance during the execution of a Java application:
class MyNumber {
int number;
MyNumber(int number) {this.number = number;}
public int hashCode() {
return ((int)(Math.random() * 100));
}
public boolean equals(Object o) {
if (o != null && o instanceof MyNumber)
return (number == ((MyNumber)o).number);
else
return false;
}
public static void main(String args[]) {
Map map = new HashMap<>();
MyNumber num1 = new MyNumber(2500);
map.put(num1, "Shreya");
System.out.println(map.get(num1));
}
}

Prints random hashcode values on each
invocation

Prints “null”
(most probably)

In the preceding code, when you add key-value num1, "Shreya" to HashMap, you most
likely won’t be able to retrieve Shreya using the same key, num1. This is because each
call to num1.hashCode() might return a different value (the chances of returning the
same hashCode() values aren’t ruled out, but are very low).
INEFFICIENT OVERRIDING OF HASHCODE()

In real projects, always strive for generating distinct values in hashCode(). Distinct
hashCode() values and faster object access are directly related in collection objects
that use hashing functions to retrieve and store values. Here’s an example of inefficient overriding of method hashCode():
class MyNumber {
long number;
MyNumber(long number) {this.number = number;}
public int hashCode() {
return 1654;
}
}

In the preceding code, method hashCode() returns the same hash-code value for all
the objects of MyNumber. This essentially stores all the values in the same bucket, if

Licensed to Mark Watson 

66

CHAPTER 1 Java class design

objects of the above class are used as keys in class HashMap (or in similar classes that
use hashing), and reduces it to a linked list, drastically reducing its efficiency.
Read the questions on method hashCode() carefully. You
might be questioned on incorrect, inappropriate, or inefficient overriding
of hashCode().

EXAM TIP

EFFECTS OF USING MUTABLE OBJECTS AS KEYS

Java recommends using immutable objects as keys for collection classes that use the hashing algorithm. What if you don’t? The exam might query you on this important question.
Revisiting the example used in the previous section, what happens if the value of
the field number is changed during the course of the application? In this case, you’ll
never be able to retrieve the corresponding value in the HashMap, because the HashMap
will not be able to look for the right bucket:
class MyNumber {
int number;
MyNumber(int number) {this.number = number;}
public int hashCode() {
return number;
}
public boolean equals(Object o) {
if (o != null && o instanceof MyNumber)
return (number == ((MyNumber)o).number);
else
return false;
}
public static void main(String args[]) {
Map map = new HashMap<>();
MyNumber num1 = new MyNumber(2500);
map.put(num1, "Shreya");
num1.number = 100;
System.out.println(map.get(num1));
}
}

Add value Shreya
to HashMap using
key num1.
Modify field number
of key num1, which is
used by equals() and
hashCode().
Prints “null”—can’t
locate object with
modified key.

In the preceding code, the field used to determine the hash code of an object is modified in main(). With the modified key, HashMap won’t be able to retrieve its corresponding object.
In the next section, you’ll cover when, why, and how you can cast an instance to
another type and use the instanceof operator.

1.5

Casting and the instanceof operator
[1.4] Use the instanceof operator and casting
Imagine that you enroll yourself for flying classes, where you expect to be trained by
an experienced pilot. Even though your trainer might also be a swimming champion,

Licensed to Mark Watson 

Casting and the instanceof operator

67

you need not know about it. You need not care about the characteristics and behavior
that’s not related to flying. Now think of a situation when you do care about the swimming skills of your instructor. Imagine that when you’re attending the flying classes,
your friend enquires whether your flying instructor also conducts swimming classes and,
if yes, whether she would be willing to assist your friend. In this case, a need arises to
enquire about the swimming skills (additional existing skills) of your flying instructor.
Similarly, in Java, you can refer to an object of a derived class using a reference
variable of its base class or implemented interface. But you might need to access the
members of the derived class, which aren’t defined in its base class or the implemented interface. Here’s when casting can help. Casting shows how an object of a
type can be used as an object of another type, either implicitly or explicitly. The
instanceof operator is used to logically test whether an object is a valid type of a
class or an interface.

1.5.1

Implicit and explicit casting
Let’s start with the definitions of the interface Printable and classes ShoppingItem
and Book to show implicit and explicit casting. Class Book extends class ShoppingItem
and implements the interface Printable as follows:
public interface Printable {
void print();
}
public class ShoppingItem {
public void description() {
System.out.println("Shopping Item");
}
}
public class Book extends ShoppingItem implements Printable {
public void description() {
System.out.println("Book");
}
public void print() {
System.out.println("Printing book");
}
}

Figure 1.28 shows the inheritance relationship between these classes.

Printable
ShoppingItem

Implements
<>
Book

Figure 1.28 Relationship between
classes ShoppingItem and Book
and the interface Printable

Licensed to Mark Watson 

68

CHAPTER 1 Java class design

Now let’s create variables of type Printable and ShoppingItem and assign to them
objects of the type Book:
class Shopping {
public static void main(String args[]) {
Book book = new Book();
Printable printable = book;
printable.print();
ShoppingItem shoppingItem = book;
shoppingItem.description();
}

b

c

Implicit
casting

Acceptable

}

The code at B shows how an object of type book is implicitly referred to, or casted to,
type Printable. The code at c shows how an object of type book is implicitly referred
to, or casted to, type ShoppingItem. Objects of subclasses can be implicitly casted to
their base classes or the interfaces that they implement.
As shown in the preceding code block for the class Book, you can see that Book
defines a method description(). Let’s try to access it using the printable variable:
class Shopping {
public static void main(String args[]) {
Printable printable = new Book();
printable.description();
}
}

b

Won’t compile—can’t
access method
description() in Printable.

The code at B fails to compile with the following message:
Shopping.java:4: error: cannot find symbol
printable.description();
^
symbol:
method description()
location: variable printable of type Printable
1 error

Because the type of the reference variable printable is Printable, the compiler refers
to the definition of the interface Printable when you call method description() on
printable. Figure 1.29 shows what happens behind the scenes.
But you know that the actual object is of type Book. Is there a way to treat the reference variable printable as a Book? Yes, there is! You need to inform the compiler you
know what you’re doing by using an explicit cast, as follows (see also figure 1.30):
class Shopping {
public static void main(String args[]) {
Printable printable = new Book();
((Book)printable).description();
}
}

Licensed to Mark Watson 

Casting and the instanceof operator

I just consulted interface

Printable.It doesn’t define
method description().

printable.description();
In

interface Printable{
public void print();
}

Java
compiler

Consult
Compilation
error

Out

Figure 1.29 The Java compiler doesn’t compile code if you try to access description(), defined
in class Book, by using a variable of the interface Printable.

Okay! So now I must consult class
Book to determine the existence
of method description().
((Book)printable).description();

In

Java
compiler

Consult

Out

class Book extends ShoppingItem
implements Printable{
public void description(){
....
}
....
....
....
}

Compilation
successful

Figure 1.30 Explicit casting can be used to access description() defined in class Book by
using a variable of the interface Printable.

Licensed to Mark Watson 

69

70

CHAPTER 1 Java class design

interface Printable {
void print();
}
class ShoppingItem {
public void description() {
System.out.println("Shopping Item");
}
}
class Chair extends ShoppingItem {
public void description() {
System.out.println("Chair");
}
}
class Book extends ShoppingItem
implements Printable {
public void description() {
System.out.println("Book");
}
public void print() {
System.out.println("Printing book");
}
}

Figure 1.31

Printable
ShoppingItem

<>

<>

Chair

Implements

Book

Set of classes and interfaces, with their UML representation

In the preceding code, (Book) is placed just before the name of the variable, printable,
to cast it to Book. Note how a pair of parentheses surrounds (Book)printable. Casting
in this line of code is another method of telling the compiler that you know that the
actual object being referred to is Book, even though you’re using a reference variable
of type Printable.

1.5.2

Combinations of casting
To work with a combination of casting, let’s work with a set of classes and interfaces, as
shown in figure 1.31.
ASSIGNMENTS WITH IMPLICIT CASTING

Implicit upcasting is allowed. You can assign a reference variable of a derived class to a
reference variable of its own type, its base classes, and the interfaces that it implements
as follows:
public class UpcastWithImplicitCasting {
public static void main(String[] arguments) {
Book book = new Book();
Chair chair = book;
ShoppingItem shoppingItem = book;
Printable printable = book;
Object object = book;

Okay—a
book is
Printable.

Chair chair2 = new Chair();
Printable printable2 = chair;
}
}

Won’t compile—both Book and
Chair extend ShoppingItem, but
don’t belong to a single line of
inheritance.
Okay—a book is
a ShoppingItem.

Okay—a book
is an Object.
Won’t compile—Chair
doesn’t implement Printable.

Licensed to Mark Watson 

71

Casting and the instanceof operator

Implicit downcasting isn’t allowed. You can’t assign reference variables of a base class
to reference variables of its derived classes or to the interfaces that it doesn’t implement. For example

Won’t
compile—a
ShoppingItem
isn’t
necessarily
a chair.

Won’t compile—a
ShoppingItem isn’t
necessarily a book.

public class DowncastWithImplicitCasting {
public static void main(String[] arguments) {
ShoppingItem shoppingItem3 = new ShoppingItem();

Won’t compile—
a ShoppingItem
isn’t Printable.

Book book3 = shoppingItem3;
Chair chair3 = shoppingItem3;
Printable printable3 = shoppingItem3;
Object object3 = shoppingItem3;
}
}

EXAM TIP

Okay—a chair
is an Object.

In the absence of explicit casting, you’ll never get ClassCast-

Exception—a RuntimeException.
ASSIGNMENT WITH EXPLICIT CASTING

Both implicit and explicit upcasting are allowed. So, for the exam, let’s focus on
explicit downcasting.
Java recommends programming to an interface, which implies using reference
variables of a base class or implementing interfaces to refer to the actual objects. But
you might need to cast an object referred by a base class to its specific type. You can
downcast an object to a type that falls in its inheritance tree using explicit casting. For
a nonfinal class, you can explicitly cast its object to any interface type, even if the class
doesn’t implement the interface. Let’s see what happens when you accept a method
parameter of type ShoppingItem and try to cast it explicitly to other types:

public class DowncastWithExplicitCasting {
static void downCast(ShoppingItem item) {
Book book = (Book)item;
Chair chair = (Chair)item;
Printable printable = (Printable)item;
}
public static void main(String args[]) {
ShoppingItem item = new ShoppingItem();
downCast(item);
}
}

b

Compiles with casting—will
throw ClassCastException;
can’t downcast instance of
parent object to subclass type.

c

Compiles with casting—will
throw ClassCastException;
ShoppingItem doesn’t
implement Printable.

The code at B and c compiles with an explicit cast. But its individual lines will fail at
runtime. At runtime, Java can determine the exact type of the object being casted. It
throws a ClassCastException if you’re trying to cast types that aren’t allowed.
NOTE For the exam, you need to be very clear whether an explicit cast will

result in a compilation error or a runtime exception (ClassCastException).

Licensed to Mark Watson 

72

CHAPTER 1 Java class design

Does the preceding code make you wonder why an explicit cast from a ShoppingItem
instance to Printable is permitted, even though ShoppingItem doesn’t implement
Printable? It’s to allow subclasses of ShoppingItem to implement Printable and use
the reference variable of type Printable to refer to its instances. So what happens if
you try to cast a final class’s instance to an interface it doesn’t implement? The code
won’t compile:
interface Printable {}
final class Engineer {}
class Factory {
public static void main(String[] args) {
Engineer engineer = new Engineer();
Printable printable = (Printable)engineer;
}
}

Won’t compile—can’t
cast final class Engineer’s
instance to Printable.

Class String is defined as a final class. Watch out for questions that explicitly cast String objects to interfaces they don’t implement. They won’t compile.

EXAM TIP

What about casting null to a type? You can explicitly cast null to any type without a
compilation error or runtime exception (ClassCastException):
static void castNull() {
Book book = (Book)null;
Chair chair = (Chair)null;
Printable printable = (Printable)null;
}

You can explicitly cast null to any type. It won’t generate a
compilation error or throw a ClassCastException.

EXAM TIP

ACCESS OF MEMBERS WITH EXPLICIT CASTING

You can access methods and variables of explicitly casted variables in single or multiple lines of code:
public class AccesMembersWithExplicitCasting {
static void accessMember(ShoppingItem item) {
Book book = (Book)item;
book.description();
((Book)item).description();
}
}

c

b

Cast a reference variable
and access its method
in multiple steps.

Cast objects and call their
members in a single step.

Here the code at B casts a reference item to Book in one line and then accesses its
method description(). At c, note how the object referred by item is castedenclosed within () to call its member method description(). The inclusion in () is
due to the fact that the dot operator has precedence over the casting parentheses.

Licensed to Mark Watson 

73

Casting and the instanceof operator

If you cast an instance to a class outside its inheritance tree,
you’ll get a compiler error. If you cast an instance to a class within its
inheritance tree, but the types don’t match at runtime, the code will
throw a ClassCastException.

EXAM TIP

Points to remember for casting
■

■

■

■

■

■

An instance can be implicitly casted to its superclasses or interfaces that it
implements.
An instance of a nonfinal class can be explicitly casted to any interface at compile time.
Classes in the same inheritance tree can be casted to each other using explicit
casting at compile time.
Objects of classes that don’t form part of the same inheritance tree cannot
be casted.
Casting to an interface is successful at runtime if the class implements the
interface.
Casting to a derived class type is successful at runtime if the casted object is
actually a type of the derived class to which it’s casted.

In the previous examples, you learned how mismatching of objects and explicit casting can throw a ClassCastException. In the next section, you’ll see how you can prevent this by using the instanceof operator to safely cast objects to a type.

1.5.3

Using the instanceof operator
The instanceof operator is used to logically test whether an object is a valid type of a
class or an interface. You should proceed with explicit casting only if this operator
returns true, or you risk running into a ClassCastException at runtime. For example,
consider equals(), which defines a method parameter of type Object. When you override equals() to determine the equality of objects of your class, you might need to
query the state of the accepted argument before you move forward with an explicit cast:
class Course {
String title;
Course(String t) {title = t;}
public boolean equals(Object obj) {
if (obj instanceof Course) {
Course c = (Course)obj;
return (title.equals(c.title));
}
else
return false;
}
}

b

Use instanceof to
verify if obj is an
instance of Course.

c

Explicitly cast
obj to Course.

The code at B ensures that the type of the accepted method parameter—that is,
obj—is Course, before it moves forward with the explicit casting of obj to Course c.

Licensed to Mark Watson 

74

CHAPTER 1 Java class design

In the absence of this check, the code at c would execute for all non-null method
parameters, which can result in a ClassCastException if the object passed to equals()
isn’t of type Course.
The operator instanceof returns false if the reference variable being compared to is null.

EXAM TIP

In the previous example, the type of method parameter to equals() is Object, which
is the parent class of all classes. But if the instanceof operator uses inconvertible
types, the code won’t compile. In the following example, the instanceof operator
uses a reference variable of type Course to test whether the object that it refers to can
be an instance of class Student. Because Course and Student are unrelated, class Test
won’t compile:
class Course {}
class Student {}
public class TestInstanceof {
public static void main(String[] args) {
Course c = new Course();
Student s = new Student();
System.out.println(c instanceof Student);
}
}

Won’t compile—can’t use
instanceof to compare
inconvertible types.

The instanceof operator never throws a runtime exception; it
returns either true or false. If the instanceof operator uses inconvertible types, the code won’t compile.

EXAM TIP

The instanceof operator is preceded by a value (literal value or a variable name) and
is followed by a class, interface, or enum name. It’s acceptable to use the literal value
null with the instanceof operator:
class Course {
public static void main(String[] args) {
System.out.println(null instanceof Course);
}
}

Prints “false”—null
can’t be an instance
of any class.

The literal value null isn’t an instance of any class. So
 instanceof  will return false whenever the  is null.
EXAM TIP

Using instanceof versus getClass in method equals()
Using instanceof versus getClass is a common subject of debate about proper use
and object orientation in general (including performance aspects, design patterns,
and so on). Though important, this discussion is beyond the scope of this book. If
you’re interested in further details, refer to Josh Bloch’s book Effective Java.

Licensed to Mark Watson 

75

Packages

How would you help
your fans remember
that o is lowercase
in instanceof ?

I’m a Java keyword
and all Java keywords
are lowercase.

Interviewer
Figure 1.32

instanceof
Remember that o in instanceof is lowercase.

Note that o in instanceof is lowercase; take a look at figure 1.32 for a fun way of remembering this.
Next we’ll move forward with defining the Java classes and interfaces in named
packages. This is a common requirement in real Java applications. So let’s get started.

1.6

Packages
[1.7] Use package and import statements
In this section, you’ll learn what Java packages are and how to create them. You’ll use
the import statement, which enables you to use simple names for classes and interfaces defined in separate packages.

1.6.1

The need for packages
You can use packages to group together a related set of enums, classes, and interfaces.
Packages also provide namespace management. You can create separate packages to
define classes for separate projects, such as Android games and online healthcare systems. Further, you can create subpackages within these packages, such as separate subpackages for GUIs, database access, networking, and so on.
In real-life projects, you’ll never work with a package-less class or
interface. Almost all organizations that develop software have strict package-naming rules, which are often documented.

NOTE

If you don’t include an explicit package statement in a class or an interface, it’s part of
a default package.

Licensed to Mark Watson 

76

1.6.2

CHAPTER 1 Java class design

Defining classes in a package using the package statement
You can define classes and interfaces in a package by using the package statement as
the first statement in your class or interface (only comments can precede the package
statement). Here’s an example:
package certification;
class ExamQuestion {
//..code
}

The class in the previous code defines a class ExamQuestion in the certification
package. You can define an interface, MultipleChoice, in a similar manner:
package certification;
interface MultipleChoice {
//..code
}

Figure 1.33 shows the UML representation of the
certification package, with class ExamQuestion and
interface MultipleChoice.
The name of the package in the previous examples
is certification. You may use such names for small
projects that contain only a few classes and interfaces,
but it’s common for organizations to use subpackages
to define all their classes. For example, if folks at
Oracle define a class to store exam questions for a Java
Associate exam, they might use the package name
com.oracle.javacert.associate. For subpackages, the
package statement includes the complete package name.
Figure 1.34 shows its UML representation along with
the corresponding class definition.

certification

ExamQuestion

MultipleChoice

Figure 1.33 A UML
representation of the
certification package,
class ExamQuestion, and
interface MultipleChoice

NOTE A fully qualified name for a class or interface is formed by prefixing its name with its package name (separated by a period). The fully
qualified name of the ExamQuestion class is certification.ExamQuestion in figure 1.33 and com.oracle.javacert.associate.ExamQuestion in figure 1.34.

package com.oracle.javacert.associate;
class ExamQuestion {
// variables and methods
}

Figure 1.34

com.oracle.javacert.associate

ExamQuestion

A subpackage and its corresponding class definition

Licensed to Mark Watson 

Packages

77

Rules to remember about packages
Here are a few important rules about packages:
■
■
■
■

■

■

Per Java naming conventions, package names should all be in lowercase.
The package and subpackage names are separated using a dot (.).
Package names follow the rules defined for valid identifiers in Java.
For packaged classes and interfaces, the package statement is the first statement in a Java source file (a .java file). The exception is that comments can
appear before or after a package statement.
There can be a maximum of one package statement per Java source code file
(.java file).
All the classes and interfaces defined in a Java source code file will be defined
in the same package. There’s no way to define them in different packages.

DIRECTORY STRUCTURE AND PACKAGE HIERARCHY

The hierarchy of the classes defined in packages should match the hierarchy of the
directories in which these classes and interfaces are defined in the code. For example,
class ExamQuestion in the certification package should be defined in a directory
with the name certification. The name of the certification directory and its location
are governed by the rules shown in figure 1.35.

This can be any directory.
This structure should match the
package hierarchy, certification.

Figure 1.35

Matching directory structure and package hierarchy

For the package example shown in figure 1.35, note that there isn’t any constraint on
the location of the base directory in which the directory structure is defined. Examine
figure 1.36.

This can be any directory.
This structure should
match the package hierarchy,
com.oracle.javacert.associate.

Figure 1.36 There’s no constraint on the location of the base
directory to define directories corresponding to package hierarchy.

Licensed to Mark Watson 

78

CHAPTER 1 Java class design

SETTING THE CLASS PATH FOR CLASSES IN PACKAGES

To enable the JRE to find your classes, interfaces, and enums defined in packages, add
the base directory that contains your Java code to the class path.
For example, to enable the JRE to locate the certification.ExamQuestion class
from the previous examples, add the directory C:\MyCode to the class path. To enable
the JRE to locate class com.oracle.javacert.associate.ExamQuestion, add the
directory C:\ProjectCode to the class path.
You don’t need to bother setting the class path if you’re working with an integrated
development environment (IDE). But I strongly encourage you to learn how to work
with a simple text editor and how to set a class path. This can be particularly helpful
with your projects at work. I’ve also witnessed many interviewers querying candidates
on the need for class paths.

1.6.3

Using simple names with import statements
The import statement enables you to use simple names instead of using fully qualified
names for classes and interfaces defined in separate packages. Let’s work with an example, in which classes LivingRoom and Kitchen are defined in the package home and
classes Cubicle and ConferenceHall are defined in the package office. Class Cubicle
uses (is associated to) class LivingRoom in the package home, as shown in figure 1.37.
home

office

LivingRoom

Cubicle

Kitchen

ConferenceHall

Figure 1.37 A UML
representation of
classes LivingRoom
and Cubicle, defined
in separate packages,
with their associations

Class Cubicle can refer to class LivingRoom without using an import statement:
package office;
class Cubicle {
home.LivingRoom livingRoom;
}

For no import statement, use fully qualified
name to refer to LivingRoom from package home

Class Cubicle can use the simple name for class LivingRoom by using the import
statement:
package office;
import home.LivingRoom;

Import
statement

class Cubicle {
LivingRoom livingRoom;
}

No need to use fully qualified
name of LivingRoom

Licensed to Mark Watson 

79

Packages
NOTE The import statement doesn’t embed the contents of the imported
class in your class, which means that importing more classes doesn’t
increase the size of your own class. It lets you use the simple name for a
class or interface defined in a separate package.

1.6.4

Using packages without using the import statement
Classes in the java.lang package are automatically imported in all the Java classes,
interfaces, and enums. To use simple names for classes, interfaces, and enums from
other packages, you should use the import statement. It’s possible to use a class or
interface from a package without using the import statement by using its fully qualified name:
Missing import
statement
class AnnualExam {
certification.ExamQuestion eq;
}

Define a variable of ExamQuestion
by using its fully qualified name.

But using a fully qualified class name can clutter your code if you use multiple variables of interfaces and classes defined in other packages. Don’t use this approach in
real projects.
For the exam, it’s important to note that you can’t use the import statement to use
multiple classes or interfaces with the same names from different packages. For example, the Java API defines class Date in two commonly used packages: java.util and
java.sql. To define variables of these classes in a class, use their fully qualified names
with the variable declaration:
Missing import
statement
class AnnualExam {
java.util.Date date1;
java.sql.Date date2;
}

Variable of type
java.sql.Date

Variable of type
java.util.Date

An attempt to use an import statement to import both these classes in the same class
will not compile:
import java.util.Date;
import java.sql.Date;
class AnnualExam { }

Code to import classes with same name
from different packages won’t compile

In the preceding code, you want to use a shortcut (Date) but your shortcut refers to
either java.util.Date or java.sql.Date. So the Java compiler has no way of
knowing which is which (both have Date as their simple name), therefore the compiler error.

Licensed to Mark Watson 

80

CHAPTER 1 Java class design

certification

ExamQuestion

MultipleChoice

1.6.5

Figure 1.38 A UML representation
of the certification package

Importing a single member versus all members of a package
You can import either a single member or all members (classes and interfaces) of a package using the import statement. First, revisit the UML notation of the certification
package, as shown in figure 1.38.
Examine the following code for class AnnualExam:
Imports only
ExamQuestion

import certification.ExamQuestion;
class AnnualExam {
ExamQuestion eq;
MultipleChoice mc;
Will not
}
compile

Compiles
okay

By using the wildcard character, an asterisk (*), you can import all of the public members, classes, and interfaces of a package. Compare the previous class definition with
the following definition of class AnnualExam:
import certification.*;
class AnnualExam {
ExamQuestion eq;
MultipleChoice mc;
}

Imports all classes and
interfaces from certification
Also compiles
okay

Compiles
okay

When you work with an IDE, it may automatically add import statements for classes
and interfaces that you reference in your code.

1.6.6

The import statement doesn’t import the whole package tree
You can’t import classes from a subpackage by using an asterisk in the import statement. For example, the UML notation in figure 1.39 depicts the package com.oracle
.javacert with class Schedule and two subpackages, associate and webdeveloper.
The associate package contains class ExamQuestion, and the webdeveloper package
contains class MarkSheet.
The following import statement will import only the Schedule class; it won’t
import classes ExamQuestion and MarkSheet:
import com.oracle.javacert.*;

Imports
Schedule only

Licensed to Mark Watson 

81

Packages

com.oracle.javacert

Schedule
associate

ExamQuestion

webdeveloper

MarkSheet

Figure 1.39 A UML representation
of the com.oracle.javacert
package and its subpackages

Similarly, the following import statement will import all the classes from the associate and webdeveloper packages:
import com.oracle.javacert.associate.*;
import com.oracle.javacert.webdeveloper.*;

1.6.7

Imports
ExamQuestion only
Imports
MarkSheet only

Importing classes from the default package
What happens if you don’t include a package statement in your class or interface? In
this case, they become part of a default, no-name package. This default package is automatically imported in the Java classes and interfaces defined within the same directory
on your system.
For example, classes Person and Office, which aren’t defined in an explicit package, can use each other if they’re defined in the same directory:
class Person {
// code
}
class Office {
Person p;
}

Not defined in an
explicit package
Person accessible
in Office

Members of a named package can’t access classes and interfaces defined in the default package.

EXAM TIP

1.6.8

Static imports
You can import an individual static member of a class or an interface, or all its static
members, by using the import static statement. Though accessible using an instance,
the static members are usually accessed by prefixing their name with the class or interface names. By using static import, you can drop the prefix and just use the name of
the static variable or method.

Licensed to Mark Watson 

82

CHAPTER 1 Java class design

In the following code, class ExamQuestion defines a public static variable named
marks and a public static method named print():
package certification;
public class ExamQuestion {
static public int marks;
public static void print() {
System.out.println(100);
}
}

Public static
variable marks
Public static
method print()

Variable marks can be accessed in class AnnualExam using the import static statement. The order of the keywords import and static can’t be reversed:
package university;
import static certification.ExamQuestion.marks;
class AnnualExam {
AnnualExam() {
Access variable marks
marks = 20;
without prefixing it
}
with its class name
}

EXAM TIP

Correct statement is
import static, not
static import

This feature is called static imports, but syntax is import static.

To use all public and static members of class ExamQuestion in class AnnualExam without importing each of them individually, you can use an asterisk with the import
static statement:
package university;
Imports all static
import static certification.ExamQuestion.*;
members of
ExamQuestion
class AnnualExam {
AnnualExam() {
marks = 20;
Uses marks and print() without
print();
prefixing them with their class names
}
}

Because variable marks and method print() are public, they’re accessible to class
AnnualExam. By using import static you don’t have to prefix them with their class
name. But if they were defined using any other access modifier, they wouldn’t be
accessible in AnnualExam because both these classes are defined in separate packages
and AnnualExam doesn’t inherit ExamQuestion.

1.7

Summary
This chapter covers the basic building blocks of the Java class design, starting with
access modifiers, and then overloading and overriding methods, creating packages,
and using classes from other packages.
As a Java programmer, you should understand the role of access modifiers in
designing your classes. We covered how access modifiers enable a class to control who
can access it, to what extent, and how.

Licensed to Mark Watson 

Review notes

83

Efficient design and implementation of an application also depends on correct
and appropriate overloaded and overridden methods. You witnessed multiple examples on the need for overloading and overriding methods, including the correct
ingredients. We also covered why all the methods of a base class can’t be overridden.
A discussion of the nonfinal methods of class java.lang.Object, which is the parent class of all the Java classes, showed you why and how to override its methods. The
methods of class Object are called by various other classes and JRE, which makes it
crucial for a developer to override the relevant methods from class Object before
shipping them off to be used by other people.
You also learned how you can use casting to refer to specific behavior of derived class
objects when they’re referred to their base class references. The instanceof operator is
used to logically test whether an object is a valid type of a class or an interface.
In the final section, we worked with package and import statements. It’s important
to group your classes, interfaces, enums, and other Java entities depending on their
functionality. In real programming projects, you’d always work with classes organized
in packages.

REVIEW NOTES
This section lists the main points covered in this chapter.

Java access modifiers
■

■

■
■

■

■

■

■
■
■
■

The access modifiers control the accessibility of your class and its members outside the class and package.
Access modifiers defined by Java are public, protected, and private. In the
absence of an explicit access modifier, a member is defined with the default
access level.
The public access modifier is the least restrictive access modifier.
Classes and interfaces defined using the public access modifier are accessible
to related and unrelated classes outside the package in which they’re defined.
The members of a class defined using the protected access modifier are accessible to classes and interfaces defined in the same package and to all derived
classes, even if they’re defined in separate packages.
The members of a class defined without using an explicit access modifier are
defined with package accessibility (also called default accessibility).
The members with package access are accessible only to classes and interfaces
defined in the same package.
A class defined using default access can’t be accessed outside its package.
The private members of a class are only accessible to itself.
The private access modifier is the most restrictive access modifier.
A top-level class, interface, or enum can only be defined using the public or
default access. They can’t be defined using protected or private access.

Licensed to Mark Watson 

84

CHAPTER 1 Java class design
■

■

Method parameters and local variables can never be defined using an explicit
access modifier. They don’t have access control–only scope. Either they’re in
scope or out of scope.
If accessibility of an existing Java entity or its member is decreased, it can break
others’ code.

Overloaded methods and constructors
■

■
■
■

■

■

■

■
■
■
■
■
■

Overloaded methods are methods with the same name but different method
parameter lists.
A class can overload its own methods and inherited methods from its base class.
Overloaded methods accept different lists of arguments.
The argument lists of overloaded methods can differ in terms of change in the
number, type, or position of parameters that they accept.
Overloaded methods are bound at compile time. Unlike overridden methods
they’re not bound at runtime.
A call to correctly overloaded methods can also fail compilation if the compiler
is unable to resolve the call to an overloaded method.
Overloaded methods might define a different return type or access or nonaccess modifier, but they can’t be defined with only a change in their return types
or access or nonaccess modifiers.
Overloaded constructors must be defined using different argument lists.
Overloaded constructors can’t be defined by just a change in the access modifiers.
Overloaded constructors can be defined using different access modifiers.
A constructor can call another overloaded constructor by using the keyword this.
A constructor can’t invoke another constructor by using its class’s name.
If present, the call to another constructor must be the first statement in a
constructor.

Method overriding and virtual method invocation
■

■

■

■
■
■

Method overriding is an OOP language feature that enables a derived class to
define a specific implementation of an existing base class method to extend its
own behavior.
A derived class can override an instance method defined in a base class by
defining an instance method with the same method signature.
Whenever you intend to override methods in a derived class, use the annotation
@Override. It will warn you if a method can’t be overridden or if you’re actually
overloading a method rather than overriding it.
Overridden methods can define the same or covariant return types.
A derived class can’t override a base class method to make it less accessible.
Overriding methods must define exactly the same method parameters; the use
of a subclass or parent class results in overloading methods.

Licensed to Mark Watson 

Sample exam questions
■

■

■

■

■
■

85

Static methods can’t be overridden. They’re not polymorphic and they’re bound
at compile time.
In a derived class, a static method with the same signature as that of a static
method in its base class hides the base class method.
A derived class can’t override the base class methods that aren’t accessible to it,
such as private methods.
Constructors cannot be overridden because a base class constructor isn’t inherited by a derived class.
A method that can be overridden by a derived class is called a virtual method.
Virtual method invocation is invocation of the correct method–determined
using the object type and not its reference.

Java packages
■
■
■
■

■

■

■

■

■

■

■

You can use packages to group together a related set of classes and interfaces.
The package and subpackage names are separated using a period.
Classes and interfaces in the same package can access each other.
An import statement allows the use of simple names for classes and interfaces
defined in other packages.
You can’t use the import statement to access multiple classes or interfaces with
the same names from different packages.
You can import either a single member or all members (classes and interfaces)
of a package using the import statement.
You can’t import classes from a subpackage by using the wildcard character, an
asterisk (*), in the import statement.
A class from the default package can’t be used in any named package, regardless of whether it’s defined within the same directory or not.
You can import an individual static member of a class or all its static members
by using an import static statement.
An import statement can’t be placed before a package statement in a class. Any
attempt to do so will cause the compilation of the class to fail.
The members of the default package are accessible only to classes or interfaces
defined in the same directory on your system.

SAMPLE EXAM QUESTIONS
Q 1-1. Which of the following points should you incorporate in your application design?
a
b
c
d

Create related classes in a single package.
Don’t make derived classes overload methods from their base class.
Expose the functionality of your classes using public methods.
Create private methods to work as helper methods for the public methods.

Licensed to Mark Watson 

86

CHAPTER 1 Java class design

Q 1-2. What is the output of the following code?
class Wood {
public Wood() {
System.out.println("Wood");
}
{
System.out.println("Wood:init");
}
}
class Teak extends Wood {
{
System.out.println("Teak:init");
}
public Teak() {
System.out.println("Teak");
}
public static void main(String args[]) {
new Teak();
}
}
a

Wood:init
Wood
Teak:init
Teak

b

Wood
Wood:init
Teak:init
Teak

c

Wood:init
Teak:init
Wood
Teak

d

Wood
Wood:init
Teak
Teak:init

Q 1-3. Examine the following code and select the answer options that are correct
individually.
class Machine {
void start() throws Exception { System.out.println("start machine"); }
}
class Laptop {
void start() { System.out.println("Start Laptop"); }
void start(int ms) { System.out.println("Start Laptop:"+ms); }
}
a
b
c

Class Laptop overloads method start().
Class Laptop overrides method start().
Class Machine overrides method start().

Licensed to Mark Watson 

Sample exam questions
d
e

87

Class Machine won’t compile.
Class Laptop won’t compile.

Q 1-4. Given that classes Class1 and Class2 exist in separate packages and source
code files, examine the code and select the correct options.
package pack1;
public class Class1 {
protected String name = "Base";
}
package pack2;
import pack1.*;
class Class2 extends Class1{
Class2() {
Class1 cls1 = new Class1();
name = "Derived";
System.out.println(cls1.name);
}
}
a
b
c
d
e
f

//line 1
//line 2
//line 3

Class2 can extend Class1 but it can’t access the name variable on line 2.
Class2 can’t access the name variable on line 3.
Class2 can’t access Class1 on line 1.
Class2 won’t compile.
Line 3 will print Base.
Line 3 will print Derived.

Q 1-5. Select the correct option.
a
b

c

d

The declaration of private variables to store the state of an object is encouraged.
The protected members of a class aren’t accessible outside the package in which
the class is defined.
The public members of a class that’s defined with default access can be accessed
outside the package.
If you change the signature or implementation of a private method, other classes
that use this method cease to compile.

Q 1-6. Given the following code
interface Scavenger{}
class Bird{}
class Parrot extends Bird{}
class Vulture extends Bird implements Scavenger{}
class BirdSanctuary {
public static void main(String args[]) {
Bird bird = new Bird();
Parrot parrot = new Parrot();

Licensed to Mark Watson 

88

CHAPTER 1 Java class design
Vulture vulture = new Vulture();
//INSERT CODE HERE
}
}

In which of the following options will the code, when inserted at //INSERT CODE HERE,
throw a ClassCastException?
a
b
c
d

Vulture vulture2 = (Vulture)parrot;
Parrot parrot2 = (Parrot)bird;
Scavenger sc = (Scavenger)vulture;
Scavenger sc2 = (Scavenger)bird;

Q 1-7. Assuming that all of the following classes are defined in separate source code
files, select the incorrect statements.
package solarfamily;
public class Sun {
public Sun() {}
}
package stars;
public class Sun {
public Sun() {}
}
package skyies;
import stars.Sun;
import solarfamily.Sun;
class Sky {
Sun sun = new Sun();
}
a
b
c
d

e

// line1
// line2
// line 3

Code compilation fails at line 1.
Code compilation fails at line 2.
Code compilation fails at line 3.
The code compiles successfully and class Sky creates an object of class Sun from
the stars package.
The code compiles successfully and class Sky creates an object of class Sun from
the solarfamily package.

Q 1-8. Select the correct options.
class Color {
String name;
Color(String name) {this.name = name;}
public String toString() {return name;}
public boolean equals(Object obj) {
return (obj.toString().equals(name));
}
}

Licensed to Mark Watson 

Sample exam questions
a
b
c
d
e

89

Class Color overrides method toString() correctly.
Class Color overrides method equals() correctly.
Class Color fails to compile.
Class Color throws an exception at runtime.
None of the above.

Q 1-9. Given the following code
class Book {
String isbn;
Book(String isbn) {this.isbn = isbn;}
public int hashCode() {
return 87536;
}
}

Select the correct option.
a

b
c
d

Objects of the class Book can never be used as keys because the corresponding
objects wouldn’t be retrievable.
Method hashCode() is inefficient.
Class Book will not compile.
Though objects of class Book are used as keys, they will throw an exception
when the corresponding values are retrieved.

Q 1-10. What is the output of the following code?
class Wood {
String wood = "Wood";
public Wood() {
wood = "Wood";
}
{
wood = "init:Wood";
}
}
class Teak extends Wood {
String teak;
{
teak = "init:Teak";
}
public Teak() {
teak = "Teak";
}
public static void main(String args[]) {
Teak teak = new Teak();
System.out.println(teak.wood);
System.out.println(teak.teak);
}
}

Licensed to Mark Watson 

90

CHAPTER 1 Java class design
a

init:Wood
init:Teak

b

init:Wood
Teak

c

Wood
init:Teak

d

Wood
Teak

Q 1-11. Given the following code
class Cloth {}
class Shirt extends Cloth implements Resizable{}
class Shorts extends Cloth {}
interface Resizable {}
class Factory {
public static void main(String sr[]) {
Shirt s = new Shirt();
//INSERT CODE HERE
System.out.println(res);
}
}

Which options will print true?
a

boolean res = new Cloth() instanceof Shirt;

b

boolean res = new Shirt() instanceof Resizable;

c

boolean res = null instanceof Factory;

d

Cloth cloth = new Cloth();
Shirt shirt = new Shirt();
boolean res = shirt instanceof cloth;

ANSWERS TO SAMPLE EXAM QUESTIONS
A 1-1. a, c, d
[1.1] Use access modifiers: private, protected, and public
[1.3] Overload constructors and methods
[1.7] Use package and import statements
Explanation: Option (a) is correct. A package enables you to create a namespace to
group related classes and interfaces together.
Option (b) is incorrect. A base class overloads its base class method, as required.
Making derived classes overload their base class methods doesn’t make it an incorrect
or inefficient design.
Options (c) and (d) are also correct. The functionality of your classes should be
exposed using the public methods. The private methods are called within the class in
which they’re defined. They usually work as helper methods.

Licensed to Mark Watson 

Answers to sample exam questions

91

A 1-2. a
[1.3] Overload constructors and methods
Explanation: When a class is compiled, the contents of its initializer block are added
to its constructor, just before its own contents. For example, here’s the decompiled
code for class Wood. As you can see, the contents of its initializer block are added to
its constructor:
class Wood
{
public Wood()
{
System.out.println("Wood:init");
System.out.println("Wood");
}
}

A 1-3. a
[1.2] Override methods
[1.3] Overload constructors and methods
Explanation: Class Laptop correctly overloads the method start() by defining a different parameter list.
Options (b) and (c) are incorrect because classes Laptop and Machine are unrelated. A derived class can override its base class method.
Method start() qualifies as a valid overridden method in class Laptop, if Laptop
extends class Machine. It’s acceptable for an overriding method to not throw any
checked exception, even if the base class method is throwing a checked exception.
Options (d) and (e) are incorrect because both classes will compile successfully.
A 1-4. b, d
[1.7] Use package and import statements
Explanation: A derived class can access a protected member of its base class, across
packages, directly. But if the base and derived classes are in separate packages, then
you can’t access protected members of the base class by using reference variables of
class Base in a derived class. So, Class2 doesn’t compile.
Options (e) and (f) are incorrect because Class2 won’t compile.
A 1-5. a
[1.1] Use access modifiers: private, protected, and public
[1.7] Use package and import statements
Explanation: Option (b) is incorrect because the protected members of a class are
accessible by the derived classes, outside the package in which the class is defined.

Licensed to Mark Watson 

92

CHAPTER 1 Java class design

Option (c) is incorrect because a class with default access isn’t visible outside the
package within which it’s defined. If the class isn’t visible itself, it doesn’t matter
whether its members are accessible or not.
Option (d) is incorrect because a private method can’t be used outside the class in
which it’s defined.
A 1-6. b, d
[1.4] Use the instanceof operator and casting
Explanation: ClassCastException is thrown at runtime. So the options that don’t fail
to compile are eligible to be considered for the following question: Will they throw a
ClassCastException?
Option (a) is incorrect because it fails to compile.
Option (b) is correct because classes Bird and Parrot are in the same hierarchy
tree, so an object of base class Bird can be explicitly casted to its derived class Parrot
at compilation. But the JVM can determine the type of the objects at runtime. Because
an object of a derived class can’t refer to an object of its base class, this line throws a
ClassCastException at runtime.
Option (c) is incorrect because class Vulture implements the interface Scavenger,
so this code will also execute without the explicit cast.
Option (d) is correct. An instance of a nonfinal class can be casted to any interface type using an explicit cast during the compilation phase. But the exact object
types are validated during runtime and a ClassCastException is thrown if the
object’s class doesn’t implement that interface. Class Bird doesn’t implement the
interface Scavenger and so this code fails during runtime, throwing a ClassCastException.
A 1-7. b
[1.7] Use package and import statements
Explanation: Class Sky fails with the following error message:
Sky.java:3: error: stars.Sun is already defined in a single-type import
import solarfamily.Sun;
^
1 error

A 1-8. a
[1.2] Override methods
Explanation: Class Color overrides method toString() correctly, but not method
equals(). According to the contract of method equals(), for any non-null reference

Licensed to Mark Watson 

93

Answers to sample exam questions

values x and y, x.equals(y) should return true if and only if y.equals(x) returns
true—this rule states that two objects should be comparable to each other in the
same way. Class Color doesn’t follow this rule. Here’s the proof:
class TestColor {
public static void main(String args[]) {
Color color = new Color("red");
String string = "red";
System.out.println(color.equals(string));
System.out.println(string.equals(color));

// prints true
// prints false

}
}

A 1-9. b
[1.6] Override the hashCode, equals, and toString methods from the Object class to
improve the functionality of your class
Explanation: Method hashCode() returns the same hash code for all the objects of
this class. This essentially makes all the values be stored in the same bucket if objects
of the preceding classes are used as keys in class HashMap (or similar classes that use
hashing), and reduces it to a linked list, drastically reducing its efficiency.
Option (a) in incorrect. Book instances can be used to retrieve corresponding key
values but only in limited cases—when you use the same keys (instances) to store and
retrieve values. Even though hashCode() will return the same value for different Book
instances, equals() will always compare the reference variables and not their values,
returning false.
A 1-10. d
[1.3] Overload constructors and methods
Explanation: When a class is compiled, the contents of its initializer block are added
to its constructor just before its own contents. For example, here’s the decompiled
code for class Wood. As you can see, the contents of its initializer block are added to
its constructor:
class Wood
{
public Wood()
{
wood = "Wood";
wood = "init:wood";
wood = "Wood";
}
String wood;
}

// initial initialization
// re-assignment by the initializer block
// re-assignment by the constructor

Licensed to Mark Watson 

94

CHAPTER 1 Java class design

A 1-11. b
[1.4] Use the instanceof operator and casting
Explanation: Option (a) prints false.
Option (c) prints false. It doesn’t fail to compile because null is a valid literal
value that can be used for objects.
Option (d) fails to compile. The instanceof operator must be followed by the
name of an interface, class, or enum.

Licensed to Mark Watson 

Advanced class design

Exam objectives covered in this chapter

What you need to know

[2.1] Identify when and how to apply
abstract classes

The design requirements and implications of using
abstract classes in your application.

[2.2] Construct abstract Java classes and
subclasses

Construction and inheritance with abstract Java
classes.

[2.3] Use the static and final keywords

The need for defining static and final members
(classes, methods, initializer blocks, and variables).
The implications of defining nonstatic/nonfinal members as static/final members, and vice versa.

[2.4] Create top-level and nested classes

The flavors of nested classes—inner, static nested,
method local, and anonymous.
The design benefits, advantages, and disadvantages of
creating inner classes.
How each type of nested class is related to its outer
class.
The access and nonaccess modifiers that can be used
with the definition of these classes and their members.

[2.5] Use enumerated types

How to compare enumerated types with regular
classes. How to define enums with constructors, variables, and methods. How to define enums within
classes, interfaces, and methods. How to override
methods of a particular enum constant.
Use of variables of enum types—when to use the enum
name and when to leave it. Use of enumerated types in
switch constructs. The default methods available to
all enums.

95

Licensed to Mark Watson 

96

CHAPTER 2 Advanced class design

While designing your application, you might need to answer questions like these:
■

■

■

■

■

How do I ensure that a derived class implements an inherited behavior in its
own specific manner?
When do I prevent my class from being extended or methods from being
overridden?
When do I make objects share the same copy of a variable and when do I provide them with their own separate individual copy?
When do I create an inner class to perform a set of related tasks and when do I
let the top-level class handle it?
How do I define constants by using enums?

Design decisions require insight into the benefits and pitfalls of multiple approaches.
When armed with adequate information, you can select the best practices and
approaches to designing your classes and application.
The topics covered in this chapter will help you answer the aforementioned questions. I’ll take you through examples and give you multiple choices to help you determine the best option for designing your classes. This chapter covers
■
■
■
■

Abstract classes
Keywords static and final
Enumerated types
Nested and inner classes
EXAM TIP Take note of the relationship between an exam objective
heading and its subobjectives. For example, the topics of using the
static and final keywords, using enumerated types, and creating toplevel and nested classes are included within the main objective advanced
class design. So, apart from using the correct syntax of all of these, the
exam will query you on the impact of their use on the design of a class
and an application.

Let’s start with the first exam objective in this chapter, identifying when and how to
apply abstract classes.

Licensed to Mark Watson 

Abstract classes and their application

2.1

97

Abstract classes and their application
[2.1] Identify when and how to apply abstract classes
[2.2] Construct abstract Java classes and subclasses
Imagine you’re asked to bring a bouquet of flowers. Because no particular flower is
specified, you can choose any flower. How is the term flower used here? It communicates
a group of properties and behavior, which are applicable to multiple types of flowers.
Flowers like tulip, rose, hibiscus, and lotus, though similar, are also unique. In this
example, you can compare the term flower to an abstract class: the term captures basic
properties and behavior, yet enforces individual flower types to implement some of that
behavior in a unique manner—all flowers must have petals, though of different size,
color, or shape.
In this section, we’ll focus on how the exam will test you on identifying abstract
classes, and understanding their need, construction, use, and application. We’ll also
cover the dos and don’ts of creating abstract classes. For the exam, it’s important to compare the similarities and differences of abstract classes and concrete classes. These differences affect the creation and use of these classes. Let’s start by identifying abstract classes.

2.1.1

Identify abstract classes
An abstract class is an incomplete class or is considered to be incomplete. You define it by
using the keyword abstract. You can’t instantiate an abstract class, but you can subclass
it to create abstract or concrete derived classes. The choice of defining an abstract class
depends on the application context in which the classes are created; it all depends on
the details that you need for a class in an application. For example, a class Animal might
be defined as an abstract class in one application but not in another.
Imagine that you need to create a simple application, GeoAnimals, which helps
young children identify a predefined set of common wild animals, while including
basic information like the food the animals eat and their habitat. Figure 2.1 shows (a

Lion

Tiger

Elephant

food:String
avgLife:double

food:String
avgLife:double
striped:boolean

food:String
avgLife:double

eat()
live()

eat()
live()

eat()
live()
moveTrunk()

Figure 2.1 Classes Lion, Tiger, and Elephant identified for creating the
application GeoAnimals

Licensed to Mark Watson 

98

CHAPTER 2 Advanced class design

Animal
food:String
avgLife:double
eat()
live()
<>

Lion

Tiger

Elephant

striped:boolean

moveTrunk()

Figure 2.2 Classes Lion, Tiger, and Elephant inherit class Animal.

few) classes—Lion, Elephant, and Tiger—that you might identify for this application.
I deliberately limited the number of classes to keep the example simple.
As you can see, classes Lion, Tiger, and Elephant have common attributes and
behavior. Let’s pull out another generic class—say, Animal—and make the rest of the
classes extend it. Figure 2.2 shows the new arrangement.
Now the big question: Do you need to define the base class Animal as an abstract
class? How can you determine this? You can ask yourself simple questions to answer
the big question:
■

■

Should my application be allowed to create instances of the generic class Animal?
If no, define class Animal as an abstract class.
Does class Animal include behavior that’s common to all its derived classes, but
can’t be generalized (must it be implemented by the derived classes in their own
specific manner)? If yes, define the relevant method as an abstract method and
class Animal as an abstract class.

In this sample application, you’d never need objects of class Animal because they would
always refer to a specific type of animal. So class Animal qualifies to be defined as an
abstract class. Also, eating behavior, though common to all the animals, is unique to
every specific animal. So method eat() is a perfect candidate to be defined as an
abstract method. Figure 2.3 shows the new arrangement, where class Animal is defined
as an abstract class, and method eat() is defined as an abstract method. Now all the
derived classes must implement method eat().
Because an abstract class is meant to be extended by other classes, and its abstract
methods are meant to be implemented, it’s recommended that you document its
expected behavior in your real-life projects. This documentation will enable your class
to be inherited and used appropriately.
EXAM TIP An abstract method doesn’t define an implementation. It

enforces all the concrete derived classes to implement it.

Licensed to Mark Watson 

Abstract classes and their application

Animal
food:String
avgLife:double
eat()
live()

99

Abstract
base class

Abstract
method

<>

Lion

Tiger

Elephant

eat()

striped:boolean

moveTrunk()
eat()

eat()

Figure 2.3 Abstract class Animal defines an abstract method eat() and is
inherited by classes Lion, Tiger, and Elephant.

Until this point, you’ve looked at how to identify an abstract class. Does that imply that
if you were to define class Animal in another application, you’d define it as an abstract
class? Not always. For example, an application that counts all living beings, categorized as humans, animals, and plants, might not need to define class Animal as an
abstract class because the application might not need to create its specific types; it needs
only a count of the total animals. If you need to store the type of an animal, class Animal
can define an attribute—say, species. This arrangement is shown in figure 2.4.
Another frequently asked question by new programmers or designers is, when is
an abstract base class fit to be defined as an interface? Interfaces can be defined only
when no implementation of any method is provided. Also, an interface can define
only constants, which can’t be reassigned another value by the implementing classes.
The base class Animal discussed previously can’t be defined as an interface; it can’t
define its attributes food and avgLife as constants. As an example of an interface, the
Java package java.util contains multiple interfaces, such as List. The interface List
defines multiple methods, which must be implemented by all the implementing
classes, such as ArrayList.

LivingBeing

Human

Animal

Plant

species:String
Figure 2.4 Class Animal need not be always defined as an abstract class in all
applications.

Licensed to Mark Watson 

100

CHAPTER 2 Advanced class design

Starting with Java 8, an interface can define a default implementation of its methods, so an implementing class might not necessarily override these methods. But this exam is based on Java 7 and I’ll continue to
refer to an interface as the one that can’t define method implementation.

NOTE

Now that you understand how to identify abstract classes, let’s look at how to construct
abstract classes and their subclasses, and how to apply them. On the exam, you’ll be
questioned on correct construction of abstract classes, their subclasses, and their dos
and don’ts.

2.1.2

Construct abstract classes and subclasses
To keep the code small, let’s code the abstract class Animal and only two of its derived
classes, Lion and Elephant:

b
abstract class Animal {
protected String food;
protected double avgLife;

Abstract base
class Animal

Animal(String food, double avgLife) {
this.food = food;
this.avgLife = avgLife;
}
abstract void eat();

c
d

Properties of
class Animal

Constructor

e

Abstract
method eat()

void live() {
System.out.println("Natural habitat : forest");
}

f

Nonabstract
method live()

}

At B, abstract class Animal is defined by prefixing the class definition with the keyword abstract. The code at c defines attributes to store values for food and average
life span: food and avgLife. The code at d defines a constructor for class Animal.
You can’t instantiate an abstract class, but you can create its constructors, including
overloaded constructors. At least one of the Animal constructors must be called by
instances of its derived classes. The code at e defines abstract method eat(), which
delegates the responsibility of implementing it to the derived classes. You can define
class Animal as an abstract class, even if doesn’t define any abstract methods. The code
at f defines method live(), with an implementation. If required, it can be overridden by the derived classes.
It isn’t obligatory for abstract classes to define abstract methods. Abstract methods must not define a body.

EXAM TIP

Licensed to Mark Watson 

101

Abstract classes and their application

Following is the definition of derived class Lion:
class Lion extends Animal{
Lion(String food, double avgLife) {
super(food, avgLife);
}
void eat() {
System.out.println("Lion-hunt " + food);
}
}

b

Constructor

c

Implement
method eat()

Class Lion extends the base class Animal. It defines a constructor at B, which accepts
a double value for average life and a String value for food and passes it on to its base
class’s constructor. At c, class Lion implements method eat(). Let’s now define
class Elephant:
class Elephant extends Animal{
Elephant(String food, double avgLife) {
super(food, avgLife);
}

b

Constructor

void eat() {
System.out.println("Elephant-method eat");
}

c

void moveTrunk() {
System.out.println("Elephant-method moveTrunk");
}

Implement
method eat()

d

New method
moveTrunk()

}

At B, class Elephant defines a constructor that calls its base class constructor. At c,
it implements abstract method eat() from the base class. At d, it defines a new
method, moveTrunk().
Notice the power of base class constructors to ensure that
all derived class constructors pass them a value. The base class Animal
defines only one constructor that accepts a value for its instance variables
food and avgLife. Because a derived class constructor must call its base
class’s constructor, classes Lion and Elephant define a constructor that
calls Animal’s constructor.
EXAM TIP

Let’s put all these classes to work, in class GeoAnimals, as follows:
class GeoAnimals{
Animal[] animals = new Animal[2];

b

An array of type Animal—base
class of Lion and Elephant

GeoAnimals() {
animals[0] = new Lion("Antelope", 20);
animals[1] = new Elephant("Bananas", 60);
}

c

Initialize array animals
with separate instances
of Lion and Elephant.

Licensed to Mark Watson 

102

CHAPTER 2 Advanced class design
void flashcards() {
for (Animal anAnimal : animals) {
anAnimal.eat();
anAnimal.live();
}
}

d

public static void main(String args[]) {
GeoAnimals myAnimals = new GeoAnimals();
myAnimals.flashcards();
}

Iterate through objects of
array animals, calling
methods eat() and live().

e

Create an instance of GeoAnimals
and call method flashcards().

}

Here’s the output of the preceding code (blank lines were added to improve readability):
Lion-hunt Antelope
Natural habitat : forest
Elephant-method eat
Natural habitat : forest

Let’s walk through this code. The code at B declares an array of type Animal. Though
you can’t create instances of abstract class Animal, an array of Animal can be used to
store objects of its derived classes, Lion and Elephant. The code at c initializes animals with instances of classes Lion and Elephant. The code at d iterates through the
array animals, calling methods eat() and live() on all its elements. The code at e
defines method main() that creates an object of class GeoAnimals, calling method
flashcards().
The code in this section walked you through how to create abstract classes and
their subclasses, and how to use them. The efficient use of abstract classes lies in their
identification in an application. Let’s see how well you score on identifying all abstract
and concrete classes in an application in the following “Twist in the Tale” exercise.
Twist in the Tale 2.1

The following are names of multiple classes. Your task is to arrange these in an inheritance hierarchy, connecting all base and derived classes. At end of the exercise, all
these classes should be connected, with the base class at the top and derived classes
below it.
Lion

Omnivore
Animal

Dog
Cat
Carnivore

Tiger

Herbivore
Deer

Elephant

Licensed to Mark Watson 

103

Abstract classes and their application

Here’s another example that will help you attempt the preceding exercise.
Book
Fiction

Book

CourseBook

StoryBook
History

StoryBook

CourseBook

Geology

NonFiction

Fiction

NonFiction

History

Geology

<>

An abstract method can’t be defined in a concrete class. It can
be defined in an abstract class only.

EXAM TIP

2.1.3

Understand the need for abstract classes
An abstract class represents partial implementation of an object or concept. But why
do you need partial implementation? Do abstract classes exist only so other classes can
inherit them? These questions are frequently asked by new Java application designers.
You might also have to answer these questions on the exam.
You need an abstract class to pull out and group together the common properties and
behavior of multiple classes—the same reason you need a nonabstract base class. You
define a base class as an abstract class to prevent creation of its instances. As the creator, you
think that it doesn’t include enough details to create its own objects. When you define
abstract methods in a base class, it forces all its nonabstract derived classes to implement
the incomplete functionality (abstract methods) in their own unique manner.
Because you can’t create instances of an abstract class, there’s not much sense in
creating an abstract base class, which isn’t extended by other classes.
Abstract classes make a point loud and clear: they force the concrete derived classes to implement a base class’s abstract methods, in
their own unique manner.

EXAM TIP

Note that I haven’t discussed the need for, advantages of, or disadvantages of creating
nonabstract base classes in this section. This section specifically covers base classes
that are abstract.

2.1.4

Follow the dos and don’ts of creating and using abstract classes
Apart from the points covered in the previous section, the exam will likely include
other theoretical and coding questions on the dos and don’ts of creating and implementing abstract classes.

Licensed to Mark Watson 

104

CHAPTER 2 Advanced class design

DON’T CREATE AN ABSTRACT CLASS ONLY TO PREVENT CREATION OF ITS OBJECTS
To prevent instantiation of a class by using the operator new, define all its class constructors as private. For example, class java.lang.Math in the Java API doesn’t allow

creation of its objects by defining its constructor as a private member:
package java.lang;
public final class Math{
private Math() { /*code */}
}

DON’T MAKE AN ABSTRACT CLASS IMPLEMENT INTERFACES THAT RESULT IN INVALID
METHOD IMPLEMENTATION

When a class implements an interface, the class must implement its methods (unless
the class is abstract) to meet the contract. But if the class defines methods with the
same name as the one defined in the interface, they should either comply with correct
method overriding or overloading rules or else the class won’t compile. In the following example, class Animal can’t implement interface Live:
interface Live{
boolean eat();
}

Method eat() that
returns boolean value

abstract class Animal implements Live{
public abstract void eat();
}

Won’t compile; method eat() from
Live and Animal can’t coexist.
Method eat() doesn’t
return any value.

Class Animal won’t compile because method eat() from interface Live and method
eat() defined in class Animal exist as invalid overloaded methods.
DON’T CREATE OBJECTS OF AN ABSTRACT CLASS

Code that creates objects of an abstract class won’t compile:
abstract class Animal{}
class Forest {
Animal animal = new Animal();
}

Won’t compile; can’t
instantiate abstract classes.

DON’T DEFINE AN ABSTRACT CLASS AS A FINAL CLASS

A final class can’t be extended. On the other hand, abstract classes are created so they
can be extended by other classes. Hence, abstract classes can’t be defined as final classes.
abstract final class Animal {}

Won’t compile

Licensed to Mark Watson 

105

Abstract classes and their application

DON'T FORCE AN ABSTRACT CLASS TO IMPLEMENT ALL METHODS FROM THE INTERFACE(S)
IT IMPLEMENTS

An abstract class can implement multiple interfaces. It might not implement all the
abstract methods from the implemented interface(s), leaving them to be implemented by all its nonabstract derived classes:
interface Live{
void eat();
}
abstract class Animal implements Live{}

Abstract class Animal doesn’t
implement eat() from interface Live.

DO USE AN OBJECT OF AN ABSTRACT CLASS TO REFER TO OBJECTS OF ITS NONABSTRACT
DERIVED CLASSES

An abstract class can’t be instantiated. But this doesn’t stop you from using a reference
variable of an abstract base class to refer to an instance of its nonabstract derived class:
abstract class Animal{}
class Deer extends Animal{}
class Forest{
Animal animal = new Deer();
}

Abstract class variable can refer
to instance of its derived class

Comparing an abstract class with a concrete class is obvious, as covered in the next section. This comparison will help you with multiple exam objectives: identifying abstract
classes, their construction, and their application.

2.1.5

Compare abstract classes and concrete classes
Do you think the constructor of an abstract base class is called in the same manner as
that of a concrete base class? Yes, indeed. Table 2.1 answers many more questions like
this by comparing abstract and concrete classes.
Table 2.1 Comparing an abstract class with a concrete class
Comparison Category

Abstract
class

Concrete
class

Create a new type

✓

✓

Use as base class

✓

✓

Extend another class

✓

✓

Implement interfaces

✓

✓

Define attributes and concrete methods

✓

✓

Require at least one constructor to be called by its derived classes

✓

✓

Define abstract methods

✓

✗

Allow object creation

✗

✓

Licensed to Mark Watson 

106

CHAPTER 2 Advanced class design

Before moving on to the next section, let’s quickly list the points to remember about
abstract classes for the exam.

Rules to remember for creating abstract classes
■
■

■
■
■
■

■
■
■

An abstract class must be defined by using the keyword abstract.
An abstract class can extend any other abstract or concrete class and implement other interfaces.
An abstract class can define multiple constructors.
An abstract class can define instance and static variables.
An abstract class can define instance and static methods.
An abstract class might not necessarily define an abstract method and can exist
without any abstract method.
A class can’t define an abstract static method.
Don’t create an abstract class just to prevent creation of its instances.
Don’t make an abstract class implement interfaces that result in incorrect overloaded or overridden methods.

Rules to remember for subclassing an abstract class
■

■

■

A concrete subclass must implement all the abstract methods in its abstract
superclass(es).
An abstract subclass might not implement all the abstract methods in its
abstract superclass(es).
A subclass must call at least one constructor from its superclass.

Identification of abstract classes is an important design decision. It changes how other
classes might use the abstract class. Similarly, creating classes that can’t be extended,
creating methods that can’t be overridden, or creating class (or static) members are
other important design decisions. In the next section, you’ll see how you can do so by
using the static and final nonaccess modifiers.

2.2

Static and final keywords
[2.3] Use the static and final keywords
Defining a class as a final class prevents it from being extended. Similarly, a static variable or method can be accessed without instances of its class; the variable or method is
available after its class is loaded into memory. These are a few examples of how the
nonaccess modifiers static and final change the default behavior of a Java entity.
For the exam, you need to understand the need for defining static and final members
(classes, methods, initializer blocks, and variables) together with their correct definition

Licensed to Mark Watson 

107

Static and final keywords

and use. You also need to know the implications of defining nonstatic/nonfinal members as static/final members, and vice versa.

2.2.1

Static modifier
You can define variables, methods, nested classes, and nested interfaces as static members. They belong to a class and not to instances. They can be accessed soon after
their class is loaded into memory. Top-level classes, interfaces, and enums can’t be
defined as static entities. Watch out for code that declares top-level classes, interfaces,
and enums as static members. Such code won’t compile. Let’s get started with static
class variables.
STATIC VARIABLES

Static variables belong to a class and are shared by all its instances. Their value is the
same for all instances of their class. A static class variable is created when its class is
loaded into memory by the JVM. It can exist and is accessible even if no instances of
the class exist. So you can use it to perform operations that span multiple instances of a
class. Class Book defines a static class variable bookCount, to count the instances of
class Book that are created while your program is running:
class Book {
static int bookCount;
public Book() {
++bookCount;
}
}

Static variable
bookCount
bookCount is incremented
in constructor

class Publisher{
public static void main(String args[]){
System.out.println(Book.bookCount);
Book b1 = new Book();
Book b2 = new Book();
System.out.println(Book.bookCount);
}
}

b

Prints “0”

c

Prints “2”

Assuming that no instances of Book were created earlier, the code at B prints 0. The
code at c prints 2 due to creation of two instances of class Book, created on the preceding lines. Each invocation of the construction increments the value of the static
class variable bookCount by 1.
EXAM TIP Unlike instance variables, which are initialized for each
instance, static class variables are initialized only once, when they are
loaded into memory. The default variable values are false for boolean;
'\u0000' for char; 0 for byte; short, int, 0L for long; 0.0F for float;
0.0D for double; and null for objects.

Because the same value of a static class variable is shared across all the instances of
a class, if modified, the same modified value is reflected across all instances. In the

Licensed to Mark Watson 

108

CHAPTER 2 Advanced class design

following code, the value of the static class variable bookCount is accessed and modified using the class name Book and instances b1 and b2 (modifications in bold):
class Book {
static int bookCount;
public Book() {
++bookCount;
}
}
class Publisher{
public static void main(String args[]){
System.out.println(Book.bookCount);
Book b1 = new Book();
Book b2 = new Book();
System.out.println(Book.bookCount);
b1.bookCount = 10;
System.out.println(b2.bookCount);
}
}

Prints “0” (access bookCount
using class name Book)
Set value of bookCount to 10,
using reference variable b1.
Prints “10” (access bookCount
using reference variable b2)

NOTE For simplicity, I’ve defined the variable bookCount with default
access, which is directly accessed and manipulated outside the class
Book. This isn’t a recommended approach in real-life projects. Encapsulate your data by defining the class and instance variables as private and
make them accessible outside their class through accessor and mutator
methods.

On the exam, you’re likely to see code that accesses a static class variable by using the
name of its class and its instances. Although a static class variable is allowed to be accessed
by using instances of a class, it’s not a preferred approach; it makes the static class variable seem to belong to an instance, which is incorrect. Always refer to a static class member by using its class name.
You can access a static member by using the name of its class
or any of its instances. All these approaches refer to the same static member. The preferred approach is to use a class name; otherwise, a static
member seems to be tied to an instance, which is incorrect.

EXAM TIP

A combination of the static and final nonaccess modifiers is used to define constants
(variables whose value can’t change). In the following code, the class Emp defines the
constants MIN_AGE and MAX_AGE:
class Emp {
public static final int MIN_AGE = 20;
static final int MAX_AGE = 70;
}

Constant MIN_AGE
Constant MAX_AGE

Licensed to Mark Watson 

Static and final keywords

109

STATIC METHODS

Static methods don’t need instances of a class. They can be called even if no instance
of the class exists. You define static methods to access or manipulate static class variables. The static methods can’t access nonstatic fields or nonstatic methods. Referring
to the example of class Book, which used the static variable bookCount to count all
instances of class Book, static methods getBookCount() and incrementBookCount()
can be created to access bookCount and manipulate it:
class Book {
private static int bookCount;
public static int getBookCount(){
return bookCount;
}
public void incrementBookCount() {
++bookCount;
}

Static method to retrieve
value of static variable
bookCount
Static method to
increment value of static
variable bookCount

}

You also use static methods to define utility methods—methods that usually manipulate
the method parameters to compute and return an appropriate value:
static double average(double num1, double num2, double num3) {
return(num1+num2+num3)/3;
}

A static method might not always define method parameters. For example, the method
random in class java.lang.Math doesn’t accept any parameters. It returns a pseudorandom number, greater than or equal to 0.0 and less than 1.0.
A static method is used to manipulate static class variables or
to define utility methods. A utility method may or may not accept method
parameters.

EXAM TIP

WHAT CAN A STATIC METHOD ACCESS?

Neither static class methods nor static class variables can access the nonstatic instance
variables and instance methods of a class. But the reverse is true: nonstatic variables
and methods can access static variables and methods because the static members of a
class exist even if no instances of the class exist. Static members are forbidden from
accessing instance methods and variables, which can exist only if an instance of the
class is created.
Examine the following code:
class MyClass {
static int x = count();
int count() { return 10; }
}

Compilation
error

Licensed to Mark Watson 

110

CHAPTER 2 Advanced class design

This is the compilation error thrown by the previous class:
MyClass.java:3: nonstatic method count() cannot be
referenced from a static context
static int x = count();
^
1 error

The following code is valid:
Static variable referencing
class MyClass {
a static method
static int x = result();
static int result() { return 20; }
int nonStaticResult() { return result(); }
Nonstatic method
}
using a static method

You can use constructors or instance initializer blocks to initialize the instance variables. But how can you initialize the static variables, after they’re loaded into memory?
Static initializer blocks are the answer.
STATIC INITIALIZER BLOCKS

A static initializer block is a code block defined using braces and prefixed by the keyword static:
static {
//code to initialize static variables
}

Because static variables can’t be initialized using the constructors of a class, a static initializer block is used to initialize static variables. This initializer block executes when a
class is loaded by the JVM into memory. You can define multiple static initializer blocks
in your code, which execute in the order of their appearance. All types of statements
are allowed in this block, including declaration, initialization, assignment, and calling
of other static variables and methods. In the following example, class AffiliateProgram
defines a static variable accountOpenBonus. The variable accountOpenBonus is initialized using a static initializer block:
class AffiliateProgram {
private static int accountOpenBonus;
static {
accountOpenBonus = 5;
}
}

Declare static
variable
Initialize static variable using
a static initializer block

You might argue that you could initialize the static variable accountOpenBonus as follows:
class AffiliateProgram {
private static int accountOpenBonus = 5;
}

Declare and initialize
a static variable

Licensed to Mark Watson 

111

Static and final keywords

But what happens if you need to use a calculated value or initialize the value of
accountOpenBonus based on the outcome of a condition:
class AffiliateProgram {
private static int accountOpenBonus;
static {
if (/* file XYZ exists */)
accountOpenBonus = 5;
else
accountOpenBonus = 15;
}
}

Conditional
assignment of variable
accountOpenBonus

Again, you might argue that you can move the preceding conditional execution to a
static method and use it to initialize variable accountOpenBonus, without using the
static initializer block:
class AffiliateProgram {
private static int accountOpenBonus = initAccountOpenBonus();
private static int initAccountOpenBonus() {
if (/* file XYZ exists */)
return 5;
else
return 15;
}

Conditional assignment of
variable accountOpenBonus
using static method

}

What happens if method initAccountOpenBonus() throws a checked exception, say,
FileNotFoundException? In this case, you must use a static initializer block to assign
the returned value from initAccountOpenBonus() to variable accountOpenBonus. As
you know, if a method throws a checked exception, its use should either be enclosed
within a try block or the method that uses it should declare the exception to be
thrown. In this case, neither is possible; the declaration of the variable accountOpenBonus can’t be enclosed within a try block because this statement doesn’t exist within
a method or code block. Here’s the relevant code:
Won’t compile; can’t declare and
initialize variable using method that
throws checked exception

import java.io.*;
class AffiliateProgram {
private static int accountOpenBonus = initAccountOpenBonus();
private static int initAccountOpenBonus() throws FileNotFoundException
{
//relevant code
}
}

Licensed to Mark Watson 

112

CHAPTER 2 Advanced class design

And here’s the way out:
Static method that

static
initializer
block

Static
class AffiliateProgram {
throws checked
variable
private static int accountOpenBonus;
exception
private static int initAccountOpenBonus()
throws FileNotFoundException{
//..relevant code
}
static {
try {
try block to catch
accountOpenBonus = initAccountOpenBonus();
FileNotFoundException
}
thrown by
catch (FileNotFoundException e) {
initAccountOpenBonus()
//.. relevant code
}
}
}

Another reason for the existence of a static initializer block is to add values (static or
dynamic) to collection objects that have already been initialized. Here’s an example:
class AddValuesToStaticVariables {
static private String[] dataStores = new String[5];
static {
dataStores[0] = "us.ny";
dataStores[1] = "jp.tk";
dataStores[2] = "gr.br";
//..code that assigns dynamic value to dataStores[]
}
}

String array dataStores is initialized.
Add explicit values
to dataStores.
Pull values from
the database
and add to String
array dataStores.

The static initializer blocks can be tricky and cumbersome to work with when it
comes to debugging them. On the exam, beware of code that defines multiple initializer blocks. If a class defines multiple initializer blocks, they execute in the order
of their appearance in a class. Let’s examine output of code that defines multiple
initializer blocks:
class StaticInitBlocks {
static int staticVar = 10;
static {
System.out.println("First");
++staticVar;
}
static {
System.out.println("Second");
++staticVar;
}
static void modifyStaticVar() {
++staticVar;
}

Licensed to Mark Watson 

Static and final keywords

113

public StaticInitBlocks() {
System.out.println("Constructor:" + staticVar);
}
public static void main(String args[]) {
new StaticInitBlocks();
modifyStaticVar();
new StaticInitBlocks();
}
}

Code in a static initializer block executes when a class is loaded in memory by JVM—
before creation of its instances. The output of the preceding code is as follows:
First
Second
Constructor:12
Constructor:13

Can the static and instance initializer blocks access the static or instance variables of a
class, like other methods? Let me modify the preceding code and use it for the next
“Twist in the Tale” exercise.
Twist in the Tale 2.2

Following is modified code for class DemoMultipleStaticBlocks. Answer the question
before you execute it on your system. Which answer correctly shows its output?
class DemoMultipleStaticBlocks {
static {
++staticVar;
}
static int staticVar ;
static {
++staticVar;
}
public DemoMultipleStaticBlocks() {
System.out.println("Constructor:" + staticVar);
}
public static void main(String args[]) {
new DemoMultipleStaticBlocks();
}
}
a
b
c
d
e

Constructor: 2
Constructor: 1
Constructor: 0

Compilation error
Runtime exception

Licensed to Mark Watson 

114

CHAPTER 2 Advanced class design

On the exam, beware of code that defines multiple initializer
blocks. If a class defines multiple initializer blocks, they execute in the
order of their appearance in a class.

EXAM TIP

Watch out for another combination on the exam: initialization of a static class variable
and its manipulation in a static block. What do you think is the order of execution in
the following code? Will the following example code print 1 or 11?

Declare
static
variable
rate and
assign 0
to it.

public class AssignManipulateStaticVariable {
static {
rate = 10;
}
static int rate = 0;
static {
++rate;
}
public AssignManipulateStaticVariable() {
System.out.println(rate);
}
public static void main(String args[]) {
new AssignManipulateStaticVariable();
}

First static initializer
block to assign 10 to rate

Second static initializer block
to increment rate by 1

Prints “1”

}

For the preceding code, the compiler rearranges the code to execute. It declares the
static variable age and then picks up the code from all the static initializer blocks and
assignment of age and combines them in a single static initializer block, in the order
of their occurrence, as follows:
static int rate;
static
{
rate = 10;
rate = 0;
++rate;
}

The preceding code explains why AssignManipulateStaticVariable prints 1 and
not 11.
STATIC CLASSES AND INTERFACES

Let’s look at other types of static entities: static classes and interfaces. These are also
referred to as nested classes, static nested classes, static interfaces, and static nested interfaces.
You can’t prefix the definition of a top-level class or an interface with the keyword
static. A top-level class or interface is one that isn’t defined within another class or
interface. The following code fails to compile:
static class Person {}
static interface MyInterface {}

Licensed to Mark Watson 

115

Static and final keywords

But you can define a class and an interface as a static member of another class. The
following code is valid:
Static nested
class

class Person {
static class Address {}
static interface MyInterface {}
}

Static nested
interface

As you know, the static variables and methods of a class are accessible without the
existence of any of its objects. Similarly, you can access a static class without an
object of its outer class. You’ll learn all about the other details of the static classes in
section 2.3.2 .

2.2.2

Nonaccess modifier—final
The decision to apply the nonaccess modifier final to a class, interface, variable, or
method is an important class design decision. To start, should you define your class as
a final class? Yes, if you don’t want it to be subclassed. Should you define your method
as a final method? Yes, if you don’t want any of its subclasses to override it. Do you
want to define a variable as a final variable? Yes, if after the variable is initialized,
you don’t want it to be reassigned another value. Knowing these details will enable
you to make the right decisions—when, why, where, and how to apply the nonaccess
modifier final and when not to. Apart from testing you on how to use the modifier
final in code, the exam will also query you on the implications of its use on the
design or behavior of code. Let’s start with final variables.
FINAL VARIABLES

The final variables can be initialized only once. You can tag all types of variables—
static variables, instance variables, local variables, and method parameters—with the
nonaccess modifier final. Because of the differences in how these variable types are
initialized, they exhibit different behavior. Let’s start with defining a static variable
as a final variable:
class TestFinal {
static final int staticFinal = 10;
}

A final static class variable can be initialized with its declaration, or by using a static initializer block, which is guaranteed to execute only once for a class. Because a static method
can be called multiple times, it can’t define code to initialize a final (static) variable:
class TestFinal {
static final int staticFinal2 = 12345;

Static final variable initialized
with its declaration

static final int staticFinal;
static {
staticFinal = 1234;
}

Static initializer
block to initialize
static variable

Static final
variable not
initialized

Licensed to Mark Watson 

116

CHAPTER 2 Advanced class design
static void setStaticFinal(int value) {
staticFinal = value;
}

Won’t compile; static method can
execute multiple times and so it can’t
include initialization of final variable.

}

Because the constructor of a class executes on creation of every instance of the
class, you can’t initialize a final static variable in the constructor. The following code
won’t compile:
class FinalStatic {
static final int finalVar;

Final static variable
can’t be initialized in
constructor of a class

FinalStatic() {
finalVar = 10;
}
}

Similarly, though you can initialize a final instance variable in the class’s constructor or
its instance initializer block, you can’t initialize it in an instance method. Instance methods can execute more than once:
Instance final variable
initialized with its declaration

class InstanceFinalVariables {
final int finalVar2 = 710;

Instance final variable
not initialized

final int finalVar;
InstanceFinalVariables() {
finalVar = 10;
}
void setValue(int a) {
finalVar = a;
}

Class’s constructor initializes
instance final variable
Won’t compile; a method may execute
multiple times and so can’t include code
to initialize a final variable.

}

If a static or instance variable is marked final, it must be initialized, or the code won’t compile.

EXAM TIP

Interestingly, you can survive code with an uninitialized final local variable, if you
don’t use it:
class MyClass {
void setValue(int a) {
final int finalLocalVar1;
finalLocalVar1 = 20;
final int finalLocalVar2;
}
}

Final local variable declared and
initialized on separate lines
Uninitialized final local
variable; compiles
successfully

Licensed to Mark Watson 

117

Static and final keywords

In the preceding code, if you try to use the uninitialized final local variable finalLocalVar2, your code won’t compile. Modified code is as follows:
class MyClass {
void setValue(int a) {
final int finalLocalVar1;
finalLocalVar1 = 20;

Won’t compile when
you try to use an
uninitialized local
variable.

final int finalLocalVar2;
System.out.println(finalLocalVar2);
}
}

Method parameters are initialized when the method is invoked. If a method marks
its method parameter(s) as final, the method body can’t reassign a value to it,
as follows:
class MyClass {
void setValue(final int finalMethodParam) {
finalMethodParam = 10;
}
}

Final method
parameter
Won’t compile; value
can’t be assigned to final
method parameter.

There’s a difference between final primitive variables and final object reference
variables. The final primitive variables can’t change, but the object referred to by
final object reference variables can be changed. Only the final reference itself can’t
be changed:
Final method
class MyClass {
parameter
void addCondition(final StringBuilder query) {
query.append("WHERE id > 500");
query = new StringBuilder("SELECT name FROM emp");
}
}
Won’t compile; can’t reassign

Can modify object
referred to by
final reference
variable query.

another object to final
reference variable query.

CONDITIONAL ASSIGNMENT OF FINAL VARIABLES

What happens when you initialize a final variable within an if-else construct, switch
statement, or for, do-while, or while loop? In this case, code that assigns a value to
the final variable might not execute. If the Java compiler is doubtful about the initialization of your final variable, the code won’t compile. For example, the constructor of
MyClass assigns a value to its final instance variable finalVar by using an if statement, as shown in the following code listing.

Licensed to Mark Watson 

118

CHAPTER 2 Advanced class design

Listing 2.1

Conditional assignment of final instance variable in class’s constructor

class MyClass {
final int finalVar;
MyClass(double a, double b) {
if (a > b)
finalVar = 20;
else if (b >= a)
finalVar = 30;
}
}

b

c

Assigns 20 to
finalVar if a > b

Assigns 30 to finalVar
if b >= a

Class MyClass fails compilation with the following compilation error message:
variable finalVar might not have been initialized

The code at B assigns a value to finalVar if the condition a > b evaluates to true. The
code at c assigns a value to finalVar if condition b >= a evaluates to true. The compiler has its doubts about being able to execute (and thus initialize) in all conditions.
So, the Java compiler will consider initialization of a final variable complete only if the
initialization code will execute in all conditions. Adding an else branch results in successful code compilation:
class MyClass {
final int finalVar;
MyClass(double a, double b) {
if (a>b)
finalVar = 20;
else
finalVar = 30;
}
}

b

c

Assigns 20 to
finalVar if a > b

Assigns 30 to finalVar
otherwise

In the preceding code, B initializes finalVar to 30 if the condition a > b evaluates to
true. Otherwise, it initializes finalVar to 30 at c. Let’s modify the code in listing 2.1
so it uses constant literal values instead of variables, in if conditions:
class MyClass {
final int finalVar;
MyClass(double a, double b) {
if (1>2)
finalVar = 10;
else if (100>10)
finalVar = 20;
}
}

Assigns 10 to
finalVar if 1 > 2

b

Assigns 20 to finalVar
if 100 > 10

The preceding code compiles successfully, because with the constant values, the compiler can determine that code at B will execute, initializing a value to the final variable for sure.

Licensed to Mark Watson 

Static and final keywords

119

Let’s modify the code again, so it continues to use the variables in the if conditions. In listing 2.1, the Java compiler complained that variable finalVar might not
have been initialized. So let’s explicitly assign a value to variable finalVar, before the
start of the if statement, as follows:
class MyClass {
final int finalVar;
MyClass(double a, double b) {
finalVar = 100;
if (a>b)
finalVar = 20;
else if (b>=a)
finalVar = 30;
}
}

b
c

Explicit assignment
to final variable
finalVar

Conditional
assignment to final
variable finalVar

The code at B initializes finalVar, and the code at c tries to assign a value to it, conditionally. Because a final variable can’t be reassigned a value, the preceding code fails
compilation with the following compilation error message:
variable finalVar might already have been assigned
finalVar = 20;
^

On the exam, look out for multiple initializations of a final variable. Code snippets that try to reinitialize a final variable won’t compile.

EXAM TIP

The simplest way to initialize a final variable is to do so with its declaration. If not initialized with its declaration, a static final variable can be initialized in the class’s static initializer block. An instance final variable can be initialized in its constructor or the instance
initializer block. A local final variable can be assigned a value in the method in which it’s
defined. A final method parameter can’t be reassigned a value within the method.
It’s time for you to attempt the next “Twist in the Tale” exercise, which tests you on
understanding assignment of a base class’s final instance variable from the derived class.
Twist in the Tale 2.3

Let’s modify the code used in the preceding examples so class MyClass does not initialize its final instance variable finalVar. This variable is initialized in its derived
class, MyDerivedClass, as follows:
abstract class MyClass {
public final int finalVar;
}
class MyDerivedClass extends MyClass {
MyDerivedClass() {
super();
finalVar = 1000;
}
}

Licensed to Mark Watson 

120

CHAPTER 2 Advanced class design

Your task is to first think about the possible output of the following code before you
compile it on your system:
class Test {
{System.out.println(new MyDerivedClass().finalVar);}
public static void main(String args[]) {
new Test();
}
}

FINAL METHODS

The final methods defined in a base class can’t be overridden by its derived classes.
The final methods are used to prevent a derived class from overriding the implementation of a base class’s method. Can you think of any scenario where you’d need this?
Picture this: the base class of all the Java classes, java.lang.Object, defines multiple
final methods—wait, notify, getClass. Methods wait() and notify() are used in
threading and synchronization. If the derived classes were allowed to override these
methods, how do you think Java would implement threading and synchronization?
If a derived class tries to override a final method from its base class, it won’t compile, as follows:
class Base {
final void finalMethod() {}
}
class Derived extends Base {
void finalMethod() {}
}

Final method
in base class
Won’t compile; final method in
base class can’t be overridden.

The preceding code fails to compile, with the following compilation error message:
finalMethod() in Derived cannot override finalMethod() in Base
final void finalMethod() {}
^
overridden method is final
1 error

As you know, you can override only what is inherited by a derived class. The following
code compiles successfully, even though the derived class seems to override a final
method from its base class:
class Base {
private final void finalMethod() {}
}
class Derived extends Base {
final void finalMethod() {}
}

Private methods aren’t
inherited by derived classes.
Compiles
successfully

Licensed to Mark Watson 

121

Static and final keywords

The base class’s private methods aren’t inherited by a derived class. In the previous
code, method finalMethod() is defined as a private method in class Base. So it
doesn’t matter whether it’s marked as a final method. Method finalMethod() defined
in class Derived doesn’t override the base class’s method finalMethod(). Class
Derived defines a new method, finalMethod().
The private methods of a base class aren’t inherited by its
derived classes. A method using the same signature in the derived class
isn’t an overridding method, but a new method.

EXAM TIP

FINAL CLASSES

You can prevent a class from being extended by marking it as a final class. But why
would you do so? A class marked as a final class can’t be derived by any other class. For
example, class String, which defines an immutable sequence of characters, is defined
as a final class. It’s a core class, which is used in a lot of Java API classes and userdefined classes. What happens, say, if a developer extends class String and modifies
its equals() method to return a value true for all method parameter values passed to
it? Because we can’t extend the final class String, let’s create a class MyString and
override its equals() method to return true without comparing any values:
class MyString {
String name;
MyString (String name) {this.name = name;}
public boolean equals(Object o) {
return true;
}
}

Many classes from the Java API, like HashMap and ArrayList, rely heavily on the correct implementation of equals() for searching, deleting, and retrieving objects. Imagine the effect it would have if you try to use objects of class MyString in an ArrayList
and retrieve a matching value:
class UseMyStringInCollectionClasses {
public static void main(String args[]) {
ArrayList list = new ArrayList<>();
MyString myStrEast = new MyString("East");
MyString myStrWest = new MyString("West");
list.add(myStrEast);
System.out.println(list.contains(myStrWest));
}
}

Creates
ArrayList—list
Add only one element
to list—myStrEast
Prints “true”—list can
find myStrWest, which
was never added to it.

Surprisingly, the preceding code prints true even though list doesn’t contain a
matching MyString object. This is because method contains90 in ArrayList uses the
equals() (overridden) method of the objects it holds. If you can’t get all of this explanation, don’t worry. Collection classes are covered in detail in chapter 4. Imagine the

Licensed to Mark Watson 

122

CHAPTER 2 Advanced class design

havoc that objects of an extended String class can cause, if class String wasn’t
defined as a final class, was allowed to be extended, and its methods overridden.
You can mark a class as a final class by prefixing its definition with the keyword
final:
final class FinalClass {
//.. this need not be detailed here
}

Class is marked final by
adding final to its definition.

You can’t reverse the position of the keywords final and class:
class final ClassBeforeFinalWontCompile

{}

Won’t compile; position of keywords
class and final can’t be interchanged.

The original intent of defining an abstract class was to extend it to create more meaningful and concrete classes. Because a final class can’t be extended, you can’t define a
class both as final and abstract:
abstract final class FinalAbstractClassDontExist {}

Won’t compile; a class
can’t be defined both as
final and abstract.

If you try to extend a final class, your class won’t compile:
final class Base {}
class Derived extends Base {}

Final class
Won’t compile; can’t
extend a final base class.

Look out for trick questions on the exam that extend final
classes from the Java API, like class String and the wrapper classes Byte,
Short, Integer, Long, Float, Double, Boolean, and Character. When you
don’t look at the source code of the base class and see that it’s marked
final, it’s easy to overlook that the classes that extend it won’t compile.
Though the authors of this exam claim not to include trick questions,
they also state that they expect the candidates to know “their stuff.”
EXAM TIP

The enumerated types share some characteristics of the final keyword. Enumerated
types enable you to define a new type, but with a predefined set of objects. Let’s see
how in the next section.

2.3

Enumerated types
[2.5] Use enumerated types
Think of the courses offered by a university or maybe even the roles within an organization: each defines a finite and predefined set of objects. These finite and predefined

Licensed to Mark Watson 

123

Enumerated types

sets of objects can be defined as enumerated types, or enums. An enum defines a new
custom data type (like interfaces and classes). Users are allowed to use only existing
enum objects; they can’t create new enum objects. Type safety was the main reason
for introducing enumerated types in Java version 5.0, discussed further in the following section.

2.3.1

Understanding the need for and creating an enum
Let’s assume that you have been assigned the task of creating a gaming application
that can be played at exactly three levels: beginner, intermediate, and expert. How
would you restrict your variable to be assigned only these three values?
You can accomplish this by creating an enum. An enum enables you to create a
type, which has a fixed set of constants. Following is an example of the enum Level,
which defines three programming levels:
enum Level { BEGINNER, INTERMEDIATE, EXPERT }

The enum values are
constant values.

An enum lets you define a new type, the way classes and interfaces enable you to
define your own types. The preceding line of code creates a new type, Level, which
defines the constants BEGINNER, INTERMEDIATE, and EXPERT of type Level. (Syntactically, you can use any case for defining these constant values, but following Oracle’s
recommendation of using uppercase letters for constant values will save you a lot of
headaches.) These constants are also static members and are accessible by using the
name of the enum in which they’re defined.
You can assign a gaming level, defined by the enum Level for a game. Let’s define
a class, Game, which defines an instance variable, gameLevel, of type Level, as follows:
class Game {
Level gameLevel;
}

Variable of
type Level

Class GameApp defines a field game of type Game and initializes it as follows:
class GameApp {
Game game = null;
public void startGame () {
game = new Game();
game.gameLevel = Level.BEGINNER;
}

Assigns constant
BEGINNER

}

The class GameApp demonstrates the real benefit of all this enum business. Because the
variable gameLevel (defined in class Game) is of type Level, you can assign only one of the
constants defined in the enum Level—that is, Level.BEGINNER, Level.INTERMEDIATE,
or Level.EXPERT.
Let’s look into the finer details of enums, as discussed in the next section.

Licensed to Mark Watson 

124

2.3.2

CHAPTER 2 Advanced class design

Adding implicit code to an enum
When you create an enum, Java adds implicit code and modifiers to its members.
These details will help you explain the behavior of enum constants, together with how
to access and use them. Let’s work with the enum Level created in the previous section (2.3.1) and decompile its Level.class file, using a decompiler (like JD):
enum Level { BEGINNER, INTERMEDIATE, EXPERT }

A decompiler converts Java byte code (.class) to a Java source file (.java). The newly
created Java source file will include any implicit code that was added during the compilation process. Listing 2.2 shows decompiled enum Level (to make the code easier
to understand, I’ve added some comments):
Listing 2.2

Decompiled enum Level

b
final class Level
{
public static
public static
public static

extends Enum

enum is implicitly
declared final.

c

final Level BEGINNER;
final Level INTERMEDIATE;
final Level EXPERT;

enum constants are
implicitly public,
static, and final.

private static final Level $VALUES[];
static
{
BEGINNER = new Level("BEGINNER", 0);
INTERMEDIATE = new Level("INTERMEDIATE", 1);
EXPERT = new Level("EXPERT", 2);
$VALUES = (new Level[] {
BEGINNER, INTERMEDIATE, EXPERT
});
}
public static Level[] values()
{
return (Level[])$VALUES.clone();
}

f

public static Level valueOf(String s)
{
return (Level)Enum.valueOf(Level, s);
}
private Level(String s, int i)
{
super(s, i);
}

h

d
e

Array to store
reference to all
enum constants

Creation of
enum constants
occurs in static
initializer block

Method values return
an array of all enum
constants.

g

Method valueOf() parses a
String value and returns
corresponding enum constant

Private
constructor

}

In the decompiled code, at B you can notice that an enum is implicitly defined as
a final entity. At c you can notice that all enum constants are implicitly declared
as public, final, and static variables. The code at d defines an array to store a

Licensed to Mark Watson 

Enumerated types

125

reference to all enum constants. The variables are declared at c and d. They are
initialized in a static initializer block at e. Method values() returns an array of all
enum constants at f and valueOf() returns an enum constant for a corresponding
String value at g. The code at h defines a private constructor.
If the enum constants are themselves created in a static initializer block, when does
a static initializer block in an enum execute? See for yourself in the next “Twist in the
Tale” exercise.
Twist in the Tale 2.4

Let’s add some code to the enum Level so it defines a constructor and a static initializer block. Examine the code and determine the correct options that follow.
enum Level {
BEGINNER;
static{ System.out.println("static init block"); }
Level(){
System.out.println("constructor");
}
public static void main(String... args){
System.out.println(Level.BEGINNER);
}
}
a

constructor
static init block
BEGINNER

b

static init block
constructor
BEGINNER

c

constructor
static init block
beginner

d

static init block
constructor
beginner

In listing 2.2 you’ll notice that all enum constants of BEGINNER, INTERMEDIATE, and
EXPERT are created in the order they were defined and assigned an ordinal: 0, 1, and 2.
Are enum constants created in this manner? Let’s check it out in the next section.

2.3.3

Extending java.lang.Enum
All enums in Java extend the abstract class java.lang.Enum, defined in the Java API.
As always, it’s interesting to peek at the source code from the Java API, to understand
why some pieces of code behave in a particular way. Let’s look at the (partial) code of
class java.lang.Enum, which will help you get the hang of how the enum constants
are created, their order, and their default names. Please note that the comments

Licensed to Mark Watson 

126

CHAPTER 2 Advanced class design

aren‘t part of the code from class java.lang.Enum. These comments have been added
to clarify the code for you.
Listing 2.3 Partial code listing of class java.lang.Enum

public abstract class Enum>
implements Comparable, Serializable {
private final String name;
private final int ordinal;
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
public final String name() {
return name;
}
//.. rest of the code

Name of the
enum constant
Position of
enum constant
Name and position of
enum constant is saved
on its creation

Default implementation
of toString() returns
name of enum constant
Method name() is marked final
and can’t be overridden; returns
enum constant’s name.

}

The class Enum defines only one constructor with String and int parameters to
specify its name and ordinal (order). Every enum constant is implicitly assigned an
order on its creation. Let’s refer back to the example of enum Level as defined in
listing 2.2. The enum constant values BEGINNER, INTERMEDIATE, and EXPERT are created within the enum Level, in its static initializer block (refer to code listing 2.1),
as follows:
public static final level BEGINNER = new Level ("BEGINNER", 0);
public static final level INTERMEDIATE = new Level ("INTERMEDIATE", 1);
public static final level EXPERT = new Level ("EXPERT", 2);

EXAM TIP

Watch out for exam questions that use methods like

Collections.sort() from the Collections API to sort enum constants.

The default order of enum constants is their order of definition. The
enum constants aren’t sorted alphabetically.
Now, examine the following code:
public class TestEnum {
public static void main(String args[]) {
System.out.println(Level.BEGINNER.name());
System.out.println(Level.BEGINNER);
}
}

Prints
“BEGINNER”

Licensed to Mark Watson 

Also prints “BEGINNER”
by calling method
toString()

127

Enumerated types

Note both methods—toString() and name() defined in java.lang.Enum—return the
value of the instance variable name (revisit code listing 2.3—class java.lang.Enum
defines an instance variable name). Because method name() is a final method, you
can’t override it. But you can override method toString() to return any description
that you want.
For an enum constant BEGINNER in enum Level, calling
System.out.println(Level.BEGINNER) returns the name of the enum
constant—that is, BEGINNER. You can override toString() in an enum to
EXAM TIP

modify this default return value.
Because a class can extend from only one base class, an attempt to make your enum
extend any other class will fail. The following code won’t compile:
class Person {}
enum Level extends Person { BEGINNER, INTERMEDIATE, EXPERT }

Won’t
compile

But you can make your enum implement any number of interfaces. A class can extend
only one base class but can implement multiple interfaces. The following code compiles successfully:
interface MyInterface {}
enum Level implements MyInterface { BEGINNER, INTERMEDIATE, EXPERT }
You can’t explicitly make a class extend java.lang.Enum:
class MyClass extends java.lang.Enum {}

Will
compile

Won’t
compile

An enum implicitly extends java.lang.Enum, so it can’t extend
any other class. But a class can’t explicitly extend java.lang.Enum.

EXAM TIP

2.3.4

Adding variables, constructors, and methods to your enum
You can add variables, constructors, and methods to an enum. You can also override
the nonfinal methods from the java.lang.Enum class. Following is an example:
enum IceCream {
VANILLA, STRAWBERRY, WALNUT, CHOCOLATE;

b

c

private String color;
public String getColor() {
return color;
}
public void setColor(String val) {
color = val;
}

d

enum constants
Instance variable in
enum IceCream

Method
getColor()

e

Method
setColor()

Licensed to Mark Watson 

128

CHAPTER 2 Advanced class design
public String toString() {
return "MyColor:"+ color;
}

f

Override method
toString()

}

The code at B defines a list of enum constants: VANILLA, STRAWBERRY, WALNUT, and
CHOCOLATE in enum IceCream. Note that this constant list must be the first in the
enum definition and should be followed by a semicolon. A semicolon is optional if
you don’t add methods and variables to your enum. The code at c defines an
instance variable color in enum IceCream. The code at d and e adds methods getColor() and setColor() to enum IceCream. The code at f overrides the public
toString() method inherited from class java.lang.Enum.
The enum constant list must be the first in the enum definition and should be followed by a semicolon. A semicolon is optional if
you don’t add methods and variables to your enum.

EXAM TIP

You can call the methods defined in the preceding example, as follows:
public class UseIceCream {
public static void main(String[] args) {
IceCream.VANILLA.setColor("white");
System.out.println(IceCream.VANILLA.getColor());
System.out.println(IceCream.VANILLA);
}
}

b
c
d

The output of this code is as follows:
white
MyColor:white

Are you thinking that the code at B is invalid because enum values are constant values? Note that this code is absolutely valid. VANILLA is a type of enum IceCream, and
you can call methods that are available to it. The code at c calls the method getColor(), and the code at d calls method toString(), which was overridden in enum
IceCream.
You can also define constructors in your enum and override methods that apply
only to particular enum constants, as follows in listing 2.4 (modifications in bold):
Listing 2.4

enum IceCream with custom constructor and constant specific class body

enum IceCream {
VANILLA("white"),
STRAWBERRY("pink"),

Licensed to Mark Watson 

129

Enumerated types

This method
can’t be
executed.

c

WALNUT("brown") {
public String toString() {
return "WALNUT is Brown in color";
}
public String flavor() {
return "great!";
}
},
CHOCOLATE("dark brown");
private String color;
IceCream(String color) {
this.color = color;
}
public String toString() {
return "MyColor:" + color;
}

d

b

Methods defined
between { and }
are available only
to enum constant
WALNUT.

Constructor that
accepts string

}

The code at B, known as a constant specific class body, defines overridding methods
for a particular enum constant, WALNUT. The code at d defines a constructor for
enum IceCream, but it can be used only within an enum. A constructor in an enum
can be defined only with default or private access; public and protected access levels aren’t allowed.
An enum can’t define a constructor with public or protected
access level.
EXAM TIP

In the preceding code, it might be strange to note that though you can define a
method flavor() at c, you can’t call it, as follows:
public class UseIceCream {
Prints
public static void main(String[] args) {
“MyColor:white”
System.out.println(IceCream.VANILLA);
System.out.println(IceCream.WALNUT);
Prints “WALNUT is
//System.out.println(IceCream.WALNUT.flavor());
Brown in color”
}
}
Won’t compile

This behavior can be attributed to WALNUT creating an anonymous class and overriding
the methods of enum IceCream. But it’s still referenced by a variable of type IceCream,
which doesn’t define the method flavor. If this leaves you guessing about what all this
stuff with anonymous classes is, you can go grab a glass of anonymous inner classes in
section 2.4.4.
Let’s see how class IceCream’s constant WALNUT returns a custom value for its
toString() method, that it overrides in its constant specific class body. In the following example, class IceCreamParlor uses method values() to access all enum constants and outputs their values:
class IceCreamParlor {
public static void main(String args[]) {

Licensed to Mark Watson 

130

CHAPTER 2 Advanced class design
for (IceCream ic : IceCream.values())
System.out.println(ic);
}
}

Method values()
returns an array of
all enum constants.

The output of the preceding code is
MyColor:white
MyColor:pink
WALNUT is Brown in color
MyColor:dark brown

In the preceding output, notice how the String representation of WALNUT differs from
the other enum constants.
An enum constant can define a constant specific class body and
use it to override existing methods or define new variables and methods.

EXAM TIP

2.3.5

Where can you define an enum?
You can define an enum as a top-level enum, or as a member of a class or an interface.
Until now, you worked with a top-level enum. The following code shows you how to
define an enum as a member of another class or interface:
class MyClass {
enum Level { BEGINNER, INTERMEDIATE, EXPERT }
}
interface MyInterface {
enum Level { BEGINNER, INTERMEDIATE, EXPERT }
}

enum as a member
of other class
enum as a member
of an interface

But you can’t define an enum local as a method. For example, the following code
won’t compile:
class MyClass {
void aMethod() {
enum Level { BEGINNER, INTERMEDIATE, EXPERT }
}
}

At the end of this section on enums, let’s revisit the important rules that you should
remember for the exam.

Rules to remember about enums
■

■

An enum can define a main method. This means that you can define an enum
as an executable Java application.
The enum constant list must be defined as the first item in an enum, before the
declaration or definition of methods and variables.

Licensed to Mark Watson 

131

Enumerated types

(continued)
■ The enum constant list might not be followed by a semicolon, if the enum
doesn’t define any methods or variables.
■ When an enum constant overrides an enum method, the enum constant creates
an anonymous class, which extends the enum.
■ An enum constant can define a constant specific class body and use it to override existing methods or define new variables and methods.
■ An enum implicitly extends java.lang.Enum, so it can’t extend any other class.
But a class can’t explicitly extend java.lang.Enum. An enum can implement
interface(s).
■ An enum can never be instantiated using the keyword new.
■ You can define multiple constructors in your enums.
■ An enum can’t define a constructor with public or protected access level.
■ An enum can define an abstract method. Just ensure to override it for all your
enum constants.
■ The enum method values() returns a list of all the enum constants.
■ An enum can be defined as a top-level enum, or as a member or another class
or interface. It can’t be defined local to a method.

When you’re consuming a lot of information, missing the small and simple details is
easy. Let’s check whether you remember and can spot some basic information about
enums in the next “Twist in the Tale” exercise.
Twist in the Tale 2.5

Let’s modify the code used in enum IceCream in this section. Examine the code and
determine the correct options that follow.
public enum IceCreamTwist {
VANILLA("white"),
STRAWBERRY("pink"),
WALNUT("brown"),
CHOCOLATE("dark brown");
String color;
IceCreamTwist(String color) {
this.color = color;
}
public static void main(String[] args) {
System.out.println(VANILLA);
System.out.println(CHOCOLATE);
}

//line1
//line2

}
a
b

Compilation error: Can’t run an enum as a standalone application.
Compilation error at (#1) and (#2): Can’t access VANILLA and CHOCOLATE in a
static main method.

Licensed to Mark Watson 

132

CHAPTER 2 Advanced class design

c

No errors. Output is
VANILLA
CHOCOLATE

d

No errors. Output is
white
dark brown

An enum defines a new type with limitations. Similarly, nested and inner classes define
a new type with constraints on their use. Defined within another class, nested and
inner classes are characterized with a different set of behavior that sets them apart
from the top-level classes. Let’s uncover these details in the next section.

2.4

Static nested and inner classes
[2.4] Create top-level and nested classes
A nested class is a class defined within another class. Nested classes that are declared as
static are referred to as static nested classes. Nested classes that aren’t declared as static
are referred to as inner classes. Like a regular top-level class, an inner or static nested
class can define variables and methods.
You can also define inner classes within methods and without a name. Figure 2.5
shows the types of inner classes, distinguished by their placement within the top-level
class and whether they are defined as and with static members.
Before we dive into a detailed discussion of all the flavors of the inner classes,
table 2.2 provides a quick definition of the types of inner classes.
Table 2.2 Flavors of inner classes and their definitions
Type of inner class

Description

Static or static nested
class

Is a static member of its enclosing class and can access all the static variables and members of its outer class

Inner or member class

Is an instance member of its enclosing class. It can access all the
instance and static members of its outer class, including private members.

Method local inner class

Is defined within a method. Local inner classes are local to a method. They
can access all the members of a class, including its private members, but
they can be accessed only within the method in which they’re defined.

Anonymous inner class

Is a local class without a name

For the exam, you’ll need to know why inner classes and static nested classes are
important in the design of an application, their advantages and disadvantages, and
how to create and use them. Let’s start with a discussion of the advantages of inner

Licensed to Mark Watson 

133

Static nested and inner classes
Class Outer {
class Inner{

static class StaticNested{

}

}

Outer class

Inner class

Local inner class

Anonymous
inner class

Static nested class

void foo(){
class LocalInner{}
}

static void foo(){
class LocalInner{}
}

Object foo(){
return new Object{}{
public String toString(){
return "anonymous";
}
};
}

static Object foo(){

}

return new Object{}{
public String toString(){
return "anonymous";
}
};
}

Nonstatic

Static

Figure 2.5 An outer class showing all types of inner classes that it can define: inner class, static
nested class, local inner class, and anonymous inner class

classes, followed by a detailed discussion of all these classes. At the end of this section,
we’ll discuss their disadvantages.

2.4.1

Advantages of inner classes
Inner classes offer multiple advantages. To start, they help you objectify the functionality of a class, within it. For example, you might define a class Tree, which defines
operations to add objects, remove objects, and sort them based on a condition.
Instead of defining methods and variables to sort them within the class Tree, you
could encapsulate sorting functionality within another class TreeSort. Because the
class TreeSort would always work with Tree and might not be needed outside the class
Tree, TreeSort can be defined as an inner class within class Tree. Another example
for using inner classes is as parameter containers. Instead of using long method signatures, inner classes are often used to keep method signatures compact by passing reference parameters of inner classes instead of a long list of individual parameters.
Just as you can organize your top-level classes by using packages, you can further
organize your classes by using inner classes. Inner classes might not be accessible to all
other classes and packages.

Licensed to Mark Watson 

134

CHAPTER 2 Advanced class design

Inner classes also offer a neat way to define callback methods. For example, consider a user-interface-intensive GUI application, which defines multiple controls (buttons, keys, and screen) to accept a user’s input. These user controls should register
listeners, which are classes that define methods that are called back, when a user control receives an input. Instead of defining a single class to handle all callback methods
for multiple user controls, you can use inner classes to define callback methods for
individual user controls.
Let’s start with the simplest type of inner class: a static nested class.

2.4.2

Static nested class (also called static inner class)
A static nested class is a static class that’s defined (nested) within another class. It’s
referred to as a nested class and not an inner class because it isn’t associated with any
instance of its outer class. You’d usually create a static nested class to encapsulate partial functionality of your main class, whose instance can exist without the instance of
its outer class. It can be accessed like any other static member of a class, by using the
class name of the outer class. A static nested class is initialized when it’s loaded with its
outer class in memory. Figure 2.6 shows a static nested class.

Class Outer {
class Inner{

static class StaticNested{

}

}

Outer class

Inner class

In this section:
Static nested class

Local inner class

Anonymous
inner class

Static nested class

void foo(){
class LocalInner{}

static void foo(){
class LocalInner{}

}

}

Object foo(){
return new Object{}{
public String toString(){
return "anonymous";

static Object foo(){
return new Object{}{
public String toString(){
return "anonymous";

}

}

};

};
}

}
}

Nonstatic

Static

Figure 2.6 A static nested class within an outer class

Licensed to Mark Watson 

Static nested and inner classes

135

In the following (simplified) example, class DBConnection defines a static nested class,
DBConnectionCache, which creates and stores database connections with default connection values. When requested a database connection, class DBConnection checks if a
default connection for the specified database exists. If yes, it returns the default connection; otherwise it creates and returns a new connection.
class DBConnection {
public DBConnection (String username, String pwd, String URL) {
// code to establish a Database connection
}
public DBConnection OracleConnection
(String username, String pwd, String URL) {
DBConnection conn = DBConnectionCache.getDefaultOracleConnection();
if (conn != null) {
return conn;
}
else {
//establish and return new DBconnection using method parameters
}
}
/*
* Oversimplified version of a static nested class which uses default
* values to establish DB connections and store them in a static array
*/
static class DBConnectionCache {
static DBConnection connections[];
static {
connections = new DBConnection[3];
connections[0] = new DBConnection
(/*arguments to establish a connection to an ORACLE DB*/);
connections[1] = new DBConnection
(/*arguments to establish a connection to a MySQL DB*/);
}
static DBConnection getDefaultOracleConnection() {
return connections[0];
}
static DBConnection getDefaultMySQLConnection() {
return connections[1];
}
}
}

EXAM TIP In the preceding example, access to nested class DBConnectionCache can be restricted by using an appropriate access modifier with its

definition.
In the next section, you’ll work with one of the most important points to be tested on
the exam—how to instantiate static nested classes.

Licensed to Mark Watson 

136

CHAPTER 2 Advanced class design

Let’s code class StaticNested as shown in figure 2.6:
class Outer {
static int outerStatic = 10;
int outerInstance = 20;
static class StaticNested {
static int innerStatic = 10;
int innerInstance = 20;
}
}

A static nested class isn’t usually referred to as an inner class,
because it isn’t associated with an object of the outer class.

NOTE

When you create a static nested class, it’s compiled as a separate class file. The .class
file for a static nested file includes the name of its outer class. On compiling the code
shown in the preceding example, the compiler generates two .class files, Outer.class
and Outer$StaticNested.class.
As with a regular top-level class, a static nested class is a type and you can instantiate it. Multiple separate instances of a static nested class can be created. Each instance
of the static nested class can have a different value for its instance variables. Let’s
instantiate the StaticNested class from the preceding example code:
class Outer {
static int outerStatic = 10;
int outerInstance = 20;
static class StaticNested {
static int innerStatic = 10;
int innerInstance = 20;
}
public static void main(String args[]) {
StaticNested nested1 = new StaticNested();
Outer.StaticNested nested2 = new Outer.StaticNested();

Modify only
the value of
innerInstance
for nested1.

nested1.innerStatic = 99;
nested1.innerInstance = 999;

When static nested
class is instantiated
within its outer class,
it doesn’t need to be
prefixed with its
outer class name
(though it can).
Modify the value
of innerStatic for
all instances of
StaticNested.

System.out.println(nested1.innerStatic + ":" +
nested1.innerInstance);
System.out.println(nested2.innerStatic + ":" +
nested2.innerInstance);

Prints
“99:999”

Prints
“99:20”

}
}

When a static nested class is instantiated outside its outer class, you must prefix it with
its outer class name:
When static nested class is instantiated outside its outer
class, it must be prefixed with its outer class name
class AnotherClass {
Outer.StaticNested nested1 = new Outer.StaticNested();
StaticNested nested2 = new StaticNested();
}

Licensed to Mark Watson 

Won’t compile

137

Static nested and inner classes

StaticNested one = new StaticNested();
Outer.StaticNested two = new Outer.StaticNested();
StaticNested three = new Outer.new StaticNested();
StaticNested four = new Outer().new StaticNested();
StaticNested five = Outer.new StaticNested();
Figure 2.7 Correct and incorrect instantiation of a static nested class

For the exam, it’s important to remember the syntax of instantiating a static nested
class: the count of operator new and its placement. It uses the new operator once, just
before the name of the static nested class. Figure 2.7 highlights correct and incorrect
instantiation code snippets.
Another point that you must remember when instantiating a static nested class is
when to prefix the name of a static nested class with its outer class. Figure 2.8 shows

Hi

Hi

StaticNested !

Outer.StaticNested !

Hi Shreya!
Hi Paul!
Within
class
Outer

Method
Shreya()

Method
Paul()

Class
StaticNested

Hi

Hi

StaticNested !

Outer.StaticNested !

Hi Paul!
Who is that
girl calling?
Outside
class
Outer

Method
Shreya()

Method
Paul()

Class
StaticNested

Figure 2.8 An interesting way to remember that you must prefix the name of the static inner class
with its outer class when referring to it outside its outer class.

Licensed to Mark Watson 

138

CHAPTER 2 Advanced class design

an interesting way to remember that you must prefix the name of the static inner
class with its outer class when you’re referring to it outside its outer class. For the
rest of the cases you might, but it isn’t mandatory, prefix the name of the static
nested class with its outer class. In figure 2.8 when method Shreya doesn’t prefix
StaticNested with its outer class, outside the class Outer, StaticNested doesn’t seem
to recognize the call.
ACCESSING MEMBERS OF A STATIC NESTED CLASS

To access the static members of a static nested class, you need not create an object of
this class. You need an object of a static nested class to access its instance members.
Here’s an example:
Object of StaticNested class required to
access its instance members
class Outer1 {
public static void main(String args[]) {
System.out.println(new Outer.StaticNested().innerInstance);
System.out.println(Outer.StaticNested.innerStatic);
}
}
Object of StaticNested class not required

to access its static members

On the exam, you might be asked whether you can instantiate a
static nested class, how to instantiate it, and whether it can define instance
or static members, or both.

NOTE

ACCESS LEVELS OF A STATIC NESTED CLASS

A static nested class can be defined using all access levels: private, default access,
protected, and public. The accessibility of the static nested class depends on its
access modifier. For example, a private static nested class can’t be accessed outside its
outer class. The access of a static nested class also depends on the accessibility of
its outer class. If the outer class is defined with the default access, an inner nested
class with public access won’t make it accessible outside the package in which its
outer class is defined.
MEMBERS OF OUTER CLASS ACCESSIBLE TO STATIC NESTED CLASS

A static nested class can access only the static members of its outer class. An example follows.
class Outer {
static int outerStatic = 10;
int outerInstance = 20;
static class StaticNested {
static int innerStatic = outerInstance;
int innerInstance = outerInstance;;
}

Can’t access instance variables
from a static nested class

}

Licensed to Mark Watson 

139

Static nested and inner classes

Rules to remember about static nested classes
■

■
■

■

■
■

2.4.3

To create an object of a static nested class, you need to prefix its name with the
name of its outer class (necessary only if you’re outside the outer class).
A static nested class can define both static and nonstatic members.
You need not create an object of a static nested class to access its static members. They can be accessed the way static members of a regular class are
accessed.
You should create an object of a static nested class to access its nonstatic
members, by using the operator new.
A static nested class can be defined using any access modifier.
A static nested class can define constructor(s).

Inner class (also called member class)
The definition of an inner class is enclosed within another class, also referred to as an
outer class. An inner class is an instance member of its outer class. An instance of an inner
class shares a special bond with its outer class and can’t exist without its instance. Figure 2.9 illustrates the placement of an inner class within an outer class.
Class Outer {
class Inner{

static class StaticNested{

}

}

Outer class

Inner class

In this section:
Inner class
Local inner class

Anonymous
inner class

Static nested class

void foo(){
class LocalInner{}
}

static void foo(){
class LocalInner{}
}

Object foo(){
return new Object{}{
public String toString(){
return "anonymous";

static Object foo(){
return new Object{}{
public String toString(){
return "anonymous";

}

}
};

};
}
}

}
Nonstatic

Figure 2.9 Placement of an inner class within an outer class

Licensed to Mark Watson 

Static

140

CHAPTER 2 Advanced class design

You’d usually create an inner class to encapsulate partial functionality of your main
class such that the existence of the inner class instance isn’t possible without its outer
class instance. This is in contrast to a nested static class, which can be used without an
instance of its outer class.
For example, the following code defines a class Tree and an inner class TreeSort.
Tree defines operations to add, remove, and sort objects based on a condition.
Instead of defining methods and variables to sort the tree elements within class Tree,
it encapsulates sorting functionality within class TreeSort. Class TreeSort would
always work with Tree and might not be needed without class Tree:
class Node {
Object value;
Instances of Node
Node left, right;
used to store Tree
}
elements
class Tree {
Tree() {}
Node rootNode;
void addElement(Object value) {
//.. code //
}
void removeElement(Object value) {
//.. code //
}
void sortTree(boolean ascending) {
new TreeSort(ascending).sort();
Defining sorting code
}
in a separate inner

class makes class Tree

class TreeSort{
simpler and cleaner.
boolean ascendingSortOrder = true;
TreeSort(boolean order) {
ascendingSortOrder = order;
}
void sort() {
// outer class’s rootNode and sort tree values
// sorting code can be complex
}
}
}

Figure 2.10 illustrates the bare-bones inner class Inner, defined within another class,
Outer, and how the compiler generates separate class files for the outer and inner classes.
Throughout this section, I refer to the concept of outer and inner classes as outer class
and inner class. I refer to names of the outer class and inner class by using code font.
You can create an outer class and inner class using any names. The
names of these classes are chosen as Outer and Inner so it’s easy for you
to recognize them.
NOTE

Licensed to Mark Watson 

141

Static nested and inner classes

Let me assign
separate space
to inner class

In

class Outer {
Java
compiler

class Inner{}

Outer.class

}
Outer$Inner.class
Out
Separate class files for
outer and inner classes

Figure 2.10 The compiler generates separate class files for an outer class and inner class.
The name of the inner class is prefixed with the name of the outer class and a $ sign.

CHARACTERISTICS OF INNER CLASSES

Because an inner class is a member of its outer class, an inner class can be defined
using any of the four access levels: public, protected, default access, and private.
Like a regular top-level class, an inner class can also define constructors, variables,
and methods. But an inner class can’t define nonfinal static variables or methods, as
shown in figure 2.11.

An inner class…
class Outer {
protected class Inner{
Inner(){}
public String publicInner = "Inner";
private int privateInner = 20;
//static int staticInner = 10;
//static void staticMethod(){}
}

Can be defined
using any
access modifier

Can define
constructors

}

Can define instance
variables and methods

Can’t define static
methods and nonfinal
static variables

Figure 2.11 Characteristics of an inner class: it can be defined using any access modifier, can
define constructors, and can define instance variables and methods. An inner class can define static
members variables but not static methods.

Licensed to Mark Watson 

142

CHAPTER 2 Advanced class design

CREATION OF AN INNER CLASS

Whenever you instantiate an inner class, remember that an instance of an inner class
can’t exist without an instance of the outer class in which it’s defined. Let’s look at creating an inner class:
■
■
■
■

Within an outer class, as an instance member
Within a method of an outer class
Within a static method of an outer class
Outside the outer class

First, a definition of a bare-bones outer class and inner class follows:
class Outer {
class Inner {}
}

Bare-bones
inner class

Bare-bones
outer class

Class Outer can instantiate inner class Inner as its instance member (additions in
bold), as follows:
class Outer {
Inner objectInner = new Inner();
class Inner {}
}

Creation of object of class
Inner in class Outer, as its
instance member

In the previous code, like all instance variables, objectInner can access an instance of
its outer class and its members, Outer. Similarly, an instance of an inner class created
within an instance method of an outer class can access the instance of its outer class.
So, you can instantiate class Inner within an instance method of class Outer, as follows
(additions in bold):
class Outer {
Inner in = new Inner();
class Inner {}
void aMethod () {
Inner objectInner = new Inner();
}
}

EXAM TIP

Instantiation of class
Inner in class Outer
within its method

You must have an outer class instance to create an inner class

instance.
Now, let’s try to instantiate class Inner within a static method of class Outer (additions
in bold):
class Outer {
class Inner {}
static void staticMethod() {
Inner in = new Inner();
}
}

b

Won’t
compile

Licensed to Mark Watson 

143

Static nested and inner classes

The code at B doesn’t compile because in method staticMethod() there’s no outer
class instance to tie the inner class instance to, which is required for creation of its
inner class Inner. But it’s possible to instantiate class Outer in the method staticMethod(). When you have an Outer instance, you can instantiate Inner:
class Outer {
class Inner {}
static void staticMethod () {
Outer outObj = new Outer();
Inner innerObj = outObj.new Inner();
}
}

b

Instance of Outer can
be created in method
staticMethod()

c

Instance of Inner is created
by calling operator new on
Outer instance

The code at B creates outObj, an Outer instance in the static method staticMethod(). outObj is used to create an instance of class Inner at c because you need
an outer class instance to create an inner class instance. It’s interesting to note that
the operator new is called on outObj to create the innerObj instance. Have you
invoked the operator new on an object earlier? The operator new is always used to
instantiate a class. But to instantiate an inner class by using an instance of an outer class,
you should invoke the operator new on the outer class’s instance.
Following is another way of creating the instance innerObj, using a single line
of code:
class Outer {
class Inner {}
static void staticMethod () {
Inner innerObj = new Outer().new Inner();
}
}

b

Single line of code creates
inner class object in outer
class’s static method

The code at B may look bizarre, because it contains two occurrences of the operator
new. The first occurrence of this operator is used to create an instance of Outer. The
second occurrence is used to create an instance of class Inner.
If another class wants to create an instance of class Inner, it needs an instance of
class Outer. If it doesn’t have one, it should first create one, just like with method
staticMethod() in the previous code snippet:
class Foo {
Inner inner;
Foo () {
Outer outer = new Outer();
inner = outer.new Inner();
}
}

Won’t
compile

Licensed to Mark Watson 

144

CHAPTER 2 Advanced class design

What makes the preceding code fail compilation? Inner isn’t a top-level class, so its
variable type should include the name of its outer class, Outer, so class Foo can find
this class. The following code compiles successfully and creates an Inner instance:
class Foo {
Outer.Inner inner;
Foo () {
Outer outer = new Outer();
inner = outer.new Inner();
}
}

Outside its outer class, the type
of inner class should include
the name of its outer class.

Similarly, a static method of class Foo can instantiate Inner, as follows:
class Foo {
public static void main(String args[]) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
}
}

The accessibility of an inner class outside its outer class depends
on the access modifier used to define the inner class. For example, an
inner class with default access can’t be accessed by classes in different
packages than the outer class.

EXAM TIP

WHAT CAN AN INNER CLASS ACCESS?

An inner class is a part of its outer class. Therefore an inner class can access all variables and methods of an outer class, including its private members and the ones that
it inherits from its base classes. An inner class can also define members with the same
name as its outer class, as shown in figure 2.12.

class Outer
private String privateOuter = "Outer";
private int sameName = 20;
class Inner{
String publicInner = privateOuter;

private variable of
class Outer accessible
in class Inner

int sameName = Outer.this.sameName;
}
}
Object of class Outer can
be accessed using Outer.this
in class Inner

Figure 2.12 An inner class can access all the members of its outer class, including its private
members. Outer class members with the same name as inner class members can be accessed
using Outer.this, where Outer is the name of the outer class.

Licensed to Mark Watson 

Static nested and inner classes

this

Class Outer

145

Refers to object of class Outer

Outer.this
this

Class Inner

Refers to object of class Inner

Figure 2.13 An inner class uses this to refer to its own object and
.this to refer to its outer class’s object.

An object uses the reference this to refer to its own object. An inner class can use the
reference this to refer to its own object, and the name of its outer class followed by
.this to refer to the object of its outer class, as shown in figure 2.13.
CAN AN INNER CLASS COEXIST WITH ONLY ITS OUTER CLASS?

Yes, an inner class can exist only with an object of its outer class. When a compiler
compiles an inner class, it seems to insert code in the inner class, which defines an
instance variable of its outer class, initialized using its constructor, as illustrated in figure 2.14.

class Outer {
class Inner{}

Code added
before byte code
generation

}
1

private final Outer this$0;
Inner (Outer outer){
this$0 = outer;
}

In
2

Out

Java
compiler

}
3

4

class Outer {
class Inner{

In

Out

Outer.class
Outer$Inner.class

Figure 2.14

Java instantiates an inner class by passing it an outer class instance.

Licensed to Mark Watson 

146

CHAPTER 2 Advanced class design

Rules to remember about inner classes
■

■

■

■

■

■
■
■

You can create an object of the inner class within an outer class or outside an
outer class.
When an inner class is created outside its outer class, its type name should
include the name of its outer class, followed by a dot (.) and then the name of
the inner class.
To create an inner class with a static method of an outer class, or outside an
outer class, call the operator new on the object of the outer class to instantiate
the inner class.
An inner class can’t define static methods. It can define final static variables but
nonfinal static variables aren’t allowed.
Members of the inner class can refer to all variables and methods of the outer
class.
An inner class can be defined with all access modifiers.
An inner class can define constructors.
An inner class can define variables and methods with any access level.

It’s time to attempt a quick exercise on inner classes in the following “Twist in the
Tale” exercise.
Twist in the Tale 2.6

Apart from modifying the code used in an earlier example, I have also modified the
class names for this exercise. Your task is to examine the following code and determine the correct answer option.
class Flower {
String color = "red";
Petal[] petals;
private class Petal {
public Petal() {System.out.println(color);}
String color = "purple";
// line 1
static final int count = 3;
// line 2
}
Flower() {
petals = new Petal[2];
// line 3
}
public static void main(String args[]) {
new Flower();
}
}

Licensed to Mark Watson 

Static nested and inner classes
a
b
c
d
e
f
g
h

147

Code prints red twice.
Code prints purple twice.
Code prints red three times.
Code prints purple three times.
Code prints nothing.
Code fails compilation due to code at (#1).
Code fails compilation due to code at (#2).
Code fails compilation due to code at (#3).

Anonymous classes are another type of inner class. As their title suggests, they don’t
have a name. In the next section, you’ll see why we need unnamed inner classes, and
when and how they are created.

2.4.4

Anonymous inner classes
As the name implies, an anonymous inner class isn’t defined using an explicit name. An
anonymous inner class is created when you combine instance creation with inheriting
a class or implementing an interface. Anonymous classes come in handy when you
wish to override methods for only a particular instance. They save you from defining
new classes. The anonymous class might override none, few, or all methods of the
inherited class. It must implement all methods of an implemented interface. The
newly created object can be assigned to any type of variable—static variable, instance
variable, local variable, method parameter, or returned from a method. Let’s start
with an example of an anonymous inner class that extends a class.
ANONYMOUS INNER CLASS THAT EXTENDS A CLASS
Let’s start with a class Pen:
class Pen{
public void write() {
System.out.println("Pen-write");
}
}

This is how a class, say, Lecture, would usually instantiate Pen:
class Lecture {
Pen pen = new Pen();
}

Licensed to Mark Watson 

148

CHAPTER 2 Advanced class design

Let’s replace this usual object instantiation by overriding method write, while instantiating Pen. To override method write() for this particular instance, we insert a class
definition between () and ; (() and ; are in bold):

b

Create an anonymous

class that extends
class Lecture {
class Pen.
Pen pen = new Pen()
{
public void write() {
System.out.println("Writing with a pen");
}
}
End class
;
definition for
End of creation of object
}
anonymous class.
referred to by variable pen,

c
d

Begin class definition
for anonymous class.
Override method
write() for instance
referred to by pen.

e

f

marked by semicolon.

The preceding code creates an anonymous class, which extends class Pen. The object
reference pen refers to an object of this anonymous class.
The code at B creates an object. Note that this line of code isn’t followed by a
semicolon. Instead, it’s followed by an opening brace at c, which starts the definition
of the anonymous class that extends Pen. The code at d overrides method write()
from the base class Pen. The closing brace at e marks the end of the definition of the
anonymous class. The code at f defines the semicolon, which is used to mark the end
of object creation, which started at B. I’ve deliberately placed the semicolon on a separate line so you can clearly identify the start and end of the anonymous class. It’s
usual to place this semicolon after the closing brace, used to mark the end of the
anonymous class.
You can create an anonymous class even if you don’t override any methods of
class Pen:
class Lecture {
static Pen pen = new Pen(){};

Create an anonymous class, which extends
Pen but doesn’t override its methods.

Prints a value similar to Pen@1034bb5;
public static void main(String args[]) {
new Pen() returns an object of Pen.
System.out.println(new Pen());
System.out.println(pen);
Prints a value similar to Lecture$1@15f5897;
}
pen refers to an instance of anonymous class
that extends Pen.

}

Similarly, you can pass an anonymous class instance to a method parameter. Now let’s
see an example of method notes() in class Lecture, which accepts a method parameter of type Pen:
class Pen{
public void write() {
System.out.println("Pen-write");
}
}

Licensed to Mark Watson 

Static nested and inner classes
class Lecture {
public void notes(Pen pen) {
pen.write();
}
}

149

Method notes() accepts
parameter of type Pen.

Here’s how another class—say, Student—calls notes() from class Lecture, subclassing
Pen, and passing the object to it:
class Student {
Call notes() by passing
void attendLecture() {
it newly baked object of
Lecture lecture = new Lecture();
anonymous subclass of Pen.
lecture.notes(new Pen() {
public void write() {
System.out.println("Okay! I am writing");
}
}
);
}
}

The preceding code can seem to be more complex than the previous example. To
understand it better, let’s build this code, line by line. In class Student, you call notes()
on object reference lecture:
class Student {
void attendLecture() {
Lecture lecture = new Lecture();
lecture.notes(/* need to pass Pen object */);
}
}

In the next step, I’ll replace the comment in the preceding code with new Pen(){}, so
I’m ready to subclass Pen (additions in bold):
class Student {
void attendLecture() {
Lecture lecture = new Lecture();
lecture.notes(new Pen(){});
}
}

I’ll insert the definition of overridden method write() within the curly brace {} of
the Pen anonymous inner class declaration, included in the method parameter to
notes() (additions in bold):
class Student {
void attendLecture() {
Lecture lecture = new Lecture();
lecture.notes(new Pen(){public void write() {
System.out.println("Okay! I am writing");
}});
}
}

Licensed to Mark Watson 

150

CHAPTER 2 Advanced class design

Let’s indent the code, to improve the readability:
class Student {
void attendLecture() {
Lecture lecture = new Lecture();
lecture.notes(new Pen(){
public void write() {
System.out.println("Okay! I am writing");
}
});
}
}

You can use an anonymous inner class to return a value from a method:
class Outer{
Object foo() {
return new Object() {
public String toString() {
return "anonymous";
}
};
}
}

Create an anonymous class
that subclasses class Object.
Override method toString()
from class Object.

ANONYMOUS INNER CLASS THAT IMPLEMENTS AN INTERFACE

Until now, you should have read that you can’t instantiate interfaces; you can’t use the
keyword new with an interface. Think again. Examine the following code, in which
the class BirdSanctuary instantiates the interface Flyable by using the keyword new:
interface Flyable{
void fly();
}

Use new to
class BirdSanctuary {
instantiate interface.
Flyable bird = new Flyable(){
public void fly() {
System.out.println("Flying high in the sky");
}
};
}

Don’t worry; you’ve been reading correctly that you can’t use the operator new with an
interface. The catch in the preceding code is that bird refers to an object of an anonymous inner class, which implements the interface Flyable.
The anonymous class used to instantiate an interface in the preceding code saved
you from creating a class beforehand, which implemented the interface Flyable.
EXAM TIP An anonymous inner class can extend at most one class or
implement one interface. Unlike other classes, an anonymous class can
neither implement multiple interfaces, nor extend a class and implement
an interface together.

Licensed to Mark Watson 

151

Static nested and inner classes

HOW TO ACCESS ADDITIONAL MEMBERS IN ANONYMOUS CLASSES

By using an anonymous class, you can override the methods from its base class or
implement the methods of an interface. You can also define new methods and variables in an anonymous class (in bold):
interface Flyable{
void fly();
}
class BirdSanctuary {
Flyable bird = new Flyable(){
public void fly() {
System.out.println("Flying high in the sky");
}
public void hungry(){
System.out.println("eat");
}
};
}

You can’t call the additional member, method hungry(), using the reference variable
bird. Why? The type of the reference variable bird is Flyable. So the variable bird
can access only the members defined in interface Flyable. The variable bird can’t
access additional methods and variables that are defined in anonymous classes that
implement it.
ANONYMOUS CLASS DEFINED WITHIN A METHOD

When an anonymous inner class is defined within a method, it can access only the
final variables of the method in which it’s defined. This is to prevent reassignment of
the variable values by the inner class. Examine the following example.
class Pizza{
Object margarita() {
String ingredient = "Cheese";
return new Pizza() {
public String toString() {
System.out.println(ingredient);
return "margarita";
}
};
}
}

b

Won’t
compile

The code at B will compile if ingredient is modified to be defined as a final
local variable.
The last type of inner class on this exam is a method local inner class, which can be
created within methods and code blocks like initializer blocks, conditional constructs,
or loops. I’ll discuss these in the next section.

Licensed to Mark Watson 

152

CHAPTER 2 Advanced class design

class Outer {
void outerMethod(){
class Inner{}

In

}
}
Separate class files

Java
compiler
Out

Outer.class
Outer$1Inner.class

Figure 2.15 Compiler generates separate class files for an outer class and method local
inner class. The name of the method local inner class is prefixed with the name Outer
class, a $ sign, and an integer.

2.4.5

Method local inner classes
The method local inner classes are defined within static or instance methods of a class.
Though these classes can also be defined within code blocks, they are typically created
within methods. So their discussion will be limited to their creation in methods. Figure 2.15 shows the definition of a bare-bones method local inner class, Inner, defined
within method outerMethod() of class Outer. The Java compiler generates separate
class files for the classes Outer and Inner. Because a class can define method local
inner classes with the same name in separate methods, the Java compiler adds a number to the name of the compiled file for the local inner classes.
A class can define multiple method local inner classes, with the same class name, in
separate methods, as follows:
class Outer {
void outerMethod () {
class Inner { }
}
static void outerMethod2 () {
class Inner { }
}
}

Class Inner defined in
method outerMethod()
Class Inner defined in
method outerMethod2()

For the preceding code, the Java compiler will generate three class files: Outer.class,
Outer$1Inner.class, and Outer$2Inner.class.
CHARACTERISTICS OF METHOD LOCAL INNER CLASSES

Recall that none of the variables within a method can be defined using any explicit
modifier (public, protected, private). Similarly, method local inner classes can’t
be defined using any explicit access modifier. But a method local inner class can

Licensed to Mark Watson 

153

Static nested and inner classes

define its own constructors, variables, and methods by using any of the four access
levels:
class Outer {
Can’t be defined using an
private int privateOuter = 10;
explicit access modifier
void outerMethod () {
class Inner {
protected Inner() {}
Can define its constructs, variables,
public int publicInner = 100;
and methods using any access level
int privateInner = privateOuter;
}
Can access all members,
}
including private members,
}
of its outer class

But a method local inner class can’t define static variables or static methods.
CREATION OF A LOCAL INNER CLASS

A method local inner class can be created only within the method in which it’s defined.
Also, its object creation can’t appear before the declaration of the local inner class, as
shown in the following code:
class Outer {
void outerMethod () {
//Inner in1 = new Inner();
class Inner {}
Inner in2 = new Inner();
}
}

Won’t
compile
Will
compile

WHAT CAN A METHOD LOCAL INNER CLASS ACCESS?

A method local inner class can access all variables and methods of its outer class,
including its private members and the ones that it inherits from its base classes. A
method local inner class can also define members with the same name as its outer
class. In this case, the members of the outer class can be referred to by using the name
of the outer class followed by the implicit reference this. Class Inner can access members of class Outer by using Outer.this, as shown in the following code:
class Outer {
private int privateOuter = 10;
void outerMethod () {
class Inner {
protected Inner() {}
public int publicInner = 100;
int privateInner = Outer.this.privateOuter;
}
}
}

2.4.6

Local inner class can
access all members
of its outer class,
including private
members.

Disadvantages of inner classes
Using inner classes is an advanced concept, and it can be difficult for inexperienced
programmers to identify, implement, and maintain them.

Licensed to Mark Watson 

154

CHAPTER 2 Advanced class design

On translation to byte code, inner classes can be accessed by classes in the same
package. Because inner classes can access the private members of their outer class,
they could break the designed encapsulation. You need to be careful when you create
the inner and static nested classes.

2.5

Summary
This chapter covers abstract classes, static and final keywords, enumerated types,
and nested and inner classes. The choice of identifying abstract classes isn’t straightforward. This chapter showed you simple examples so you’re well aware of their need,
importance, advantages, and shortcomings.
Application of the static and final nonaccess modifiers are important design
decisions. You should know about the Java entities that can use these modifiers,
together with how they change the default behavior of the entities. Incorrect design
decisions can make an application inefficient and difficult to extend or maintain.
This chapter covered enums that are used to create a new type with a finite and predefined set of objects. The definition of an enum can be as simple as including only the
name of enum constants, or as complex as including variables, constructors, and methods. The exam is sure to test you on the finer details of enums, all covered in this chapter.
Static nested classes, inner classes, anonymous inner classes, and method local
inner classes were also covered. An inner class shares an intimate relation with its
outer class. Inner classes help you objectify the functionality of a class. Identification
of an inner class is also an important design decision. It can help you further organize
your code and allow limited access to your inner classes. But an overdose of the inner
and nested classes can make your application difficult to work with and manage.

REVIEW NOTES
This section lists the main points covered in this chapter.

Abstract classes
■

■

■
■

■
■

An abstract class is defined by using the keyword abstract. It defines variables to
store the state of an object. It may define abstract and nonabstract methods.
An abstract class must not necessarily define abstract methods. But if it defines
even one abstract method, it must be marked as an abstract class.
An abstract class can’t be instantiated.
An abstract method doesn’t have any implementation. It represents a behavior
that’s required by all derived classes of an abstract class. Because the base class
doesn’t have enough details to implement an abstract method, the derived
classes are left to implement it in their own specific manner.
An abstract class can’t be instantiated.
An abstract class forces all its nonabstract-derived classes to implement the
incomplete functionality in their own unique manner.

Licensed to Mark Watson 

Review notes
■

■

■

■

■

■

155

A base class should be defined as an abstract class so it can implement the available details but still prevent itself from being instantiated.
An abstract class can be extended by both abstract and concrete classes. If an
abstract class is extended by another abstract class, the derived abstract class
might not implement the abstract methods of its base class.
If an abstract class is extended by a concrete class, the derived class must implement all the abstract methods of its base class, or it won’t compile.
A derived class must call its superclass’s constructor (implicitly or explicitly),
irrespective of whether the superclass or derived class is an abstract class or concrete class.
An abstract class can’t define abstract static methods. Because static methods
belong to a class and not to an object, they aren’t inherited. A method that
can’t be inherited can’t be implemented. Hence this combination is invalid.
Efficient use of an abstract class lies in the identification of an abstract class in
your application design so you can define common code for your objects and
leave the ones that are more specific, by defining them as abstract. You can
enforce the derived classes to implement these abstract methods.

Nonaccess modifier—static
■

■

■

■

■

■
■

Static members (fields and methods) are common to all instances of a class,
and aren’t unique to any instance of a class.
Static members exist independently of any instances of a class, and may be
accessed even when no instances of the class have been created.
Static members are also known as class fields or class methods because they are
said to belong to their class, and not to any instance of that class.
A static variable and method can be accessed using the name of an object reference variable or the name of a class.
A static method and variable can’t access nonstatic variables and methods of a
class. But the reverse works: nonstatic variables and methods can access static
variables and methods.
Static classes and interfaces are a type of nested classes and interfaces.
You can’t prefix the definition of a top-level class or an interface with the keyword static. A top-level class or interface is one that isn’t defined within
another class or interface.

Nonaccess modifier—final
■

■

You can’t reinitialize a final variable defined in any scope—class, instance, local,
or method parameter.
An instance final variable can be initialized either with its declaration in the initializer block or in the class’s constructor.

Licensed to Mark Watson 

156

CHAPTER 2 Advanced class design
■

■

■

■

■

■

■

■
■

■

■
■

■

A static final variable can be initialized either with its declaration or in the
class’s static initializer block.
You can’t initialize a final instance variable in an instance method because it
can’t be guaranteed to execute only once. Such a method won’t compile.
You can’t initialize a final static variable in a static method because it can’t be
guaranteed to execute only once. Such a method won’t compile.
If you don’t initialize a final local variable in a method, the compiler won’t complain, as long as you don’t use it.
If you try to access the value of a final local variable before assigning a value to
it, the code won’t compile.
The Java compiler considers initialization of a final variable complete only if the
initialization code will execute in all conditions. If the Java compiler can’t be
sure of execution of code that assigns a value to your final variable, it will complain (code won’t compile) that you haven’t initialized a final variable. If an if
construct uses constant values, the Java compiler can predetermine whether the
then or else blocks will execute. In this case, it can predetermine whether
these blocks of code will execute to initialize a final variable.
A final instance variable defined in a base class can’t be initialized in the
derived class. If you try to do so, your code won’t compile.
Final methods defined in a base class can’t be overridden by its derived classes.
Final methods are used to prevent a derived class from overriding the implementation of a base class’s method.
Private final methods in a base class aren’t inherited by derived classes. A
method defined using the same method signature in a derived class isn’t an
overridden method, but a new method.
A final class can’t be extended by any other class.
A class is defined as final so that it can’t be extended by any other class. This
prevents objects of derived classes from being passed on to reference variables
of their base classes.
An interface can’t be defined as final because an interface is abstract, by
default. A Java entity can’t be defined both as final and abstract.

Enumerated types
■
■
■
■

■

■

Enumerated types are also called enums.
An enum enables you to create a type, which has a fixed set of constants.
An enum can never be instantiated using the keyword new.
Unlike a class, which is defined using the keyword class, an enumerated type is
defined using the keyword enum, and can define multiple variables and methods.
If you define a variable of an enum type, it can be assigned constant values only
from that enum.
All enums extend the abstract class java.lang.Enum, defined in the Java API.

Licensed to Mark Watson 

Review notes
■

■
■

■

■

■

■

■

■
■

■
■
■

157

Because a class can extend from only one base class, an attempt to make your
enum extend any other class will fail its compilation.
The enum constants are implicit static members.
An enum can implement any interface, but its constants should implement the
relevant interface methods.
An enum can define an abstract method. Just ensure that you override it for all
your enum constants.
You can add instance variables, class variables, instance methods, and class
methods to your enums.
An enum can’t use instance variables in the overridden methods for a particular
enum constant.
You can override nonfinal methods from class java.lang.Enum, for individual
(or all) enum constants.
Your enums can also define constructors, which can be called from within
the enum.
You can define multiple constructors in your enums.
Enum constants can define new methods, but these methods can’t be called on
the enum constant.
You can define an enum as a top-level enum or within another class or interface.
You can’t define an enum local to a method.
An enum can define a main method.

Static nested classes
■

■

■
■
■

■

■

■

This class isn’t associated with any object of its outer class. Nested within its
outer class, it’s accessed like any other static member of a class—by using the
class name of the outer class.
A static nested class is accessible outside the class in which it’s defined by using
names of both the outer class and inner class.
You can define both static and nonstatic members in a static nested class.
A static nested class can define constructors.
To access the static members of a static nested class, you need not create an object
of this class. You need an object to access the instance members of this class.
The accessibility of the nested static class depends on its access modifier. For
example, a private static nested class can’t be accessed outside its class.
A static nested class can access only the static members of its outer class. Similarly, the outer class can access only the static members of its nested inner class.
An attempt to access instance members on either side will fail compilation
unless it’s accessed through an instance of the outer or static nested class.
All access levels can be used with this class—public, protected, default, and
private.

Licensed to Mark Watson 

158

CHAPTER 2 Advanced class design

Inner classes
■
■

■

■
■
■
■
■

■

An inner class is an instance member of its outer class.
An object of an inner class shares a special bond with its outer class and can’t exist
without its instance.
An inner class can be defined using any of the four access levels—public,
protected, default, and private.
Members of an inner class can refer to all variables and methods of an outer class.
An inner class can define constructors.
An inner class can define variables and methods with any access.
An inner class can’t define static methods and nonfinal static variables.
You can create an object of an inner class within an outer class or outside an
outer class.
Outside the outer class an inner class is instantiated using
Outer.Inner inner = new Outer().new Inner();

Anonymous inner classes
■

■

■

■

■

An anonymous inner class is created when you combine object instance creation with inheriting a class or implementing an interface.
An anonymous inner class might override none, few, or all methods of the
inherited class.
An anonymous inner class must implement all methods of the implemented
interface.
An instance of an anonymous class can be assigned to any type of variable
(static variable, instance variable, or local variable) or method parameter, or be
returned from a method.
The following line creates an anonymous inner class that extends Object and
assigns it to a reference variable of type Object:
Object obj = new Object(){};

■

The following line calls a method, say aMethod(), passing to it an instance of an
anonymous class that implements Runnable:
aMethod(new Runnable() {
public void run() {}
});

■

■

When an anonymous inner class is defined within a method, it can access only
the final variables of the method in which it’s defined. This is to prevent reassignment of the variable values by the inner class.
Though you can define variables and methods in an anonymous inner class,
they can’t be accessed using the reference variable of the base class or interface,
which is used to refer to the anonymous class instance.

Licensed to Mark Watson 

Sample exam questions

159

Method local inner classes
■

■

■
■

■

■

■

Method local inner classes are defined within a static or instance method of
a class.
A class can define multiple method local inner classes, with the same class
name, but in separate methods.
Method local inner classes can’t be defined using any explicit access modifier.
A method local inner class can define its own constructors, variables, and
methods by using any of the four access levels—public, protected, default,
and private.
A method local inner class can be created only within the method in which it’s
defined. Also, its object creation can’t appear before its declaration.
A method local inner class can access all variables and methods of its outer class,
including its private members and the ones that it inherits from its base classes.
It can only access the final local variables of the method in which it’s defined.
A method local inner class can define members with the same name as its outer
class. In this case, the members of the outer class can be referred to by using
Outer.this.

SAMPLE EXAM QUESTIONS
Q 2-1. Select the correct statement(s) based on the following code:
enum Keywords {
ASSERT(1.4),
DO, IF, WHILE;
double version = 1.0;

// line1
// line2
// line3

Keywords() {
this.version = 1.0;
}

// constructor 1
// constructor 1
// constructor 1

Keywords(double version) {
this.version = version;
}

// constructor 2
// constructor 2
// constructor 2

public static void main(String args[]) {
Keywords[] keywords = Keywords.values();
for (Keywords val:keywords) System.out.println(val);
}
}
a
b
c

d

e

The enum keywords won’t compile due to code at (#1).
The enum keywords won’t compile due to code at either (#2) or (#3).
If you swap the complete code at (#1) and (#2) with code at (#3), enum keywords will compile successfully.
The enum keywords will fail to compile due to the declaration of multiple
constructors.
None of the above

Licensed to Mark Watson 

160

CHAPTER 2 Advanced class design

Q 2-2. Consider the following definition of class Foo:
abstract class Foo {
abstract void run();
}

Which of the classes correctly subclass Foo? (Choose all that apply.)
a

class Me extends Foo {
void run() {/* ... */}
}

b

abstract class You extends Foo {
void run() {/* ... */}
}

c

interface Run {
void run();
}
class Her extends Foo implements Run {
void run() {/* ... */}
}

d

abstract class His extends Foo {
String run() {/* ... */}
}

Q 2-3. Which lines of code, when inserted at //INSERT CODE HERE, will print the following:
BASKETBALL:CRICKET:TENNIS:SWIMMING:
enum Sports {
TENNIS, CRICKET, BASKETBALL, SWIMMING;
public static void main(String args[]) {
// INSERT CODE HERE
}
}

d

for (Sports val:Sports.values()) System.out.print(val+":");
for (Sports val:Sports.orderedValues()) System.out.print(val+":");
for (Sports val:Sports.naturalValues()) System.out.print(val+":");
for (Sports val:Sports.ascendingValues()) System.out.print(val+":");

e

None of the above

a
b
c

Q 2-4. Given that classes Outer and Test are defined in separate packages and source
code files, which code options, when inserted independently at //INSERT CODE HERE,
will instantiate class Inner in class Test? (Choose all that apply.)
// Source code-Outer.java
package ejava.ocp;
public class Outer {
public static class Inner{}
}

Licensed to Mark Watson 

Sample exam questions

161

// Source code-Test.java
package ejava.exams;
import static ejava.ocp.Outer.Inner;
class Test {
//INSERT CODE HERE
}
a
b
c
d

Inner inner = new Inner();
Outer.Inner inner = new Outer.Inner();
Outer.Inner inner = new Inner();
Outer.Inner inner = Outer.new Inner();

Q 2-5. Given the following definition of classes Outer and Inner, select options that
can be inserted individually at //INSERT CODE HERE. (Choose all that apply.)
class Outer {
void aMethod() {
class Inner {
// INSERT CODE HERE
}
}
}
a
b
c
d
e
f
g
h

protected Inner() {}
final static String name = "eJava";
static int ctr = 10;
private final void Inner() {}
Outer outer = new Outer();
Inner inner = new Inner();
static void print() {}
static final void print() {}

Q 2-6. Given the following definition of enum Size, select the commented line number(s) in class MyClass, where you can insert the enum definition individually. (Choose
all that apply.)
enum Size {SMALL, MEDIUM, LARGE}
class MyClass {
// line1
void aMethod() {
//line2
}
class Inner {
//line3
}
static class StaticNested{
//line4
}
}

Licensed to Mark Watson 

162

CHAPTER 2 Advanced class design

a
b
c
d

The code at (#1)
The code at (#2)
The code at (#3)
The code at (#4)

Q 2-7. Given the following code, which option, when inserted at /*INSERT CODE
HERE*/, will instantiate an anonymous class referred to by the variable floatable?
interface Floatable {
void floating();
}
class AdventureCamp {
Floatable floatable = /*INSERT CODE HERE*/
}

f

new
new
new
new
new
new

g

None of the above

a
b
c
d
e

Floatable();
Floatable(){};
Floatable() { public void floating() {}};
Floatable() public void floating() {};
Floatable(public void floating() ) {}};
Floatable() { void floating() {}};

Q 2-8. What is the output of the following code? (Choose all that apply.)
interface Admissible {}
// line1
class University {
static void admit(Admissible adm) {
System.out.println("admission complete");
}
public static void main(String args[]) {
admit(new Admissible(){});
// line2
}
}
a
b
c
d

The class prints admission complete.
The class doesn’t display any output.
The class fails to compile.
University will print admission complete if code at (#2) is changed to the
following:
admit(new Admissible());

e
f

Class University instantiates an anonymous inner class.
If Admissible is defined as a class, as follows, the result of the preceding code
will remain the same:
class Admissible {}

Licensed to Mark Watson 

163

Sample exam questions

Q 2-9. Which of the following statements are correct? (Choose all that apply.)
a
b
c
d
e
f

An abstract class must define variables to store the state of its object.
An abstract class might define concrete methods.
An abstract class might not define abstract methods.
An abstract class constructor might be called by its derived class.
An abstract class can extend another nonabstract class.
An abstract class can’t define static final abstract methods.

Q 2-10. Which of the classes, when inserted at //INSERT CODE HERE, will create an
instance of class Inner? (Choose all that apply.)
class Outer {
class Inner {}
}
class Test {
//INSERT CODE HERE
}
a
b
c
d
e
f

g

h

Outer.Inner
Outer.Inner
Outer.Inner
Outer.Inner
Outer.Inner
Outer outer
Inner inner
Outer outer
Outer.Inner
Outer outer
Inner inner

inner
inner
inner
inner
inner
= new
= new
= new
inner
= new
= new

= new Outer.Inner();
= Outer().new Inner();
= Outer.new Inner();
= new Outer().new Inner();
= new Inner();
Outer();
Outer.Inner();
Outer();
= outer.new Inner();
Outer();
outer.Inner();

Q 2-11. Select the incorrect options. (Choose all that apply.).
a
b
c
d

e

An anonymous inner class always extends a class, implicitly or explicitly.
An anonymous inner class might not always implement an interface.
An anonymous inner class is a direct subclass of class java.lang.Object.
You can make an anonymous inner class do both—explicitly extend a userdefined class and an interface.
An anonymous inner class can implement multiple user-defined interfaces.

Q 2-12. Select the correct options for the classes Satellite and Moon:
abstract class Satellite{
static {
ctr = (int)Math.random();
}

// line1

Licensed to Mark Watson 

164

CHAPTER 2 Advanced class design
static final int ctr;
}
class Moon extends Satellite{
public static void main(String args[]) {
System.out.println(Moon.ctr);
}
}
a
b
c
d

// line2

// line3

Code at only (#1) fails compilation.
Code at either (#1) or (#2) fails compilation.
Code at (#3) fails compilation.
Code compiles and executes successfully.

Q 2-13. What is the output of the following code?
enum BasicColor {
RED;
static {
System.out.println("Static init");
}
{
System.out.println("Init block");
}
BasicColor(){
System.out.println("Constructor");
}
public static void main(String args[]) {
BasicColor red = BasicColor.RED;
}
}
a

Init block
Constructor
Static init

b

Static init
Init block
Constructor

c

Static init
Constructor
Init block

d

Constructor
Init block
Static init

Q 2-14. What is the output of the following code?
enum Browser {
FIREFOX("firefox"),
IE("ie"){public String toString() {return "Internet Browser";}},
NETSCAPE("netscape");
Browser(String name){}

Licensed to Mark Watson 

Sample exam questions

165

public static void main(String args[]) {
for (Browser browser:Browser.values())
System.out.println(browser);
}
}
a

FIREFOX
Internet Browser
NETSCAPE

b

FIREFOX
INTERNET BROWSER
NETSCAPE

c

FIREFOX
IE
NETSCAPE

d

firefox
ie
netscape

Q 2-15. What is the output of the following code?
class Base {
static {
System.out.print("STATIC:");
}
{
System.out.print("INIT:");
}
}
class MyClass extends Base {
static {
System.out.print("static-der:");
}
{
System.out.print("init-der:");
}
public static void main(String args[]) {
new MyClass();
}
}
a
b
c
d

STATIC:INIT:static-der:init-der:
INIT:STATIC:init-der:static-der:
STATIC:static-der:INIT:init-der:
static-der:init-der:STATIC:INIT:

Q 2-16. Select the correct statements. (Choose all that apply.).
a
b

An abstract class can’t define static final variables.
The abstract methods defined in an abstract base class must be implemented by
all its derived classes.

Licensed to Mark Watson 

166

CHAPTER 2 Advanced class design

c

d
e

An abstract class enforces all its concrete derived classes to implement its
abstract behavior.
An abstract class might not define static methods.
The initialization of the final variables defined in an abstract base class can be
deferred to its derived classes.

ANSWERS TO SAMPLE EXAM QUESTIONS
A 2-1. e
[2.3] Use the static and final keywords
[2.5] Use enumerated types
Explanation: The code compiles successfully. An enum can define and use multiple
constructors. The declaration of enum constants must follow the opening brace of the
enum declaration. It can’t follow the definition of variables or methods.
A 2-2. a, b
[2.1] Identify when and how to apply abstract classes
[2.2] Construct abstract Java classes and subclasses
Explanation: When a class extends another class or implements an interface, the methods in the derived class must be either valid overloaded or valid overridden methods.
Option (c) is incorrect. The concrete class Her extends Foo and implements Run.
To compile Her, it must implement run() with public access so that it implements
run() with default access in class Foo and run() with public access in interface Run:
class Her extends Foo implements Run {
public void run() {}
}

Because class Her in option (c) defines run() with default access, it fails to implement
public run() from interface Run and fails compilation.
Option (d) is incorrect. Method run() defined in class His and method run()
defined in class Foo don’t form either valid overloaded or overridden methods.
A 2-3. e
[2.5] Use enumerated types
Explanation: Option (a) is incorrect. The code in this option will print the natural
order of the definition of the enum (the order in which they were defined):
TENNIS:CRICKET:BASKETBALL:SWIMMING:

Options (b), (c), and (d) define nonexistent enum methods. Code in these options
won’t compile.

Licensed to Mark Watson 

Answers to sample exam questions

167

A 2-4. a
[2.4] Create top-level and nested classes
[2.5] Use enumerated types
Explanation: Due to the following static import statement, only the nested static class
Inner is visible in class Test:
import static ejava.ocp.Outer.Inner;

Class Outer isn’t visible in class Test. Options (b) and (c) will instantiate class Inner if
the following import statement is included in class Test:
import ejava.ocp.Outer;

A 2-5. a, b, d, e, f
[2.4] Create top-level and nested classes
Explanation: You can define final static variables in a method local inner class, but you
can’t define non-final static variables, static methods, or static final methods. You can
define constructors with any access modifier in a local inner class.
A 2-6. a, d
[2.4] Create top-level and nested classes
[2.5] Use enumerated types
Explanation: You can’t define an enum within a method or a nonstatic inner class.
A 2-7. c
[2.4] Create top-level and nested classes
Explanation: To instantiate an anonymous class that can be referred to by the variable
floatable of type Floatable, the anonymous class must implement the interface,
implementing all its methods.
Because interface Floatable defines method floating() (methods defined in an
interface are implicitly public and abstract), it must be implemented by the anonymous class. Only option (c) correctly implements method floating(). Following is its
correctly indented code, which should be more clear:
new Floatable() {
public void floating() {
}
};

Option (b) doesn’t implement floating(). Option (a) tries to instantiate the interface Floatable, which isn’t allowed. Option (f) looks okay, but isn’t because it makes
the method floating() more restrictive by not defining it as public.

Licensed to Mark Watson 

168

CHAPTER 2 Advanced class design

A 2-8. a, e, f
[2.4] Create top-level and nested classes
Explanation: Options (b) and (c) are incorrect because the class compiles successfully
and prints a value.
Option (d) is incorrect because it tries to instantiate the interface Admissible and
not an instance of the anonymous inner class that implements Admissible.
Option (e) is correct because code at (#2) instantiates an anonymous inner class,
which implements the interface Admissible. Because the interface Admissible
doesn’t define any methods, code at (#2) doesn’t need to implement any methods.
Option (f) is correct. If Admissible is defined as a class, the anonymous inner class
at (#2) will subclass it. Because Admissible doesn’t define any abstract methods, there
aren’t any added complexities.
A 2-9. b, c, e, f
[2.1] Identify when and how to apply abstract classes
Explanation: Note the use of can, must, and might or might not in these options.
Note that this isn’t a test on English grammar or vocabulary. The meaning of an exam
question will completely change depending on whether it uses “can” (feasible),
“must” (mandatory), or “might” (optional) in a question.
Option (a) is incorrect because it isn’t mandatory for an abstract class to define
instance variables.
Option (d) is incorrect because the constructor of all base classes—concrete or
abstract—must be called by their derived classes.
Option (f) is a correct statement. The combination of final and abstract modifiers is incorrect. An abstract method is meant to be overridden in the derived classes,
whereas a final method can’t be overridden.
A 2-10. d, g
[2.4] Create top-level and nested classes
Explanation: An inner class is a member of its outer class and can’t exist without its
instance. To create an instance of an inner class outside either the outer or inner class,
you must do the following:
■
■
■

If using a reference variable, use type Outer.Inner.
Access an instance of the Outer class.
Create an instance of the Inner class.

To create instances of both the Outer and Inner classes on a single line, you must use
the operator new with both the Outer class and Inner class, as follows:
new Outer().new Inner();

Licensed to Mark Watson 

Answers to sample exam questions

169

If you already have access to an instance of Outer class, say, outer, call new Inner() by
using outer:
outer.new Inner();

A 2-11. c, d, e
[2.4] Create top-level and nested classes
Explanation: Note that you have to select incorrect statements in this question. This
can be confusing, because most of the time on the exam, you’re asked to select correct options.
Option (a) is a correct statement. If an anonymous inner class extends a class, it
subclasses it explicitly. When it implements an interface, it implicitly extends class
java.lang.Object. If an anonymous inner class doesn’t subclass a class implicitly, it
implicitly extends java.lang.Object.
Option (b) is a correct statement. An anonymous inner class might not always
implement an interface.
Option (c) is an incorrect statement. An anonymous inner class isn’t always a
direct subclass of class java.lang.Object, if it extends any other class explicitly.
Option (d) is an incorrect statement. You can’t make an anonymous inner class
extend both a class and an interface explicitly. I deliberately used the words userdefined classes and user-defined interfaces so you wouldn’t assume that an anonymous class
implicitly subclasses a class from a Java API.
Option (e) is an incorrect statement. You can’t make an anonymous class implement multiple interfaces explicitly.
A 2-12. d
[2.2] Construct abstract Java classes and subclasses
[2.3] Use the static and final keywords
Explanation: No compilation or runtime issues exist with this code. A static initializer
block can access and initialize a static variable; it can be placed before the static variable declaration. A static variable defined in a base class is accessible to its derived
class. Even though class Moon doesn’t define the static variable ctr, it can access the
static variable ctr defined in its base class Satellite.
A 2-13. a
[2.3] Use the static and final keywords
[2.5] Use enumerated types
Explanation: The creation of enum constants happens in a static initializer block,
before the execution of the rest of the code defined in the static block. Here’s the
decompiled code for enum BasicColor, which shows how enum constants are initialized

Licensed to Mark Watson 

170

CHAPTER 2 Advanced class design

in the static block. To initialize an enum constant, its constructor is called. Note that
the contents of the default constructor and instance initializer blocks are added to the
new constructor implicitly defined during the compilation process:
final class BasicColor extends Enum
{
public static BasicColor[] values()
{
return (BasicColor[])$VALUES.clone();
}
public static BasicColor valueOf(String s)
{
return (BasicColor)Enum.valueOf(BasicColor, s);
}
private BasicColor(String s, int i)
{
super(s, i);
System.out.println("Init block");
System.out.println("Constructor");
}
public static void main(String args[])
{
BasicColor basiccolor = RED;
}
public static final BasicColor RED;
private static final BasicColor $VALUES[];
static
{
RED = new BasicColor("RED", 0);
$VALUES = (new BasicColor[] {
RED
});
System.out.println("Static init");
}
}

If you try to compile the preceding code it won’t compile because
classes can’t directly extend java.lang.Enum. I’ve included this code just to
show you how the Java compiler modifies code of an enum and adds additional code to it. It explains why an enum constructor executes before its
static block.

NOTE

A 2-14. a
[2.5] Use enumerated types
Explanation: An enum extends class java.lang.Enum, which extends class java.lang
.Object. Each enum constant inherits method toString() defined in class java
.lang.Enum. Class java.lang.Enum overrides method toString() to return the enum
constant’s name.

Licensed to Mark Watson 

Answers to sample exam questions

171

An enum constant can override any of the methods that are inherited by it. The
enum Browser defines a constructor that accepts a String method parameter, but it
doesn’t use it. All enum constants, except enum constant IE, print the name of the
constant itself.
A 2-15. c
[2.3] Use the static and final keywords
Explanation: When you instantiate a derived class, the derived class instantiates its
base class. The static initializers execute when a class is loaded in memory. So the
order of execution of static and instance initializer blocks is as follows:
■
■
■
■

1) Base class static initializer block
2) Derived class static initializer block
3) Base class instance initializer block
4) Derived class instance initializer block

A 2-16. c, d
[2.1] Identify when and how to apply abstract classes
[2.3] Use the static and final keywords
Explanation: Option (a) is incorrect. An abstract class can define static final variables.
Option (b) is incorrect. The abstract methods defined in an abstract base class
must be implemented by all its concrete derived classes. Abstract derived classes might
not implement the abstract methods from their abstract base class.
Option (e) is incorrect. The initialization of a final variable defined in an abstract
base class must complete in the class itself—with its initialization, in its initializer
block, or in its constructor. It can’t be deferred to its derived class.

Licensed to Mark Watson 

Object-oriented
design principles

Exam objectives covered in this chapter

What you need to know

[3.1] Write code that declares, implements, and/or extends interfaces

The need for interfaces. How to declare, implement,
and extend interfaces. Implications of implicit modifiers
that are added to an interface and its members.

[3.2] Choose between interface inheritance and class inheritance

The differences and similarities between implementing
inheritance by using interfaces and by using abstract or
concrete classes. Factors that favor using interface
inheritance over class inheritance, and vice versa.

[3.3] Apply cohesion, low-coupling, IS-A,
and HAS-A principles

Given a set of IS-A and HAS-A relationships, how to
implement them in code. Given code snippets, how to
correctly identify the relationships (IS-A or HAS-A) implemented by them.
How to identify and promote low coupling and high
cohesion.

[3.4] Apply object composition principles
(including HAS-A relationships)

Given that an object can be composed of multiple other
objects, how to determine the types of compositions—
and implement them in code.

[3.5] Design a class using the Singleton
design pattern

How to implement the Singleton design pattern. The
need for the existence of exactly one copy of a class.

[3.6] Write code to implement the DAO
pattern

The usability of the DAO pattern. How this pattern
enables separation of data access code in an application.

[3.7] Design and create objects using a
Factory pattern

The need for, use of, and benefits of a Factory for creating objects.
How this pattern is used in the existing Java API classes.

172

Licensed to Mark Watson 

Interfaces

173

Have you ever tried to find out the secret(s) behind the most successful people?
Almost all agree to follow a set of lifelong principles. So, articles like “Three Common
Habits of the Most Successful People” might include points similar to these:
■

■
■

Never hit the snooze button when the alarm goes off in the morning, so you
aren’t delaying your actions.
First things first: prioritize your work.
Follow your passion and do what you love, because you’ll be working almost all
your life.

These are the principles that successful people follow (though perhaps in a different
manner) to achieve the greatest height of success.
Similarly, object-oriented design (OOD) principles enable you to create better application designs, which are manageable and extensible. For example, as a programmer or
designer, you know that application requirements typically change. Implementing
these modified needs requires changes in the existing code, which usually introduces
bugs. Chances are that if the application design implements OOD principles, the modification task will require comparatively less effort. Again, as an example, if your application’s design uses the design principles of low coupling and high cohesion, chances
are low that changes in a class will affect another class.
Design patterns (for example, the Singleton pattern) also help you design better
applications. Unlike inheritance, a design pattern is an example of experience reuse
and not code reuse. Building sloping roofs in areas that receive a lot of snowfall can
be compared to using a design pattern. A sloping roof was identified as a solution to
avoid accumulation of snow on rooftops after multiple people faced issues with flat
roofs. Just as a sloping roof is applicable specifically to areas receiving snowfall, a
design pattern resolves a specific design issue.
Obviously, we can see that the object-oriented design principles are important, but
what specifically do you need to know for the exam? Well, the exam will test you on
what object-oriented design principles are and how to apply them in your applications. You’ll likely find questions on the creation of and preferred use of classes and
interfaces to design your application, and how to relate and use Java objects together.
In addition, you’ll need to know how to make the best use of design patterns in your
application. Yes, this is a lot, but I promise to walk you through each piece so you’re
prepared. This chapter covers
■
■
■
■
■
■
■
■

Interfaces—their declaration, implementation, and application
Choosing between class inheritance and interface inheritance
Relationships between Java objects
Application of object composition principles
Implementation of IS-A and HAS-A relationships between objects
Singleton design pattern
Data Access Object (DAO) design pattern
Factory design pattern

Licensed to Mark Watson 

174

CHAPTER 3 Object-oriented design principles

Interfaces are one of the most powerful concepts in Java. Believe me, not many
designers completely understand how to use them effectively in their design. To be a
good application designer, you must know how to do so. Let’s start with a quick example: in how many ways can you refer to your father? Apart from being your father, he
could also be referred to as a friend, guide, husband, swimmer, orator, teacher, manager, and much more. How can you achieve the same in Java, to refer to the same
object by using multiple types? In the next section, you’ll learn about the need for
using interfaces, followed by declaring, implementing, and extending them. Let’s
get started.

3.1

Interfaces
[3.1] Write code that declares, implements, and/or extends interfaces
Before we deep-dive into working with interfaces, note that the term interface has multiple meanings. First, an interface is a type created by using the keyword interface.
For example, the following code creates the interface Movable:
interface Moveable {
void move();
}

An interface in its second meaning is more general. It’s how two systems can interact
with each other (like your television and the remote control). It’s how classes can
interact with each other, using their public methods. For class Person, its interface
(public methods) refers to its methods eat() and work():
class Person {
public void eat() {}
public void work() {}
}

Figure 3.1 represents an interface as a type and as a group of public methods of a class.
Note that this exam objective refers to an interface as a type, which is created using
the keyword interface.

interface Movable{
void move();
}

Java
type

class Person{
public void eat(){}
public void work(){}
}

Public
methods

Figure 3.1 The term interface has two meanings: a type created using the keyword
interface and a group of public methods of a class.

Licensed to Mark Watson 

175

Interfaces

3.1.1

Understanding interfaces
We all, quite often, use interfaces in our lives. For example, when you refer to someone as a runner, do you care whether that person is also an orator, a parent, or an
entrepreneur? You care only that the person is able to run. The term runner enables
you to refer to unrelated individuals, by opening a small window to each person and
accessing behavior that’s applicable to only that person’s capacity as a runner. Someone can be referred to as a runner only if that person supports characteristics relevant
to running, though the specific behavior can depend on the person.
In the preceding example, you can compare the term runner to a Java interface,
which defines the required behavior run. An interface can define a set of behaviors
(methods) and constants. It delegates the implementation of the behavior to the
classes that implement it. Interfaces are used to refer to multiple related or unrelated
objects that share the same set of behavior. Figure 3.2 compares the interface runner
with a small window to an object, which is concerned only about the running capabilities of that object.
Similarly, when you design your application by using interfaces, you can use similar
windows (also referred to as specifications or contracts) to specify the behavior that you
need from an object, without caring about the specific type of objects. Separating the
required behavior from its implementation has many benefits. As an application
designer, you can use interfaces to establish the behavior that’s required from objects,
promoting flexibility in the design (new classes that implement an interface can be
created and used later). Interfaces make an application manageable, extensible, and
less prone to propagation of errors due to changes to existing types.
Orator

Parent

Entrepreneur

work

facilitate

invest

run

guide

run

conferences

run

expand

Window Runner
has limited access
to the objects
Figure 3.2 You can compare an interface with a window that can connect multiple
objects but has limited access to them.

Licensed to Mark Watson 

176

CHAPTER 3 Object-oriented design principles

interface Runner{
int speed();
double distance = 70;
}

interface Runner{
Becomes

public abstract int speed();
public static final double distance = 70;
}

Figure 3.3 All the methods of an interface are implicitly public and abstract. Its variables are implicitly
public, static, and final.

3.1.2

Declaring interfaces
You can define methods and constants in an interface. Declaring an interface is simple, but don’t let this simplicity take you for a ride. For the exam, it’s important to
understand the implicit modifiers that are added to the members of an interface. All
methods of an interface are implicitly public and abstract, and its variables are implicitly public, static, and final. Let’s start with the interface Runner that defines a method
speed() and a variable distance. Figure 3.3 shows how implicit modifiers are added
to the members of interface Runner during the compilation process.
Why do you think these implicit modifiers are added to the interface members?
Because an interface is used to define a contract, it doesn’t make sense to limit access
to its members—and so they are implicitly public. An interface can’t be instantiated,
and so the value of its variables should be defined and accessible in a static context,
which makes them implicitly static. Because an interface is a contract, its implementations shouldn’t be able to change it, so the interface variables are implicitly final.
Interface methods are implicitly abstract so that it’s mandatory for the classes to
implement them.
The exam will also test you on the various components of an interface declaration,
including access and nonaccess modifiers. Here’s the complete list of the components
of an interface declaration:
■
■
■
■
■

Access modifiers
Nonaccess modifiers
Interface name
All extended interfaces, if the interface is extending any interfaces
Interface body (variables and methods), included within a pair of curly braces {}

To include all the possible components, let’s modify the declaration of the interface Runner:
public strictfp interface Runner extends Athlete, Walker {}

The components of the interface Runner are shown in figure 3.4. To declare any interface, you must include the keyword interface; the name of the interface; and its body,
marked by {}.

Licensed to Mark Watson 

177

Interfaces

public

strictfp

interface

Runner

extends

Athlete, Walker

{ }

Access
modifier

Nonaccess
modifier

Keyword

Interface
name

Keyword

Name of interfaces
extended by
interface Runner

Curly
braces

Optional

Optional

Optional

Optional

Compulsory

Compulsory Compulsory

Figure 3.4 Components of an interface declaration

The optional and compulsory components of an interface can be summarized as
listed in table 3.1.
Table 3.1 Optional and compulsory components of an interface declaration
Compulsory

Optional

Keyword interface

Access modifier

Name of the interface

Nonaccess modifier

Interface body, marked by the opening
and closing curly braces {}

Keyword extends, together with the name of the base
interface(s). (Unlike a class, an interface can extend multiple
interfaces.)

The declaration of an interface can’t include a class name. An
interface can never extend any class.

EXAM TIP

Can you define a top-level, protected interface? No, you can’t. For the exam, you must
know the answer to such questions about the correct values for each component that
can be used with an interface declaration. Let’s dive into these nuances.
VALID ACCESS MODIFIERS FOR AN INTERFACE

You can declare a top-level interface (the one that isn’t declared within any other class or
interface), with only the following access levels:
■

public

■

No modifier (default access)

If you try to declare your top-level interfaces by using the other access modifiers
(protected or private), your interface will fail to compile. The following definitions
of the interface MyInterface won’t compile:
private interface MyInterface{}
protected interface MyInterface {}

Top-level interface can’t
be defined as private
Top-level interface can’t
be defined as protected

Licensed to Mark Watson 

178

CHAPTER 3 Object-oriented design principles

All the top-level Java types (classes, enums, and interfaces) can
be declared using only two access levels: public and default. Inner or
nested types can be declared using any access level.
EXAM TIP

VALID ACCESS MODIFIERS FOR MEMBERS OF AN INTERFACE

All members of an interface—variables, methods, inner interfaces, and inner classes
(yes, an interface can define a class within it!)—are public by default. Interfaces support only the public access modifier. Using other access modifiers results in compilation errors.

b
interface MyInterface {
private int number = 10;
protected void aMethod();
interface interface2{}
public interface interface4{}
}

Won’t
compile

Won’t
compile

interface2 is
implicitly prefixed
with public.

Interface member can
be prefixed with public

The code at B fails compilation with the following error message:
illegal combination of modifiers: public and private
private int number = 10;

SPECIAL CHARACTERISTICS OF METHODS AND VARIABLES OF AN INTERFACE

Methods in interfaces are public and abstract by default. The following methods
defined individually in an interface are equivalent:
int getMembers();
public abstract int getMembers();

Variables defined in interfaces are public, static, and final by default. The following
variables defined individually in an interface are equivalent:
int maxMembers = 100;
public static final int maxMembers = 100;

Because the interface variables are implicitly final, you can define only constants in an
interface. Ensure that you initialize these constants, or your code won’t compile:
interface MyInterface {
int number;
}

Won’t compile; variables
within interface must be
initialized.

VALID NONACCESS MODIFIERS FOR AN INTERFACE

You can declare a top-level interface with only the following nonaccess modifiers:
■
■

abstract
strictfp
NOTE The strictfp keyword guarantees that results of all floating-point

calculations are identical on all platforms.

Licensed to Mark Watson 

179

Interfaces

If you try to declare your top-level interfaces by using the other nonaccess modifiers
(final, static, transient, synchronized, or volatile), the interface will fail to
compile. All of the following interface declarations fail to compile:
final interface MyInterface {}
static interface MyInterface {}
transient interface MyInterface {}
synchronized interface MyInterface {}
volatile interface MyInterface {}

Won’t compile; invalid
nonaccess modifiers
used with interface
declaration

A nested interface can be defined using the nonaccess modifier static (any other
nonaccess modifier isn’t allowed):
class Outer {
static interface MyInterface1 {}
}

Nested
interface

With good coverage of interface declaration, let’s start making classes implement
interfaces.

3.1.3

Implementing interfaces
You can compare implementing an interface to signing a contract. When a concrete
class declares its implementation of an interface, it agrees to implement all its abstract
methods. A class can implement multiple interfaces. For example, class Home implements Livable and GuestHouse:
abstract
interface Livable {
method live()
void live();
}
abstract method
interface GuestHouse {
welcome()
void welcome();
}
class Home implements Livable, GuestHouse {
public void live() {}
public void welcome() {}
}

Class uses keyword
implements to
implement interface

If you don’t implement all the methods defined in the implemented interfaces, a class
can’t compile as a concrete class. Let’s modify the code of class Home, as follows:
class Home implements Livable, GuestHouse {
public void welcome() {}
}

The compiler says it all:
House.java:7: error: Home is not abstract and does not override
abstract method live() in Livable
class Home implements Livable, GuestHouse {
^
1 error

Licensed to Mark Watson 

180

CHAPTER 3 Object-oriented design principles

So a class can choose not to implement all the methods from the implemented
interface(s) and still compile successfully, but only if it’s defined as an abstract class,
as follows:
abstract class Home implements Livable, GuestHouse {
public void welcome() {}
}

Abstract class doesn’t have to
implement all methods from
implemented interfaces

EXAM TIP A concrete class must implement all the methods from the interfaces that it implements. An abstract class can choose not to implement all
the methods from the interfaces that it implements.

DEFINING AND ACCESSING VARIABLES WITH THE SAME NAME

A class can define an instance or a static variable with the same name as the variable
defined in the interface that it implements. In the following class, the interface Livable
defines variables status and ratings. Class Home implements Livable and defines
instance variable status and static variable ratings, with a default access level:
interface Livable {
boolean status = true;
public
int ratings = 10;
variables
}
class Home implements Livable {
boolean status;
Variables with
static int ratings = 7;
default access
Prints
Home() {
“false”
System.out.println(status);
System.out.println(Livable.status);
System.out.println(ratings);
System.out.println(Livable.ratings);

Prints
“true”
Prints “7”

}

Prints “11”

}

A class can define an instance or a static variable with the same
name as the variable defined in the interface that it implements. These
variables can be defined using any access level.

EXAM TIP

FOLLOWING METHOD OVERRIDING RULES FOR IMPLEMENTING INTERFACE METHODS

The methods in an interface are public, by default. So, trying to assign weaker access
to the implemented method in a class won’t allow it to compile:
interface Livable {
void live();
}
class Home implements Livable {
void live() {}
}

public
method
Won’t compile;
method implemented
using weaker access

Licensed to Mark Watson 

181

Interfaces

The compilation error message says it all:
House.java:8: error: live() in Home cannot implement live() in Livable
void live() {}
^
attempting to assign weaker access privileges; was public
1 error

EXAM TIP Because interface methods are implicitly public, the implementing class must implement them as public methods, or else the class
will fail to compile.

IMPLEMENTING MULTIPLE INTERFACES THAT DEFINE METHODS WITH THE SAME NAME

Methods in the interfaces don’t define any implementation; they come without any
baggage. But what happens if a class implements multiple interfaces that define methods with the same name? Let’s add a method live() to interface GuestHouse (modifications in bold):
interface Livable {
void live();
}
interface GuestHouse {
void welcome();
void live();
}

Interface Livable
defines method live().
Interface GuestHouse also
defines method live().

Class Home implements two interfaces, Livable and GuestHouse, both of which define
method live():
class Home implements Livable, GuestHouse {
public void live() {
System.out.println("live");
}
public void welcome() {
System.out.println("welcome");
}
}

Method live() in
Home has only one
implementation.

Both the Java compiler and Java Runtime Environment are good with the preceding
code. Because the signature of method live() is the same in both interfaces, Livable
and GuestHouse, class Home needs to define only one implementation for method
live() to fulfill both contracts (interface implementations).
OVERLAPPING METHOD IMPLEMENTATIONS WITH THEIR OVERLOAD VERSIONS

A class can try to implement multiple interfaces that define methods with the same
name. But in doing so, you can have a not-so-pleasant cocktail of overlapping method
implementations and their overloaded versions. We have two scenarios here:
■
■

Correctly overloaded methods
Invalid overloaded methods

Licensed to Mark Watson 

182

CHAPTER 3 Object-oriented design principles

Overloaded methods are defined by using the same name but a different parameter
list. For example, when implemented in class Home, method live() defined in the
interface Livable overloads method live() defined in the interface GuestHouse.
Class Home must implement both these methods:
interface Livable {
void live();
live() doesn’t accept any
}
method parameters.
interface GuestHouse {
void live(int days);
live() accepts a
}
method parameter.
class Home implements Livable, GuestHouse {
public void live() {
Correctly overloaded
System.out.println("live");
method live() from Livable
}
public void live(int days) {
Correctly overloaded
System.out.println("live for " + days);
method live() from
}
GuestHouse
}

You can’t define overloaded methods by changing only the return type of methods.
What happens if method live() in the interfaces Livable and GuestHouse returns
different types? In this case, class Home needs to implement both versions of method
live(), which can’t be qualified as overloaded methods. So class Home doesn’t compile in this case:
live() returns
interface Livable {
String.
String live();
}
live() returns
interface GuestHouse {
nothing—void.
void live();
}
class Home implements Livable, GuestHouse {
public String live() {
return null;
}
public void live() {
System.out.println("live" );
}
}

Class Home
won’t compile.
When implemented in class Home,
both versions of live() qualify as
incorrectly overloaded methods.

Here’s the compiler error for class Home:
Home.java:11: error: method live() is already defined in class Home
public void live() {
^
Home.java:7: error: Home is not abstract and does not override abstract
method live() in GuestHouse
class Home implements Livable, GuestHouse {
^

Licensed to Mark Watson 

183

Interfaces
Home.java:8: error: live() in Home cannot implement live() in GuestHouse
public String live() {
^
return type String is not compatible with void
3 errors

A class can implement methods with the same name from multiple interfaces. But these must qualify as correctly overloaded methods.

EXAM TIP

3.1.4

Extending interfaces
An interface can inherit multiple interfaces. Because all the members of an interface
are implicitly public, a derived interface inherits all the methods of its super interface(s). An interface uses the keyword extends to inherit an interface, as shown in the
following example:
interface GuestHouse {
void welcome();
}
interface PayingGuestHouse extends GuestHouse {
void paidBreakfast();
}
interface StudentPGHouse extends PayingGuestHouse {
void laundry();
}
interface ChildFriendly {
void toys();
}
interface FamilyPGHouse extends ChildFriendly, PayingGuestHouse {
void kitchen();
}

The preceding code is shown in figure 3.5 as a UML diagram.
<>
GuestHouse
+welcome()
<>
<>
PayingGuestHouse

<>
ChildFriendly

+paidBreakfast()

+toys()

<>

<>

<>
StudentPGHouse

<>
FamilyPGHouse

+laundry()

+kitchen()

Figure 3.5 UML
relationships between
interfaces that extend
other interfaces

Licensed to Mark Watson 

184

CHAPTER 3 Object-oriented design principles

By extending interfaces, you can combine methods of multiple interfaces. In the previous example, the interface FamilyPGHouse combines the methods of the interfaces
ChildFriendly, PayingGuestHouse, and GuestHouse.
When a class implements an interface, it should implement all the methods
defined in the interface and its base interfaces, unless it’s declared as abstract. If, for
example, a class implements the interface PayingGuestHouse, that class must implement method paidBreakfast() defined in the interface PayingGuestHouse, and
method welcome() defined in the interface GuestHouse. Let’s work with a concrete
example of a class implementing interfaces in the next section.

Rules to remember about interfaces
■
■

■
■

■
■

■
■

3.2

An interface is abstract by definition.
An interface can define only public, abstract methods and public, static, final
variables.
An interface uses the keyword extends to inherit other interfaces.
A class can implement multiple interfaces. An interface can extend multiple
interfaces.
An interface can define inner interfaces and (surprisingly) inner classes too.
If a class doesn’t implement all the methods of the interface that it implements,
the class must be defined as an abstract class.
A class uses the keyword implements to implement an interface.
If a class implements multiple interfaces that define methods with the same
name, the interface methods must either qualify as correctly overloaded or overridden methods, or else the class won’t compile.

Class inheritance versus interface inheritance
[3.2] Choose between interface inheritance and class inheritance
A class can implement interface(s) and a class can also extend a class and override its
methods. So the big question is, while designing classes and interfaces in your application, how do you implement inheritance to reuse existing code? Would you prefer
that your class inherit another (abstract or concrete) class or implement an interface?
There is no straight answer to this question. Depending on the requirements, you
might need to extend a class or implement an interface, because each offers a different set of benefits. To make this informed decision, let’s focus on the similarities and
differences in both approaches.

3.2.1

Comparing class inheritance and interface inheritance
An interface doesn’t include implementation details, whereas a class does. This basic
distinction has introduced differences in inheriting a class and implementing an
interface. These differences are listed in table 3.2.

Licensed to Mark Watson 

185

Class inheritance versus interface inheritance
Table 3.2 Differences between class inheritance and interface inheritance
Class inheritance

Interface inheritance

Instantiation of
derived class

Instantiation of a derived class instantiates its
base class.

Interfaces can’t be
instantiated.

How many?

A class can extend only one base class.

A class can implement multiple interfaces.

Reusing implementation details

A class can reuse the implementation details of
its base class.

An interface doesn’t include
implementation details.

Modification to base
class implementation details

With the modified base class, a derived class
might cease to offer the functionality it was originally created for; it may also fail to compile.

Interfaces don’t include
implementation details.

Now for the similarities between class and interface inheritance. In both cases, you
can refer to a derived class or implementing class by using a variable of the base class
or implemented interface.

3.2.2

Preferring class inheritance over interface inheritance
Class inheritance scores better when you want to reuse the implementation already
defined in a base class. It also scores better when you want to add new behavior to an
existing base class. Let’s examine both of these in detail.
REUSING THE IMPLEMENTATION FROM THE BASE CLASS

When we create any class, we extend and reuse class java.lang.Object. The class
Object defines code to take care of all the threading and object-locking issues, together
with providing default implementation for methods like toString(), hashCode(),
and equals(). Method toString() returns a textual description (String) of an
instance. Methods like hashCode() and equals() enable objects to be stored and
retrieved efficiently in hash-based collection classes like HashMap. What do you think
would happen if class java.lang.Object was defined as an interface? In this case,
you’d need to implement all these methods for every class that you created.
But it’s not useful to replicate this type of boilerplate code across many implementation classes. So class inheritance comes in handy here.
ADDING NEW BEHAVIOR IN ALL DERIVED CLASSES
Imagine you created a set of entities (Lion, Elephant), identified their common behavior, and moved the common behavior to their common base class (Animal). Because

you control the definition of all these classes, you might add new behavior to your base
class and make it available to all the derived classes. Examine the following definition of
the abstract class Animal and nonabstract class Lion, which extends class Animal:
public abstract class Animal {
public abstract void move();
public abstract void live();
}

Licensed to Mark Watson 

186

CHAPTER 3 Object-oriented design principles
public class Lion extends Animal {
public void move(){/*...*/}
public void live(){/*...*/}
}

Let’s add another method to Animal (modifications in bold):
public abstract class Animal {
public abstract void move();
public abstract void live();
public void eat() {/*...*/}
}
public class Lion extends Animal {
public void move(){/*...*/}
public void live(){/*...*/}
}

Addition of new
method eat

The addition of public method eat() in class Animal makes it available to all subclasses of Lion, implicitly. But adding or modifying behavior in a base class is not
always a bed of roses, as you’ll see in the next section.

3.2.3

Preferring interface inheritance over class inheritance
You may prefer interface inheritance over class inheritance when you need to define
multiple contracts for classes.
IMPLEMENTING MULTIPLE INTERFACES

Imagine you need to use a class that can be executed in a separate thread and can be
attached as an ActionListener to a GUI component. You can achieve this by making
your class implement multiple interfaces that support these functionalities—interfaces Runnable and ActionListener:
class MyClass implements Runnable, ActionListener {
//..code to implement methods from interface
//..Runnable and ActionListener
}

Interface implementation has one major advantage: a class can implement multiple
interfaces, to support multiple functionality. For the preceding example, you can pass
instances of class MyClass to all methods that define parameters of type Runnable or
ActionListener.
DEFINING A NEW CONTRACT FOR EXISTING CLASSES TO ABIDE BY

Starting with Java version 7, a new language feature has been added to the exception
handling: auto-closing resources by using a try-with-resources statement. The intent is
to define a try statement that can use streams that can be auto-closed, releasing any system resources associated with them. This prevents Java objects from using resources
that are no longer required. These unused and unclosed resources can lead to
resource leakage. Though a try statement provides a finally clause that can be
used by programmers to close streams, at times it isn’t being used to do so. So to
manage resources automatically, Java designers introduced the try-with-resources

Licensed to Mark Watson 

Class inheritance versus interface inheritance

187

statement. The objects that can be used with this statement need to define a close
method, so this method can be called to automatically close and release used
resources. To apply this constraint, Java designers at Oracle started by defining interface java.lang.AutoCloseable, as follows:
package java.lang;
public interface AutoCloseable {
void close() throws Exception;
}

The try-with-resources statement can declare only objects that implement the interface java.lang.AutoCloseable. Prior to Java 7 (starting with Java 5), many input and
output streams from the Java IO API implemented the interface java.io.Closeable:
public
public
public
public

abstract
abstract
abstract
abstract

class
class
class
class

Reader implements Readable, Closeable {
Writer implements Appendable, Closeable, Flushable {
InputStream implements Closeable {
OutputStream implements Closeable, Flushable {

To accommodate the use of class instances mentioned in the preceding code, the
existing interface java.io.Closeable was tricked (read: modified) into extending
java.lang.AutoCloseable:
package java.io;
import java.io.IOException;
public interface Closeable extends AutoCloseable {
public void close() throws IOException;
}

Also, any user-defined class that implements the interface java.lang.AutoCloseable
or any of its subinterfaces can be used with a try-with-resources statement. Here’s a
quick example of using a try-with-resources statement:
void openFile(String filename) throws Exception {
try (FileInputStream fis = new FileInputStream(new File(filename))) {
/* ... */
}
Object of FileInputStream can be declared in
}
try-with-resources because FileInputStream

implements Closeable (which extends AutoCloseable).

As you’ll see in chapter 6, a try block in try-with-resources can exist without any companion catch or finally block.
Interface inheritance added new behavior to classes like Reader and Writer without
breaking their existing code. Inheritance of the interface AutoCloseable by Closeable
defines multiple contracts for instances of these classes. They can now be assigned to a
reference variable of type AutoCloseable, enabling them to be used with a try-withresources statement.
Several other classes and interfaces implement or extend AutoCloseable, among
the main Java Database Connectivity (JDBC) interfaces (Connection, Statement,
ResultSet) and several Java Sound API interfaces.

Licensed to Mark Watson 

188

CHAPTER 3 Object-oriented design principles

FRAGILE DERIVED CLASSES

Adding to or modifying a base class can affect its derived classes. Adding new methods
to a base class can result in breaking the code of a derived class. Consider this initial
arrangement, which works well:
public abstract class Animal {
void move(){}
}
class Lion extends Animal {
void live(){}
}

Now consider a modified arrangement: a new method live() is added to base class
Animal. Because live() clashes (because of an incorrectly overridden method) with
the existing method live() in its derived class Lion, Lion will no longer compile:
public abstract class Animal {
void move(){}
String live(){
return "live";
}
}
class Lion extends Animal {
void live(){}
}

New method
added to Animal
live() in Lion neither
overloads nor overrides
live() in Animal.

Class inheritance isn’t always a good choice because derived
classes are fragile. If any changes are made to a base class, a derived class
might break. Extending classes that are from another package or are
poorly documented aren’t good candidates for base classes.

EXAM TIP

If a base class chooses to modify the implementation details of its methods, the
derived classes might not be able to offer the functionality they were supposed to, or
they might respond differently. Consider this initial arrangement:
public abstract class Animal {
String currentPosition;
public void move(String newPosition){
currentPosition = newPosition;
}
}
class Lion extends Animal {
void changePosition(String newPosition) {
super.move(newPosition);
System.out.println("New Position:" + newPosition);
}
}
class Test{
Prints “New
public static void main(String args[]) {
Position:Forest”
new Lion().changePosition("Forest");
once
}
}

Licensed to Mark Watson 

189

Class inheritance versus interface inheritance

Imagine that Animal adds another line of code to method move(). Let’s see how it
changes the code output of class Test (modification in bold):
public abstract class Animal {
String currentPosition;
public void move(String newPosition){
currentPosition = newPosition;
System.out.println("New Position:" + newPosition);
}
}
class Lion extends Animal {
void changePosition(String newPosition) {
super.move(newPosition);
System.out.println("New Position:" + newPosition);
}
public static void main(String args[]) {
new Lion().changePosition("Forest");
}

Implementation
details modified;
new code line added

Prints “New
Position:Forest”
twice

}

EXAM TIP There isn’t any clear winner when it comes to selecting the
better option from class inheritance and interface inheritance. Analyze
the given conditions or situations carefully to answer questions on this
topic.

Imagine the thought process required to modify the core Java classes when its new version is planned or executed. As you witnessed in the preceding example, changes to a
base class can break the code of its derived classes.
In the next “Twist in the Tale” exercise, let’s try to figure out how an alreadydefined class implements the interface AutoCloseable, or any of its subinterfaces, so
it can be used with a try-with-resources statement.
Twist in the Tale 3.1

As shown in the preceding examples, a try-with-resources statement can declare
resources (objects) that implement the interface java.lang.AutoCloseable or any of
its subinterfaces. A programmer has defined a class MyLaptop as follows, and wants to
use it with a try-with-resources statement. Which option will enable the programmer
to achieve this goal?
class MyLaptop {
public int open()
{
/* some code */
return 0;
}
public void charge() {
/* some code */
}

Licensed to Mark Watson 

190

CHAPTER 3 Object-oriented design principles
public int close()
/* some code */
return 1;
}

{

}
a
b

c

d

Make class MyLaptop implement interface java.lang.AutoCloseable.
Make class MyLaptop implement interface java.io.Closeable, which extends
interface java.lang.AutoCloseable.
Create a new interface MyCloseable that extends java.lang.AutoCloseable,
and make class MyLaptop implement it.
Class MyLaptop can’t implement interface java.lang.AutoCloseable or any of
its subinterfaces because of the definition of its method close().

In the next section, you’ll work with how to identify and implement IS-A and HAS-A
principles in code.

3.3

IS-A and HAS-A relationships in code
[3.3] Apply cohesion, low-coupling, IS-A, and HAS-A principles
You’ll be amazed at how easily you can identify and implement IS-A and HAS-A relationships between classes and objects, if you remember one simple rule—follow the
literal meaning of these terms:
■

IS-A —This relationship is implemented when

■

– A class extends another class (derived class IS-A base class)
– An interface extends another interface (derived interface IS-A base interface)
– A class implements an interface (class IS-A implemented interface)
HAS-A —This relationship is implemented by defining an instance variable. If a
class—say, MyClass—defines an instance variable of another class—say, YourClass—MyClass HAS-A YourClass. If MyClass defines an instance variable of an
interface—say, YourInterface—YourClass HAS-A YourInterface.
Representing IS-A and HAS-A relationships by using (quick)
UML diagrams can help you on the exam. Though you may not see UML
diagrams on the exam, creating quick UML diagrams on an erasable
EXAM TIP

board (or something similar) provided to you during the exam will help
you answer these questions.
The exam will test whether you can identify and implement these relationships in
your code, so let’s start with an example of an IS-A relationship.

Licensed to Mark Watson 

IS-A and HAS-A relationships in code

3.3.1

191

Identifying and implementing an IS-A relationship
An IS-A relationship is implemented by extending classes or interfaces and implementing interfaces. Traverse the inheritance tree up the hierarchy to identify this relationship. A derived class IS-A type of its base class and its implemented interfaces. A derived
interface IS-A type of its base interface. The reverse isn’t true; a base class or interface
isn’t a type of its derived class or interface.
IDENTIFYING AN IS-A RELATIONSHIP

Here’s a simple but long example for you to read and comprehend:
interface Movable {}
interface Hunter extends Movable {}
class Animal implements Movable {}
class Herbivore extends Animal {}
class Carnivore extends Animal implements Hunter {}
class Cow extends Herbivore {}
class Goat extends Herbivore {}
class Lion extends Carnivore {}
class Tiger extends Carnivore {}

Which of the following options do you think are correct?
■
■
■
■

Cow IS-A Hunter.
Tiger IS-A Herbivore.
Cow IS-A Movable.
Animal IS-A Herbivore.

To answer this question, refer to the preceding code, and you’ll notice that the interface Hunter is implemented only by class Carnivore. Class Cow doesn’t extend class
Carnivore. So, Cow IS-A Hunter is incorrect.
Similarly, you can refer to the preceding code to answer all the other options.
Option 2 is incorrect because class Tiger doesn’t extend class Herbivore. Option 3 is
correct because the interface Movable is implemented by class Animal, which is the
base class of Herbivore, extended by class Cow.
Option 4 is incorrect because you can’t traverse the hierarchy tree down to determine an IS-A relationship. Evaluate it like this: An Herbivore IS-A type of Animal with
some additions or modifications because an Herbivore can modify (override) methods of class Animal and add new ones. But Animal IS-NOT-A Herbivore. Animal can
also be a Carnivore.
Phew! So we had to refer to the code multiple times to answer each option. How
about representing the relationships between these classes and interfaces by using
UML notation, as shown in figure 3.6?

Licensed to Mark Watson 

192

CHAPTER 3 Object-oriented design principles

Movable

Hunter

Animal

Herbivore

Cow

Traverse up the
hierarchy tree
to determine
IS-A relationship.

Carnivore

Goat

Lion

Tiger

Extends
Implements
Interface
Class
Figure 3.6 A UML representation can help answer questions about IS-A relationships
between classes and interfaces.

If you can traverse up, from a derived class
or interface to a base class or interface, following the connecting arrows (lined or
dashed), the derived entity shares an IS-A
relationship with the base entity. If you
think that the preceding figure seems to
depict a rather polished form of a class-andinterface relationship, look at figure 3.7,
which shows the same relationship in a
rather raw form.
I understand that you may not have the
time or patience to draw neat diagrams
during the exam because of time constraints or space available to you on an
erasable board. The main point to remember is to use correct connecting lines to
connect two types. Use an arrow to show
an IS-A relationship and a line to show a
HAS-A relationship.

Movable
Animal
Cow

Hunter

Herbivore

Goat
Carnivore
Lion

Tiger

Figure 3.7 A rather raw form of the UML
diagram that you may draw on an erasable
board while taking your exam to represent
an IS-A relationship between classes and
interfaces

Licensed to Mark Watson 

IS-A and HAS-A relationships in code

193

When I attempted this exam, I drew similar not-so-good-looking diagrams. Believe
me, they helped me answer questions quickly, without referring to the code again and
again. Also, the questions on the exam may not use names that indicate an obvious
relationship between classes and interfaces. The next “Twist in the Tale” exercise will
ensure that you get the hang of this point.
Twist in the Tale 3.2

Using the following code
interface InterH {}
interface SameY extends InterH {}
class JamD implements InterH {}
class SunP extends JamD {}
class BreaU extends JamD implements SameY {}

your task is to identify which of the following statements are true:
a
b
c
d

SunP IS-A InterH.
JamD IS-A SameY.
InterH IS-A InterH.
SameY IS-A JamD.

First attempt the exercise without drawing a UML diagram, and then by drawing and
using a UML diagram. Do you think using the UML diagram helps you answer the
questions more quickly?

EXAM TIP The key to finding the types that participate in an IS-A relationship is to find your way, up the hierarchy tree, in the direction of the
arrows. This technique will not only help you with the exam, but also take
you a long way in your professional career.

IMPLEMENTING AN IS-A RELATIONSHIP

You can implement an IS-A relationship by extending classes or interfaces, or by
implementing interfaces. Here is a quick set of rules for implementing inheritance
between classes and interfaces in code:
■
■
■

A class inherits another class by using the keyword extends.
A class implements an interface by using the keyword implements.
An interface inherits another interface by using the keyword extends.

How will you implement the following relationship in code?
Herbivore IS-A Animal

Licensed to Mark Watson 

194

CHAPTER 3 Object-oriented design principles

Because you don’t know whether either Herbivore or Animal refers to a class or an
interface, you have the following possibilities:
■
■
■

Herbivore and Animal are classes. Herbivore extends Animal.
Herbivore and Animal are interfaces. Herbivore extends Animal.
Herbivore is a class, and Animal is an interface. Herbivore implements Animal.

Figure 3.8 shows these three possible implementations.
Herbivore IS-A Animal

Animal

Animal

Animal

Herbivore

class Animal {}
class Herbivore
extends Animal{}

Herbivore

Herbivore

interface Animal {}
class Herbivore
implements Animal{}

interface Animal {}
interface Herbivore
extends Animal{}

Figure 3.8 How to implement an IS-A relationship, if you don’t know whether the relationship is
between classes, interfaces, or both

Now, let’s add another relationship to the previous one. How would you implement
the following relationship and rules in code?
■
■
■

Herbivore IS-A Animal.
Carnivore IS-A Animal.
Animal can define only abstract methods and constants.

The third rule makes it clear that Animal is an interface. But you still don’t know
whether Herbivore and Carnivore are classes or interfaces. So you can have the following possibilities:
■
■
■

■

Herbivore and Carnivore are interfaces that extend the interface Animal.
Herbivore and Carnivore are classes that implement the interface Animal.
Herbivore is a class that implements the interface Animal. Carnivore is an
interface that extends the interface Animal.
Herbivore is an interface that extends the interface Animal. Carnivore is a
class that implements the interface Animal.

These relationships can be implemented as shown in figure 3.9.
The exam may specify a similar set of rules and ask you to choose the code that you
think correctly implements the specified conditions. Let’s work through another set

Licensed to Mark Watson 

195

IS-A and HAS-A relationships in code
Herbivore IS-A Animal
Carnivore IS-A Animal
Animal can only define abstract methods and constants

Animal

Herbivore

Carnivore

Animal

Herbivore

Animal

Herbivore

Carnivore

Carnivore

Animal

Herbivore

Carnivore

Figure 3.9 How to implement an IS-A relationship between three entities, one of which is
an interface

of rules and implement the relationships in code. How would you implement the following relationships and rules in code?
1
2
3
4

Abc IS-A Xyz.
Abc defines methods and instance variables.
Xyz can declare only abstract methods.
Xyz IS-A Lmn.

Rule 2 states that Abc is a class, because an interface can’t define instance variables.
Rule 3 states that Xyz is an interface, because a class can declare both abstract and
nonabstract methods. When you go up the hierarchy tree of an interface, you can find
only another interface. In other words, Lmn is also an interface. The preceding rules
evaluate to the following:
■
■
■
■

Abc is a class.
Xyz and Lmn are interfaces.
Abc implements Xyz.
Xyz extends Lmn.

Licensed to Mark Watson 

196

CHAPTER 3 Object-oriented design principles

Lmn
interface Lmn{}
interface Xyz extends Lmn{}
class Abc implements Xyz{}

Xyz

Figure 3.10 Implementing a set
of rules and an IS-A relationship
between three entities: two
interfaces, and a class

Abc

After the evaluation, these rules seem simple to implement. Figure 3.10 shows the
relationships in UML notation and in code.
When a class defines an instance variable of another class, they share a HAS-A relationship, covered in the next section.

3.3.2

Identifying and implementing a HAS-A relationship
As compared to an IS-A relationship, a HAS-A relationship is easy to identify and implement. I hope this statement relieves you! Consider this definition of the bare-bones
class Engine:
class Engine {}

Which of the following classes (Statistics, Car, PartsFactory, TestCar) do you
think shares a HAS-A relationship with class Engine?
class Statistics {
static Engine engine;
Statistics defines class
}
variable of type Engine.
class Car {
Engine engine;
Car defines instance
}
variable of type Engine.
class PartsFactory {
Object createEngine() {
Engine engine = new Engine();
PartsFactory defines
//.. code
local variable of
return engine;
type Engine.
}
}
class TestCar {
boolean testEngine(Engine engine) {
TestCar defines
//.. code
method parameter
}
of type Engine.
}

Of all the preceding classes—Statistics, Car, PartsFactory, and TestCar—only Car
shares a HAS-A relationship with the class Engine because Car defines an instance
variable of type Engine. Note that it doesn’t matter whether the instance variable
engine in class Car is initialized with an object. The HAS-A relationship is shared by
the classes.

Licensed to Mark Watson 

197

Cohesion and low coupling

Classes and interfaces can share a HAS-A relationship with
each other. If a class or interface—say, Type1—defines an instance variable of a class or interface—say, Type2, Type1 HAS-A Type2 is correct. The
reverse isn’t correct. Also, the HAS-A relationship is shared by classes,
and so the relationship isn’t affected, whether the instance variable is
initialized or not.
EXAM TIP

The exam doesn’t stop at the IS-A and HAS-A relationships. Let’s see how high cohesion
and low coupling can improve your application design.

3.4

Cohesion and low coupling
[3.3] Apply cohesion, low-coupling, IS-A, and HAS-A principles
Focused teams and team members are known to deliver better results. On the other
hand, highly dependent departments, teams, or team members might perform poorly.
The same principles can be applied to application design. Focused classes and modules
(cohesion) that aren’t highly dependent (or coupled) on other classes or modules are
generally easy to work with, reusable, and maintainable. Let’s start with the design principle cohesion, which supports creation of focused modules and classes.

3.4.1

Cohesion
Cohesion refers to how focused a class or a module is. High cohesion refers to a wellfocused class or module, whereas low cohesion refers to a class or module that doesn’t
have a well-defined responsibility. Such modules or classes might perform multiple
actions, which could have been assigned to separate classes.
Imagine a book editor who is supposed to edit book content, manage the bookprinting process, and reach out to new authors for new book ideas. Let’s define this
editor by using a class, say, Editor:
class Editor{
public void editBooks() {}
public void manageBookPrinting() {}
public void reachOutToNewAuthors() {}
}

Low cohesion; Editor is
performing diverse set
of unrelated tasks

Because this editor is managing multiple tasks over a period of time, managing all
these processes might become difficult. Also, working with multiple responsibilities
can prevent the editor from specializing in all these processes. Let’s limit the tasks to
the book-editing process:
class Editor{
public void useEditTools() {}
public void editFirstDraft() {}
public void clearEditingDoubts() {}
}

High cohesion; Editor
is performing multiple
but related tasks.

Licensed to Mark Watson 

198

CHAPTER 3 Object-oriented design principles

The preceding example creates a highly cohesive class, Editor. Highly cohesive classes
are easy to use. In the preceding example, class Editor provides a one-stop solution
for all editing tasks. Highly cohesive classes are also easy to maintain and reuse; whenever you need to add or modify any editing-related process, you know which class you
need to refer to: class Editor.
EXAM TIP

Well-designed applications aim for highly cohesive classes and

modules.
Classes and modules also perform better if they are least affected by the changes
made to other classes or modules. Let’s work with this aspect in detail in the next
section on coupling.

3.4.2

Coupling
Coupling refers to how much a class or module knows about other classes or modules.
If a class—say, Editor—interacts with another class—say, Author—by using its interface (public methods), then classes Editor and Author are loosely coupled. But if class
Editor can access and manipulate Author by using its nonpublic members, these
classes are tightly coupled.
Let’s code the class Author:
class Author {
String name;
String skypeID;
public String getSkypeID() {
return skypeID;
}
}

The terms low coupling and loose coupling refer to the same concept.
They are often used interchangeably.

NOTE

The modified class Editor is tightly coupled with class Author. The method clearEditingDoubts in class Editor accesses the nonpublic member skypeId of class Author:
class Editor{
public void clearEditingDoubts(Author author) {
setUpCall(author.skypeID);
converse(author);
}
void setUpCall(String skypeID) { /* */}
void converse(Author author) {/* */}
}

Tight coupling; nonpublic
variable skypeID is referred
to outside its class Author.

What happens, say, if a programmer changes the name of the variable skypeID in class
Author to skypeName? The code of class Editor won’t compile. As long as the public
interface of a class remains the same, it’s free to change its implementation details. In

Licensed to Mark Watson 

199

Cohesion and low coupling

this case, the name of instance variable skypeID forms part of Author’s implementation details. One suggested solution is to use the public method getSkypeID() in class
Editor (changes in bold):
class Author {
String name;
Change in instance variable
String skypeName;
name won’t affect classes
public String getSkypeID() {
that access this method
return skypeName;
}
}
class Editor{
public void clearEditingDoubts(Author author) {
setUpCall(author.getSkypeID());
Loose coupling; public
converse(author);
method getSkypeID()
}
accesses Author’s
void setUpCall(String skypeID) { /* */}
skypeName.
void converse(Author author) {/* */}
}

Interfaces also promote loose coupling across classes and modules. Assume that the
entity Author is defined as an interface, which can be implemented by specialized
authors such as TechnicalAuthor. Here’s the new arrangement:
interface Author {
interface
String getSkypeID();
Author
}
class TechnicalAuthor implements Author{
String name;
Class TechnicalAuthor
String skypeName;
implements Author.
public String getSkypeID() {
return skypeName;
}
}
class Editor{
public void clearEditingDoubts(Author author) {
Loose coupling; method
setUpCall(author.getSkypeID());
clearEditingDoubts()
converse(author);
uses interface to access
}
concrete implementations
void setUpCall(String skypeID) { /* */}
void converse(Author author) {/* */}
}

b

c

d

The code at B defines the entity Author as an interface. At c, class TechnicalAuthor
implements Author. At d, the type of parameter passed to method clearEditingDoubts() is the interface Author. So method clearEditingDoubts() is guaranteed to
access only public members of instances of Author. Also, because method clearEditingDoubts() can be passed objects of classes that implement Author, it can also
accept instances of classes that are created later, such as FictionWriter, which implement Author.

Licensed to Mark Watson 

200

CHAPTER 3 Object-oriented design principles

EXAM TIP

Well-designed applications aim for loosely coupled classes and

modules.
The tips for creating well-designed applications don’t end here. The next section covers object composition principles.

3.5

Object composition principles
[3.4] Apply object composition principles (including HAS-A relationships)
How can you use the existing functionality of a class? Inexperienced programmers or
newcomers to the Java programming language and OOP often answer this question by
saying, “inheritance.” They shouldn’t be completely blamed for this incorrect answer.
Many books, articles, and programmers overemphasize inheritance—which is correct
in a way, because inheritance is an important concept. But this might leave a lot of
newcomers with the wrong impression that inheriting a class is the best way to use
another class. Most of the time, you can use another class by composing your own class
with an object of another class. Let’s start with a quick example:
class Engine { /* code */ }
class Wheel { /* code */ }
class Car {
Engine engine;
Wheel[] wheels = new Wheel[5];
}

Car is composed of
Engine and Wheel.

Object composition enables you to use the existing functionality of classes without extending them. The approach is simple: create and
use objects of other classes in your own class.

EXAM TIP

Look around for examples of classes defined by your peers, in books or articles, or
in the Java API. You’ll be amazed to notice that composition is the way to use a class,
when you want to use the functionality of any other class. You should inherit a class
only when you think that the derived class is a type of its base class. For example, it’s
correct to say that RacingCar is a type of Car. But it’s incorrect to say that Engine is a
type of Car.
There’s another reason to favor object composition over inheritance: a base class is
fragile (refer to the subsection “Fragile Derived Classes” in section 3.2.3 for an example). A change to a base class can have major effects on all its derived classes. For
example, changing the method signature of a public method in a base class can lead
to broken code of all its derived classes. A change in the nonpublic variables or methods of a base class can affect its derived classes, if the variables or methods are used by
the derived classes.
The remaining sections cover the design patterns on the exam. Before you dive into
the details of the design patterns, let’s look at what they are and why we need them.

Licensed to Mark Watson 

Introduction to design patterns

3.6

201

Introduction to design patterns
People who live in regions that experience snowfall build sloping roofs so that snow
and ice don’t accumulate on the rooftops. This “pattern” of designing sloping roofs
was identified after multiple persons faced similar difficulties and found similar solutions. Now this is an established practice. Being ignorant about the design pattern of
building a sloping roof can cause you a lot of rework later. Similarly, in the computing
domain, multiple design patterns have been documented by observing recurring programming, behavioral, or implementation issues.

3.6.1

What is a design pattern?
A design pattern identifies a specific problem and suggests a solution to it. It’s neither
ready-made code that you can drop in your projects nor a framework to use. For
example, you might document the sloping-roof design pattern as
■
■
■

Design pattern name: Sloping roof
Problem: Accumulation of snow and ice on rooftops
Suggested solution: Build sloping roofs for all houses, offices, and buildings in
areas that receive snowfall during any time of the year. This enables snow or ice
from rooftops to slide and fall to the ground.

Notice the design pattern doesn’t include actual materials or tools to build a house.
No formal format of documentation of a design pattern exists. You
can document it the way you like.

NOTE

3.6.2

Why do you need a design pattern?
Design patterns offer experience reuse and not code reuse. Design patterns help you reuse
the experience of application designers and developers in terms of the guidelines that
you can follow to implement commonly occurring programming scenarios. By using
design patterns for known issues in your application, you’ll benefit from the experience of others and be less likely to reinvent the wheel.
The Singleton and Factory design patterns are creational patterns
initially described in the Gang of Four (GoF) book Design Patterns: Elements of Reusable Object-Oriented Software by Gamma et al. (Addison-Wesley,
1995). DAO is an integration-tier core J2EE pattern; see Core J2EE Patterns:
Best Practices and Design Strategies, Second Edition, by Deepak Alur, John
Crupi, and Dan Malks (Prentice Hall, 2003).

NOTE

In the next section, we’ll cover the first design pattern that is covered on this exam:
the Singleton design pattern.

Licensed to Mark Watson 

202

3.7

CHAPTER 3 Object-oriented design principles

Singleton pattern
[3.5] Design a class using the Singleton design pattern
Singleton is a creational design pattern that ensures that a class is instantiated only
once. The class also provides a global point of access to it.
So under what conditions would you want to have only one object of a class?
Wouldn’t the object feel lonely because there’s only one of its kind?

3.7.1

Why do you need this pattern?
Imagine the issues that can be caused by multiple browser caches or multiple thread
pools. In these scenarios, you might need only one object of a class to encapsulate all
operations for managing a pool of resources, and to also serve as a global point of reference. Other common examples include a single instance of Device Manager to
manage all the devices on your system, and a single instance of a print spooler to manage all the printing jobs.

3.7.2

Implementing the Singleton pattern
Implementation of the Singleton class involves a single class. But don’t let this simplicity dismiss the finer details that you should get right. Let’s move on to the basics of
implementing the Singleton pattern:
1

Define a private constructor for the class that implements the Singleton pattern.
To prevent any other class from creating an object of this class, mark the constructor of this class as a private member:
class Singleton {
private Singleton() {
System.out.println("Private Constructor");
}
}

Now, no class can execute new Singleton() to create an instance of this class.
But if no other class can create objects of this class, how will they use it? The
class that implements the Singleton pattern creates and manages its sole
instance by defining a static variable to store this instance.
2

Define a private static variable to refer to the only instance of the Singleton class.
A static variable ensures that the class stores and accesses the same instance. In
the following code, the variable anInstance is a class variable:
class Singleton {
private static Singleton anInstance = null;
private Singleton() {
System.out.println("Private Constructor");
}
}

Licensed to Mark Watson 

203

Singleton pattern

A well-encapsulated class should enable access to its members by using welldefined interfaces. So let’s create a method to access the private variable
anInstance.
3

Define a public static method to access the only instance of the Singleton class.
Before you access the variable anInstance, you should create it. The creation
and return of this variable is usually defined as follows (additions to previous
code in bold):
class Singleton {
private static Singleton anInstance = null;
public static Singleton getInstance() {
if (anInstance == null)
anInstance = new Singleton();
return anInstance;
}
private Singleton() {
System.out.println("Private Constructor");
}
}

If anInstance hasn’t
been initialized
Initialize
anInstance
Return
anInstance

A class can request an object of class Singleton by calling the static method getInstance():

Prints
“true”

class UseSingleton {
public static void main(String args[]) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}

New instance of class
Singleton created and
returned when
accessed first time
Previously created
instance returned for
subsequent calls to
method getInstance()

The output of this code confirms that an object of class Singleton is created only once:
Private Constructor
true

These steps ensure that only one object of class Singleton ever exists. But what happens if multiple classes request an object of class Singleton at exactly the same time?
This may lead to the creation of more than one object of class Singleton. Don’t worry;
we have ways to fix this one too, as discussed in the next section.

3.7.3

Ensuring creation of only one object in the Singleton pattern
Though the previous code seems to guarantee that only one instance of Singleton will
be created, concurrent access of method getInstance() may result in creation of
multiple instances. This can be a problem in multithreaded environments, such as
application servers and servlet engines. Before you fix the issue of concurrent creation

Licensed to Mark Watson 

204

CHAPTER 3 Object-oriented design principles

of an object in a Singleton pattern, you need to ensure that you understand the finer
details of this issue. So let’s get started.
UNDERSTANDING THE PROBLEM OF CONCURRENT ACCESS
Imagine that two objects request class Singleton to return its instance at exactly the
same time, by calling method getInstance():
class Singleton {
private static Singleton anInstance = null;
public static Singleton getInstance() {
if (anInstance == null)
anInstance = new Singleton();
return anInstance;
}
private Singleton() {
System.out.println("Private Constructor");
}
}

Because each call will discover that the variable anInstance hasn’t been initialized,
each method call will create a new object and assign it to the variable anInstance.
Method getInstance() may also return separate objects for each call. This is shown
in figure 3.11.
If you think that it doesn’t make a difference if you create multiple objects of a
class that implements the Singleton pattern, think again. The definition of the previously defined class Singleton is oversimplified. Real classes that implement the
Singleton pattern define much more meaningful code in their constructors—for
example, initializing their own resources, starting threads, or creating database or network connections. Obviously, a class won’t like to do all these again, when it’s not supposed to.

Calling
method 1

Calling
method 2
public static Singleton getInstance() {
if (anInstance == null)
anInstance = new Singleton() ;
return anInstance;
}
Create new
Singleton object

Figure 3.11 Multiple concurrent calls to method getInstance() can
create multiple objects of class Singleton.

Licensed to Mark Watson 

205

Singleton pattern

FIXING CONCURRENT CREATION: EAGER INITIALIZATION

There are multiple ways to ensure that an object of a class that implements the Singleton pattern is initialized only once. To begin with, eager initialization will enable you to
initialize the static variable as soon as the class is loaded:
Eager initialization; anInstance is
initialized as soon as class loaded

b

class Singleton {
private static final Singleton anInstance = new Singleton();
public static Singleton getInstance() {
return anInstance;
Simply returns
}
anInstance
private Singleton() {
System.out.println("Private Constructor");
}
}

c

The code at B executes when the class is loaded by the Java class loaders. So an object
of class Singleton is created before any class requests it. When any other object
requests an object of class Singleton, using method getInstance(), the code at c
simply returns the Singleton instance anInstance. The preceding code ensures that
multiple objects of class Singleton aren’t created.
FIXING CONCURRENT CREATION: SYNCHRONIZED LAZY INITIALIZATION

Though this seems to be the perfect solution, eager initialization creates an object of
class Singleton, even if it’s never used. Don’t worry; every problem has a solution.
Let’s not employ eager initialization and synchronize method getInstance():
class Singleton {
private static Singleton anInstance;
synchronized public static Singleton getInstance() {
if (anInstance == null)
anInstance = new Singleton();
return anInstance;
}
private Singleton() {
System.out.println("Private Constructor");
}
}

No eager
initialization

B

Method
getInstance()
defined as
synchronized
method

Method getInstance() is defined as a synchronized method at B. This means that
multiple threads or objects can’t execute this method concurrently. So this again saves
us from multiple-object creation of a class implementing the Singleton pattern. If
you’re thinking that this is the last way to fix multiple-object creation issues for a
Singleton class, take a deep breath, my friend, because there’s more to it.
On the exam, you might also see a variation of the previously defined synchronized method getInstance(). Because synchronized methods don’t allow concurrent
execution, your application may feel a performance hit if a lot of classes in your application call method getInstance(). Java can rescue you this time, by synchronizing

Licensed to Mark Watson 

206

CHAPTER 3 Object-oriented design principles

method getInstance() partially (if you’re new to threading and concurrency and
can’t understand the following code, don’t worry. Just refer to chapters 10 and 11 on
threading and concurrency):
public static Singleton getInstance() {
if (anInstance == null) {
synchronized (Singleton.class) {
if (anInstance == null)
anInstance = new Singleton();
}
}
return anInstance;
}

Don’t synchronize
complete method
Synchronize code
block that creates
new object

After the thread acquires a lock on Singleton.class and enters the synchronized
block, the code checks whether anInstance is null (again), before creating a new
object. This is to ensure that after the lock is acquired, the condition hasn’t changed
and anInstance is still null.
On the exam, all of these approaches (eager initialization, synchronization of the complete method getInstance(), and partial synchronization of method getInstance()) may be presented, and you may be
questioned about the right approach for implementing the Singleton pattern. All these approaches are good. Beware of modified code that tries to
synchronize a partial getInstance() method, which doesn’t synchronize
the code that creates an object of Singleton.
EXAM TIP

USING ENUMS

By using enums, you can implement the Singleton pattern in a thread-safe manner.
Here’s a simple implementation:
public enum Singleton {
INSTANCE;
public void initCache(){
//..code
}
}

Because enum instances can’t be created by any other class, the enum Singleton will
ensure the existence of only one of its instances, Singleton.INSTANCE.
Even though using a single-element enum is the best way to implement the Singleton pattern, you must know all the previously discussed
approaches to answer questions on this topic on the exam.

NOTE

After making yourself aware of the multiple rules that you need to follow to apply the
Singleton pattern, test yourself on it with the next “Twist in the Tale” exercise.

Licensed to Mark Watson 

Singleton pattern

207

Twist in the Tale 3.3

Does the class in the following code apply the Singleton pattern correctly?
class Singleton {
private Singleton anInstance;
synchronized public Singleton getInstance() {
if (anInstance == null)
anInstance = new Singleton();
return anInstance;
}
}

The Singleton pattern is also referred to as an anti-pattern. It has
been overused by developers and designers, who make a lot of assumptions about the applications that use it. It also makes testing difficult.

NOTE

Even before the Singleton pattern was officially recognized and used, single-object
instances with global access have been implemented using static variables. But this has its
own set of disadvantages.

3.7.4

Comparing Singleton with global data
Programmers have been creating and using single instances of a class by defining
them as static variables for quite a long time. Some of them do that even now. But
doing so requires the following serious considerations:
■

■

■

Possibility of creating multiple objects of the same type—Using a static variable doesn’t
stop you (or any other user) from creating another object of the class and referring to it by another name. Limiting creation of only one object is the responsibility of the application developer and isn’t included as part of the class design
in this case. This, as you know, can introduce issues when multiple (unwanted)
objects are created.
Eager initialization—Static variables are usually initialized before any class uses
them. This risks allocation of resources and other processing that may never
have been required or used (for example, initializing resources of the class used
as a global variable) and other tasks that it may define in its constructor (for
example, starting threads, or creating database or network connections).
Pollution of namespace—Using multiple static variables within an application is
sure to pollute the namespace, which is, again, not a preferred approach.

The API of a language, product, or service can be huge, and it isn’t possible for users
to know about all its classes. It makes a lot of sense to be able to create and use objects
of a class by specifying a set of requirements. The Factory pattern makes this feasible.
Apart from hiding the implementation details of object creation, it enables developers
to extend an API and users to use the newer classes.

Licensed to Mark Watson 

208

3.8

CHAPTER 3 Object-oriented design principles

Factory pattern
[3.7] Design and create objects using a factory pattern
Imagine you need to open files, say, Hello.doc and Hello.xml, programmatically using
your Java application. To do so, you’d need instances of classes, say, WordProcessor
and TextEditor, that can open these files. One of the obvious approaches is to use the
operator new to create an instance of WordProcessor and TextEditor to open files.
But this would result in tight coupling between the application that opens files and
the classes that are used to open the files. What happens if you need to use another
class, say, QuickProcessor, to open .doc files in the future?
In this section, you’ll work with how to use the Factory pattern to prevent tight coupling between classes. This pattern also eliminates direct constructor calls in favor of
invoking a method. One of the most frequently used design patterns, multiple Factory
patterns exist:
■
■
■

Simple Factory or Static Factory pattern
Factory Method pattern
Abstract Factory pattern

On the exam, most of the questions on the Factory pattern refer to the Simple Factory pattern.

3.8.1

Simple Factory pattern (or Static Factory pattern)
This pattern creates and returns objects of classes that extend a common parent class
or implement a common interface. The objects are created without exposing the
instantiation logic to the client. The calling class is decoupled from knowing the exact
name of the instantiated class.
Figure 3.12 shows the UML class diagram for classes used in the sample code to
exhibit the Simple Factory pattern.

Uses
Client
Request for
App object
AppFactory

<>
App
<>

<>

WordProcessor

TextEditor

Creates
Figure 3.12 UML class diagram for interfaces and classes implementing the Simple
Factory pattern

Licensed to Mark Watson 

Factory pattern

209

In the following sample code, class Client is decoupled from knowing the exact subclass, either WordProcessor or TextEditor, that it needs to open a file. It calls AppFactory’s method getAppInstance(), passing it the file extension, which returns an
appropriate App object. It gives you the flexibility of modifying class AppFactory without impacting class Client. You might want to modify method getAppInstance() to
return another App instance, say, XMLEditor, to open a .txt file. For instance
interface App {
void open(String filename);
}
Interface App
class WordProcessor implements App {
implemented
public void open(String filename) {
by classes
System.out.println("Launch WordProcessor using " + filename);
WordProessor
and TextEditor
}
}
class TextEditor implements App {
public void open(String filename) {
System.out.println("Launch TextEditor using " + filename);
}
}
class AppFactory {
public static App getAppInstance(String fileExtn) {
Implements Simple
App appln = null;
Factory pattern by
if (fileExtn.equals(".doc")) {
returning App
appln = new WordProcessor();
object according to
}
parameter value
else if (fileExtn.equals(".txt") ||
fileExtn.equals(".xml")) {
appln = new TextEditor();
}
return appln;
}
}
class Client{
public static void main(String args[]) {
Client is decoupled from
App app = AppFactory.getAppInstance(".doc");
classes TextEditor and
app.open("Hello.doc");
WordProcessor; it calls
AppFactory.getAppApp app2 = AppFactory.getAppInstance(".xml");
Instance() to get App object.
app2.open("Hello.xml");
}
}

Because method getAppInstance() in class AppFactory is a static
method, this pattern is also referred to as the Static Factory pattern or
Factory Class pattern.

NOTE

Method getAppInstance() is manageable with just a few comparisons (if-else) statements. What happens if method getAppInstance() is supposed to return App instances
for a wide variety of file extensions? Because it can become unmanageable, let’s work
with a variation of the Simple Factory pattern—that is, the Factory Method pattern.

Licensed to Mark Watson 

210

CHAPTER 3 Object-oriented design principles

Uses
Client

<>
App

Request for
App object
<>
AppFactory

<>

<>

WordAppFactory

TextEditAppFactory

WordProcessor
Creates

TextEditor
Creates

Figure 3.13 UML class diagram for interfaces and classes implementing the Factory Method pattern

3.8.2

Factory Method pattern
The intent of the Factory Method pattern is to define an interface for creating an
object but let subclasses decide which class to instantiate. The Factory Method pattern
lets a class defer instantiation to its subclasses.
Figure 3.13 shows the UML class diagram for classes used in the sample code to
exhibit the Factory Method pattern.
In the following example code, to get the required App object, class Client uses one
of the subclasses of abstract class AppFactory and calls its method getAppInstance().
Method getAppInstance() calls method getApp(), which is a factory method defined
as an abstract method in class AppFactory. It is implemented by its concrete factory
classes, WordAppFactory and TextEditAppFactory. The subclasses WordAppFactory
and TextEditAppFactory implement getApp() to return a specific App object. It allows
Client to use WordProcessor and TextEditor, but decouples it from knowing their
names. This arrangement promotes flexibility to change the App object returned from
concrete factory classes (WordAppFactory and TextEditAppFactory):
interface App {
void open(String filename);
}
class WordProcessor implements App {
public void open(String filename) {
System.out.println("Launch WordProcessor using " + filename);
}
}
class TextEditor implements App {
public void open(String filename) {
System.out.println("Launch TextEditor using " + filename);
}
}

Licensed to Mark Watson 

Interface App
implemented
by classes
WordProcessor and
TextEditor

Factory pattern

Concrete
Factory
classes

211

abstract class AppFactory {
Abstract
public App getAppInstance() {
Factory class
App appln = getApp();
return appln;
Factory
}
Method
public abstract App getApp();
}
class WordAppFactory extends AppFactory {
public App getApp() {
return new WordProcessor();
Implement Factory
}
Method to return a
}
specific App object.
class TextEditAppFactory extends AppFactory {
public App getApp() {
return new TextEditor();
}
Client class uses variable
}
of Abstract Factory
class Client {
pattern to refer to a
public static void main(String args[]) {
concrete factory object
AppFactory factory = new WordAppFactory();
App app = factory.getAppInstance();
Because factory refers to
app.open("Hello.doc");
WordAppFactory object,
app = new TextEditAppFactory().getAppInstance();
app.open("Hello.xml");
}

call to getAppInstance()
returns WordProcessor
object

}

Now, what happens if you were required to create families of related classes, such as
applications that can open rich format files for editing in Windows and Mac systems? Because these systems might use separate applications for similar purposes,
let’s modify the example used in the previous section to use the Abstract Factory
pattern.

3.8.3

Abstract Factory pattern
The Abstract Factory pattern is used to create a family of related products (in contrast,
the Factory Method pattern creates one type of object). This pattern also defines an
interface for creating objects, but it lets subclasses decide which class to instantiate.
Figure 3.14 shows the UML class diagram for classes used in the sample code to
exhibit the Abstract Factory pattern.
In the following example code, to get the required App and Font objects, class
Client uses one of the subclasses of abstract class AppFactory and calls its methods getAppInstance() or getFontInstance(). The Concrete Factory pattern classes
WordAppFactory and TextEditAppFactory return an appropriate concrete object for
the interfaces App and Font. This pattern also allows Client to use WordProcessor,
TextEditor, RichFont, and RegularFont, but decouples it from knowing their
names. This arrangement also promotes flexibility to change the App or Font object

Licensed to Mark Watson 

212

CHAPTER 3 Object-oriented design principles
Uses

<>
Font

Uses

<>
App

Client
Request for
App object

<>

<>

<>
AppFactory

RichFont

RegularFont

Creates

<>

<>

WordAppFactory

TextEditFactory

WordProcessor

Creates

TextEditor
Creates

Creates

Figure 3.14

UML class diagram for interfaces and classes implementing the Abstract Factory pattern

returned from Concrete Factory pattern classes (WordAppFactory and TextEditAppFactory).
interface App { /* code */ }
class WordProcessor implements App { /* code */ }
class TextEditor implements App { /* code */ }
interface Font { /* code */ }
class RichFont implements Font { /* code */ }
class RegularFont implements Font { /* code */ }
abstract class AppFactory {
protected abstract App getApp();
protected abstract Font getFont();

interface App implemented
by classes WordProcessor
and TextEditor
interface Font
implemented by classes
RichFont and RegularFont
Abstract
Factory class

public App getAppInstance() {
App appln = getApp();
return appln;
}
public Font getFontInstance() {
Font font = getFont();
return font;
}
}
class WordAppFactory extends AppFactory {
protected App getApp() {
return new WordProcessor();
}
protected Font getFont() {
return new RichFont();
}
}

Concrete
Factory
classes

class TextEditAppFactory extends AppFactory {
protected App getApp() {
return new TextEditor();
}

Licensed to Mark Watson 

213

Factory pattern
protected Font getFont() {
return new RegularFont();
}
}
class ClientAbstractFactoryMethod {
public static void main(String args[]) {
AppFactory factory1 = new WordAppFactory();
App app1 = factory1.getAppInstance();
Font font1 = factory1.getFontInstance();
System.out.println(app1 + ":" + font1);

Concrete Factory class
used to create objects
of App and Font

AppFactory factory2 = new TextEditAppFactory();
App app2 = factory2.getAppInstance();
Font font2 = factory2.getFontInstance();
System.out.println(app2 + ":" + font2);
}
}

The sample code used in the sections on Factory Method and
Abstract Factory patterns can use App or Font as an interface or abstract
or concrete class.

NOTE

In the next section, you’ll learn the terms and phrases that the exam uses to test you
on the benefits of using the Factory pattern.

3.8.4

Benefits of the Factory pattern
The exam won’t ask you to select the benefit of a specific Factory pattern—that is,
Simple Factory, Factory Method, or Abstract Factory. Here are the benefits that apply
to all Factory patterns:
■
■
■
■
■

Prefer method invocation over direct constructor calls
Prevent tight coupling between a class implementation and your application
Promote creation of cohesive classes
Promote programming to an interface
Promote flexibility. Object instantiation logic can be changed without affecting
the clients that use objects. They also allow addition of new concrete classes.

Here’s a list of what doesn’t apply or isn’t related to the Factory pattern:
■

■

■
■

It doesn’t eliminate the need of overloading constructors in class implementations.
It doesn’t encourage the use of any particular access modifier. It isn’t compulsory to define private members to use this pattern.
It won’t slow your application.
It isn’t related to how to monitor objects for change.

The exam will question you on the classes from the Java API that use this pattern. Let’s
cover them in the next section.

Licensed to Mark Watson 

214

3.8.5

CHAPTER 3 Object-oriented design principles

Using the Factory pattern from the Java API
You’ll find this important design pattern in multiple classes in the Java API.
Some of these classes are listed in table 3.3.
Table 3.3 Classes and methods from the Java API that use the Factory pattern
Class

Method

Description

java.util.Calendar

getInstance()

Gets a calendar using
the default time zone
and locale.

java.util.Arrays

asList()

Returns a fixed-size list
backed by the specified
array.

java.util.ResourceBundle

getBundle()

Overloaded versions of
this method return a
resource bundle using
the specified base name,
target locale, class
loader, and control.

java.sql.DriverManager

getConnection()

Establishes and returns
a connection to the given
database URL.

java.sql.DriverManager

getDriver()

Attempts to locate
and return a driver
that understands the
given URL.

java.sql.Connection

createStatement()

Overloaded version of
this method creates a
statement object for
sending SQL statements
to the database and generates ResultSet
objects with the given
type, concurrency, and
holdability.

java.sql.Statement

executeQuery()

Executes the given SQL
statement, which returns
a single ResultSet
object.

java.text.NumberFormat

getInstance()
getNumberFormat()

Returns a generalpurpose number format
for the current default
locale.

java.text.NumberFormat

getCurrencyInstance()

Returns a currency format for the current
default locale.

Licensed to Mark Watson 

215

DAO pattern
Table 3.3

Classes and methods from the Java API that use the Factory pattern
Class

Method

Description

java.text.NumberFormat

getIntegerInstance()

Returns an integer format for the current
default locale.

java.util.concurrent
.Executors

newFixedThreadPool()
newCachedThreadPool()
newSingleThreadExecutor()

Creates a thread pool.

NOTE

Refer to chapter 12 for detailed coverage of the Factory method

getInstance() defined in class NumberFormat.

Almost all applications need to store data to a persistent medium in one form or
another. Data persistence can range from using simple text files to full-fledged database management systems. In the next section, we’ll cover how the Data Access Object
(DAO) pattern enables you to separate code that communicates with the data source
from the classes that use the data.

3.9

DAO pattern
[3.6] Write code to implement the DAO pattern
Imagine your employee application needs to read its data from and write to multiple
sources like flat files, relational databases, XML, or JSON. Add to this the differences in
accessing the data for different vendor implementations. How would your application
manage to work with data stored in a different format, with different data management systems, offering separate features, using separate APIs? This section shows you
how the DAO pattern helps in a similar situation.

3.9.1

What is the DAO pattern?
The DAO pattern abstracts and encapsulates all access to a data store (flat files, relational databases, XML, JSON, or any other data source). It manages the connection
with the data source to access and store the data. It shields a client from knowing how
to retrieve or store data and lets it specify what data to retrieve and store. So it makes
the client code flexible to work with multiple data sources.
The DAO pattern decouples classes that define business or
presentation logic from the data persistence details.

EXAM TIP

3.9.2

Implementing the DAO pattern
Identify the data that you need to store to or retrieve from a data store (say, Emp).
Define an interface, a DAO, say, EmpDAO, to expose the data’s CRUD operations. The

Licensed to Mark Watson 

216

CHAPTER 3 Object-oriented design principles

Uses
Client

<>
EmpDAO

Encapsulates

Creates/modifies

Uses/
modifies

Emp
Figure 3.15

Data store

EmpDAOImpl
UML class diagram of classes and interfaces implementing the DAO pattern

implementation details are hidden from clients and defined in a class (say, EmpDAOImpl). If the implementation details to access data in the data source change, it
doesn’t affect a client. This pattern allows an application to adapt to different data
stores or its version without affecting a client. Figure 3.15 shows the UML class diagram of classes implementing the DAO pattern.
In the following sample code, class Emp encapsulates the employee data that can
be read from and stored to multiple types of data stores. The interface EmpDAO
exposes the operations that can be performed with Emp objects in a data store. Class
EmpDAOImpl implements EmpDAO, connecting to a data store and retrieving Emp from
it and updating it in the data store. Note how class Client is decoupled from the
data storage and retrieval details. Class Client works with EmpDAO and not with its
specific implementation.
class Emp {
int id;
String name;
int age;
String address;
}
interface EmpDAO {
public int create(Emp e);
public Emp get(int id);
public boolean delete(Emp e);
public boolean update(Emp e);
public Collection getAll();
}
class EmpDAOImpl implements EmpDAO {
public int create(Emp e) {
/* connect to datastore, insert data for employee e */
}
public Emp get(int id) {
/* connect to datastore, retrieve and return data for employee-id id */
}
public boolean delete(Emp e) {
/* connect to datastore and delete data for employee-id e.id */
}

Licensed to Mark Watson 

217

DAO pattern
public boolean update(Emp e) {
/* connect to datastore and update employee data */
}
public Collection getAll() {
/* connect to datastore, retrieve emp data, return as Collection */
}
}
class Client {
public static void main(String args[]) {
Emp emp = new Emp();
emp.id = 10; emp.name = "Harry";
Create
emp.age = 39; emp.address = "UK";

instance of
EmpDAO.

EmpDAO dao = new EmpDAOImpl();
dao.create(emp);
emp.name = "Harry M"; emp.age = 40;
dao.update(emp);

Update emp
object in
data store.
Retrieve
object from
data store.

Emp emp2 = dao.get(11);
if (emp2 != null) dao.delete(emp2);
}
}

EXAM TIP

Create Emp
object.
Initialize
emp object.
Insert emp data in data
store using method create().
Modify existing
values of emp object.
Delete data corresponding
to retrieved data from
data store.

The CRUD operations form the basis of the DAO pattern.

The preceding example uses only one implementation of the DAO interface. How
would an application manage working with multiple DAO implementations? You can
use the Factory pattern—that is, Simple Factory, Factory Method, or Abstract Factory—with the DAO pattern to work with multiple DAO implementations, as shown in
the next section.
The exam might ask you whether it’s common to use the Factory pattern with the DAO pattern. The answer is yes (as shown in the
next section). But it isn’t mandatory to use the Factory pattern with the
DAO pattern (as shown in this section).
EXAM TIP

3.9.3

Using the Simple Factory pattern with the DAO pattern
You can use a Factory pattern to work with multiple DAO pattern implementations.
The following example uses the Simple Factory pattern. Method getInstance() in
class DAOFactory returns an instance of the EmpDAO implementation, which can be
used by a client class (Client). I haven’t repeated the class details deliberately for
Emp and EmpDAO to keep the code short. They’re the same as used in the example in
the preceding section. The code for classes EmpDAOOracleImpl and EmpDAOMySQLImpl can be assumed to be the same as the code for class EmpDAOImpl used in the
preceding example.
class Emp { /* code */ }
interface EmpDAO { /* code */ }

Licensed to Mark Watson 

218

CHAPTER 3 Object-oriented design principles
class EmpDAOOracleImpl implements EmpDAO { /* code */ }
class EmpDAOMySQLImpl implements EmpDAO { /* code */ }

DAOFactory
uses Simple
Factory
pattern.

abstract class DAOFactory {
public static int ORACLE = 1;
public static int MYSQL = 2;
public static EmpDAO getEmpDAOInstance(int DBtype) {
if (DBtype == ORACLE)
return new EmpDAOOracleImpl();
else if (DBtype == MYSQL)
return new EmpDAOMySQLImpl();
else
return null;
}
}

EmpDAO implementation
for Oracle Database
EmpDAO implementation
for MySQL Database
Static Factory
pattern to return
implementation
of EmpDAO

Get EmpDAO
implementation
for Oracle DB.

class Client {
public static void main(String args[]) {
EmpDAO empDAO = DAOFactory.getEmpDAOInstance(DAOFactory.ORACLE);
Emp emp = new Emp();
emp.id = 10; emp.name = "Harry";
Insert emp data
emp.age = 39; emp.address = "UK";
in data store.
empDAO.create(emp);
}
}

To keep the code simple, this and the next section use DAO implementation classes for only two different data stores, Oracle and MySQL.

NOTE

In the next section, you’ll see how you can decouple data storage and retrieval for
multiple type of objects (Emp, Dept) from multiple data stores (Oracle, MySQL) using
the Factory Method or Abstract Factory patterns with the DAO pattern.

3.9.4

Using the Factory Method or Abstract Factory pattern with
the DAO pattern
In the following example, class Client needs to store and retrieve objects of Emp and
Dept to and from a data store. To decouple Client from the persistence details, the interfaces EmpDAO and DeptDAO define all data store operations with Emp and Dept objects.
The example code defines implementation classes for Oracle and MySQL data
stores. Classes EmpDAOOracleImpl and DeptDAOOracleImpl define implementation
details for an Oracle data store and classes EmpDAOMySQLImpl and DeptDAOMySQLImpl
define implementation details for a MySQL data store. The abstract class DAOFactory
defines abstract methods getEmpDAO() and getDeptDAO(), which are implemented by
its subclasses OracleDAOFactory and MySQLDAOFactory.
To store Emp and Dept objects to an Oracle database, class Client can use OracleDAOFactory, and to store them to a MySQL database, class Client can use MySQLDAOFactory:
class Emp { /* code */ }
class Dept { /* code */ }

Data objects
to persist

Licensed to Mark Watson 

219

DAO pattern
interface EmpDAO { /* code */ }
interface DeptDAO { /* code */ }

DAO pattern

class EmpDAOOracleImpl implements EmpDAO { /* code */ }
class DeptDAOOracleImpl implements DeptDAO { /* code */ }

DAO pattern
implementation for
MySQL database

class EmpDAOMySQLImpl implements EmpDAO { /* code */ }
class DeptDAOMySQLImpl implements DeptDAO { /* code */ }
abstract class DAOFactory {
protected abstract EmpDAO getEmpDAO();
protected abstract DeptDAO getDeptDAO();
public EmpDAO getEmpDAOInstance() {
return getEmpDAO();
}
public DeptDAO getDeptDAOInstance() {
return getDeptDAO();
}
}

Abstract Factory
pattern class

class OracleDAOFactory extends DAOFactory {
protected EmpDAO getEmpDAO() {
return new EmpDAOOracleImpl();
}
protected DeptDAO getDeptDAO() {
return new DeptDAOOracleImpl();
}
}
class MySQLDAOFactory extends DAOFactory {
protected EmpDAO getEmpDAO() {
return new EmpDAOMySQLImpl();
}
protected DeptDAO getDeptDAO() {
return new DeptDAOMySQLImpl();
}
}

Factory to return
Oracle DAO
implementations

Factory to return
MySQL DAO
implementations

class Client {
public static void main(String args[]) {
DAOFactory factory = new OracleDAOFactory();
EmpDAO empDAO = factory.getEmpDAOInstance();
DeptDAO deptDAO = factory.getDeptDAOInstance();
Emp emp = new Emp();
empDAO.create(emp);
Dept dept = new Dept();
deptDAO.update(dept);
}
}

DAO pattern
implementation for
Oracle database

Create
OracleDAOFactory.
Access EmpDAO and
DeptDAO implementations.

Insert emp data
in database.
Update dept data
in database.

In the next section, you’ll see what terms and phrases the exam might use to test you
on the benefits of using the DAO pattern.

Licensed to Mark Watson 

220

3.9.5

CHAPTER 3 Object-oriented design principles

Benefits of the DAO pattern
The benefits of the DAO pattern are
■

■

■

■

■
■

It abstracts and encapsulates all access to a data source. It manages the connection to the data source to obtain and store data.
It promotes programming to an interface. It completely hides the data access
implementation from its clients.
It decouples the business logic layer and persistence layer. It makes the code
independent of any changes to a data source or its vendor (for example, plaintext, XML, LDAP, MySQL, Oracle, or DB2).
It promotes flexibility. Because the interfaces accessible to client classes don’t
change, new implementation classes can be added.
The DAO pattern might also include Factory pattern classes.
It prevents tight coupling between client classes and DAO implementation
classes. It promotes the creation of cohesive classes.

Design patterns help you to reuse the experience of other programmers to create
robust application designs. When you work with real-life projects, identify recurrent
issues across projects and their probable solutions. You never know, you might identify
and pen your own design patterns. Good luck to you!

3.10 Summary
In this chapter, you covered interfaces and how to use them in class design. Given the
task of designing an application, API, or framework, you need to define multiple interfaces and abstract classes. But the choice of using abstract classes and interfaces isn’t
straightforward.
An interface can define only constants and abstract methods. The methods of an
interface are implicitly abstract and can’t define any implementation details. The
interfaces are used to define a contract, which all the classes should adhere to. Interfaces only specify the behavior that should be supported by their implementing classes;
the implementation details are left for the classes.
You can subclass an abstract class to create concrete classes only if you implement
all of the class’s abstract methods. Similarly, a concrete class should implement all the
methods of an interface.
For the exam, you also need to know when to use interface inheritance and when
to use class inheritance. Class inheritance helps you reuse implementation details provided in the base classes, so the derived classes don’t have to write all the code themselves. Class inheritance also scores better when you want to add new behavior to an
existing base class. You may prefer interface inheritance over class inheritance when
you need to define multiple contracts for classes. Interface implementation has one
major advantage of allowing a class to implement multiple interfaces, so an object of
the class can be assigned to variables of multiple interface types.

Licensed to Mark Watson 

Summary

221

Java objects share multiple relationships with other objects. IS-A and HAS-A are two
important relationships shared by Java objects. The IS-A relationship is implemented
using inheritance. You can implement the IS-A relationship by extending classes,
extending interfaces, and implementing interfaces. When a class has an instance variable of a certain type, the class HAS-A .
Both inheritance and composition enable you to reuse the functionality of a class,
but with a difference. Most often, newcomers to programming or OOP aren’t sure
whether to use inheritance or composition, to use another object. So they inherit a
class when they want to use it in another class. You should use inheritance when the
extended class is a specialized type of the base class. You should use composition when
you simply want to use the functionality being offered by a class in another class.
Design patterns help you reuse the experience of other application designers and
developers, in terms of the guidelines and suggested solutions for implementing an
application’s commonly occurring logic. A design pattern enables you to reuse experience and not code. We discussed all three design patterns that are on the exam: Singleton, DAO, and Factory method.
Singleton is a class design pattern that ensures that a class is instantiated only
once. The class also provides a global point of access to it. This pattern is usually
applied to only one class. Common examples include a single instance of Device
Manager to manage all the devices on your system, or a single instance of print
spooler to manage all printing jobs. To apply the Singleton pattern, you should
mark the constructor of a class as private so that no other class can call it. Make the
class itself create the sole instance, referred by a static variable. You can define a
static method to access this sole instance.
The Factory pattern prevents tight coupling between the classes it uses from their
concrete class implementation. It also eliminates direct constructor calls in favor of
invoking a method. Multiple variations of this pattern exist: Simple Factory, Factory
Method, and Abstract Factory.
The Simple Factory pattern creates and returns objects of classes that extend a
common parent class or implement a common interface. The objects are created
without exposing the instantiation logic to the client. The calling class is decoupled
from knowing the exact name of the instantiated class. The intent of the Factory
Method pattern is to define an interface for creating an object but let subclasses
decide which class to instantiate. The Factory Method pattern lets a class defer instantiation to its subclasses. The Abstract Factory pattern is used to create a family of related
products (in contrast, the Factory Method pattern creates one type of object). This
pattern also defines an interface for creating objects, but it lets subclasses decide
which class to instantiate.
You learned what the DAO pattern is and how to implement it in code. This pattern encapsulates all access to the persistent store to access and manipulate the data.
The DAO pattern also manages the connection to the data store to retrieve and store
the data. Usually, a DAO class accesses and manipulates a separate data object. An

Licensed to Mark Watson 

222

CHAPTER 3 Object-oriented design principles

application usually defines a separate DAO for separate data objects that should be
persisted. You need this design pattern so you can decouple data access code from the
business logic. This eases the transition from using various data storage formats and
vendors and creates more cohesive classes. The DAO pattern is frequently used with
the Factory pattern.

REVIEW NOTES
This section lists the main points covered in this chapter.

Interfaces
■
An interface is an example of separating the behavior that an object should support from its implementation. An interface is used to define behavior by defining a group of abstract methods.
■
All members (variables and methods) of an interface are implicitly public.
■
You declare an interface using the keyword interface. An interface can define
only public, final, static variables and public, abstract methods.
■
The methods of an interface are implicitly abstract and public.
■
The variables of an interface are implicitly public, static, and final.
■
You can declare a top-level interface only with public and default access.
Valid nonaccess modifiers that can be applied to an interface are abstract
and strictfp.
■
An interface that’s defined within another interface can be defined with any
access modifier.
■
An interface can’t extend a class.
■
An interface can extend multiple interfaces. It can’t implement another interface.
■
An interface can define inner interfaces and (surprisingly) inner classes too.
■
Because all the members of an interface are implicitly public, a derived interface inherits all the methods of its base interface.
■
You can compare interface implementation to the signing of a contract. When
a concrete class declares an implementation of an interface, it agrees to and
must implement all its abstract methods.
■
If you don’t implement all the methods defined in the implemented interfaces,
a class can’t compile as a concrete class. A concrete class must implement all the
methods from the interfaces that it implements. An abstract class might not
implement all the methods from the interfaces that it implements.
■
A class can define an instance or a static variable with the same name as the variable defined in the interface that it implements. These variables can be defined
using any access level.
■
Because the methods in an interface are implicitly public, if you try to assign a
weaker access to the implemented method in a class, it won’t compile.

Licensed to Mark Watson 

Review notes
■

223

A class can inherit methods with the same name from multiple interfaces.
There are no compilation issues if these methods have exactly the same method
signature or if these methods can coexist in the implemented class as overloaded methods. The class won’t compile if these methods coexist as incorrectly
overloaded or overridden methods.

Class inheritance versus interface inheritance
■

■

■

■

Class inheritance scores better when you want to reuse the implementation
already defined in a base class. It also scores better when you want to add new
behavior to an existing base class.
You can add new behavior to an abstract or nonabstract base class, and you may
not break all the classes that subclass it.
You may prefer interface inheritance over class inheritance when you need to
define multiple contracts for classes.
Interface implementation has one major advantage of allowing a class to implement multiple interfaces, so an object of the class can be assigned to variables of
multiple interface types.

IS-A and HAS-A relationships in code
■
■

■

■

■
■

An IS-A relationship is implemented using inheritance.
You can traverse the inheritance tree up the hierarchy to identify an IS-A relationship. A derived class IS-A type of its base class and its implemented interfaces. A derived interface IS-A type of its base interface. A base class or interface
is not a type of its derived class or interface.
The key to finding the entities that participate in an IS-A relationship is to find
your way, up the hierarchy tree, in the direction of the arrows. This technique
not only will help you with the exam, but also will take you a long way in your
professional career.
You can implement an IS-A relationship by extending classes, extending interfaces, or implementing interfaces.
A HAS-A relationship is implemented using association.
The relationship MyClass HAS-A YourClass is implemented by defining an
instance variable of type YourClass in MyClass. Defining an instance variable of
type MyClass in YourClass will implement the relationship YourClass HAS-A
MyClass.

Cohesion and low coupling
■
■

■

Cohesion refers to how focused a class or a module is.
High cohesion refers to a well-focused class or module, whereas low cohesion
refers to a class or module that doesn’t have a well-defined responsibility.
Well-designed applications aim for highly cohesive classes and modules.

Licensed to Mark Watson 

224

CHAPTER 3 Object-oriented design principles
■

■

■

■

Coupling refers to how much a class or module knows about other classes
or modules.
Loosely coupled classes interact with each other by using their interface (public
methods).
Low coupling and loose coupling refer to the same concept and are often used
interchangeably.
Well-designed applications aim for loosely coupled classes and modules.

Object composition principles
■

■

■

Newcomers to programming often extend a class when they want to use a class
in another class. They use inheritance in place of composition.
You should extend a class (inheritance) when you want the objects of the
derived classes to reuse the interface of their base class.
You should define an object of another class (composition) when you want to
use the functionality offered by the class.

Singleton pattern
■

■
■
■

■

■
■

■

■

Singleton is a creational design pattern that ensures that a class is instantiated
only once. The class also provides a global point of access to it.
It is used in scenarios when you might need only one object of a class.
Implementation of the Singleton pattern involves a single class.
A class that implements the Singleton pattern must define its constructor as
private.
A Singleton class uses a static private reference variable to refer to its sole
instance.
A Singleton class defines a static method to access its sole instance.
To avoid threading issues with the creation of the sole instance of the Singleton
class, you might use either of the following to create its sole instance:
– Eager initialization—instantiate the object with its declaration
– Synchronized lazy initialization—create the instance using a synchronized
method or code block
You can also use enums to implement the Singleton pattern because enum
instances can’t be created by any other class.
On the exam, all of these approaches (eager initialization, synchronization of
the complete method getInstance(), and partial synchronization of method
getInstance()) may be presented, and you may be questioned about the
right approach for implementing the Singleton pattern. All these approaches
are good. Beware of modified code that tries to synchronize a partial method
getInstance(), which doesn’t synchronize the code that creates an object of
Singleton.

Licensed to Mark Watson 

Review notes

225

Factory pattern
■
One of the most frequently used design patterns, multiple flavors of this pattern
exist: Simple Factory, Factory Method, and Abstract Factory.
■
The Simple Factory pattern creates and returns objects of classes that extend a
common parent class or implement a common interface. The objects are created without exposing the instantiation logic to the client. The calling class is
decoupled from knowing the exact name of the instantiated class.
■
The intent of the Factory Method pattern is to define an interface for creating
an object but let subclasses decide which class to instantiate. The Factory
Method pattern lets a class defer instantiation to its subclasses.
■
The Abstract Factory pattern is used to create a family of related products (in
contrast, the Factory Method pattern creates one type of object). This pattern
also defines an interface for creating objects but it lets subclasses decide which
class to instantiate.
■
The benefits of the Factory pattern are
– Prefers method invocation over direct constructor calls
– Prevents tight coupling between a class implementation and your application
– Promotes creation of cohesive classes
– Promotes programming to an interface
– Promotes flexibility. Object instantiation logic can be changed without affecting the clients that use objects. It also allows the addition of new concrete
classes.
■
The following don’t apply to the Factory pattern:
– It doesn’t eliminate the need of overloading constructors in class implementations.
– It doesn’t encourage the use of any particular access modifier. It isn’t compulsory to define private members to use this pattern.
– It won’t slow your application.
– It isn’t related to how to monitor objects for change.
■
The Java API uses the Factory pattern in many of its classes, including
– Calendar.getInstance()
– Arrays.asList()
– ResourceBundle.getBundle()
– DriverManager.getConnectionEstablish(), DriverManager.getDriver()
– Connection.createStatement()
– Statement.executeQuery()
– NumberFormat.getInstance(), NumberFormat.getNumberFormat(), NumberFormat.getCurrencyInstance(), NumberFormat.getIntegerInstance()
– Executors.newFixedThreadPool(), Executors.newCachedThreadPool(),
Executors.newSingleThreadExecutor()

Licensed to Mark Watson 

226

CHAPTER 3 Object-oriented design principles

DAO pattern
■
The DAO pattern encapsulates all communication with a persistent store to
access and manipulate the stored data.
■
The DAO pattern also manages the connection to the data store to retrieve and
store the data.
■
An application usually defines separate DAO classes for each type of data object
that should be persisted.
■
The CRUD operations form the basis of the DAO pattern.
■
The DAO pattern removes the direct dependency between an application and
the data persistence implementation.
■
The DAO pattern is frequently used with the Factory pattern.

SAMPLE EXAM QUESTIONS
Q 3-1. What is the output? Choose the best answer.
interface Online {
String course = "OCP";
int duration = 2;
}
class EJavaGuru implements Online {
String course = "OCA";
public static void main(String args[]) {
EJavaGuru ejg = new EJavaGuru();
System.out.print(ejg.course);
System.out.print(EJavaGuru.duration);
}
}
a
b
c
d
e
f

// n1
// n2

Compilation fails at line n1.
Compilation fails at line n2.
Compilations fails at both lines n1 and n2.
Code prints “OCA2”.
Code prints “OCP2”.
Code throws a runtime exception.

Q 3-2. In the next Java version, designers are planning to create a new switch statement. This statement should be able to accept an object of type SwitchArgument and
be able to call method defaultValue() on it. Which of the following options describe
feasible (workable) options?
a

b
c

Define class SwitchArgument and make class java.lang.Object extend class
SwitchArgument.
Define method defaultValue in class java.lang.Object.
Define interface SwitchArgument with no methods. The classes that need to be
used in this switch statement can implement interface SwitchArgument.

Licensed to Mark Watson 

Sample exam questions
d

227

Define interface SwitchArgument with method defaultValue(). The classes
that need to be used in this switch statement can implement interface SwitchArgument.

Q 3-3. Given the following code, select the correct options:
class AbX {}
class Sunny extends AbX {}
interface Moon {}
class Sun implements Moon {
Sunny AbX;
}
a
b
c
d
e
f
g

Sunny IS-A Moon.
Sunny HAS-A AbX.
Sun HAS-A AbX.
Sun HAS-A Sunny.
AbX HAS-A Moon.
Sun IS-A Abx.
Sunny IS-A Abx.

Q 3-4. Given the following statements, chose the options that are correct individually:
■
■
■
■
■
■

a
b
c
d
e
f

ABCD can’t define instance variables.
XYZ can only define public methods.
ABCD can extend XYZ.
XYZ can’t implement ABCD.
LMN can define instance variables.
LMN can’t extend ABCD.
ABCD is a class.
ABCD is an interface.
XYZ is a class.
XYZ is an interface.
LMN is a class.
LMN is an interface.

Q 3-5. Which of the following options are correct?
a

b

If you add a method to your interface, you’ll break all the classes that implement it.
If you add a nonabstract method to a base abstract class, its subclasses might not
always succeed to compile.

Licensed to Mark Watson 

228

CHAPTER 3 Object-oriented design principles

c
d

When you work with an interface type, you decouple from its implementation.
Code that works with a reference variable of an abstract base class works with
any object of its subclasses.

Q 3-6. Given the following statements, choose the corresponding code implementation:
■
■
■
■

Apple HAS-A Ball.
Ball IS-A Cone.
Cone HAS-A Apple.
Dot IS-A Ball.

a

class
class
class
class

Apple { String Ball; }
Cone { String Apple; }
Ball extends Cone {}
Dot extends Ball {}

b

class
class
class
class

Apple {Ball Ball;}
Cone {Apple Apple;}
Ball extends Cone {}
Dot extends Ball {}

c

class
class
class
class

Apple {Ball aVar;}
Cone {Apple age;}
Ball extends Cone {}
Dot extends Ball {}

d

class Apple {Ball var;}
interface Cone {Apple a;}
interface Ball implements Cone {}
interface Dot implements Ball {}

Q 3-7. What is true about interfaces? (Choose all that apply.)
a
b
c
d

They force an implementing class to provide its own specific functionality.
An object of a class implementing an interface can be referred to by its own type.
An interface can define constructors to initialize its final variables.
An interface can define a static initializer block to initialize its variables.

Q 3-8. Select all incorrect statements.
a
b

c
d

e
f

An abstract class may define abstract methods.
An abstract class forces all its concrete derived classes to implement all its
abstract methods.
An abstract class does provide enough details for its objects to be created.
An abstract class is used to group the common behaviors of a set of similar
objects, but which itself may be incomplete.
An abstract class may not be used to create a new type.
You can create an instance of an abstract class.

Licensed to Mark Watson 

Sample exam questions

229

Q 3-9. Assuming that the names of the classes used in the following code represent the
actual objects, select the correct options.
class Ray {}
class Satellite {}
class Sun { Ray rays; }
class Moon extends Satellite {}
class Earth {}
class SolarSystem {
Earth a;
Moon b;
}
a
b
c
d
e
f

Sun is associated with Ray.
Moon is composed of Satellite.
SolarSystem is composed of Earth and Moon.
SolarSystem is associated with Earth.
SolarSystem is associated with Moon.
Ray is composed of Sun.

Q 3-10. Given the following code, which options correctly declare, implement, and
extend these interfaces?
interface Coverable {}
interface Package {}
interface Ship extends Coverable, Package {}
a

class Book implements Ship {}

b

class Container implements Coverable {}
class Bottle extends Container{}

c

interface Voyage implements Ship {}
class Fan implements Voyage {}

d

interface Delivery extends Ship{}
interface Payment extends Package, Delivery{}
class Product extends Payment {}

Q 3-11. Which of the following code options implements the Singleton pattern correctly?
a

class King {
private static King king = null;
private King() {}
public static King getInstance() {
king = new King();
return king;
}
}

Licensed to Mark Watson 

230

CHAPTER 3 Object-oriented design principles
b

class King {
private static King king = new King();
public static King getInstance() {
return king;
}
}

c

class King {
private static King king = new King();
private King() {}
private static King getInstance() {
return king;
}
}

d

class King {
private static King king;
public static King getInstance() {
if (king == null)
king = new King();
return king;
}
}

e

None of the above

Q 3-12. Given the following definition of class King, which option, when replacing
//INSERT CODE HERE//, implements the Singleton pattern correctly? (Choose all
that apply.)
class King {
private static String name;
private static King king = new King();
// INSERT CODE HERE //
public static King getInstance() {
return king;
}
}
a

private King() {}

b

private King() {
name = null;
}

c

private King() {
name = new String("King");
}

d

private King() {
if (name != null)
name = new String("King");
}

e

None of the above

Licensed to Mark Watson 

Sample exam questions

231

Q 3-13. Given the following definition of class King, which option, when replacing
//INSERT CODE HERE//, implements the Singleton pattern correctly with no concurrent creation of objects of class King? (Choose all that apply.)
class Jungle {}
class King {
private static King king = null;
private King() {}
//INSERT CODE HERE//
}
a

public static synchronized King getInstance() {
if (king == null)
king = new King();
return king;
}

b

public static King getInstance() {
if (king == null) {
synchronized (Jungle.class){
king = new King();
}
}
return king;
}

c

public static King getInstance() {
synchronized (Jungle.class){
if (king == null) {
king = new King();
}
}
return king;
}

d

synchronized static public King getInstance() {
synchronized (Jungle.class){
if (king == null) {
king = new King();
}
}
return king;
}

Q 3-14. Given the following statements, select all options that are correct individually:
■
■
■
■

a
b

Class Queen implements the Singleton pattern.
Class King implements the Singleton pattern.
Class Prince doesn’t implement the Singleton pattern.
Class Princess doesn’t implement the Singleton pattern.
Only class Queen can create an object of class King.
Either class King or class Queen can create an object of class King.

Licensed to Mark Watson 

232

CHAPTER 3 Object-oriented design principles

c
d
e

Only class King can create its object.
Both classes King and Queen can create objects of Prince and Princess.
All classes (King, Queen, and Princess) can create objects of Prince.

Q 3-15. Given the definition of class Person as follows, which options do you think are
correct implementations of a class that implements the DAO pattern for class Person
(there are no compilation issues with this code)?
class Person {
int id;
String name;
int age;
}
a

class PersonDAO {
class DAO {
Person person;
}
}

b

class PersonDAO {
Person findPerson(int id) { /* code */ }
Person seekPerson(int id) { /* code */ }
}

c

class PersonDAO {
static Person findPerson(int id)
static int create(Person p) { /*
static int update(Person p) { /*
static int delete(Person p) { /*
}

d

class PersonDAO {
Person findPerson(int id)
int create(Person p) { /*
int update(Person p) { /*
int delete(Person p) { /*
}

{ /*
code
code
code

{ /*
code
code
code

code */ }
*/ }
*/ }
*/ }

code */ }
*/ }
*/ }
*/ }

Q 3-16. Select the correct statements:
a

b
c
d

The DAO pattern helps decouple code that inserts data in persistence storage
from code that deletes data in persistence storage.
The DAO pattern helps encapsulate persistence data logic.
The DAO eases migration of persistent data from one vendor to another.
The DAO promotes low coupling and high cohesion.

Licensed to Mark Watson 

Sample exam questions

233

Q 3-17. Given the following definition of class Person, which of its methods would you
need to move to another class, say, PersonDAO, to implement the DAO pattern?
class Person {
int id;
String name;
int age;
int getId() {return id;}
void setId(int id) {this.id = id;}
String getName() {return name;}
void setName(String name) {this.name = name;}
int getAge() {return age;}
void setAge(int age) {this.age = age;}
void find() { /* code to find Person with this id in DB */ }
void insert() { /* code to insert Person with its details in DB */ }
void modify() { /* code to update Person with this id in DB*/ }
void remove() { /* code to remove Person with this id in DB */ }
}
a
b
c
d

Methods getId(), setId(), find(), insert(), modify(), remove()
Methods find(), insert(), modify(), remove()
Methods getId(), setId(), getName(), setName(), getAge(), setAge()
Methods getId(), setId()

Q 3-18. Given
class Animal {}
class Herbivore extends Animal {}
class Carnivore extends Animal {}
class Cow extends Herbivore {}
class Tiger extends Carnivore {}
class Client{
public void createAnimal(String eatingHabits) {
Animal foo = null;
if (eatingHabits.equals("grass"))
foo = new Cow();
else if (eatingHabits.equals("deer"))
foo = new Tiger();
}
}

What are the benefits of moving creation of Animal instances from class Client to a
separate class, say, Animals?
a

b

c
d
e

To enable class Client to use Animal instances without the need to know its
instance creation logic
To promote extensibility—specific Animal classes can be added later, which
might be returned by class Animals
To implement the Singleton pattern
To implement DAO
To enable low coupling and high cohesion

Licensed to Mark Watson 

234

CHAPTER 3 Object-oriented design principles

Q 3-19. Given
interface Animal {}
class Cat implements Animal {}
class Tiger implements Animal {}
class Factory {
static Animal getInstance(String type) {
if (type.equals("Tiger"))
return new Tiger();
else if (type.equals("Cat"))
return new Cat();
else
return getAnimal();
}
private static Animal getAnimal() {
return new Cat();
}
}

Select code that initializes an Animal reference using a Factory:
a
b
c
d
e

Animal animal = Factory.getInstance();
Animal animal = Factory.getAnimal();
Animal animal = Factory.getInstance("Animal");
Animal animal = Factory.getAnimal("Tiger");
Animal animal = new Factory().getInstance("Cat");

Q 3-20. Which of the following use the Factory pattern? (Choose all that apply.)
a
b
c
d
e

Object.equals();
Calendar.getInstance()
DriverManager.getDriver();
Object.wait();
NumberFormat.getDateInstance();

ANSWERS TO SAMPLE EXAM QUESTIONS
A 3-1. d
[3.1] Write code that declares, implements, and/or extends interfaces
Explanation: Class EJavaGuru defines an instance variable course. Interface Online
also defines a variable with the same name—course (which is implicitly static). Class
EJavaGuru implements Online. Using EJavaGuru’s instanceName.course will refer to
its instance variable. Using Online.course will refer to the variable course from
Online. Using EJavaGuru.course will result in a compilation error. Code on line n1
compiles successfully and prints OCA.

Licensed to Mark Watson 

Answers to sample exam questions

235

Because the variables defined in an interface are implicitly static and final, the variable duration can be accessed as EJavaGuru.duration. Code on line n2 compiles successfully and prints 2.
However, a class can’t define static and instance variables with the same name. The
following class won’t compile:
class EJavaGuru {
String course;
static String course;
}

A 3-2. d
[3.2] Choose between interface inheritance and class inheritance
Explanation: Option (a) is incorrect. java.lang.Object is the base class of all classes
in Java. Making class java.lang.Object extend another class can be extremely risky.
Adding a method with a particular signature can break code of some other class, if it
has defined a method with the same name (defaultValue()) but a different signature that isn’t compatible, forming invalid overloaded methods.
Option (b) is incorrect. Because the requirement expects an object of SwitchArgument, adding just method defaultValue() to class java.lang.Object won’t serve
the purpose. To define a new type, we need to define SwitchArgument as either a class
or an interface.
Option (c) is incorrect. The requirement mentions that the object of SwitchArgument, passed to a switch statement, should define method defaultValue().
Defining this method in interface SwitchArgument ensures that all classes that implement interface SwitchArgument define method defaultValue().
Option (d) is correct. Creation of type SwitchArgument as an interface with
method defaultValue() provides a convenient option for all existing classes that
want to be passed as an argument to the switch statement. When the classes implement the interface SwitchArgument, they’ll be responsible for implementing method
defaultValue().
A 3-3. d, g
[3.3] Apply cohesion, low-coupling, IS-A, and HAS-A principles
Explanation Option (a) is incorrect. Classes Sunny and Moon are unrelated.
Option (b) is incorrect. Class Sunny extends class AbX; it doesn’t define a variable
of type AbX. The correct relationship here would be Sunny IS-A AbX.
Option (c) is incorrect. Class Sun defines a variable of type Sunny. So the correct
relation would be Sun HAS-A Sunny. The IS-A and HAS-A relationships don’t reflect the
names of the variables.

Licensed to Mark Watson 

236

CHAPTER 3 Object-oriented design principles

Option (d) is correct. Class Sun defines a variable of type Sunny, so the relationship
Sun HAS-A Sunny is correct.
In option (e), class AbX doesn’t define any variable of type Moon, so this relationship is incorrect.
In option (f), class Sun doesn’t extend class AbX, so this relationship is incorrect.
In option (g), class Sunny extends class AbX, so this relationship is correct.
A 3-4. b, d, e
[2.1] Identify when and how to apply abstract classes
[2.2] Construct abstract Java classes and subclasses
[3.1] Write code that declares, implements, and/or extends interfaces
Explanation: Option (a) is incorrect. As specified, ABCD can’t define an instance variable, but a class can define instance variables.
Option (b) is correct. All the variables defined in an interface are implicitly public,
final, and static. The static variables can’t exist as instance variables.
Option (c) is incorrect. XYZ can’t exist as a class. As specified, XYZ can define only
public methods, whereas a class can define nonpublic methods.
Option (d) is correct. XYZ can exist as an interface because all the methods in an
interface are implicitly public. All methods in a class aren’t implicitly public.
Option (e) is correct, and (f) is incorrect. LMN is a class because it can define
instance methods and can’t extend ABCD, an interface.
A 3-5. a, b, c, d
[2.1] Identify when and how to apply abstract classes
[2.2] Construct abstract Java classes and subclasses
[3.1] Write code that declares, implements, and/or extends interfaces
Explanation: Option (a) is correct. If you add a method to your interface, all the
classes that implement the interface will fall short on the definition of the newly
added method and will no longer compile.
Option (b) is correct. If you add a nonabstract method to your base class, you can
break its subclasses. If a subclass method has the same name as the newly added
method in the base class, which doesn’t qualify as a valid overloaded or overriding
method, the subclass won’t compile.
Option (c) is correct. When you work with an interface type, you’re free to work
with any object that implements the interface.
Option (d) is correct. Objects of all subclasses can be assigned to a reference variable of its abstract or nonabstract base class. So code that works with the abstract base
class will work with objects of any of its subclasses.

Licensed to Mark Watson 

Answers to sample exam questions

237

A 3-6. b, c
[3.3] Apply cohesion, low-coupling, IS-A, and HAS-A principles
Explanation: An IS-A or a HAS-A relationship is defined between the types of the variables, and not their names.
Option (a) is incorrect. Class Apple HAS-A String and not Ball.
Option (b) is correct. Class Apple defines a variable Ball of type Ball. So Apple
HAS-A Ball. It’s acceptable to define a variable with the name of its class. Class Cone
defines a variable Apple of type Apple. So it satisfies the relationship Cone HAS-A
Apple. Class Ball extends class Cone, so it satisfies Ball IS-A Cone. Class Dot extends
class Ball. So it satisfies Dot IS-A Ball.
Option (c) is also correct. Class Apple defines a variable aVar of type Ball. So
Apple HAS-A Ball. Class Cone defines a variable age of type Apple. So it satisfies the
relationship Cone HAS-A Apple. Class Ball extends class Cone, so it satisfies Ball IS-A
Cone. Class Dot extends class Ball. So it satisfies Dot IS-A Ball.
Option (d) is incorrect. An interface can’t implement another interface. It can
only extend it.
A 3-7. a, b
[3.1] Write code that declares, implements, and/or extends interfaces
Explanation: Options (c) and (d) are incorrect. An interface can neither define a
constructor nor a static initializer block.
A 3-8. c, e, f
[2.1] Identify when and how to apply abstract classes
Explanation: Option (c) is an incorrect statement, because objects of an abstract class
can’t be created, even if the class doesn’t define any abstract method.
Option (e) is an incorrect statement. An abstract class defines a new type. This type
can be used to define variables in multiple scopes (instance variables, static variables,
method parameters, and local variables).
Option (f) is an incorrect statement, because you can’t create an object of an
abstract class.
A 3-9. a, c, d, e
[3.4] Apply object composition principles (including HAS-A relationships)
Explanation: Option (a) is correct. Sun gives out rays, so Sun is associated with Ray.
Option (b) is incorrect. Moon is a type of Satellite. Composition is a whole-part relationship; if the enclosing object goes out of scope, the part also goes out of scope.

Licensed to Mark Watson 

238

CHAPTER 3 Object-oriented design principles

Option (c) is correct. If SolarSystem goes out of scope, all the objects that it’s
composed of, including Earth and Moon, will go out of scope.
Options (d) and (e) are correct. Composition is a special type of association, and
objects in this relationship are associated with each other.
Option (f) is incorrect. Ray isn’t composed of Sun. It’s the other way around:
Sun is composed of Ray. If Sun goes out of scope, Ray also goes out of scope (is no
longer visible).
A 3-10. a, b
[3.1] Write code that declares, implements, and/or extends interfaces
Explanation: Option (c) is incorrect, because an interface (Voyage) can’t implement
another interface (Ship).
Option (d) is incorrect, because a class (Product) can’t extend an interface (Payment).
A 3-11. e
[3.5] Design a class using the Singleton design pattern
Explanation: Option (a) is incorrect. It creates a new object of class King, whenever
method getInstance() is called. On the contrary, the Singleton pattern creates only
one instance of a class.
Option (b) is incorrect. A Singleton should define a private constructor so
that no other class can create its objects. The class defined in this option doesn’t
define any constructor. In the absence of a constructor, the Java compiler creates a
default constructor for a class with the access modifier as that of the class itself.
Because the class in this option is defined with default or package access, a constructor with default access will be created for it by the Java compiler. Because
other classes can use its constructor to create new objects of this class, it doesn’t
qualify as a Singleton.
Option (c) is incorrect. There is no way to access an object of class King outside
the class itself. Variable king is a static private variable, so it can’t be accessed directly.
The constructor of the class is marked private, so it can’t be used to create objects of
this class. Method getInstance() is also private, so no other class can call it.
Option (d) is incorrect. Though the variable king is private, and method getInstance creates and returns an object of class King, the catch here is that this class
doesn’t define a constructor. As mentioned in the explanation of option (b), in the
absence of a constructor, the Java compiler creates a default constructor. Because
other classes can use its constructor to create new objects of this class, it doesn’t qualify as a Singleton.

Licensed to Mark Watson 

Answers to sample exam questions

239

A 3-12. a, b, c, d
[3.5] Design a class using the Singleton design pattern
Explanation: All the options are trying to confuse you with the correct implementation
of method getInstance() of a class that uses the Singleton pattern with its constructor.
A class that implements the Singleton pattern should have a private constructor, so no
other class can create its objects. The implementation of the constructor isn’t detailed
by the Singleton pattern. The class may choose to include or exclude whatever it feels
is good for it.
A 3-13. a, c, d
[3.5] Design a class using the Singleton design pattern
Explanation: In option (a), the complete method getInstance() is synchronized,
which ensures that only one Thread executes this method and creates an instance of
class King (assigning it to the static variable king), if it’s null.
Option (b) is incorrect. Method getInstance() synchronizes only the code king =
new King();. So multiple methods can still execute method getInstance() concurrently and query whether the variable king is null. If, say, two threads find it null,
they both will execute the following code (though not at the same time):
king = new King();

When the second Thread executes the preceding code, it creates and assigns another
object of class King to variable king. This method fails to prevent multiple creations of
objects of class King.
Option (c) is correct. Method getInstance() synchronizes the part of the method
that creates an object of class King. When the control is within the synchronized
block, the code checks again to confirm that variable king is still null. If true, it creates an object of class King and assigns it to the variable king.
Option (d) is correct. It defines the same code as option (c), but with a difference:
this option applies the synchronized keyword to the method also. Though synchronizing the code block and the complete method isn’t required, it isn’t incorrect to do
so. Because this method prevents creation of multiple objects of class King, it qualifies
as a correct implementation of method getInstance().
A 3-14. c, d, e
[3.5] Design a class using the Singleton design pattern
Explanation: Options (a) and (b) are incorrect and (c) is correct. Only a class that
implements the Singleton pattern can create its object, because its constructor is
marked private. No other class can. Only class King can create its own object by calling its constructor from its other method.

Licensed to Mark Watson 

240

CHAPTER 3 Object-oriented design principles

Options (d) and (e) are correct. If a class doesn’t implement the Singleton pattern, we can assume that creation of its multiple objects is allowed. So another class
can also create its objects.
A 3-15. c, d
[3.6] Write code to implement the DAO pattern
Explanation: Options (a) and (b) are incorrect. A class that implements the DAO pattern should define methods for CRUD operations (create, retrieve, update, and
delete). Options (a) and (b) don’t define all these methods.
Options (c) and (d) are correct. Both these options define methods for CRUD
operations. You can implement these methods as static or nonstatic.
A 3-16. b, c, d
[3.6] Write code to implement the DAO pattern
Explanation: Option (a) is incorrect. The DAO pattern helps separate and decouple
application logic from persistence storage logic. It isn’t used to decouple different
data manipulation operations.
A 3-17. b
[3.6] Write code to implement the DAO pattern
Explanation: To implement the DAO pattern, you should move the methods that
interact with the persistent data storage to a separate class. In class Person, the getter
and setter methods are for assigning and retrieving object fields. They don’t work with
data in persistence storage. The rest of the methods (find(), insert(), modify(),
and remove()) work with persistent data and should be moved to another class to
implement the DAO pattern. The DAO pattern doesn’t specify any rules for conventions on naming these methods and the type that they return.
A 3-18. a, b, e
[3.7] Design and create objects using a Factory pattern
Explanation: Here’s one of the ways you can move Animal instance creation logic to
class Animals:
class Amimals{
public static Animal createAnimal(String eatingHabits) {
Animal foo = null;
if (eatingHabits.equals("grass"))
foo = new Cow();

Licensed to Mark Watson 

Answers to sample exam questions

241

else if (eatingHabits.equals("deer"))
foo = new Tiger();
return foo;
}
}

Option (a) is correct. Moving method createAnimal() to a separate class frees class
Client from knowing the logic of creating Animal instances. It can call Animals
.createAnimal(), passing it a String value to get an appropriate Animal instance.
Option (b) is correct. Method createAnimal() in class Animals can be modified
to include instantiation of other specific Animal instances without modifying its API.
Options (c) and (d) are incorrect. The stated modification is neither related to
data persistence nor to creating just one instance of a class.
Option (e) is correct. With the modification, class Client doesn’t need to know
about the specific implementations of class Animal. Class Client can concentrate on
using Animal instances rather than knowing how to create them.
A 3-19. c, e
[3.7] Design and create objects using a Factory pattern
Explanation: Class Factory doesn’t expose the object creation logic of Animal objects
and uses the Factory pattern to create and return its instances.
Option (a) won’t compile. Though you might dismiss it as a trivial or tricky option,
note that it’s easy to find similar options on the exam.
Options (b) and (d) won’t compile because getAnimal() is a private method and
it doesn’t define the method parameters.
Option (e) is correct. A static method can be accessed using both the class name
and an instance.
A 3-20. b, c, e
[3.7] Design and create objects using a Factory pattern
Explanation: Methods equals() and wait() in class Object don’t use the Factory pattern.

Licensed to Mark Watson 

Generics and collections

Exam objectives covered in this chapter

What you need to know

[4.1] Create a generic class

How to define generic classes, interfaces, and methods with single and multiple type parameters
How to define generic methods with a generic or regular class

[4.2] Use the diamond for type inference

How to drop the type from the angle brackets to
instantiate generic classes
How to use wildcards to create and instantiate
generic classes

[4.3] Analyze the interoperability of collections that use raw types and generic types

What happens when you lose type safety by using
variables of raw types and objects of generic types
How to determine and differentiate scenarios that
would generate compilation errors and warnings

[4.4] Use wrapper classes, autoboxing, and
unboxing

How and when values are boxed and unboxed when
used with wrapper classes

[4.5] Create and use List, Set, and
Deque implementations

How to create objects of the List interface
(ArrayList, LinkedList), objects of the
Deque interface (ArrayDeque, LinkedList),
and objects of the Set interface (HashSet,
LinkedHashSet, and TreeSet)
How each implementing class stores data, manipulates it, searches it, and iterates over it
How the List, Set, and Deque implementations
use methods hashCode(), equals(),
compare(), and compareTo()
Given a set of requirements, how to choose the best
interface or its implementing class

242

Licensed to Mark Watson 

Introducing generics: WARM-UP

243

Exam objectives covered in this chapter

What you need to know

[4.6] Create and use Map implementations

How to instantiate Map objects: HashMap,
LinkedHashMap, and TreeMap
How Map implementations use methods hashCode(),
equals(), compare(), and compareTo()

[4.7] Use java.util.Comparator and
java.lang.Comparable

How to define natural and custom ordering of objects of
a class

[4.8] Sort and search arrays and lists

How to sort and search arrays and lists using methods
from classes Arrays and Collections
Importance of using sorted collections for searching
values

Imagine you need to collect pencils at your workplace. You request all your fellow
workers to drop their pencils in a box at the main entrance of the office. When you
open the box the next day, you also find ink pens and marker pens (!), which you
didn’t ask for. Even though you mentioned pencils, people could add pens to the box
because no one stopped them from doing so. Now imagine that you could use a box
that wouldn’t allow adding any item other than a pencil. Would you prefer it? If you
answered yes, you’d prefer to use generics. In Java, generics empower you to specify the
type of objects that you’d like to work with so that you don’t work with other types—
knowingly or unknowingly.
Now imagine that you need to sort all the collected pencils according to their color
and size. Would you like to do that yourself, or would you prefer a magic box that
would accept all the pencils and return them to you in a sorted order? If you chose the
magic box, you’d like using the collections framework. The Java collections framework includes multiple interfaces and classes to store and manipulate a collection of
objects, including the methods that sort and search them.
This chapter covers
■
■
■
■
■
■
■
■

Creating and using generic types
Using the diamond for type inference
Analyzing the interoperability of collections that use raw types and generic types
Using wrapper classes, autoboxing, and unboxing
Creating and using List, Set, and Deque implementations
Creating and using Map implementations
Working with the java.util.Comparator and java.lang.Comparable interfaces
Sorting and searching arrays and lists

Let’s start with an introduction to generics, in the next warm-up section. Feel free to
skip it and move to the next section if you’re an experienced generics programmer.

Licensed to Mark Watson 

244

4.1

CHAPTER 4

Generics and collections

Introducing generics: WARM-UP
Generics enable you to abstract over types. They add type safety to collection classes.
Introduced with Java version 5.0, generics enable developers to detect certain bugs
during compilation so they can’t creep into the runtime code. Debugging an application is a costly affair, in terms of the time and effort required to find a bug and then
fix it. The sooner you can detect a bug, the easier it is to fix it. While developing software, it’s easier to fix a bug during unit testing than it is to fix the same bug during
integration testing or, say, when it shows up months after an application goes live. A
bug is easier to fix in the development phase than in the maintenance phase.

4.1.1

Need for introducing generics
Before generics were introduced, programmers used to assume that a class, interface,
or method would work with a certain data type. For example, figure 4.1 shows how a
programmer would assume that the ArrayList referred to by lst would contain
String objects. But because lst is a collection of objects of type Object, it can accept
any type of data (other than primitives). An issue can creep in when these different
types of objects are treated as String types during runtime.
In figures 4.1 and 4.2, ArrayList-lst is created with an initial
capacity (and not size) of two elements. The size of an ArrayList increases
as more elements are added to it.

NOTE

With the introduction of generics, programmers could indicate their intent of using a
particular type of data with a class, interface, or method (not enums, because enums
can’t have generic type parameters). Figure 4.2 shows how you can indicate that an

List lst = new ArrayList(2);

null
null

lst

Paul

lst.add("Paul");
lst.add(newInteger(1));

1

lst

for (int i = 0; i < lst.size(); ++i){
String str = (String)lst.get(i);
System.out.println(str.length());
}

Paul

1

String
cast

String
cast

Figure 4.1 Before generics were added, collection classes like ArrayList allowed the
addition of any type of data. A programmer’s assumption of adding only a particular type of data
to a collection was met with a casting exception at runtime.

Licensed to Mark Watson 

245

Introducing generics: WARM-UP

List lst = new ArrayList<>(2);

null
lst

Paul

lst.add("Paul");
lst

lst.add(new Integer(1));

null

null

Compilation error
no suitable method found
for add(Integer)

Figure 4.2 Post-generics, you can mark your intent of using a particular data type with a
class, method, or interface. If the code doesn’t adhere to the restrictions, the code fails
to compile.

ArrayList referred to by lst will accept only objects of type String. Code that tries to

add an object of any other type won’t compile.
As shown in figure 4.2, with generics, the incorrect data type is determined during
compilation. This compilation-time safety enables you to identify bugs during development, thus building better code.
The basic purpose behind using generics is to enable you to
mark your intent of using a class, method, or interface with a particular
data type. Generics add compile-time safety to collections.

EXAM TIP

4.1.2

Benefits and complexities of using generics
Apart from compile-time safety, you also get the following benefits with generics:
■

■

■

Removing explicit casts—Prior to generics, you needed to add casts when you had
a list with strings and you wanted to get a string out of the list. With generics this
isn’t needed anymore.
Better code readability—Without explicit casting, code is less cluttered, which
improves readability.
Developing generic algorithms—Just as you need not hard-code values when you
work with methods and can accept them as method parameters, generics help
you parameterize over data types and develop algorithms that work with multiple data types.

But every new concept or approach has its own set of limitations and complexities,
and using generics is no exception. As you work through this chapter, you’ll see how
adding generics to the collections framework created new complexities. (Coverage is
limited to the exam topics.)

Licensed to Mark Watson 

246

CHAPTER 4

Generics and collections

In the next section, you’ll create your own generic entities. If you haven’t already
worked with generic entities, it might take a while for all the related concepts to sink in.

4.2

Creating generic entities
[4.1] Create a generic class
On the exam, you’ll be tested on how to create generic classes, interfaces, and methods—within generic and nongeneric classes or interfaces.

4.2.1

Creating a generic class
In this section, we’ll start with an example of a nongeneric class and then modify it to
create a generic class. You’ll learn how to use a generic class and how important variable naming conventions are for the type parameters.
A NONGENERIC CLASS

To understand how to create a generic class, let’s begin with an example of a nongeneric
class, Parcel:
class Parcel {
private Object obj;
public void set(Object obj) {
this.obj = obj;
}
public Object get() {
return obj;
}
}

Class ParcelNonGeneric can use class Parcel, calling its method set() to assign an
object of class Book. It can retrieve this object by using get() and cast it to class
Phone(!). Even though not desired, it’s allowed:
class Phone{}
class Book{}
class ParcelNonGeneric {
public static void main(String args[]) {
Parcel parcel = new Parcel();
parcel.set(new Book());
System.out.println((Phone)parcel.get());
}
}

Assign object
of Book
Cast object of Book to
Phone; code compiles but
throws ClassCastException
at runtime.

ADDING TYPE SAFETY TO A NONGENERIC CLASS

Let’s see how you add type safety to class Parcel. Let’s define class Parcel as a generic
class by adding a type parameter to it, so that you can retrieve only the object type that
you assign to it, as shown in figure 4.3.

Licensed to Mark Watson 

Creating generic entities

247

Type parameter T
class Parcel  {
private T t;

Variable of type T

public void set (T t){
this.t=t;

Method parameter
of type T

}
public T get(){
return t;

Returns an object
of type T

}
}
Figure 4.3 How to convert a nongeneric class to a generic class by adding
type parameters

As shown in the preceding code, the declaration of generic class Parcel includes the
type parameter T. After adding the type information, it’s read as Parcel or Parcel
of T. The generic class Parcel defines a private instance variable of type T, and
get() and set() methods to retrieve and set its value. Methods get() and set() use
the parameter type T as their method parameter and return type.
EXAM TIP The first occurrence of T is different from its remaining occurrences because only the first one is surrounded by <>.

USING A GENERIC CLASS

Having seen how to create a generic class, let’s see how you can use it. Class UseGenericParcel instantiates Parcel and calls its methods get() and set(). Note that you don’t
need an explicit cast when you use Book instance by calling parcel.get():
Parameterized type
class Book{}
Parcel indicates
class UseGenericParcel {
Parcel will work with
public static void main(String args[]) {
instances of Book.
Parcel parcel = new Parcel();
parcel.set(new Book());
set() accepts
Book myBook = parcel.get();
Book instance.
get() returns Book
}
instance; no explicit
}
casts required.

With the generic class Parcel, UseGenericParcel can use method set() to assign
an object of type Book. But UseGenericParcel can’t cast the retrieved object to an
unrelated class, say, Phone. If it tries to do so, the code won’t compile (as shown in
figure 4.4).

Licensed to Mark Watson 

248

CHAPTER 4

Generics and collections

class Phone{}
class Book {}
class UseGenericParcel{
public static void main(String[] args){
Parcel parcel = new Parcel();

Actual
parameter

parcel.set(new Book());
System.out.println((Phone)parcel.get());

Parameterized
type

}
}

Won’t compile
get() returns Book
Can’t cast Book to Phone

Figure 4.4 A class that uses a generic class uses a parameterized type, replacing the formal
parameter with an actual parameter. Also, invalid casts aren’t allowed.

EXAM TIP A type parameter can be used in the declaration of classes,
variables, method parameters, and method return types.

VARIABLE NAMES USED FOR TYPE PARAMETERS

You must follow the variable naming rules for type parameters; for instance, you can’t
use Java keywords. As per Oracle’s naming conventions, you should use uppercase single characters for type parameters. This also sets them apart from other variables and
method parameters, which use camelCase. Though the constants use uppercase, they
aren’t usually limited to single characters.
Now, what happens if you don’t follow the conventions for naming type parameters? Here’s an interesting exam question. For the modified definition of class Parcel
in the following code, do you think method set() can be passed String objects?
class MyClass{}
class Parcel{
private MyClass t;
public void set(MyClass t) {
this.t = t;
}
}

Yes, it can. In the preceding code, MyClass is used as a placeholder for a type argument
that you pass to class Parcel—it doesn’t refer to class MyClass. So you can instantiate
Parcel, passing it a type argument, say, String, and pass a String value to its method
set(). For example
class UseParcel {
public static void main(String args[]) {
Parcel parcel = new Parcel<>();

Licensed to Mark Watson 

249

Creating generic entities
parcel.set("OCP");
System.out.println(parcel.get().length());
}
}

GENERIC CLASS EXTENDING ANOTHER GENERIC CLASS

A generic class can be extended by another generic class. In the following example,
generic class GenericBookParcel extends generic class Parcel:
Generic
extended class

class Parcel {}
class GenericBookParcel extends Parcel {}

In all cases, an extended class must be able to pass type arguments to its base class. For
the preceding example, the type argument passed to class GenericBookParcel is passed
to its base class, Parcel, when you instantiate GenericBookParcel. For example
GenericBookParcel parcel = new GenericBookParcel<>();

The preceding example passes argument String to GenericBookParcel’s type parameter T. But if you define GenericBookParcel in a way that it can’t pass an argument to
the parameters of its base class, the code won’t compile. Do you think the following
code will compile?
Won’t compile; no way
to pass argument to T

class Parcel {}
class GenericBookParcel extends Parcel {}

No, it won’t. In the preceding code, class GenericBookParcel defines a type parameter X, but doesn’t include T in its type parameter list. Because this arrangement prevents GenericBookParcel from passing type arguments to its base class Parcel, it
fails to compile.
You can also define new type parameters for a derived class when you extend a
generic base class. In the following example, class GenericBookParcel defines two
type parameters X and T:
Compiles
successfully

class Parcel {}
class GenericBookParcel extends Parcel {}

Here’s another example, in which the derived class passes type arguments to its
generic base class in its declaration:
class Parcel {}
class GenericBookParcel extends Parcel {}

//

Type argument
Book passed to
base class Parcel

A type argument must be passed to the type parameter of a base
class. You can do so while extending the base class or while instantiating
the derived class.

EXAM TIP

Licensed to Mark Watson 

250

CHAPTER 4

Generics and collections

NONGENERIC CLASS EXTENDING A GENERIC CLASS

You can extend a generic base class to define a nongeneric base class. To do so, the
derived class doesn’t define any type parameters but passes arguments to all type
parameters of its generic base class. For example
class Parcel{}
class NonGenericPhoneParcel extends Parcel {}

In the preceding example, NonGenericPhoneParcel is a nongeneric class that passes
argument Phone to its base class Parcel.
Watch out for exam questions that try to pass type arguments to a nongeneric class.
For class NonGenericPhoneParcel defined in the preceding example code, the following code won’t compile:
NonGenericPhoneParcel var = new NonGenericPhoneParcel<>();

EXAM TIP

Won’t
compile

You can’t pass type arguments to a nongeneric class.

MULTIPLE TYPE PARAMETERS

The example of generic class Parcel used in this section defines one type parameter.
A generic class with multiple type parameters takes the following form:
class ClassName  { /* code */}

In the next section on generic interfaces, you’ll also work with multiple type parameters.

4.2.2

Working with generic interfaces
A generic interface enables you to abstract over types. In this section, you’ll see how to
define and implement generic interfaces.
DEFINING A GENERIC INTERFACE

The declaration of a generic interface includes one or more type parameters. Let’s
look at an example of a generic interface that can accept multiple type parameters:
the MyMap interface accepts two type parameters and defines methods put() and
get(). You can compare the MyMap interface to a simplified version of the Map interface, defined in the java.util package:
MyMap accepts two type
parameters—K and V.
interface MyMap{
void put(K key, V value);
V get(K key);
}

put() accepts a key of type
K and a value of type V.
For a key of type K, get()
returns a value of type V.

Licensed to Mark Watson 

251

Creating generic entities

NONGENERIC CLASS IMPLEMENTING A GENERIC INTERFACE

When a nongeneric class implements a generic interface, the type parameters don’t
follow the class name. For the implemented interface, the type parameters are replaced
by actual types:
class MapLegendNonGeneric implements MyMap {
public void put(String s, Integer i) {}
public Integer get(String s) { return null; }
}

In the preceding example, MapLegendNonGeneric is a nongeneric class which implements generic interface MyMap (defined in the previous section).
When implementing a generic interface, take note of the type parameters and how
they are used in method declarations (method parameters and return types). The
methods of an implementing class must implement or override all the interface methods. In the following example, class MapLegendNonGeneric won’t compile because it
doesn’t override the abstract method get(String) in MyMap (the return type of get()
is declared to be String, not Integer):
class MapLegendNonGeneric implements MyMap {
public void put(String s, Integer i) {}
public String get(String s) { return null; }
Won’t
}
compile

EXAM TIP A nongeneric class can implement a generic interface by
replacing its type parameters with actual types.

GENERIC CLASS IMPLEMENTING A GENERIC INTERFACE

Here’s an example of declaring a generic class that implements a generic MyMap interface. To pass the type parameter information to a class, the type parameters must follow both the name of the class and the interface implemented by the class:
interface MyMap{
void put(K key, V value);
V get(K key);
}
class MapLegendGeneric implements MyMap {
public void put(K key, V value) { }
public V get(K key) { return null; }
}

Type parameters are
included right after class
and interface names.

You might also choose a combination. In the following examples, the classes define
only one parameterized type, V or K. While implementing the MyMap interface, the
classes pass actual parameters (String or Integer) to one of the interface’s parameterized types (K or V):
class MapLegendGeneric2 implements MyMap {
public void put(String key, V value) {}
public V get(String key) { return null; }
}

Licensed to Mark Watson 

252

CHAPTER 4

Generics and collections

class MapLegendGeneric3 implements MyMap {
public void put(K key, String value) {}
public String get(K key) { return null; }
}

It’s important to use a correct combination of type parameters and actual parameters
in the method declarations. The following class won’t compile because class MapLegendGeneric doesn’t implement method put(K key, String value) from the
MyMap interface:
class MapLegendGeneric4 implements MyMap {
public void put(Object value, K key) {}
public String get(K key) { return null; }
}

Won’t
compile

EXAM TIP Generic classes and interfaces are collectively referred to as
generic types.

4.2.3

Using generic methods
A generic method defines its own formal type parameters. You can define a generic
method in a generic or a nongeneric class.
GENERIC METHODS DEFINED IN A NONGENERIC CLASS OR INTERFACE

A nongeneric class doesn’t define type parameters. To define a generic method in a
nongeneric class or interface, you must define the type parameters with the method,
in its type parameter section. A method’s type parameter list is placed just after its access
and nonaccess modifiers and before its return type. Because a type parameter could
be used to define the return type, it should be known before the return type is used.
An example
abstract class Courier {
public  void deliver(E[] array) {
for (E item : array) {
System.out.println("Delivering - " + item);
}
}
}

Nongeneric
class
Generic
method

For a generic method (defined in a nongeneric class or interface), its type parameter list is placed just after the access and nonaccess
modifiers and before its return type.

EXAM TIP

GENERIC METHODS DEFINED IN A GENERIC CLASS OR INTERFACE

The following example defines a generic interface, and a generic method that defines
its own type parameter.
interface Map{
 void mapMaterial(T t);
}

Generic interface
declaration
Generic method declaration
with its own type parameters

Licensed to Mark Watson 

253

Creating generic entities

You can also define a generic constructor in a generic class:
class Phone {
 Phone(T t) {
//..code
}
}

Generic constructor
declaration with
type parameter T

Generic class
declaration with
type parameter X

Instantiating Phone
Phone c = new Phone("Android");

In the following “Twist in the Tale” exercise, let’s see whether you can determine the
difference between the presence and absence of angle brackets in a definition of
generic entities.
Twist in the Tale 4.1

Consider this definition of the Map interface discussed in a previous section:
interface MyMap{
void put(K key, V value);
V get(K key);
}

Now modify that definition to the following:
interface MyMap{
void put(K key, V value);
 get(K key);
}

Do you think these modifications will make any difference to the definition of the
MyMap interface?

In the next section, let’s see how you can limit the parameter types that you can pass
to a generic class, interface, or method.

4.2.4

Bounded type parameters
You can limit the type of objects that can be passed as arguments to generic classes,
interfaces, and methods by using bounded type parameters.
NEED FOR BOUNDED TYPE PARAMETER

Without a bounded type parameter (and explicit type casting), you can access only
the members defined in the superclass of all classes—that is, class Object.
In the following example, the generic class Parcel won’t be able to access method
getWeight() of class Gift:
abstract class Gift{
abstract double getWeight();
}

Licensed to Mark Watson 

254

CHAPTER 4

Generics and collections

class Book extends Gift{
public double getWeight() {return 3.2;}
}
class Phone extends Gift{
public double getWeight() { return 1.1; }
}
class Parcel{
private T t;
public void set(T t) {
this.t = t;
Won’t compile;
}
type of t is Object.
public void shipParcel() {
if (t.getWeight() > 10)
System.out.println("Ship by courier ABC");
else
System.out.println("Ship by courier XYZ");
}
}

To access members of class Gift in Parcel, you can limit the type of objects that can
be passed to class Parcel (to Gift and its subclasses) by using bounded parameters
(discussed next).
DEFINING BOUNDED TYPE PARAMETERS

You can specify the bounds to restrict the set of types that can be used as type arguments to a generic class, interface, or method. It also enables access to the methods
(and variables) defined by the bounds.
Let’s restrict the type of objects that can be passed to class Parcel to Gift so that
the methods of class Parcel can access the methods and variables of class Gift.
Because the definitions of classes Gift, Book, and Phone are the same as in the preceding section, they aren’t repeated in the following code:
class Parcel{
Bounded type
private T t;
parameter
public void set(T t) {
this.t = t;
Compiles; type
}
of t is Gift.
public void shipParcel() {
if (t.getWeight() > 10)
System.out.println("Ship by courier ABC");
else
System.out.println("Ship by courier XYZ");
}
}

b

c

In the preceding code, the code at B defines a bounded parameter for class Parcel
with its bounds as class Gift. Because the bound of t is Gift, its method getWeight()
can be accessed using t c.
The keyword implements isn’t used to specify the bound as an interface. The following code won’t compile:
class Parcel{}

Won’t compile

Licensed to Mark Watson 

Creating generic entities

255

EXAM TIP For a bounded type parameter, the bound can be a class,
interface, or enum, but not an array or a primitive type. All cases use the
keyword extends to specify the bound. If the bound is an interface, the
implements keyword isn’t used.

On the exam, you might see a question that tries to instantiate a generic class by passing it a type argument that doesn’t comply with its bounded parameter. What do you
think happens in this case—a compilation error or a runtime exception? What do
you think is the output of the following code, which tries to instantiate class Parcel
with type parameter ?
Parcel p = new Parcel<>();

The preceding code will not compile because the type argument String isn’t within
bounds of type variable T.
DEFINING MULTIPLE BOUNDS

A type parameter can have multiple bounds. The list of bounds consists of one class
and/or multiple interfaces. The following example defines a generic class Parcel, the
type parameter T of which has multiple bounds:
interface Wrappable{}
interface Exchangeable{}
class Gift{}
class Parcel {}

In this case, the type argument that you pass to the bounded type parameter must be a
subtype of all bounds. If you try to pass a type argument that doesn’t subtype all the
bounds, your code won’t compile.
For a type parameter with multiple bounds, the type argument
must be a subtype of all bounds.

EXAM TIP

4.2.5

Using wildcards
The wildcard ? represents an unknown type. You can use it to declare the type of a
parameter; a local, instance, or static variable; and return value of generic types. But
you can’t use it as a type argument to invoke a generic method, create a generic class
instance, or for a supertype.
NEED TO USE AN UNKNOWN TYPE

Before you understand how to use the wildcard, you must know where and why you
need it. Say you’re given the following class inheritance tree:
class Gift{}
class Book extends Gift{}
class Phone extends Gift{}

You can assign an object of class Book or Phone to a reference variable of type Gift:
Gift gift = new Book();
gift = new Phone();

Licensed to Mark Watson 

256

CHAPTER 4

Generics and collections

But the following assignment isn’t valid:
Won’t compile

List wishList = new ArrayList();

You can assign an ArrayList to a variable of type List. But the type that you pass to it
in the angle brackets must be the same. Though ArrayList implements List
for any type T, ArrayList implements neither ArrayList nor List<>.
So assignment of ArrayList to a variable of type List isn’t allowed
with generics.
You can assign an instance of a subclass, say, String, to a variable of its base class, Object. But you can’t assign ArrayList to
a variable of type List. Inheritance doesn’t apply to the type
parameters.
EXAM TIP

You can use a wildcard to get around this. In the following example, you can assign an
ArrayList of any type to wishList:
? refers to any type

List wishList = new ArrayList();

Because ? refers to an unknown type, wishList is a list of an unknown type. So it’s
acceptable to assign a list of Book objects to it.
ADDING OBJECTS TO COLLECTIONS DEFINED USING A WILDCARD

On the exam, take note of code that tries to add objects to collections that are defined
by using the wildcard. Referring to our example, if you try to add or insert a Book
instance into the ArrayList referred by the variable wishList, the code won’t compile:
List wishList = new ArrayList();
wishList.add(new Book());

? refers to
any type
Won’t compile

Because of the ? you can invoke method add() with literally any object—String,
Integer, Book, Phone, and others. But ArrayList should only have Book
instances. Because the compiler can’t guarantee it, it forbids adding anything to the
list when using a wildcard ?.
ITERATING COLLECTIONS WITH A WILDCARD

You can iterate a collection defined using wildcard ?. Note that the type of the variable
used to refer to the list values is Object—the base class of all Java classes. Here’s an
example of using ? in wrapGift() to iterate a List of any type:
class Gift{}
class Book extends Gift{
String title;
Book(String title) {
this.title = title;
}

Class Book
extends Gift.

Licensed to Mark Watson 

257

Creating generic entities
public String toString() {
return title;
}
}
class Courier {
public static void wrapGift(List list) {
for (Object item : list) {
System.out.println("GiftWrap - " + item);
}
}
public static void main(String args[]) {
List bookList = new ArrayList();
bookList.add(new Book("Oracle"));
bookList.add(new Book("Java"));
wrapGift(bookList);

wrapGift will accept list
of any unknown type
Type of variable item
is Object, superclass
of all objects.

wrapGift will accept a
list of Book objects.

List stringList = new ArrayList();
stringList.add("Paul");
stringList.add("Shreya");
wrapGift will accept a
wrapGift(stringList);

list of String objects.

}
}

When you use a wildcard to declare your variables or method
parameters, you lose the functionality of adding objects to a collection. In
this case, using the add method will result in compilation failure.

EXAM TIP

The wildcard ? accepts objects of all unknown types. Let’s use bounded wildcards to
limit the types of objects that we can use.

4.2.6

Using bounded wildcards
To restrict the types that can be used as arguments in a parameterized type, you can
use bounded wildcards.
UPPER-BOUNDED WILDCARDS

You can restrict use of arguments to a type and its subtypes by using ,
where Type refers to a class, interface, or enum.
In upper-bounded wildcards, the keyword extends is used for
both a class and an interface.

EXAM TIP

Consider the following classes:
class Gift{}
class Book extends Gift{}
class Phone extends Gift{}

For a variable that uses the upper-bounded wildcard , the following
assignments are valid:
Book and
Phone
extend Gift.

List myList1 = new ArrayList();
List myList2 = new ArrayList();
List myList3 = new ArrayList();

Licensed to Mark Watson 

Though Gift doesn’t
extend itself, this
assignment is valid.

258

CHAPTER 4

Generics and collections

Let’s see how you can use the upper-bounded wildcard in method parameters. Let’s
modify the method wrapGift(), used in the previous section, to restrict its type arguments to Gift or its subclasses (modifications in bold):
public static void wrapGift(List list) {
for (Gift item : list) {
System.out.println("GiftWrap - " + item);
}
}

wrapGift() will
accept List of Gift
or List of classes
that extend Gift.

In the preceding method wrapGift(), the loop variable item
can be of type Gift or its subtype, Object.
EXAM TIP

For the preceding method, you can pass to it List of Gift or objects that extend class
Gift. If you try to pass it a list of any other object type, it won’t compile.
List bookList = new ArrayList();
bookList.add(new Book("Oracle"));
bookList.add(new Book("Java"));
wrapGift(bookList);

With bounded wildcard
, wrapGift()
will accept List of class Book.

List stringList = new ArrayList();
stringList.add("Paul");
Won’t compile; with bounded wildcard
stringList.add("Shreya");
, wrapGift() won’t
wrapGift(stringList);

accept List of class String.

For the exam, you must know the operations that are allowed for variables declared by
using upper-bounded wildcards. You can iterate and read values from a collection
declared with upper-bounded wildcards. But you can’t write any values to the collection. For example, you can’t add any object to a List defined as List because such a list can refer to a list of either Gift, Book, or Phone. Adding a
mismatched object can pollute the list, which isn’t allowed.
For collections defined using upper-bounded wildcards, you
can’t add any objects. You can iterate and read values from such collections.

EXAM TIP

It’s interesting to note that class String is a final class that can’t be subclassed. If you
try to define a class that extends class String, it won’t compile:
class MyClass extends String {}

Won’t compile; can’t
extend final class String.

But it’s acceptable to define an upper-bounded wildcard that extends class String.
Here’s the modified code:
public static void wrapGift(List list) {
for (String item : list) {
System.out.println("GiftWrap - " + item);
}
}

Licensed to Mark Watson 

Accept objects of
class String or
objects of classes
that extend String.

259

Creating generic entities
EXAM TIP

You can use final classes in upper-bounded wildcards. Although

class X extends String won’t compile,  will compile

successfully.
LOWER-BOUNDED WILDCARDS

You can restrict use of type arguments to a type and its base or supertypes by using
, where Type refers to a class, interface, or enum. Consider the following classes:
class Gift{}
class Book extends Gift{}
class Phone extends Gift{}

For a variable that uses the lower-bounded wildcard , note the following assignments:

Gift
extends
Object.

List myList1 = new ArrayList();
Gift> myList2 = new ArrayList();
Gift> myList3 = new ArrayList();
Phone> myList4 = new ArrayList();

Won’t compile; gift
doesn’t extend Phone.
Valid; Phone
extends Gift.

So, what can you read from and add to collection objects defined using lower-bounded
wildcards? Here’s an example:

Won’t
compile

List list = new ArrayList();
list.add(new Gift());
list.add(new Book());
list.add(new Phone());
list.add(new Object());
for (Object obj : list) System.out.println(obj);

EXAM TIP

List is
assigned ArrayList.
Can add instances of
Gift or its subclasses to
List.
Elements are read as instance
Object, superclass of Gift.

In the preceding example, the loop variable obj can’t be of

type Gift.
Table 4.1 lists wildcard and bounded wildcard variables, and the types of values that
can be read from and written to them.
Table 4.1 Variables and the values that can be read from or added to them
Variable

Read objects of type

Write objects of type

List

Object

N/A

List

Gift

N/A

List

Object

Gift and its subclasses

Licensed to Mark Watson 

260

4.2.7

CHAPTER 4

Generics and collections

Type erasure
When class UseGenericParcel instantiates Parcel, it uses the parameterized type
Parcel, replacing the formal type parameter T with the actual parameter Book.
When you do this, you can assume to be using the following definition of class Parcel,
where all references of T are replaced with Book:
class Parcel{
private Book t;
public void set(Book t) {
this.t = t;
}
public Book get() {
return t;
}
}

This isn’t how a generic
class is compiled; this
is how it behaves.

Though the preceding code can help to a great extent to show how a generic class
behaves, it’s incorrect. It might make you think that you have access to multiple versions of compiled code, which is incorrect. In this section, you’ll see that type information is erased during the compilation process; this is called type erasure.
On compilation, the type information of a generic class or an interface is erased.
The compilation process generates one class file for each generic class or interface;
separate class files aren’t created for parameterized types.
When a generic class is compiled, you don’t get multiple versions of the compiled class files. A generic class gets compiled into a single class file, erasing the type information during the compilation process.

EXAM TIP

The compiler erases the type information by replacing all type parameters in generic
types with Object (for unbounded parameter types) or their bounds (for bounded
parameter types). The compiler might insert type casts to preserve type safety and
generate bridge methods to preserve polymorphism in extended generic types.
Though the exam might not include explicit questions on the
contents of a class file after type erasure, it will help you to understand
generics better and answer all questions on generics.

NOTE

ERASURE OF GENERIC TYPE IN CLASSES, INTERFACES, AND METHODS
For a generic class Parcel, which uses an unbounded type parameter, say, T
class Parcel{
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}

Licensed to Mark Watson 

Creating generic entities

261

On compilation, the Java compiler replaces all occurrences of T with Object:
class Parcel {
private Object t;
public void set(Object t) {
this.t = t;
}
public Object get() {
return t;
}
}

Here’s an example of an interface that uses both bounded and unbounded type
parameters:
interface MyMap{
void put(K key, V value);
V get(K key);
}

For the preceding interface, the Java compiler would replace all occurrences of K with
its first bound class, String, and V with Object:
interface MyMap {
void put(String key, Object value);
Object get(String key);
}

Similarly, for generic methods, the unbounded and bounded type parameters are
replaced by Object or their first bound class. For the generic method deliver() in
class Courier
abstract class Courier {
public  void deliver(E[] array) {
for (E item : array) {
System.out.println("Delivering - " + item);
}
}
}

The Java compiler would replace all occurrences of E with Object:
abstract class Courier {
public void deliver(Object[] array) {
for (Object item : array) {
System.out.println("Delivering - " + item);
}
}
}

Licensed to Mark Watson 

262

CHAPTER 4

Generics and collections

BRIDGE METHODS

The Java compiler might need to create additional methods, referred to as bridge
methods, as part of the type erasure process. In the following example, class BookParcel extends Parcel:
class Book {}
class Parcel{
private T t;
public void set(T t) {
this.t = t;
}
}
class BookParcel extends Parcel {
public void set(Book book) {
super.set(book);
}
}

For the preceding code, during type erasure, the Java compiler erases and adds a
bridge method to class BookParcel—that is, set(Object). This is to add type safety to
class BookParcel, ensuring that only Book instances can be assigned to its field t.
Because method set(Object) accepts Object but casts it to Book, it will throw a
ClassCastException for any other object type:
class Parcel {
private Object t;
public void set(Object obj) {
t = obj;
}
}
class BookParcel extends Parcel {
public void set(Book book) {
super.set(book);
}
public void set(Object obj) {
set((Book)obj);
}
}

4.2.8

Throws ClassCastException
for objects others than Book

Refreshing the commonly used terms
Table 4.2 lists the new terms that were introduced with generics, which you are sure to
see on the exam.
Table 4.2 Commonly used terms with generics and their meanings
Term

Meaning

Generic types

A generic type is a generic class or a generic interface, having one or more type
parameters in its declaration.

Parameterized types

An invocation of a generic type is generally known as a parameterized type. For
generic type List, List is a parameterized type.

Licensed to Mark Watson 

Using type inference

263

Table 4.2 Commonly used terms with generics and their meanings
Term

Meaning

Type parameter

You use type parameters to define generic classes, interfaces, or methods.
E in List is a type parameter.

Type argument

A type argument specifies the type of objects to be used for a type parameter.
For List, String is a type argument.

Wildcard

A wildcard is represented by a ? (a question mark). It refers to an
unknown type.

Bounded wildcard

A wildcard is bounded when it is a base or supertype of a type.

Raw type

The name of a generic class, or a generic class without any type arguments, is
a raw type. For List, List is a raw type.

In the next section, you’ll learn how the compiler can determine type arguments if
you don’t specify them while creating instances of generic types.

4.3

Using type inference
[4.2] Use the diamond for type inference
Imagine solving a riddle with multiple constraints in the form of hints. You resolve the
constraints to derive the answer. You can compare type inference with generating and
solving constraints to promote flexibility in a programming language. Here’s a simple
(nongeneric) example: Java constrains the numeric operands of an addition operator
(+) to be at least promoted to the int data type. So when + is used with int and short
types, the type of the resultant value can be inferred to be an int type. Type inference is a
Java compiler’s capability to determine the argument type passed to an expression or
method by examining its declaration and invocation.
With generics, you usually use angle brackets (<>, also referred to as the diamond)
to specify the type of arguments to instantiate a generic class or invoke a generic
method. What happens if you don’t specify the type arguments? The Java compiler
might be able to infer the argument type by examining the declaration of the generic
entity and its invocation. But if it can’t, it’ll throw a warning, an error, or an exception.
In this section, you’ll see how to answer the exam questions on using the diamond for
type inference to instantiate generic classes and invoke generic methods.
NOTE By throwing an unchecked warning, the compiler states that it can’t
ensure type safety. The term unchecked refers to operations that might
result in violating type safety. This occurs when the compiler doesn’t have
enough type information to perform all type checks.

Licensed to Mark Watson 

264

4.3.1

CHAPTER 4

Generics and collections

Using type inference to instantiate a generic class
When generics were introduced with Java 5, it was mandatory to include the type arguments to instantiate a generic class. Consider the generic class Parcel:
class Parcel{
//..code
}

The following code instantiates Parcel, passing it type argument String:
Type arguments included
to invoke constructor of
generic class Parcel.

Parcel parcel = new Parcel();

But with Java 7, you can drop the type arguments required to invoke the constructor
of a generic class and use an empty set of type arguments, <>:
Parcel parcel = new Parcel<>();

With Java 7, empty set of
type arguments can invoke
constructor of generic class

In the preceding code, the compiler can infer the type argument passed to Parcel as
String. But an attempt to drop the diamond will result in a compilation warning:
Parcel parcel = new Parcel();

Compilation warning;
attempt to assign raw
type to generic type

Imagine another situation. What happens if you attempt to try it the other way around?
Do you think the following code is valid?
Parcel<> parcel = new Parcel();

Won’t compile

The preceding code won’t compile. Imagine what happens if Parcel defines a generic
constructor:
class Parcel{
 Parcel(X x) {}
public static void main(String[] args) {
new Parcel(new StringBuilder("Java"));
}
}

Compiler infers type
of formal parameter
X as StringBuilder.

In the preceding code, String is passed as an explicit type argument to the type
parameter T. The type of the parameter X (specified by the constructor) is inferred by
the compiler to be StringBuilder, which is passed to Parcel’s constructor.

Licensed to Mark Watson 

Understanding interoperability of collections using raw types and generic types

4.3.2

265

Using type inference to invoke generic methods
A Java compiler can’t infer the type parameters by using the diamond in the case of
generic methods. It uses the type of the actual arguments passed to the method to
infer the type parameters. Let’s add the generic method deliver() to class Parcel:
Type parameter X to
generic method deliver()
class Parcel {
public  void deliver(X x) {
System.out.println(x.getClass());
}
public static void main(String args[]) {
Parcel parcel = new Parcel<>();
parcel.deliver(new Integer(10));
//parcel.<>deliver(new Integer(10));
parcel.deliver("Hello");
}
}

Outputs type of argument
passed to deliver().
Type of parameter X is Integer;
determined using Integer
object passed to deliver().
Won’t compile; can’t use
<> with generic method.

Type of parameter X is String;
inferred using actual argument
passed to deliver().

Here’s the output of the preceding code:
class java.lang.Integer
class java.lang.String

The next section covers an important topic: mixing generic and raw types. For the
exam, you must know how the code behaves when you mix them: compilation warnings, errors, and runtime exceptions. You should also understand that by mixing them,
you risk losing type safety.

4.4

Understanding interoperability of collections using raw
types and generic types
[4.3] Analyze the interoperability of collections that use raw types and
generic types
Before we start with how collections that use a raw type operate with generic types, you
should know why this interoperability was allowed. When generics were introduced
with Java 5, there was a lot of existing Java code, which didn’t use generics. Because a
new enhancement can’t render existing code useless, the existing code that didn’t use
generics needed to be valid and interoperable, to be made to work with generics. This
is also referred to as migration compatibility. This, however, introduced multiple complications, including generation of bridge methods and explicit casts.
To recap, when a generic class is used without its type information, it’s referred to
as its raw type. For example, for the generic class Parcel, its raw type is Parcel.

Licensed to Mark Watson 

266

CHAPTER 4

Generics and collections

Raw types exist only for generic types. Watch out for exam questions that might mention raw types for nongeneric classes and interfaces.

EXAM TIP

Let’s examine the interoperability of code that mixes the assignment of objects of
generic types with reference variables of raw types, and vice versa.

4.4.1

Mixing reference variables and objects of raw and generic types
You can assign a parameterized type to its raw type. But the reverse will give a compiler
warning. Consider the following generic class:
class Parcel {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}

The following assignment is allowed:
Parcel parcel = new Parcel();

But you lose the type information for the class Parcel in the preceding code. When
you call its method set() (passing it a method parameter of any type), you’ll get a
compiler warning:
Parcel parcel = new Parcel();
parcel.set("harry");

Because you lose type information
when you use variable of raw type,
you can pass String object to set(),
instead of Phone object

Here’s the detailed warning that the compiler generates when you compile the code
using the flag -Xlint:unchecked, which informs you that the raw type Parcel is
unable to comprehend type parameter T:
warning: [unchecked] unchecked call to set(T) as a member of the raw type
Parcel
parcel.set(new String("harry"));
^
where T is a type-variable:
T extends Object declared in class Parcel
1 warning

This happens because the variable parcel of raw type Parcel doesn’t have access to
generic type information.
On the exam, watch out for code that mixes raw with generic type. For such code,
you’ll need to determine whether the code compiles with or without any warning, or
throws a runtime exception. In the following code (for class Parcel defined in this

Licensed to Mark Watson 

Understanding interoperability of collections using raw types and generic types

267

section), parcel.set(Phone) will compile with a warning, but attempt to assign the
return value of parcel.get() to a variable of type Phone:
Parcel parcel = new Parcel();
parcel.set(new Phone());
Phone phone = parcel.get();

Compiles with
warning

Won’t compile; with
reference variable of
raw type

When you mix raw with generic types, you might get a compiler warning or error, or a runtime exception.

EXAM TIP

Let’s get our heads around this with a pictorial representation (see figure 4.5). I’ve
deliberately used interface List and class ArrayList from the collections framework
because you might get to see them in similar code on this exam.
List List = new ArrayList();

Hey! I am
clueless about
parameterized types.

List
raw type

list.add(new String("Shreya"));
list.add(new Integer(1));
!!!
list.add(new Object());

Compiler warnings!
This is an unchecked
call to add(T t).
List
raw type

String value = list.get(0);

Compilation error!
Without type parameter
information, compiler
can’t ensure list.get()
will return String object.
List
raw type

Figure 4.5 When you use a reference variable of a raw type, you lose the type information.

Licensed to Mark Watson 

268

CHAPTER 4

Generics and collections

Now, let’s try to assign a raw type to a parameterized type:
Parcel parcel = new Parcel();

This code generates the following compilation warning:
warning: [unchecked] unchecked conversion
Parcel parcel = new Parcel();
^
required: Parcel
found:
Parcel

But it doesn’t generate any compiler warning for methods that accept type parameters:
Generates
compilation
warning
Parcel parcel = new Parcel();
parcel.set(new Phone());
//parcel.set(new String());
Phone phone = parcel.get();

Compiles
successfully

No compiler
warnings

Won’t compile if uncommented;
reference variable parcel knows
it’s the parameter types.

Let me modify this preceding code and use a combination of raw and generic types in
the next “Twist in the Tale” exercise. If you can answer this question correctly, you’ll
answer it correctly on the exam too!
Twist in the Tale 4.2

Consider the definition of the following generic type MyMap and class CustomMap that
implements it:
interface MyMap{
void put(K key, V value);
V get(K key);
}
class CustomMap implements MyMap {
K key;
V value;
public void put(K key, V value) {
this.key = key; this.value = value;
}
public V get(K key) {
return value;
}
}

Which options are true about the following code?
class Twist4_2 {
public static void main(String args[]) {
CustomMap map = new CustomMap();
map.put(new String("1"), "Selvan");

//1
//2

Licensed to Mark Watson 

269

Understanding interoperability of collections using raw types and generic types
String strVal = map.get(new Integer(1));
System.out.println(strVal);

//3
//4

}
}
a

Class Twist4_2 will compile successfully if you replace line 1 with the following line:
CustomMap map = new CustomMap();

b
c
d

Code on line 2 will generate a compiler warning.
Code on line 3 will compile if the type of variable strVal is Object.
The code outputs null without any modifications.

On the exam, you might see questions on generics that include other classes from the
collection framework, like Stack. So the next example uses Stack (covered in detail
later in this chapter).
Let’s look at another scenario, where we mix a method that uses parameters of raw
types with actual objects that include generic type information:

b

pushItems()
defines
parameter
of raw type

class Interoperability {
public static void pushItems(Stack stackParam, Object item) {
stackParam.push(item);
Generates warning:
}
[unchecked] unchecked
public static void main(String args[]) {
call to push(E) as member
Stack stackObj = new Stack();
of raw type Stack.
stackObj.push("Paul");
Pushes
pushItems(stackObj, new Integer(77));
Calls pushItems(),
String
String value = stackObj.pop();
which pushes
object.
System.out.println(value);
Integer to stackObj.
}
}
Throws ClassCastException

Instantiates
Stack of
String
objects.

d

c

e

f

g

at runtime.

The code at B defines method pushItems(), with parameter of raw type Stack.
Whenever you use a raw type, you lose all the type information. So the code at c
throws a compilation warning stating that you made an unchecked call to push(E). It’s
warning you that you might end up adding incorrect data to your Stack object. Code
in method main() instantiates a Stack of String objects at d. At e, the code pushes
a String object to stackObj. So far, so good. A call to method pushItems() f pushes
an Integer object to stackObj. Why is this allowed? Because the method parameter
stackParam is of a raw type, there is no type information, so the compiler doesn’t
know that the original object (stackObj) only allows strings; therefore, the integer is
successfully pushed to the stack allowing only strings (stackObj). The code at g
throws a ClassCastException at runtime because the type of the returned object is
Integer and not String.

Licensed to Mark Watson 

270

CHAPTER 4

Generics and collections

That’s exactly one of the possible problems when you mix generics with nongenerics
code. That’s also why you get that compiler warning: to warn you that the compiler can’t
protect you from doing stupid things, like putting an integer in an only-string stack.
As per polymorphism, you can assign an object of a subclass to reference a variable
of its base class. But this subtyping rule doesn’t work when you assign a collection-of-aderived-class object to a reference variable of a collection of a base class. Let’s see why
in the next section.

4.4.2

Subtyping with generics
Because the class ArrayList implements the List interface, you can assign an object
of ArrayList to a reference variable of List. Similarly, because class String extends
class Object, you can assign an object of String to a reference variable of Object.
With generics, you must follow certain subtyping rules. The following line is valid
because a generic class is a subtype of its raw type:
List list = new ArrayList();

But the following isn’t valid:
List list = new ArrayList();

Won’t compile

This assignment isn’t allowed. If you declare a reference variable List list,
whatever you assign to the list must be of generic type Object. A subclass of Object is
not allowed. Figure 4.6 shows the relationship between the interface List, class ArrayList, and classes Object and String. It also shows the related valid and invalid code.
List
List list = new ArrayList();
ArrayList

Object
Object obj = new String();
String

List list = new ArrayList();

List list = new ArrayList();
Compilation error
Figure 4.6 Object of ArrayList isn’t compatible with a reference variable of
type List.

Licensed to Mark Watson 

271

Introducing the collections framework: WARM-UP

Apart from generics, this exam will also test you on the collections framework. It’ll
include many implementations of the interfaces List, Set, Deque, and Map, together
with classes Comparator and Comparable. It’ll also test you on how to search and sort
arrays and lists. The next warm-up section introduces collections and the collections
framework. Experienced developers can skip the introduction section.

4.5

Introducing the collections framework: WARM-UP
Imagine that you have to process a list of results submitted by registered users of
your website for an opinion poll. You aren’t concerned about the order of receiving
or processing these results, but you won’t accept multiple votes from the same user.
Imagine that in another case, you’re creating a drawing application that includes an
Undo button. You need to keep track of the order in which the drawing commands
are selected, so you can undo the last command. Duplicate commands are allowed
in a drawing application. Imagine yet another case, when you’re looking up a word
in a dictionary. The words are ordered alphabetically, but duplicate words don’t
exist in the dictionary.
These scenarios show examples of needing to store and retrieve collections of data
in various manners. For one collection, you might need to retrieve data in the order in
which it was generated. For another collection, you might not allow duplicate values
but would prefer data to be sorted on a data item. Figure 4.7 depicts these examples,

Collection of results
of opinion poll

Collection of commands
in a drawing application

Collection of words and
their meanings in dictionary
Word

Shreya’s opinion
Paul’s opinion
Selvan’s opinion
Harry’s opinion

4-Color circle blue

Exception

3-Draw circle

False

2-Color circle red

Synchronized

1-Draw circle

Volatile

Meaning

Sorted
Ordered
Duplicate
allowed
Figure 4.7 Depending on how you need to process a collection of data, you might need a data
structure that can sort the collection values, retain the insertion order of the elements, and not allow
duplicate elements.

Licensed to Mark Watson 

272

CHAPTER 4

Generics and collections

along with a table on their requirements: whether the structure used to store the data
needs to be sorted, be ordered, or allow duplicate values.
A collection is an object that can group other objects. Each object in a collection is
referred to as an element. The Java collections framework is architecture for representing and manipulating collections. This framework defines many interfaces to support
the need for storing a collection of data in various formats. These collections might
need to be ordered, to be sorted, to allow duplicate values, to be immutable, to be of
fixed size, and more. The collections framework includes high-performance, highquality implementations of useful data structures to store a collection of your objects.
It includes the following:
■

■

■

Interfaces—Multiple interfaces like List, Set, Deque, and Map model the data
structures used for storing, accessing, and manipulating a collection of data.
Implementations—Concrete classes like ArrayList, HashSet, and TreeMap implement the interfaces.
Algorithms—Classes like Collections and Arrays contain utility methods like
sort() and search() for sorting and searching List objects or arrays.
Don’t confuse the interface Collection with the class Collections.
Collection is the base interface in the collections framework that is
extended by most of the other interfaces. Class Collections defines utilNOTE

ity methods to operate on or return collections.
Figure 4.8 shows the main interfaces and their implementations in the collections
framework (limited to exam coverage). It also shows the classes Collections and
Arrays, which define utility methods to sort and search List or array objects.

The importance of the collection framework
A recruiting manager once asked me why my technical architect places so much
emphasis on the ability of a possible recruit to work with data structures and on
the recruit’s experience with the collections framework. I replied that the collections
framework is an extremely powerful and flexible framework. A developer who can use
its existing classes to store and search data effectively or who can craft out a custom
implementation by using the existing framework will be an asset to any organization.

But at the same time, using the collections framework can be overwhelming. The key
to optimal use of the collections framework is to get the basics right. So, let’s start with
the base interface in the Java collections framework: Collection.
EXAM TIP All the collection classes are generic; they all define type
parameters. Watch out for exam questions that use them without type
parameters; these are referred to as raw types.

Licensed to Mark Watson 

273

Working with the Collection interface

Iterable

Collection

Set

Queue

List

HashSet

Map

HashMap
ArrayList

Deque

LinkedHashSet

LinkedHashMap
LinkedList

ArrayDeque

SortedSet

SortedMap

TreeSet

TreeMap

Object

Collections

Arrays

Interfaces

Extends

Implementations

Implements

Algorithms
Figure 4.8 The main interfaces and their implementations in the collections framework. Class
Collections, a utility class that implements various algorithms for searching and sorting, is also
a part of the collections framework.

4.6

Working with the Collection interface
[4.5] Create and use List, Set, and Deque implementations
The interface Collection represents a group of objects known as its elements.
There is no direct implementation of Collection; no concrete class implements it.
It’s extended by more-specific interfaces such as Set, List, and Queue. This collection is used for maximum generality—to work with methods that can accept objects
of, say, Set, List, and Queue. Figure 4.9 shows the basic Collection interface and its
main subinterfaces.

Licensed to Mark Watson 

274

CHAPTER 4

Generics and collections

Iterable

Collection

List

Set

Queue

Map

SortedSet

Deque

SortedMap

<>
Figure 4.9 The core Collection interface and the main interfaces that extend it

All collection classes are generic. Here’s the declaration of the Collection interface:
public interface Collection
extends Iterable

All collection classes
are generic.

A thorough understanding of the Collection interface will help you absorb a lot of
concepts and classes that we cover in the rest of this chapter.
EXAM TIP

4.6.1

The Map interface doesn’t extend the core Collection interface.

The core Collection interface
The Collection interface implements the Iterable interface, which defines method
iterator(), enabling all the concrete implementations to access an Iterator to
iterate over all the collection objects. So for all the implementing classes, you’d be
able to access an Iterator to iterate over its elements.
When you work with the concrete implementations of the collections framework,
you’ll notice that almost all the classes provide two constructors: a void (no-argument)
constructor and another that accepts a single argument of type Collection. The
former creates an empty collection, and the latter creates a new collection with the
same elements as its argument, dropping any elements that don’t fit the new collection being created. As an interface, Collection can’t enforce this requirement
because it can’t define a constructor. But the classes that implement this interface
implement it.
The methods of the Collection interface aren’t marked synchronized. The synchronized methods result in a performance drop, even in single-threaded code, and
so the creators of the collections framework opted out for them. If you’ve worked with

Licensed to Mark Watson 

275

Working with the Collection interface

Unsupported operations
When specific concrete classes implement the methods defined from the Collection
interface, the classes might not need to support all its operations specified. For
example, a list that’s immutable might not support the Collection’s method
add() that adds elements to itself. In this case, this immutable list can choose to
return false or throw UnsupportedOperationException from method add(). All
methods of the Collection interface that modify itself specify (but don’t mandate)
that classes that don’t support these operations might throw UnsupportedOperationException.

or read about the Hashtable or Vector classes, which were introduced before the Java
collections framework, you’d notice that the methods of these data structures are synchronized. With the existing collections framework, you can get the same functionality (without synchronization) by using the collection classes HashMap and ArrayList.
The main target of the exam is to prepare you to write efficient,
real-world applications. A solid understanding of the collections framework will go a long way for you, both on the exam and in your career.

NOTE

4.6.2

Methods of the Collection interface
Figure 4.10 shows the methods of the Collection interface, grouped by their functionality: methods that modify Collection, methods that query it, and miscellaneous.
Interface Collection

Modification methods
Add elements

Query methods

Miscellaneous

contains()

iterator()

equals()

toArray()

add()
isEmpty()
addAll()
size()
Remove elements
clear()
remove()
removeAll()
retainAll()
Figure 4.10

Methods defined in the Collection interface, grouped by their functionality

Licensed to Mark Watson 

276

CHAPTER 4

Generics and collections

Because most of the interfaces implement the Collection interface, most of the implementation classes include methods to add, remove, and query its elements and retrieve
an Iterator to retrieve the collection elements. The implementation, though, varies
across classes.
As you proceed and work with the implementation of classes in the collection
framework, you’ll notice that almost all the classes mention that the Iterator
returned by method iterator() is fail-fast. This implies that if you try to modify the
structure of a collection by adding or removing elements from it, after the Iterator is
created, the Iterator will throw the exception ConcurrentModificationException.
But this doesn’t happen if you modify the collection by using the Iterator’s own add
or remove methods. This important behavior prevents collections from returning arbitrary values during concurrent access.
As we move on to the next section to discuss more interfaces (namely, Set, List,
and Deque), you’ll notice how each of them might suggest a different implementation
of the methods from the Collection interface, to support the specific data structure
that they represent.

4.7

Creating and using List, Set, and Deque implementations
[4.5] Create and use List, Set, and Deque implementations
Each of the interfaces List, Set, and Deque model different data structures. The List
interface allows null and duplicate values and retains the order of insertion of objects.
Set doesn’t allow addition of duplicate objects. Deque is a linear collection that supports the insertion and removal of elements at both its ends.
In the following sections, when you further explore these interfaces and their
implementations, you’ll notice the similarities in how each implementation is created.
Let’s explore the List interface and its implementations.

4.7.1

List interface and its implementations
The List interface models an ordered collection of objects. It returns the objects to
you in the order in which you added them to a List. It allows you to store duplicate
elements. Figure 4.11 shows valid example data that you’d typically store in a List.
In a List, you can control the position where you want to store an element. This is
the reason that this interface defines overloaded methods to add, remove, and retrieve
List of shopping items

Sequence of pizza orders

1. External HDD

1. Deliver XYZ to ABC.

2. iPhone5s

2. Deliver ABX to YYY.

3. Wooden frame

3. Deliver MM to ZZ.

4. Black ink

4. Deliver XYZ to ABC.

Figure 4.11 Examples of
data elements that you
could store in a List

Licensed to Mark Watson 

277

Creating and using List, Set, and Deque implementations
Interface List

Modification methods
Add elements
add(E e)

Query methods

Miscellaneous

contains(Object o)

hashCode()

containsAll
(Collection c)

toArray()
toArray(T[] a)

add(int index, E e)
equals(Object o)
addAll(Collection
 c)

get(int index)

Iterator methods

indexOf(Object o)
addAll(int index,
Collection
 c)

isEmpty()

iterator()

lastIndexOf
(Object o)

listIterator()

Remove elements
size()
clear()
remove(int index)

listIterator
(int index)

subList(int from,
int to)

remove(Object o)
removeAll
(Collection c)
retainAll
(Collection c)
Modify elements
set(int index, E c)

Figure 4.12

Methods of the List interface, grouped by their functionality

elements at a particular position. Apart from including the iterator method to return
an Iterator, List also includes a method to return a ListIterator, to iterate the
complete list or a part of it. Figure 4.12 shows
List
Queue
the methods of the List interface, grouped by
their functionality to help you to retain the
information better.
In this section, I’ll cover only one of the two
ArrayList
Deque
implementations of interface List: ArrayList
(shown in figure 4.13). Because the other List
LinkedList
implementation, LinkedList, also implements
the interface Deque, I’ll cover it in the next sec- Figure 4.13 The List interface and its
implementations (on the exam)
tion on Deque.

Licensed to Mark Watson 

278

CHAPTER 4

Generics and collections

ARRAYLIST CLASS
An ArrayList is a resizable array implementation of the List interface. It’s interesting to note that internally, an ArrayList uses an array to store its elements. An ArrayList defines multiple constructors:
Constructs empty list with
initial capacity of 10
ArrayList()
ArrayList(Collection c)
ArrayList(int initialCapacity)

Constructs list containing elements
of specified collection, in the order
they’re returned by iterator
Constructs empty list with
specified initial capacity

Class ManipulateArrayList creates an ArrayList and manipulates it using methods
add(), remove(), set(), and contains():
Creates ArrayList
with default initial
capacity of 10.

class ManipulateArrayList {
public static void main(String args[]) {
ArrayList list = new ArrayList<>();
list.add("Harry");
list.add("Selvan");
list.add("Harry");

Replaces
value at
position 0
with String
Shreya,
retrieving
replaced
value.

Adds String objects
Harry, Selvan, and Harry;
duplicate values allowed

Adds String object Paul at
first position, shifting existing
list elements to right

list.add(0, "Paul");

Uses equals() to find and
remove first occurrence of
value matching String Harry

list.remove("Harry");
String oldValue = list.set(0, "Shreya");

contains() searches
sequentially and
uses equals() to
find first matching
occurrence

list.get(7);

Retrieves element at position 7;
throws IndexOutOfBoundsException
because only three elements
remain in list
List can return
multiple iterators,
Iterator and
ListIterator.

System.out.println("list contains Harry : " +
list.contains("Harry"));

ListIterator iterator = list.listIterator();
while (iterator.hasNext())
hasNext() returns
System.out.println(iterator.next());
}
}

next() accesses and
returns next value

boolean value indicating
whether iterator can
access more values

It’s interesting to note that an ArrayList uses the size variable to keep track of the
number of elements inserted in it. By default, an element is added to the first available
position in the array. But if you add an element to an earlier location, the rest of the
list elements are shifted to the right. Similarly, if you remove an element that isn’t the
last element in the list, ArrayList shifts the elements to the left. As you add more elements to an ArrayList that can’t be added to its existing array, it allocates a bigger

Licensed to Mark Watson 

279

Creating and using List, Set, and Deque implementations

array and copies its elements to the new array. An ArrayList maintains a record of its
size, so that you can’t add elements at arbitrary locations. Figure 4.14 shows how elements are added, removed, and modified in an ArrayList.
0

1

2

3

4

5

6

7

8

9

a) Create an ArrayList
with initial capacity 10
b) Size = 0

0

1

2

3

4

5

6

7

8

9

a) Add elements in
sequence
b) Size = 3

3

4

5

6

7

8

9

a) Add “Paul” at
position 0
b) Shift elements
to right
c) Size = 4

3

4

5

6

7

8

9

a) Remove first
occurrence of “Harry”
b) Shift elements
to left
c) Size = 3

3

4

5

6

7

8

9

create an ArrayList


list.add (“Harry”)
list.add (“Selvan”)
list.add (“Harry”)
Harry

Harry
Selvan

0

1

2

list.add (0 “Paul”)

Paul

Selvan
Harry

0

1

Harry

2

list.remove (“Harry”)

Paul

Harry
Selvan

0

1

2

a) Replace element
at position 0

list.set (0 “Shreya”)

Shreya

Harry

Selvan
0

1

2

3

4

5

6

7

8

9

list.get (7)

Shreya

Harry

Selvan

a) Size of ArrayList
with occupied
position = size = 3
b) 7>3
c) Throw exception
IndexOutOf
BoundsException

Figure 4.14 An ArrayList offers a resizable array. Internally, it uses an array to store its elements. It
manipulates this array to add, remove, or modify ArrayList elements. The elements of the internal array
are moved to the left or right, when elements are removed from or added to it, respectively. If the ArrayList
exceeds the existing size of the internal array, its elements are copied to a new array with increased size.

Licensed to Mark Watson 

280

CHAPTER 4

Generics and collections

What happens when you ask an ArrayList to remove an object by using method
remove(Object obj)? It sequentially searches the ArrayList to find the target object.
Have you ever wondered how the class ArrayList determines the equality of objects?
In the preceding example, you’re trying to remove a String object with the value
Harry. ArrayList compares the target object and the object that it stores by using
method equals(). If a match is found, the ArrayList removes the first occurrence of
the String value Harry.
To remove an element, an ArrayList first searches through its
elements to find an element that can be considered equal to the target element. It does so by calling method equals() on the target object and its
own objects, one by one. If a matching element is found, remove(Object)
removes the first occurrence of the match found.
EXAM TIP

IMPORTANCE OF THE EQUALS METHOD IN FINDING AND REMOVING VALUES FROM AN ARRAYLIST
The example code in the preceding section uses String instances, which override
method equals(). Let’s work with an example in which the class, whose objects are
stored by an ArrayList, doesn’t override method equals().
In the following example, class UsingEquals stores Emp instances in an ArrayList.
Class UsingEquals tries to remove an Emp object from its ArrayList by using method
remove(). Do you think it’ll work? Here’s the code:
class UsingEquals {
public static void main(String args[]) {
ArrayList list = new ArrayList();
list.add(new Emp(121, "Shreya"));
list.add(new Emp(55, "Harry"));
list.add(new Emp(15, "Paul"));
list.add(new Emp(121, "Shreya"));
System.out.println(list.size());
Emp emp = new Emp (121, "Shreya");
list.remove(emp);
System.out.println(list.size());
}
}
class Emp {
int id;
String name;
Emp(int id, String name) {
this.id = id;
this.name = name;
}
}

Create an
ArrayList of
Emp objects.

Prints “4”
Tries to remove object
referred by emp from list
No match found; no
objects removed;
prints “4”.

In the preceding example, no Emp objects were removed from list. This is because
Emp doesn’t define method equals(), so the default implementation of method
equals() of class Object is used. As you already (should) know, this compares the
object references for equality and not the object contents. So method remove() fails

Licensed to Mark Watson 

281

Creating and using List, Set, and Deque implementations

to find a matching object referred by emp in list. The answer is to override method
equals() in class Emp (modified code in bold):
class Emp {
int id;
String name;
Emp(int id, String name) {
this.id = id;
this.name = name;
}
public boolean equals(Object obj) {
if (obj instanceof Emp) {
Emp emp = (Emp)obj;
if (emp.id == this.id && emp.name.equals(this.name))
return true;
}
return false;
}
}

equals() returns
true when Emp is
compared with
another Emp that
shares same
value for instance
variables id and
name

With the preceding definition of class Emp, class UsingEquals will be able to find and
remove a matching value for the Emp instance referred by emp.
If you’re adding instances of a user-defined class as elements
to an ArrayList, override its method equals() or else its methods
contains() or remove() might not behave as expected.
EXAM TIP

I’ve often observed that when people read the collection framework (which is seemingly
complicated), they tend to overlook simple concepts. For example, here’s one simple
concept: reference variables store a reference to the object that they refer to, and they
can be reassigned a new object. In the next “Twist in the Tale” exercise, let’s see how this
concept can be used on the exam to test you on the collection framework.
Twist in the Tale 4.3

What is the output of the following code?
import java.util.*;
class RemoveArrayListElements {
public static void main(String args[]) {
ArrayList list = new ArrayList<>();
Integer age1 = 20;
Integer age2 = 20;
list.add(age1);
list.add(age2);
System.out.print(list.size() + ":");
age1 = 30;
list.remove(age1);
System.out.print(list.size());
}
}

Licensed to Mark Watson 

282

CHAPTER 4

a
b
c
d
e

Generics and collections

1:1
2:1
2:2
1:0
2:0

The ArrayList methods clear(), remove(), and removeAll()
offer different functionalities. clear() removes all the elements from an
ArrayList. remove(Object) removes the first occurrence of the specified
element, and remove(int) removes the element at the specified position.
removeAll() removes from an ArrayList all of its elements that are contained in the specified collection.
EXAM TIP

The other important methods of class ArrayList are get() and contains().
The other implementation of the List interface, LinkedList, also implements the
Deque interface, covered in the next section.

4.7.2

Deque interface and its implementations
A queue is a linear collection of objects. A Deque is a double-ended queue, a queue
that supports insertion and deletion of elements at both its ends. Let’s revisit the
hierarchy of the Deque interface, as shown in figure 4.15. The Deque interface extends
the Queue interface.
As a double-ended queue, a Deque can work as both a queue and a stack. A queue is
a linear collection of elements, in which the elements are added to one end and are
processed (or taken off) from the other end. For example, in a queue of people at a

Iterable

Collection

List

Set

Queue

Map

SortedSet

Deque

SortedMap

<>
Figure 4.15

Hierarchy of the Deque interface

Licensed to Mark Watson 

283

Creating and using List, Set, and Deque implementations

Ticket counter

Stack of plates
Last plate that enters
the stack is the first
plate to get off it

First in, first out (FIFO)
Figure 4.16

Last in, first out (LIFO)

Real-world examples of a queue and a stack

ticket counter, new persons enter the queue at its end. The tickets are issued to the
people at its beginning. A queue is also referred to as a first in, first out (FIFO) list.
A stack is a linear collection of elements that allows objects to be added and removed
at the same end. For example, in a stack of plates, the plates are always added to the top
and removed from the top. A stack is also referred to as a last in, first out (LIFO) list data
structure. Figure 4.16 shows real-world examples of a queue and a stack.
The Deque interface defines multiple methods to add, remove, and query the existence of elements from both its ends. Because Deque works as both a queue and a stack,
it’s easier to get a hang of its methods if you understand the operations and corresponding methods used for queues and stacks. As I mentioned previously, in a queue,
elements are typically added to its tail (or end), and taken off from its head (or beginning). Figure 4.17 shows the Queue methods (Deque extends Queue) used to add elements to the end of a queue and remove or query elements from its beginning.
0

1

2

3

4

5

6

7

head

tail

Remove elements
remove
Query elements

Add elements
add(E)
offer(E)

element
peer
Query and remove
poll
Figure 4.17 Queue methods used to work with Deque as a FIFO data structure

Licensed to Mark Watson 

284

CHAPTER 4

0

1

2

3

4

Generics and collections
5

6

7

head

Add elements
push(E)
Remove elements
pop()
Query and remove elements
peek()

Figure 4.18 The stack methods
used to work with Deque as a
LIFO data structure

Now, let’s see how a Deque works as a stack. In a stack, elements are added and taken
off the same end of the queue: its head. Figure 4.18 shows the stack methods used to
add and remove elements from its head, using Deque as a LIFO data structure.
For a stack, which allows insertion at only one end, the purpose of method push()
is implicit: elements are added at (and removed from) the top. Similarly, with Queue,
which usually enables insertion only at its tail, the purpose of method add() or
offer() seems implicit: to insert elements at the tail of the list. Now, because Deque
supports a double-linked list, supporting insertion at both its ends, method add() or
offer() could be ambiguous. So Deque supports other methods with explicit purposes, including addFirst(), addLast(), offerFirst(), and offerLast(). So you can
see multiple methods that serve the same purpose. Figure 4.19 shows the Deque interface, representing it as a list with a head and tail. Elements of all implementations of
Deque might not be implemented as a contiguous list. It’s just to show the beginning
and end of a list. This figure shows the methods that are used to add, delete, and
query methods at both ends of Deque. The figure also shows other methods that
remove and query the Deque elements at random positions.
EXAM TIP The legacy class Stack is used to model a LIFO list. The addition, removal, and query operations of a Stack are named push(), pop(),
and peek(). Though the Deque interface isn’t related to Stack, Deque
supports a double-ended queue and can be used as a Stack. Deque also
defines the methods push(), pop(), and peek() to add, remove, and
query elements at its beginning.

Though the Deque implementations aren’t required to prohibit the addition of null
values, it is strongly recommended that they do because certain Deque implementations return null to signal that the underlying list is empty.

Licensed to Mark Watson 

285

Creating and using List, Set, and Deque implementations
0

1

2

3

4

5

6

7

head

tail

Add elements to head

Add elements to tail

addFirst(E)

add(E)

offerFirst(E)

addLast(E)

bush(E)

offer(E)
offerLast(E)

Remove elements from head

Removes element at tail

remove()

removeLast()

removeFirst()
pop()
Query elements at head

Query elements at tail

element()

getLast()

getFirst()

peekLast()

peek()
peekFirst()
Query and remove from head

Queries and removes from tail
pollLast()

poll()
pollFirst()
Remove at random position

Remove at random position

Iterator

remove(Object)

contains(Object O)

iterator()

removeFirstOccurrence()

size()

descendingIterator()

removeLastOccurrence()
Figure 4.19 Deque methods used to add, remove, and query elements at both its ends. The figure
also includes other methods, which operate at random positions.

Licensed to Mark Watson 

286

CHAPTER 4

List

Generics and collections

Queue

Deque

LinkedList

ArrayDeque

Figure 4.20 The Deque interface and
its implementations (on the exam)

IMPLEMENTATIONS OF THE DEQUE INTERFACE
This section covers two main Deque implementations: ArrayDeque and LinkedList, as

shown in figure 4.20.
CLASS ARRAYDEQUE

Let’s get started with class ArrayDeque. It’s a resizable array implementation of the
Deque interface. Here’s a list of constructors of this class:
Constructs empty array deque with
initial capacity to hold 16 elements.
ArrayDeque()
ArrayDeque(Collection c)
ArrayDeque(int numElements)

Constructs deque containing
elements of specified
collection, in order of return
by collection’s iterator
Constructs empty array deque
with initial capacity to hold
specified number of elements

Let’s work with an example using the ArrayDeque constructor and other methods from
this class:

String
array

import java.util.*;
class TestArrayDeque {
public static void main(String... args) {
String strArray[] = {"A1", "B2", "C3"};

ArrayDeque arrDeque = new
ArrayDeque(Arrays.asList(strArray));

push() adds
element at
Deque beginning

arrDeque.push("D4");
arrDeque.offer("E5");

offer() adds
element at
Deque end

Can’t add null to
ArrayDeque; will throw
NullPointerException.

//arrDeque.push(null);
System.out.println(arrDeque.pop());
System.out.println(arrDeque.remove());

pop() returns
and removes
element at
Deque beginning

arrDeque.add("F6");
System.out.println(arrDeque.peek());
System.out.println(arrDeque);
}

}

Creates ArrayDeque
from String List;
Arrays.asList converts
array to List.

#J

remove() also returns and removes
element at Deque beginning
add() adds an element
to end of queue
peek() queries and returns
element at beginning of queue

Licensed to Mark Watson 

Creating and using List, Set, and Deque implementations

287

Here’s the output of the preceding code:
D4
A1
B2
[B2, C3, E5, F6]

Whenever Deque adds or removes an element, it modifies its pointers to the beginning
and end. The take-away from the preceding code is that you need to take note of the
methods that add to the beginning or end of the list. You also need to take note of
methods like peek(), which only queries Deque; remove(), which removes elements
from Deque; and poll(), which queries and removes an element from Deque. Method
poll() queries and removes, and method remove() just removes. Method poll()
returns null when Deque is empty and remove() throws a runtime exception.
All the insertion methods (add(), addFirst(), addLast(),
offer(), offerFirst(), offerLast(), and push()) throw a NullPointerException if you try to insert a null element into an ArrayDeque.
EXAM TIP

This is a classic example of how to implement a requirement or a recommendation in a concrete class. The Deque interface suggests that the
implementing classes shouldn’t allow null elements because some of its
special methods return null to indicate that the underlying Deque is
empty. To implement this suggestion, the methods that add elements to
class ArrayDeque throw NullPointerException when you try to add a null
element to it.
You can iterate over the elements of Deque by using an Iterator, returned by methods
iterator() and descendingIterator().
Together with the Deque-specific methods discussed in this section,
ArrayDeque also implements methods inherited from the Collection
interface, such as contains(), indexOf(), and others.
NOTE

CLASS LINKEDLIST
Class LinkedList implements both the List and Deque interfaces. So it’s a doublelinked list implementation of the List and Deque interfaces. It implements all List
and Deque operations. Unlike ArrayDeque, it permits addition of null elements.
Here are the constructors of class LinkedList:
Constructs
empty list.

LinkedList()
LinkedList(Collection c)

Constructs list containing elements
of specified collection, in order of
return by collection’s iterator

So what happens when you add elements to or remove elements from a LinkedList?
Let’s work with an example:
import java.util.*;
class TestLinkedList {

Licensed to Mark Watson 

288

CHAPTER 4

Generics and collections

public static void main(String... args) {
LinkedList list = new LinkedList();

Creates empty
LinkedList of
String objects.

list.offer("Java");
list.push("e");
list.add(1, "Guru");

Uses push() to
add String "e"
to beginning.

Uses offer() to add
String "Java” to end

Uses add(1, "Guru") to insert
"Guru" at position 1, adjusting
references for adjacent values.

System.out.println(list);

Finds and removes first
matching occurrence
for String object "e"

System.out.println(list.remove("e"));

Iterator it = list.iterator();
while(it.hasNext()) System.out.println(it.next());
}
}

Iterates in sequential
manner, from first
element to last

Figure 4.21 shows how a LinkedList maintains a reference to its first and last elements. It also shows how, in the absence of using an array, each node in a LinkedList
maintains a reference to its previous and next element. Whenever you add elements
to or remove elements from a LinkedList, it modifies the previous and next references of its adjacent elements. As you can see, because each list element maintains a
reference to its previous and next element, this list can be traversed in forward and
reverse directions.
New LinkedList
();

first
last

null
null

list.offer
("Java")

first

item:

Java

last

prev: null
next: null

list.push("e")

list.add
(1, "Guru")

list.remove
("e")

first

first

first

item:

e

item:

Java

prev: null

prev:

next:

next: null

item:

e

item:

Guru

last

item:

Java

prev: null

prev:

prev:

next:

next:

next: null

item:

Guru

item:

Java

prev: null

prev:

next:

next: null

last

last

Figure 4.21 Pictorial representation of code that adds elements to and removes elements from a
LinkedList

Licensed to Mark Watson 

Creating and using List, Set, and Deque implementations

289

Note the difference in internal manipulation of an ArrayList or ArrayDeque and a
LinkedList. A LinkedList doesn’t move a set of elements when you add a new element to it. It modifies the value of the reference variables prev and next, for adjacent
elements, to keep track of its sequence of elements. This is unlike ArrayList or
ArrayDeque, which copy a set of array elements to the right or left, when elements are
added to or removed from it.
On the exam you’ll get questions to choose the most appropriate interface or class
based on a given scenario. A LinkedList is like an ArrayList (ordered by index) but
the elements are double-linked to each other. So besides the methods from List, you
get a bunch of other methods to add or remove at the beginning and end of this list.
So it’s a good choice if you need to implement a queue or a stack. A LinkedList is useful when you need fast insertion or deletion, but iteration might be slower than an
ArrayList.
Because a LinkedList implements List, Queue, and Deque, it
implements methods from all these interfaces.

EXAM TIP

In the next “Twist in the Tale” exercise, let me modify the preceding code and see
whether you can determine how that affects the code output. Let’s see whether you
still remember the inheritance concepts covered in chapter 3.
Twist in the Tale 4.4

What is the output of the following code?
import java.util.*;
class TestLinkedList {
public static void main(String... args) {
List list = new LinkedList();
list.offer("Java");
list.push("e");
list.add(1, "Guru");
list.remove("e");
System.out.println(list);
}
}
a
b
c
d
e

[Guru, Java]
[Java, Guru]
[e, Guru, Java]

Compilation error
Runtime exception

Let’s explore another important interface, Set, and its implementing classes in the
following section.

Licensed to Mark Watson 

290

CHAPTER 4

Red

Generics and collections

Red
Green

Yellow

Yellow
Valid set

Figure 4.22

4.7.3

Green

Duplicate
elements

Red

Invalid set

Valid and invalid examples of Set

Set interface and its implementations
The Set interface models the mathematical Set abstraction. It’s a collection of objects that
doesn’t contain duplicate elements. Figure 4.22 shows valid and invalid examples of Set.
The Set interface doesn’t allow duplicate elements and the
elements are returned in no particular order.

EXAM TIP

To determine the equality of objects, Set uses its method equals(). For two elements,
say e1 and e2, if e1.equals(e2) returns true, Set doesn’t add both these elements.
Set defines methods to add and remove its elements. It also defines methods to query
itself for the occurrence of specific objects. Because it indirectly implements the
Iterable interface, it includes method iterator() to retrieve an Iterator. It also
includes methods to convert it into an array. Figure 4.23 shows the methods of the Set
interface, grouped for convenience.
Interface Set

Modification methods
Add elements
add(E e)
addAll(Collection
 c)

Query methods

Miscellaneous

contains(Object o)

hashCode()

containsAll
(Collection c)

iterator()
toArray()

equals(Object o)
toArray(T[] a)

Remove elements

isEmpty()
size()

clear()
remove(Object o)
removeAll
(Collection c)
retainAll
(Collection c)

Figure 4.23

Methods of the Set interface, grouped for convenience

Licensed to Mark Watson 

291

Creating and using List, Set, and Deque implementations

The exam will query you on the use of Set and its methods. For example, it may query
on the appropriate scenarios for using Set or its implementation. It might include a
question such as this: When you try to add duplicate String values to a Set, does it
throw an exception or simply ignore the duplicate value?
The answer to these questions can vary with the implementing classes. For example, one implementation may return false if you add a duplicate String value, but
another may throw an exception. Let’s look at implementations of the Set interface,
how they’re related, and the behavior of their methods in the next section.

4.7.4

Set implementation classes
For the exam, we’ll work on the main Set implementation classes: HashSet, LinkedHashSet, and TreeSet, as shown in figure 4.24.

Set

HashSet

CLASS HASHSET
Class HashSet implements the Set interface. As
required by the Set interface, it doesn’t allow dupli-

LinkedHashSet

cate elements. Also, it makes no guarantee to the
SortedSet
order of retrieval of its elements. It’s implemented
using a HashMap. To store and retrieve its elements, a
TreeSet
HashSet uses a hashing method, accessing an
object’s hashCode value to determine the bucket in
Figure 4.24 The Set interface and
which it should be stored.
its implementations (on the exam)
Before I discuss HashSet further, it’s important
that you understand the meaning of buckets and
importance of methods hashCode() and equals(). Let’s use a simple example of a
hotel—when guests leave the hotel they must leave their room key at reception. There
the key is put in one big bucket. So when guests arrive they say their room number
and the receptionist has to go through all the keys until he finds the matching one
(compare it with method equals()). Then a new system is introduced. Instead of having one big bucket, they have some smaller buckets, each with a label (1–9). From now
on they apply the same algorithm each time—when the room key is left at reception,
the receptionist adds all numbers of the room and repeats this process until just 1 number is left (e.g. 236 -> 2+3+6=11 -> 1+1=2). The key is put in the bucket with that number
(hashCode). When guests arrive and want their key back, they say the room number, the
receptionist applies the algorithm (compare it with hashCode()), goes to the corresponding bucket, and searches for the matching room key (method equals()).
Let’s see what happens when the class AddElementsToHashCode tries to add unique
and duplicate objects to a HashSet:
class AddElementsToHashSet {
public static void main(String args[]) {
String str1 = new String("Harry");
String str2 = new String("Shreya");

Licensed to Mark Watson 

292

CHAPTER 4

Generics and collections

String str3 = new String("Selvan");
String str4 = new String("Shreya");
HashSet set = new HashSet<>();

Add str1
to set.

set.add(str1);
set.add(str2);
set.add(str3);
set.add(str4);

Add str3
to set.

Create new
HashSet

Add str2 to set.
Duplicate String “Shreya”.
Not added to set.

for (String e : set) System.out.println(e);

Prints Harry, Shreya,
and Selvan (not
always in this order).

}
}

In the preceding code, the string object "Shreya" referred by the variable str4 isn’t
added to set. HashSet uses the hashCode() values of the string objects to determine
the appropriate bucket to add them to. A bucket can store multiple objects. Before
adding an object, HashSet compares its existing elements in the target bucket by
using methods hashCode() and equals() to avoid duplicates being added.
A few important points about working with the preceding example code on
HashSet:
■
■
■

Method hashCode() doesn’t call method equals().
Method equals() doesn’t call method hashCode().
Classes should override their hashCode() methods efficiently to enable collection classes like HashSet to store them in separate buckets.

To make the concept sink in, here’s the next “Twist in the Tale” exercise for you.
Let’s examine the role of equals() and hashCode() in storing and retrieving elements in HashSet.
Twist in the Tale 4.5

Given the following definition of class Person, which options are correct for class
Twist4_5?
class Person {
String name;
Person(String name) { this.name = name; }
public String toString() { return name; }
}
class Twist4_5 {
public static void main(String args[]) {
HashSet set = new HashSet();
Person p1 = new Person("Harry");
Person p2 = new Person("Shreya");
Person p3 = new Person("Selvan");
Person p4 = new Person("Shreya");
set.add(p1);
set.add(p2);
set.add(p3);

Licensed to Mark Watson 

293

Creating and using List, Set, and Deque implementations
set.add(p4);
for (Person e : set) System.out.println(e);
}
}
a
b

HashSet adds all fours objects, referred to by variables p1, p2, p3, and p4.
If class Person overrides method hashCode() as follows, only p1 would be added
to set:
public int hashCode() {
return 10;
}

c

If class Person overrides both methods hashCode() and equals() as follows,
only p1 would be added to set:
public boolean equals(Object o) {
return true;
}
public int hashCode() {
return 10;
}

d

If class Person overrides only method equals() as follows, only p1, p2, and p3
will be added to set:
public boolean equals(Object o) {
if (o instanceof Person) {
return this.name.equals(((Person)o).name);
}
else
return false;
}

The following example shows some of the methods of class HashSet in action:
import java.util.*;
class ManipulateHashSet {
public static void main(String args[]) {
List list = new ArrayList();
list.add("Shreya");
list.add("Selvan");
HashSet set = new HashSet();
set.add("Harry");
set.addAll(list);

Adds all elements from
list; no duplicate elements.

System.out.println(set.contains("Shreya"));
System.out.println(set.remove("Selvan"));

Returns true.

for (String e : set) System.out.println(e);
}

Creates and
populates ArrayList.

Selvan is removed
from HashSet.

}

Licensed to Mark Watson 

294

CHAPTER 4

Generics and collections

EXAM TIP Watch out for questions that add null to a HashSet. A HashSet allows storing of only one null element. All subsequent calls to storing null values are ignored.

Class HashSet uses hashing algorithms to store, remove, and retrieve its elements. So
it offers constant time performance for these operations, assuming that the hash function disperses its elements properly among its buckets. Covering writing an efficient
hash function is beyond the scope of this book. You can find complete books on this
topic. Efficient and faster removal, addition, and retrieval of objects have always been
a requirement, and many have completed a doctorate on it.
Access the source code of class String from the Java API. Examine
how it overrides hashCode(), using its individual characters to generate
its hash code.
NOTE

CLASS LINKEDHASHSET
A LinkedHashSet offers the benefits of a HashSet combined with a LinkedList. It
maintains a double-linked list running through its entries. As with a LinkedList, you
can retrieve objects from a LinkedHashSet in the order of their insertion. Like a
HashSet, a LinkedHashSet uses hashing to store and retrieve its elements quickly. A
LinkedHashSet permits null values. LinkedHashSet can be used to create a copy of a
Set with the same order as that of the original set.
In the following example code, class UseLinkedHashSet creates LinkedHashSet of
City. It uses method add() to add individual City instances and method addAll() to

add all objects of the specified collection:
class City {
String name;
City(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
class UseLinkedHashSet {
public static void main(String args[]) {
Set route = new LinkedHashSet<>();
route.add(new City("Seattle"));
route.add(new City("Copenhagen"));
route.add(new City("NewDelhi"));

List extends
Collection.

Objects from LinkedAddSet can be retrieved in
their insertion order.

List extendedRoute = new ArrayList<>();
extendedRoute.add(new City("Beijing"));
extendedRoute.add(new City("Tokyo"));
addAll() accepts
route.addAll(extendedRoute);
Iterator iter = route.iterator();
while (iter.hasNext())
System.out.println(iter.next());

Collection object.
Prints “false” because City
doesn’t override equals().

Licensed to Mark Watson 

Creating and using List, Set, and Deque implementations

295

System.out.println(route.contains(new City("Seattle")));
}
}

The output of the preceding code is:
Seattle
Copenhagen
NewDelhi
Beijing
Tokyo
false

In the preceding code, note that addAll() accepts a Collection object. So you can
add elements of an ArrayList to a LinkedHashSet. The order of insertion of objects
from extendedRoute to route is determined by the order of objects returned by
extendedRoute’s iterator (ArrayList objects can be iterated in the order of their
insertion). Because you can retrieve objects from a LinkedHashSet in the order of
their insertion, iter iterates the City objects in the order of their insertion (as shown
in the code output).
Watch out for exam questions that create a LinkedHashSet by
using a reference variable of type List. A LinkedHashSet implements the
Collection and Set interfaces, not List.
EXAM TIP

The next class on the exam is TreeSet, which uses a binary tree behind the scenes.
CLASS TREESET
A TreeSet stores all its unique elements in a sorted order. The elements are ordered
either on their natural order (achieved by implementing the Comparable interface)
or by passing a Comparator, while instantiating a TreeSet. If you fail to specify either
of these, TreeSet will throw a runtime exception when you try to add an object to it.
Unlike the other Set implementations like HashSet and LinkedHashSet, which use
equals() to compare objects for equality, a TreeSet uses method compareTo() (for
the Comparable interface) or compare() (for the Comparator interface) to compare

objects for equality and their order. As discussed in detail in the next sections on the
Comparator and Comparable interfaces, the implementation of method compare() or
compareTo() should be consistent with method equals() of the object instances,
which are added to a TreeSet. If two object instances are equal according to their
method equals(), but not according to their methods compare() or compareTo(),
the Set can exhibit inconsistent behavior.
Constructors of class TreeSet
Constructs
new, empty
tree set,
sorted
according
to specified
comparator

Constructs new, empty tree set, sorted
according to natural ordering of its elements
TreeSet()
TreeSet(Collection c)
TreeSet(Comparator comparator)
TreeSet(SortedSet s)

Constructs new tree set containing
elements in specified collection, sorted
according to natural ordering of elements
Constructs new tree set containing same
elements and using same ordering as
specified sorted set

Licensed to Mark Watson 

296

CHAPTER 4

Generics and collections

Behind the scenes, a TreeSet uses a Black-Red binary tree. This tree modifies itself as
you add more values to it so it has the least number of levels and the values are distributed as evenly as possible. Let’s create a TreeSet, using another collection of objects:
class TestTreeSet {
public static void main(String args[]) {
String[] myNames = {"Shreya", "Harry", "Paul", "Shreya", "Selvan"};
TreeSet treeSetNames = new
TreeSet created
TreeSet(Arrays.asList(myNames));
using List of
String values
Iterator it = treeSetNames.descendingIterator();
while (it.hasNext())
descendingIterator()
System.out.println(it.next());
returns TreeSet values
Prints “Shreya
}
in descending order.
Selvan Paul
}
Harry”.

In the absence of passing a Comparator instance to a TreeSet
constructor, the objects that you add to a TreeSet must implement
Comparable. In the preceding example, String (which implements
Comparable) objects are added to the TreeSet. Watch out for storing
objects of wrapper classes, Enum and File in a TreeSet; they all implement Comparable. The natural order of enum constants is the order in
which they’re declared. StringBuffer and StringBuilder don’t implement Comparable.
EXAM TIP

All the collection classes include constructors to use another collection object to
instantiate itself. But depending on how it’s implemented, it might not include all the
elements from the collection passed to its constructor.
A List allows the addition of duplicate elements, but a Set doesn’t. In the preceding example, when you create a TreeSet using a List, which contains duplicate elements, one of the duplicate elements is dropped from the TreeSet. TreeSet also
includes iterators to iterate over its values in ascending or descending order.

4.8

Map and its implementations
[4.6] Create and use Map implementations
Unlike the other interfaces from the collections framework, like List and Set, the
Map interface doesn’t extend the Collection interface. In this section, you’ll work
with Map and SortedMap interfaces and their implementations like HashMap, LinkedHashMap, and TreeMap.

4.8.1

Map interface
Imagine locking or unlocking the door of your home using a key. This key allows you
to restrict access to your home to a key holder. You can compare a Map with a pool of
keys, mapped to the values that they can unlock. A key can map to a 0 or a 1 value.

Licensed to Mark Watson 

297

Map and its implementations

A Map doesn’t allow the addition of duplicate keys.
Items added to a Map aren’t ordered. The retrieval
order of items from a Map object isn’t guaranteed to
be the same as its insertion order. The Map interface
declares methods to add or delete a key-value pair or
query the existence of a key or value. It also defines
methods to retrieve the set of keys, values, and keyvalue pairs.

Map

HashMap
LinkedHashMap

SortedMap

Map objects don’t allow the addition of duplicate keys.

EXAM TIP

TreeMap
Figure 4.25 The Map interface and

The addition of a null value as a key or value depends its implementations (on the exam)
on a particular Map implementation. For example,
HashMap and LinkedHashMap allow the insertion of
null as a key, but TreeMap doesn’t—it throws an exception.
As shown in figure 4.25, the Map implementations on the exam are HashMap,
LinkedHashMap, and TreeMap. Let’s get started with HashMap.

4.8.2

HashMap
A HashMap is a hash-based Map that uses the hash value of its key (returned by hashCode()) to store and retrieve keys and their corresponding values. Each key can refer
to a 0 or 1 value. The keys of a HashMap aren’t ordered. The HashMap methods aren’t
synchronized, so they aren’t safe to be used in a multithreaded environment.
The keys of a HashMap aren’t ordered. The HashMap methods
aren't synchronized, so they aren’t safe to be used in a multithreaded
environment.

EXAM TIP

CREATING A HASHMAP AND ADDING VALUES TO IT
Let’s create a HashMap that stores employee names as keys and their salaries as the corresponding values. The following code creates a HashMap with a default initial capacity
and adds values to it using method put(Object key, Object value):
Map salaryMap = new HashMap<>();
salaryMap.put("Paul", 8888.8);
salaryMap.put("Shreya", 99999.9);
salaryMap.put("Selvan", 5555.5);

HashMap defines another constructor (declaration shown below), which accepts a
Map object:
HashMap(Map m)

You can use the preceding constructor to create a HashMap by passing it another Map
instance:
Map salaryMap = new HashMap<>();
Map copySalaryMap = new HashMap<>(salaryMap);

Licensed to Mark Watson 

298

CHAPTER 4

Generics and collections

The exam might question you on whether the addition or removal of key-value pairs
to and from salaryMap will reflect in copySalaryMap. The following example shows
that when you delete a key-value pair from salaryMap, it’s not removed from copySalaryMap:
Map salaryMap = new HashMap<>();
salaryMap.put("Paul", 8888.8);
salaryMap.put("Shreya", 99999.9);
Map copySalaryMap = new HashMap(salaryMap);
Set keys = copySalaryMap.keySet();
for (String k : keys)
System.out.println(k);
salaryMap.remove("Paul");
keys = copySalaryMap.keySet();
for (String k : keys)
System.out.println(k);

Create copySalaryMap
using
salaryMap.

Outputs two
key values.

Remove a key-value
pair from salaryMap.
Still outputs
two key values.

You can create a HashMap by passing its constructor another
Map object. Additions of new key-value pairs or deletions of existing keyvalue pairs in the Map object passed to the constructor aren’t reflected in
the newly created HashMap.
EXAM TIP

On the exam watch out for the type of the key and value used by the Map object that
you pass to the HashMap constructor. The following code won’t compile:
Map salaryMap = new HashMap<>();
Map copySalaryMap = new HashMap<>(salaryMap);

Won’t
compile

Because a HashMap stores objects as its keys and values, it’s common to see code that
stores another collection object (like an ArrayList) as a value in a Map (on the exam).
For example
Map> salaryMap = new HashMap<>();

When working with generics, note how the type parameters are passed to constructors. You can replace the preceding code with the following:
Map> salaryMap = new HashMap>();

But the following are invalid instantiations:
Map> salaryMap = new HashMap<<>, List<>>();
Map> salaryMap = new HashMap>();
Map> salaryMap =
new HashMap>();

Licensed to Mark Watson 

Won’t
compile

299

Map and its implementations

RETRIEVING KEYS AND VALUES
You can call method get() on a HashMap to retrieve the value for a key. For example
enum IceCream {CHOCOLATE, STRAWBERRY, WALNUT};
Map> iceCreamMap = new HashMap<>();
List iceCreamLst = new ArrayList<>();
iceCreamLst.add(IceCream.WALNUT);
iceCreamLst.add(IceCream.CHOCOLATE);
iceCreamMap.put("Shreya", iceCreamLst);
System.out.println(iceCreamMap.get("Shreya"));

Prints “[WALNUT,
CHOCOLATE]”

On the exam, you might see a code snippet similar to the preceding code, with a difference in type arguments passed to the initialization of HashMap. The following code
compiles without any warning:
Map iceCreamMap = new HashMap<>();

Methods containsKey() and containsValue() check for the existence of a key or a
value in a Map, returning a boolean value. Methods get() and containsKey() rely on
appropriate overriding of key’s hashCode() and equals() methods (discussed in
detail in the previous section on HashSet). In the following example, class Emp doesn’t
override these methods. Do you think method get() will work as expected?
class Emp {
String name;
String name) {
this.name = name;
}
}
Map empMgrMap = new HashMap<>();
empMgrMap.put(new Emp("Shreya"), new Emp("Selvan"));
System.out.println(empMgrMap.get(new Emp("Shreya")));

Prints
“null”

The preceding code outputs null.
The String class and all the wrapper classes override their
hashCode() and equals() methods. So they can be correctly used as keys
in a HashMap.
EXAM TIP

Let’s see how overriding methods hashCode() and equals() helps. Here’s the modified code, in which class Emp overrides methods hashCode() and equals():
class Emp {
String name;
Emp(String name) {
this.name = name;
}
public int hashCode() {
return name.hashCode();
}

Licensed to Mark Watson 

300

CHAPTER 4

Generics and collections

public boolean equals(Object o) {
if (o instanceof Emp)
return ((Emp)o).name.equals(name);
else
return false;
}
}
class Test {
public static void main(String args[]) {
Map empMgrMap = new HashMap<>();
empMgrMap.put(new Emp("Shreya"), new Emp("Selvan"));
System.out.println(empMgrMap.get(new Emp("Shreya")));
}
}

HashMap uses hashing functions to add or retrieve key-value
pairs. The key must override both methods equals() and hashCode() so
that it can be added to a HashMap and retrieved from it.

EXAM TIP

Do you think methods containsKey() and containsValue() will work as expected, if
class Emp overrides only method equals() and not method hashCode()? Here’s the
modified code (Emp doesn’t override hashCode()):

Prints
“true”

class Emp {
String name;
Emp(String name) {
this.name = name;
}
public boolean equals(Object o) {
if (o instanceof Emp)
return ((Emp)o).name.equals(name);
else
return false;
}
}
class Test {
public static void main(String args[]) {
Map empMgrMap = new HashMap<>();
empMgrMap.put(new Emp("Shreya"), new Emp("Selvan"));
System.out.println(empMgrMap.containsKey(new Emp("Shreya")));
System.out.println(empMgrMap.containsValue(new Emp("Selvan")));
}
}

c

b

Prints
“false”

Class Emp in the preceding example overrides method equals() and not method
hashCode(). Because method containsKey() uses both methods hashCode() and
equals() to determine the equality of keys, the code at B outputs false. Because
method containsValue() uses method equals() and not method hashCode() to
determine the equality of HashMap values, the code at c outputs true.
When objects of a class that only overrides method equals()
and not method hashCode() are used as keys in a HashMap, containsKey() will always return false.
EXAM TIP

Licensed to Mark Watson 

301

Map and its implementations

ADDING DUPLICATE OR NULL KEYS

What happens if you add a duplicate key to a HashMap? Will the method call be
ignored or will its new value replace the key’s previous value? The latter is true. At the
end of execution of the following code, salaryMap will store 99999.9 as the value for
key "Paul".
Map salaryMap = new HashMap<>();
salaryMap.put("Paul", 8888.8);
salaryMap.put("Paul", 99999.9);

If you add a key-value pair to a HashMap such that the key
already exists in the HashMap, the key’s old value will be replaced with the
new value.
EXAM TIP

Do you think you can add a key-value pair to a HashMap with null as the key? The
HashMap allows the addition of a maximum of one null key. For example
Map salaryMap = new HashMap<>();
salaryMap.put(null, 88.8);
Prints
salaryMap.put(null, 99.9);
“99.9”
System.out.println(salaryMap.get(null));
String s = null;
Prints
salaryMap.put(null, 77.7);
“77.7”
System.out.println(salaryMap.get(s));

EXAM TIP

You can add a value with null as the key in a HashMap.

REMOVING HASHMAP ENTRIES
You can use method remove(key) or clear() to remove one or all key-value pairs of a
Map. Method remove() removes the mapping for the specified key from a Map if it is
present. It returns the value associated with the key, or null if the key doesn’t exist in
the map. Method remove() is simple to work with. For example
Map salaryMap = new HashMap<>();
salaryMap.put("Paul", 88.8);
System.out.println(salaryMap.remove("Paul"));

Removes Paul, 88.8
pair and prints “88.8”.

Method remove() can return a null value, irrespective of
whether the specified key exists in a HashMap. It might return null if a
matching key isn’t present in HashMap, or if null is stored as a value for
the specified key.

EXAM TIP

What happens if you try to remove a key-value pair from a HashMap that uses List as a
key? Here’s an example:
Map flavorNameMap = new HashMap<>();
List iceCreamLst = new ArrayList<>();
iceCreamLst.add(IceCream.WALNUT);
iceCreamLst.add(IceCream.CHOCOLATE);

Licensed to Mark Watson 

302

CHAPTER 4

Generics and collections

flavorNameMap.put(iceCreamLst, "Shreya");
List iceCreamLst2 = new ArrayList<>();
iceCreamLst2.add(IceCream.WALNUT);
iceCreamLst2.add(IceCream.CHOCOLATE);
System.out.println(flavorNameMap.remove(iceCreamLst2));

Matches key referred
by iceCreamLst and
removes value "Shreya".

Because the size and order of elements in lists iceCreamLst and iceCreamLst2 are the
same, they’re considered equal by their equals() methods. The ArrayList also overrides its hashCode(), returning the same hash-code values for equal lists. This enables
method remove() to find the specified list and remove its corresponding values.
EXAM TIP For a HashMap, methods that query or search a key use the
key’s methods hashCode() and equals().

Method clear() doesn’t accept any method arguments and returns void. At the end
of execution of the following code, salaryMap wouldn’t have any key-value pairs:
Map salaryMap = new HashMap<>();
salaryMap.put("Paul", 88.8);
salaryMap.put("Shreya", 88.8);
salaryMap.clear();

Method remove() removes a maximum of one key-value pair
from a HashMap. Method clear() clears all the entries of a HashMap.
Method remove() accepts a method parameter but clear() doesn’t.
EXAM TIP

DETERMINING THE SIZE OF HASHMAP
You can use methods size() and isEmpty() to query a HashMap’s size. Method size()
returns an int value representing the count of key-value mappings in a HashMap.
Method isEmpty() returns a boolean value—true represents a HashMap with no key-

value mappings.
COPYING ANOTHER MAP OBJECT
You can use method putAll() to copy all the mappings from the specified map to a
HashMap. What happens if the source and target HashMap have the same keys? If the
map reference passed to putAll() defines keys that already exist in this map, then the

values in this map are replaced. For example
Map map = new HashMap<>();
map.put(1, "Shreya");
map.put(11, "Paul");
Map anotherMap = new HashMap<>();
anotherMap.put(1, "Harry");
anotherMap.putAll(map);

For Integer value 1,
anotherMap has
value "Shreya".

Licensed to Mark Watson 

Map and its implementations

303

Method putAll() accepts an argument of type Map. It copies
all the mappings from the specified map to the map that calls putAll().
For common keys, the values of the map that calls putAll() are replaced
with the values of the Map object passed to the putAll() method.
EXAM TIP

RETRIEVING KEYS, VALUES, AND KEY-VALUE PAIRS
The Map interface defines methods keySet(), values(), and entrySet() to access
keys, values, and key-value pairs of a Map. The following example shows these methods

in action:
enum Color {RED, BLUE, YELLOW};
Map colorMap = new HashMap<>();
colorMap.put(Color.RED, "Passion");
colorMap.put(Color.BLUE, "Stability");
colorMap.put(Color.YELLOW, "Energy");
Collection mood = colorMap.values();
Set colors = colorMap.keySet();
Set> colorsMood = colorMap.entrySet();
for (String s : mood)
System.out.println(s);
for (Color c : colors)
System.out.println(c);
for (Map.Entry pair : colorsMood)
System.out.println(pair.getKey() + ":" + pair.getValue());

Because the order of iteration of a HashMap might change, the following is one of the
probable outputs of the preceding code:
Passion
Energy
Stability
RED
YELLOW
BLUE
RED:Passion
YELLOW:Energy
BLUE:Stability

EXAM TIP Method values() returns a Collection object, method keySet() returns a Set object, and method entrySet() returns a Map.Entry

object.

Licensed to Mark Watson 

304

CHAPTER 4

Generics and collections

Class HashTable legacy code
Class HashTable wasn’t a part of the collections framework initially. It was retrofitted
to implement the Map interface in Java 2, making it a member of the Java Collection
framework. But it’s considered legacy code. It’s roughly equivalent to a HashMap, with
some differences. The operations of a HashMap aren’t synchronized, whereas the
operations of a HashTable are synchronized. But if you need to work with a HashMap
in a multithreaded environment (which needs synchronized methods), you can use
class ConcurrentHashMap (covered in chapter 11).
Unlike a HashMap, a HashTable doesn’t allow the addition of null keys or values.

4.8.3

LinkedHashMap
The LinkedHashMap IS-A HashMap with a predictable iteration order. Like a LinkedList (covered previously in this chapter), a LinkedHashMap maintains a double-linked
list that runs through all its entries. This linked list is used to retrieve the LinkedHashMap elements in the order they were inserted. Like a HashMap, the methods of a
LinkedHashMap aren’t synchronized.
The following example shows that (unlike a HashMap) a LinkedHashMap would
always iterate over its elements in their order of insertion:
Map colorMap = new HashMap<>();
colorMap.put("Red", 1);
colorMap.put("Blue", 2);
colorMap.put("Yellow", 3);
colorMap.put("Purple", 4);
colorMap.put("Orange", 5);
for (Integer i : colorMap.values())
System.out.print(i);

Iteration order of map elements
can vary with each execution

System.out.println("");
Map linkedColorMap = new LinkedHashMap<>();
linkedColorMap.put("Red", 1);
linkedColorMap.put("Blue", 2);
linkedColorMap.put("Yellow", 3);
linkedColorMap.put("Purple", 4);
linkedColorMap.put("Orange", 5);
for (Integer i : linkedColorMap.values())
System.out.print(i);

Prints “12345”

Here’s a probable output of the code:
21345
12345

NOTE

Methods to add, retrieve, remove, or query the elements of a

LinkedHashMap work in a similar manner as discussed in the previous section on HashMap.

Licensed to Mark Watson 

305

Map and its implementations

4.8.4

TreeMap
A TreeMap is sorted according to the natural ordering of its keys or as defined by a
Comparator passed to its constructor. It implements the SortedMap interface. Like
HashMap and LinkedHashMap, the operations of a TreeMap aren’t synchronized, which
makes it unsafe to be used in a multithreaded environment.
NOTE The Comparable and Comparator interfaces are discussed in detail
in the next section.

Because the key-value pairs of a TreeMap are always sorted, querying a TreeMap (using
methods containsKey() and get()) is faster in comparison to querying keys of other
unsorted implementations of the Map interface.
In one of the previous sections, you learned how HashMap uses methods hashCode() and equals() of its key to add, remove, or query it. But TreeMap performs all
key comparisons by using method compareTo() or compare() of its keys. Two keys are
considered equal by a TreeMap if the key’s method compareTo() or compare() considers them equal.
Let’s get started by creating some TreeMap objects.
CREATING TREEMAP OBJECTS
When you create a TreeMap object, you should specify how its keys should be ordered.
A key might define its natural ordering by implementing the Comparable interface. If
it doesn’t you should pass a Comparator object to specify the key’s sort order.

Because this is an important point to note for the exam, I’ll cover multiple scenarios here. Let’s start with instantiating a TreeMap, which uses enum objects as its keys
(enums define their natural order by implementing the Comparable interface):
enum IceCream {STRAWBERRY, CHOCOLATE, WALNUT};
Map flavorMap = new TreeMap<>();
flavorMap.put(IceCream.CHOCOLATE, "Paul");
flavorMap.put(IceCream.STRAWBERRY, "Shreya");

Natural order of enums
is their sequence of
declaration

for (String s : flavorMap.values())
System.out.println(s);

The output of the preceding code is:
Shreya
Paul

In the preceding output, note that IceCream.STRAWBERRY precedes IceCream
.CHOCOLATE. The natural order of enum elements is the sequence in which they’re
defined. The set of values that you retrieve from a TreeMap is sorted on its keys and not
on its values.
The natural order of enum elements is the sequence in which
they’re defined. The set of values that you retrieve from a TreeMap is
sorted on its keys and not on its values.

EXAM TIP

Licensed to Mark Watson 

306

CHAPTER 4

Generics and collections

All the wrapper classes and String class implement the Comparable interface, so you
can use their objects as TreeMap keys. Let’s see what happens when you use objects of
a user-defined class, say, Flavor, which doesn’t define its natural sort order, as keys
to TreeMap:
class Flavor {
String name;
Flavor class
Flavor(String name) {
doesn’t implement
this.name = name;
Comparable.
}
}
class CreateTreeMap {
public static void main(String args[]) {
Map flavorMap = new TreeMap<>();
flavorMap.put(new Flavor("Chocolate"), "Paul");
}
}

TreeMap
instantiation
doesn’t throw
an exception.
Throws ClassCastException.

In the preceding code, note that you can instantiate a TreeMap by neither passing it a
Comparator object, nor using keys that implement the Comparable interface. But an
attempt to add a key-value pair to such a TreeMap will throw a runtime exception.
You can create a TreeMap without passing it a Comparator
object or without using keys that implement the Comparable interface.
But adding a key-value pair to such a TreeMap will throw a runtime exception, ClassCastException.
EXAM TIP

Now, what happens if the keys used in a TreeMap define a natural order and a
Comparator object is also passed to a TreeMap constructor? What happens if the natural order of the keys doesn’t match with the order defined by the Comparator object?
Or, is the natural order of keys ignored if a Comparator object is passed to a TreeMap
object? Let’s answer these questions using the next example:
class Flavor implements Comparable {
String name;
Flavor(String name) {
this.name = name;
}
public int compareTo(Flavor f) {
return this.name.compareTo(f.name);
}
}
class MyComparator implements Comparator {
public int compare(Flavor f1, Flavor f2) {
return f2.name.compareTo(f1.name);
}
}

TreeMap
creation

Natural order of
Flavor instances is
alphabetical order
of its names.

MyComparator orders
Flavor instances in
reverse alphabetical
order of its names.

class CreateTreeMap {
public static void main(String args[]) {
Map flavorMap = new TreeMap<>(new MyComparator());

Licensed to Mark Watson 

307

Map and its implementations
flavorMap.put(new Flavor("Chocolate"), "Paul");
flavorMap.put(new Flavor("Vanilla"), "Selvan");
for (Flavor flavor : flavorMap.keySet())
System.out.println(flavor.name);
}
}

The output of the preceding code is:
Vanilla
Chocolate

The preceding code shows that when you pass a Comparator object to a TreeMap constructor, the natural order of its keys is ignored.
When you pass a Comparator object to a TreeMap constructor,
the natural order of its keys is ignored.

EXAM TIP

Class TreeMap implements the SortedMap interface. Watch out for similar code on the
exam that tries to instantiate a SortedMap. It won’t compile. For example
Map map = new SortedMap();

Won’t compile

COMPARING KEYS: TREEMAP VERSUS HASHMAP
Unlike a HashMap, a TreeMap uses method compare() or compareTo() to determine
the equality of its keys. In the following example, a TreeMap can access the value
associated with a key, even though its key doesn’t override its method equals() or
hashCode():
class Flavor implements Comparable {
String name;
Flavor(String name) {
this.name = name;
}
public int compareTo(Flavor f) {
return this.name.compareTo(f.name);
}
}
class CreateTreeMap {
public static void main(String args[]) {
Map flavorMap = new TreeMap<>();
flavorMap.put(new Flavor("Chocolate"), "Paul");
flavorMap.put(new Flavor("Apple"), "Harry");
System.out.println(flavorMap.get(new Flavor("Apple")));
}
}

Prints
“Harry”

In this section on TreeMap, you learned how user-defined classes can use the Comparable
and Comparator interfaces to define a natural or custom order of objects. The next
section covers these interfaces in detail.

Licensed to Mark Watson 

308

4.9

CHAPTER 4

Generics and collections

Using java.util.Comparator and java.lang.Comparable
[4.7] Use java.util.Comparator and java.lang.Comparable
Until now, you have used method equals() to compare objects for equality. But when
it comes to sorting a collection of objects, you must also compare objects to determine
whether an object is less than or greater than another object. To do so, you can use
two interfaces: java.lang.Comparable and java.util.Comparator.

4.9.1

Comparable interface
The Comparable interface is used to define the natural order of the objects of the class
that implements it. It is a generic interface (using T as type parameter) and defines
only one method, compareTo(T object), which compares the object to the object
passed to it as a method parameter. It returns a negative integer, zero, or a positive
integer if this object is less than, equal to, or greater than the specified object. Here’s
the definition of the Comparable interface:
package java.lang;
public interface Comparable {
public int compareTo(T o);
}

Generic
interface

Here’s an example of class Person that implements the Comparable interface:
class Person implements Comparable {
String name;
int age;

Person constructor
accepts name and age.

Person (String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Person person) {
Natural order of instances of Person
return (this.age-person.age);
is based on int value of its age
}
public String toString() {
Overridden toString()
return name;
to return name
}
}

The Comparable interface is used to define the natural order of
the objects of the class that implements it.

EXAM TIP

Some collection classes, like TreeSet and TreeMap, store their elements in a sorted
order. You can specify the sort order of the elements by making their class implement
the Comparable interface. Here’s an example in which TreeSet stores instances of
class Person, which implements Comparable:
class TestComparable {
public static void main(String args[]) {
TreeSet set = new TreeSet<>();

Licensed to Mark Watson 

Using java.util.Comparator and java.lang.Comparable

309

set.add(new Person("Shreya", 12));
set.add(new Person("Harry", 40));
set.add(new Person("Paul", 30));
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

The TreeSet values are returned in ascending order of age of class Person. Here’s the
output of the preceding code:
Shreya
Paul
Harry

EXAM TIP Method compareTo() returns a negative integer, zero, or a
positive integer if this object is less than, equal to, or greater than the
specified object.

It’s important to note that the implementation of method compareTo() should be
consistent with the implementation of method equals(). This rule is recommended,
but not required.
For any two object instances a and b, if a.compareTo(b) returns a value 0, then
a.equals(b) should return true. Let’s see what happens if we implement compareTo()
in an inconsistent manner in class Person and add its instances to a TreeSet (changes
in bold):
class Person implements Comparable {
String name;
int age;
Person (String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Person person) {
return 0;
}
public String toString() {
return name;
}

compareTo
returns 0.

}
class TestComparable {
public static void main(String args[]) {
TreeSet set = new TreeSet<>();
Person p1 = new Person("Shreya", 12);
Person p2 = new Person("Harry", 40);
Person p3 = new Person("Paul", 30);

Licensed to Mark Watson 

310

CHAPTER 4
set.add(p1);
set.add(p2);
set.add(p3);

Generics and collections

p2 and p3 aren’t added to set because
Set doesn’t allow duplicate values.

Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}

Prints only one
value, Shreya.

}
}

Classes like TreeSet and TreeMap store their elements in a sorted order. Before set
adds the second element, p2, it compares it to the existing element, p1. Because
p1.compareTo(p2) returns 0, set doesn’t add the duplicate element and returns false.
The same steps are repeated when set tries to add p3. At the end, only one element,
p1, is added to set.
Modify method compareTo() in the preceding example so
that TreeSet returns the values in descending order of Person’s age.

QUICK EXERCISE

What if you want to sort the elements of class Person based on its instance variable,
name? Also, can you do this if you can’t modify the source code of class Person? Yes, it’s
possible by using the Comparator interface, as discussed in the next section.

4.9.2

Comparator interface
The Comparator interface is used to define the sort order of a collection of objects,
without requiring them to implement this interface. This interface defines methods
compare() and equals(). You can pass Comparator to sort methods like Arrays.sort
and Collections.sort. It’s also passed to collection classes like TreeSet and TreeMap
that require ordered elements.
The Comparator interface is used to specify the sort order for classes that
■
■
■

Don’t define a natural sort order
Need to work with an alternate sort order
Don’t allow modification to their source code so that natural ordering can be
added to them
EXAM TIP Unlike the Comparable interface, the class whose objects are
being compared need not implement the Comparator interface.

Here’s the source code for this interface:
package java.util;
public interface Comparator {
int compare(T o1, T o2);
boolean equals(Object obj);
}

Licensed to Mark Watson 

311

Using java.util.Comparator and java.lang.Comparable

Like the Comparable interface, method compare() in Comparator returns a negative integer, zero, or a positive integer if o1 is less than, equal to, or greater than o2.
Let’s modify the example used in the preceding section to use Comparator instead
of Comparable:
import java.util.*;
class TestComparator {
public static void main(String args[]) {
TreeSet set = new TreeSet<>(
new Comparator(){
public int compare(Person p1, Person p2) {
return (p1.age-p2.age);
}
}
);
set.add(new Person("Shreya", 12));
set.add( new Person("Harry", 40));
set.add(new Person("Paul", 30));

Class TreeSet
is passed an
anonymous
inner class.

Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
class Person {
String name;
int age;
Person (String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name;
}
}

Class Person
doesn’t implement
the Comparator
interface.

The output of the preceding code is:
Shreya
Paul
Harry

As you noticed, class Person no longer needs to implement Comparable. Class TreeSet accepts Comparator to define the sort order of its elements. What happens if
class Person implements the Comparable interface, which sorts it on name, and the
Comparator interface sorts it on age? What do you think is the output of the code in
the next “Twist in the Tale” exercise?

Licensed to Mark Watson 

312

CHAPTER 4

Generics and collections

Twist in the Tale 4.6

What is the output of the following class?
class Twist4_6 {
public static void main(String args[]) {
TreeSet set = new TreeSet<>(new Comparator(){
public int compare(Person p1, Person p2) {
return (p1.age-p2.age);
}
});
Person p1 = new Person("Shreya", 12);
Person p2 = new Person("Harry", 40);
Person p3 = new Person("Paul", 30);
set.add(p1);
set.add(p2);
set.add(p3);
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+":");
}
}
}
class Person implements Comparable{
String name;
int age;
Person (String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Person person) {
return name.compareTo(person.name);
}
public String toString() { return name; }
}
a
b
c
d

Shreya:Paul:Harry:
Harry:Paul:Shreya:
Paul:Shreya:Harry:
Harry:Shreya:Paul:

Like the Comparable interface, the implementation of method compare() in Comparator
should be consistent with the implementation of method equals(). For any two
object instances a and b, if compare(a, b) returns a value 0, then a.equals(b) should
return true.
In the next section, let’s see why you need sorted data and how to use Comparable
and Comparator to sort and search arrays and lists.

Licensed to Mark Watson 

Sorting and searching arrays and lists

313

4.10 Sorting and searching arrays and lists
[4.8] Sort and search arrays and lists
How do you view the list of names in a phone directory or the list of selected candidates for admission to a university? Usually, these lists are sorted on their names or on
their registration numbers (for university students). Do you think it’s easier and faster
to find a particular name or a candidate in a sorted list? Yes, it is.
You might need data in a sorted order for multiple reasons: to display information
in an ascending or descending order, or to search for particular data. Searching data
is always faster in a sorted list. Searching an unsorted list requires comparing all list
elements with the target element, resulting in a time- and processing-intensive task. In
today’s world, when we’re overwhelmed with data, fast searching and retrieval of data
is crucial.
For the exam, you need to know how to search and sort arrays and List by using
the existing methods from the collections framework classes, Arrays and Collections
to be specific. The OCP Java SE 7 Programmer II exam won’t ask you to create or write
your own sorting methods. Let’s get started with the sorting methods that are accessible using classes Arrays and Collections.

4.10.1 Sorting arrays
The class Arrays in the collections framework defines multiple methods to sort arrays
of primitive data types and objects. You can use these methods to sort either a complete array or a part of it. Table 4.3 lists the sorting methods for arrays of byte, int,
and Object. The class Arrays defines similar methods for other primitive data types:
char, short, long, float, and double. Please note that I’ve deliberately excluded them from
this list to keep the table short.
Table 4.3 Class Arrays defines sort methods for arrays of Object and all primitive data types
(excluding type boolean)
Method name

Method description

static void sort(byte[] a)

Sorts the specified array into ascending numerical order

static void sort(byte[] a,
int fromIndex, int toIndex)

Sorts the specified range of the array into ascending order

static void sort(int[] a)

Sorts the specified array into ascending numerical order

static void sort(int[] a,
int fromIndex, int toIndex)

Sorts the specified range of the array into ascending order

static void sort(Object[] a)

Sorts the specified array of objects into ascending order,
according to the natural ordering of its elements

Licensed to Mark Watson 

314

CHAPTER 4

Generics and collections

Table 4.3 Class Arrays defines sort methods for arrays of Object and all primitive data types
(excluding type boolean) (continued)
Method name

Method description

static void sort(Object[] a,
int fromIndex, int toIndex)

Sorts the specified range of the specified array of objects
into ascending order, according to the natural ordering of
its elements

static  void sort(T[] a,
Comparator c)

Sorts the specified array of objects according to the order
induced by the specified comparator

static  void sort(T[] a,
int fromIndex, int toIndex,
Comparator c)

Sorts the specified range of the specified array of objects
according to the order induced by the specified comparator

EXAM TIP

All the methods in table 4.3 that sort a partial array accept

fromIndex and toIndex values. The element stored at position fromIndex is sorted, but the element stored at position toIndex isn’t.

Let’s look at a simple example of sorting an int array:
class SortArrays {
public static void main(String args[]) {
int[] intArray = {20, 14, 4, 10, 5, 3};
for (int a:intArray) System.out.print(a + " ");
Arrays.sort(intArray);
System.out.println();
for (int a:intArray) System.out.print(a + " ");
System.out.println();
intArray = new int[]{20, 14, 4, 10, 5, 3};
for (int a:intArray) System.out.print(a + " ");
Arrays.sort(intArray, 1, 5);
System.out.println();
for (int a:intArray) System.out.print(a + " ");
}

int array with
6 elements
Sorts all elements
of array intArray.
Reinitializes
intArray.
Sorts elements at
positions 1, 2, 3, and 4,
excluding element at
position 5.

}

EXAM TIP

A quick reminder that the index of an array is 0-based.

The output of the preceding code is as follows:
20 14 4 10 5 3
3 4 5 10 14 20
20 14 4 10 5 3
20 4 5 10 14 3

When you sort an array of objects using the sort method from class Arrays, it uses
the natural sort order of the instances. If the objects don’t specify a natural sort
order, an overloaded version of sort can be passed a Comparator. A lot of classes

Licensed to Mark Watson 

315

Sorting and searching arrays and lists

like String and wrapper classes implement Comparable and define a natural sort
order. The String values are sorted in alphabetical or lexicographic order. On the
exam you might be queried about the natural sorting order of String values, which
differ only in the case of their letters. What do you think is the output of the following sorting operation?

String[] strArray = {"ocP", "oCP", "OcP", "OCp", "Ocp"};
for (String str:strArray) System.out.print(str + " ");
Arrays.sort(strArray);
System.out.println();
for (String str:strArray) System.out.print(str + " ");

Literal String
values that differ
only in their case
sort() sorts
strArray
Prints OCp OcP
Ocp oCP ocP

Each character has a corresponding ASCII or Unicode value. The uppercase letters
have a lower ASCII value than their lowercase counterparts.
Watch out for exam questions that sort string objects starting
with a space. A space has a lower ASCII or Unicode value than lowercase
or uppercase letters. Let’s see how you can use a comparator to sort the
objects of a user-defined class:

EXAM TIP

class SortObjects {
public static void main(String args[]) {
Person p1 = new Person("Shreya", 32);
Person p2 = new Person("Harry", 40);
Person p3 = new Person("Paul", 30);
Person[] objArray = new Person[]{p1, p2, p3};
Arrays.sort(objArray,
new Comparator(){
public int compare(Person p1, Person p2) {
return (p1.age-p2.age);
}
}
);
for (Person p:objArray) System.out.print(p + " ");
}
}
class Person {
String name;
int age;
Person (String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name+":"+age;
}
}

Licensed to Mark Watson 

sort() is passed array
of instances of Person
and comparator that
defines sort order for
Person instances

316

CHAPTER 4

Generics and collections

The preceding code sorts the Person instances on the increasing order of their ages,
printing this:
Paul:30 Shreya:32 Harry:40

Imagine what happens if you neither use a Comparator nor define a natural ordering
for your class. Find out by attempting the next “Twist in the Tale” exercise.
Twist in the Tale 4.7

What is the output of the following class?
import java.util.*;
class Twist4_7 {
public static void main(String args[]) {
Person p1 = new Person("Shreya", 32);
Person p2 = new Person("Harry", 40);
Person p3 = new Person("Paul", 30);
Person[] objArray = new Person[]{p1, p2, p3};
Arrays.sort(objArray);
for (Person p:objArray) System.out.print(p + " ");
}
}
class Person {
String name;
int age;
Person (String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Person person) {
return (this.age-person.age);
}
public String toString() {
return name+":"+age;
}
}
a
b
c
d
e

Shreya:32 Paul:30 Harry:40
Paul:30 Shreya:32 Harry:40
Shreya:32 Harry:40 Paul:30

Compilation error
Runtime exception

Licensed to Mark Watson 

317

Sorting and searching arrays and lists

4.10.2 Sorting List using Collections
Class Collections defines two sorting methods to sort objects of List:
Sorts specified list into ascending order,
according to natural ordering of elements
static > void sort(List list)
static  void sort(List list, Comparator c)

Sorts specified list according to order
induced by specified comparator

Here’s an example of sorting a list using method Collections.sort():
class SortList {
public static void main(String args[]) {
List integers = new ArrayList<>();
integers.add(new Integer(200));
integers.add(new Integer(87));
integers.add(new Integer(999));
for (Integer i : integers) {
System.out.println(i);
}
System.out.println("After calling Collections.sort()");
Collections.sort(integers);
for (Integer i : integers) {
System.out.println(i);
}
}
}

The output of the preceding code is:
200
87
999
After calling Collections.sort()
87
200
999

What would happen if we add another item to a list after it was sorted? Will it be
sorted too? Let’s find out using the next example:
class SortList {
public static
Star s1 =
Star s2 =
Star s3 =

void main(String... args) {
new Star("Sun", 7777.77);
new Star("Sirius", 999999.99);
new Star("Pilatim", 222.22);

Creates new ArrayList,
referred by list.

List list = new ArrayList<>();
list.add(s1); list.add(s2); list.add(s3);

Licensed to Mark Watson 

Adds Star
instances to list.

318

CHAPTER 4

Generics and collections

Collections.sort(list);
list.add(new Star("Litmier", 4444.44));
Collections.reverse(list);

Sorts list.
Adds another Star
instance to list;
this isn’t sorted.

for (Star star:list) System.out.println(star);
}
}
class Star implements Comparable {
String name;
double mass;
Star(String name, double mass) {
this.name = name;
this.mass = mass;
}
public int compareTo(Star other) {
return (int)(this.mass - other.mass);
}
public String toString(){
return name + ":" + mass;
}
}

Reverses order of
list; doesn’t sort in
descending order.

Here’s the output of the preceding code:
Litmier:4444.44
Sirius:999999.99
Sun:7777.77
Pilatim:222.22

Once sorted, new elements are added to a list according to the
specific algorithm used by the underlying data structure. After you sort
elements of an ArrayList, the new elements are added to its end.

EXAM TIP

4.10.3 Searching arrays and List using collections
Classes Arrays and Collections define method binarySearch() to search a sorted
array or a List for a matching value using the binary search algorithm. The list or
array must be sorted according to the natural order of its elements or as specified by
Comparator. If you pass this method an unsorted list, the results are undefined. If
more than one value matches the target key value to be searched, this method can
return any of these values. Method binarySearch() returns the index of the search
key, if it is contained in the list; otherwise it returns (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the list: the
index of the first element greater than the key, or list.size() if all elements in
the list are less than the specified key. Note that this guarantees that the return value
will be >= 0 if and only if the key is found.
Following is the declaration of the overloaded method binarySearch(), which
searches the specified array for the specified value using the binary search algorithm:
static
static
static
static

int
int
int


binarySearch(byte[] a, byte key)
binarySearch(int[] a, int key)
binarySearch(Object[] a, Object key)
int binarySearch(T[] a, T key, Comparator c)

Licensed to Mark Watson 

Sorting and searching arrays and lists

319

The preceding list includes the searching methods for the primitive data types byte
and int and objects. I’ve deliberately not included the overloaded methods for the
rest of the primitive data types (char, short, long, float, and double) to keep it manageable. For example
public class SortSearch {
static final Comparator INT_COMPARATOR =
new Comparator() {
public int compare (Integer n1, Integer n2) {
return n2.compareTo(n1);
}
};
public static void main(String args[]) {
ArrayList list = new ArrayList<>();
list.add(9999);
list.add(10);
list.add(55);
list.add(28);
Collections.sort(list, null);
System.out.println(Collections.binarySearch(list, 55));
Collections.sort(list,INT_COMPARATOR);
System.out.println(Collections.binarySearch(list, 55));
}
}

The output of the preceding code is
2
1

Here’s the list of the overloaded method binarySearch(), which searches a range of
the specified array for the specified value by using the binary search algorithm.
Again, I’ve deliberately excluded the overloaded version of these methods for the
rest of the primitive data types (char, short, long, float, and double) to keep the
list manageable:
static int binarySearch(byte[] a, int fromIndex, int toIndex, byte key)
static int binarySearch(int[] a, int fromIndex, int toIndex, int key)
static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key)
static  int binarySearch(T[] a, int fromIndex, int toIndex, T key,
Comparator c)

Here’s the list of methods defined in class Collections to search the specified list for
the specified object using the binary search algorithm:
static  int binarySearch(List> list, T key)
static  int binarySearch(List list, T key, Comparator c)

Licensed to Mark Watson 

320

CHAPTER 4

Generics and collections

Similar to method binarySearch(), which accepts List objects, method binarySearch()v that accepts arrays requires the array to be sorted in an ascending order, or
else the results are undefined. The output value in the following example is undefined:
import java.util.*;
public class SearchArray {
public static void main(String[] args) {
Object[] myArray = new Object[3];
myArray[0] = "Java";
myArray[1] = "EJava";
myArray[2] = "Guru";
int position = Arrays.binarySearch(myArray, "Java");
System.out.println(position);
}
}

On the exam you might see a question that stores different object types in an array of
type Object[]. What do you think is the output of the following code?
import java.util.*;
public class SearchArray2 {
public static void main(String[] args) {
Object[] myArray = new Object[3];
myArray[0] = "Java";
myArray[1] = 10;
myArray[2] = 'z';
int position = Arrays.binarySearch(myArray, "Java");
System.out.println(position);
}
}

The preceding code throws a ClassCastException at runtime when it tries to convert
Integer value 10 to String.

4.11 Using wrapper classes
[4.4] Use wrapper classes, autoboxing, and unboxing
Java defines a wrapper class for each of its primitive data types. The wrapper classes
are used to wrap primitives in an object, so they can be added to a collection object.
Wrapper classes help you write cleaner code, which is easy to read. For this exam, you
should be able to use these wrapper classes and understand how boxing and unboxing applies to these classes.

4.11.1 Class hierarchy of wrapper classes
All the wrapper classes are immutable. They share multiple usage details and methods. Figure 4.26 shows their hierarchy.
All the numeric wrapper classes extend the class java.lang.Number. Classes Boolean
and Character directly extend class Object. All the wrapper classes implement the

Licensed to Mark Watson 

321

Using wrapper classes
Serializable

Object

Boolean

Character

Byte
Figure 4.26

Comparable

Number

Short

Integer

Long

Float

Double

Hierarchy of wrapper classes

interfaces java.io.Serializable and java.lang.Comparable. All these classes can
be serialized to a stream, and their objects define a natural sort order.

4.11.2 Creating objects of the wrapper classes
You can create objects of all the wrapper classes in multiple ways:
■
■
■

Assignment—By assigning a primitive to a wrapper class variable
Constructor—By using wrapper class constructors
Static methods—By calling the static method of wrapper classes, like valueOf()

For example
Boolean bool1 = true;
Character char1 = 'a';
Byte byte1 = 10;
Double double1 = 10.98;

Autoboxing

Boolean bool2 = new Boolean(true);
Character char2 = new Character('a');
Byte byte2 = new Byte((byte)10);
Double double2 = new Double(10.98);

Won’t
compile

//Character char3 = new Character("a");
Boolean bool3 = new Boolean("true");
Byte byte3 = new Byte("10");
Double double3 = new Double("10.98");
Boolean bool4 = Boolean.valueOf(true);
Boolean bool5 = Boolean.valueOf(true);
Boolean bool6 = Boolean.valueOf("TrUE");
Double double4 = Double.valueOf(10);

Constructors that
accept primitive
value

Constructors that
accept String
Using static
method
valueOf()

You can create objects of the rest of the wrapper classes (Short, Integer, Long, and
Float) in a similar manner. All the wrapper classes define constructors to create an
object using a corresponding primitive value or as a String.
Another interesting point to note is that neither of these classes defines a default
no-argument constructor. Because wrapper classes are immutable, it doesn’t make

Licensed to Mark Watson 

322

CHAPTER 4

Generics and collections

sense to initialize the wrapper objects with the default primitive values if they can’t be
modified later.
All wrapper classes (except Character) define a constructor
that accepts a String argument representing the primitive value that
needs to be wrapped. Watch out for exam questions that include a call to
a no-argument constructor of a wrapper class. None of these classes defines
a no-argument constructor.

EXAM TIP

You can assign a primitive value directly to a reference variable of its wrapper class
type—thanks to autoboxing. The reverse is unboxing, when an object of a primitive
wrapper class is converted to its corresponding primitive value. I’ll discuss autoboxing
and autounboxing in detail in the next section.

4.11.3 Retrieving primitive values from the wrapper classes
All wrapper classes define methods of the format primitiveValue(), where primitive
refers to the exact primitive data type name. Table 4.4 shows a list of the classes and
their methods to retrieve corresponding primitive values.
Table 4.4 Methods to retrieve primitive values from wrapper classes
Boolean

Character

booleanValue()

charValue()

Byte, Short, Integer, Long, Float, Double

byteValue(), shortValue(), intValue(),
longValue(),floatValue(), doubleValue()

It’s interesting to note that all numeric wrapper classes define methods to retrieve the
value of the primitive value they store, as a byte, short, int, long, float, and double.

4.11.4 Parsing a string value to a primitive type
To get a primitive data type value corresponding to a string value, you can use the
static utility method parseDataType(), where DataType refers to the type of the
return value. Each wrapper class (except Character) defines a method, to parse a
String to the corresponding primitive value, listed as follows:
Table 4.5 Parsing methods defined by wrapper classes
Class name

Method

Boolean

public static boolean parseBoolean(String s)

Character

no corresponding parsing method

Byte

public static byte parseByte(String s)

Short

public static short parseShort (String s)

Integer

public static int parseInt(String s)

Licensed to Mark Watson 

Using wrapper classes

323

Table 4.5 Parsing methods defined by wrapper classes
Class name

Method

Long

public static long parseLong(String s)

Float

public static float parseFloat(String s)

Double

public static double parseDouble(String s)

All these parsing methods throw a NumberFormatException for invalid values. Here
are some examples:
Long.parseLong("12.34");

Throws NumberFormatException:
12.34 isn’t valid long.

Byte.parseByte("1234");
Boolean.parseBoolean("true");
Boolean.parseBoolean("TrUe");

Throws NumberFormatException:
1234 is out of range for byte.
Returns Boolean true.
No exceptions; the String
argument isn’t case-sensitive.

4.11.5 Difference between using method valueOf() and constructors
of wrapper classes
Method valueOf() returns an object of the corresponding wrapper class when it’s
passed an argument of a primitive type or String. Then what is the difference between
method valueOf() and constructors of these classes, which also accept method arguments of a primitive type and String?
Wrapper classes Character, Byte, Short, Integer, and Long cache objects with values in the range of –128 to 127. These classes define inner static classes that store
objects for the primitive values –128 to 127 in an array. If you request an object of any
of these classes, from this range, method valueOf() returns a reference to a predefined object; otherwise, it creates a new object and returns its reference:
Long var1 = Long.valueOf(123);
Long var2 = Long.valueOf("123");
System.out.println(var1 == var2);
Long var3 = Long.valueOf (223);
Long var4 = Long.valueOf (223);
System.out.println (var3 == var4);

Prints “true”; var1 and var2
refer to same cached object.
Prints “false”; var3 and var4
refer to different objects.

4.11.6 Comparing objects of wrapper classes
The wrapper classes correctly implement methods hashCode() and equals(), so you
can use them in collection framework classes as keys in a map. In the following example, you can use Double objects as keys in a HashMap:
public class UseWrapperAsKeysInMap {
public static void main(String[] args) {

Licensed to Mark Watson 

324

CHAPTER 4

Generics and collections

Map map = new HashMap<>();
map.put(6.6, "OCA");
map.put(7.7, "OCP");

Prints “OCA”

System.out.println(map.get(6.6));
System.out.println(map.get(new Double(7.7)));

Prints “OCP”

}
}

EXAM TIP Integer literal values are implicitly converted to Integer objects
and decimal literal values are implicitly converted to Double objects.

Let’s modify the preceding code and try to retrieve the string value “OCP” using a
Float object with value 7.7. Do you think objects of Double and Float with the same
values are considered equal?
public class UseWrapperAsKeysInMap {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put(6.6, "OCA");
map.put(7.7, "OCP");
System.out.println(map.get(6.6));
System.out.println(map.get(new Float((float)7.7)));
}
}

Outputs ‘OCA’
Outputs ‘null’

In the preceding code, a Float object with a value can’t be used to retrieve the value
that was added to a HashMap using a Double instance. Their values don’t matter.
The objects of different wrapper classes with the same values
are not equal.

EXAM TIP

All the wrapper classes also implement the Comparable interface. You can compare
them using method compareTo() and use them in collection framework classes that
use natural ordering (like TreeSet). Method compareTo() returns a negative integer,
zero, or a positive integer as this object is less than, equal to, or greater than the specified object. What do you think is the output of the following code that adds Boolean
instances to a HashSet?
public class UseTreeSetWithWrapperClasses {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(new Boolean(true));
set.add(new Boolean("FaLSe"));
set.add(Boolean.valueOf("TrUe"));
for (Boolean b : set)
System.out.println(b);
}
}

The output of the preceding code is
false
true

Licensed to Mark Watson 

325

Autoboxing and unboxing

EXAM TIP

When arranged in natural sort order, false precedes true.

In the preceding code, you can add Boolean instances to a HashSet because Boolean
implements the Comparable interface. Because HashSet ignores the addition of duplicate values, only one Boolean.false object is added to HashSet. When instances of
a class that doesn’t implement Comparable are added to a HashSet, a ClassCastException is thrown at runtime.
The next section covers autoboxing and unboxing, used by a compiler to convert
primitive values to wrapper objects and vice versa.

4.12 Autoboxing and unboxing
[4.4] Use wrapper classes, autoboxing, and unboxing
Autoboxing is the automatic conversion of a primitive data type to an object of the corresponding wrapper class (you box the primitive value). Unboxing is the reverse process
(you unbox the primitive value), as shown in figure 4.27.
The wrapper classes use autoboxing and unboxing features quite frequently:
Double d1 = new Double(12.67);
System.out.println(d1.compareTo(21.68));

Prints -1, since
12.67 < 21.68

Compare the use of the preceding method against the following method defined by
class Double:
public int compareTo(Double anotherDouble)

Wait—did I just mention that method compareTo() defined in the class Double
accepts an object of class Double and not a double primitive data type? Then why does
the preceding code compile? The answer is autoboxing. Java converted the primitive
double to an object of class Double (by using method valueOf()), so it works correctly. The Java compiler converted it to the following at runtime:
Double d1 = new Double(12.67D);
System.out.println(d1.compareTo(Double.valueOf(21.68D)));

Autoboxing
Primitive
value
Unboxing

Figure 4.27

Object of
wrapper class

Autoboxing and unboxing

Licensed to Mark Watson 

326

CHAPTER 4

Generics and collections

Now examine the following code (an example of unboxing with autoboxing):
public class Unboxing {
public static void main (String args[]) {
ArrayList list = new ArrayList();
list.add(12.12);
Autoboxing-Add double
list.add(11.24);
Double total = 0;
for (Double d : list)
total += d;
Unbox to use operator
}
+= with total
}

List of
Double

In the preceding code, at the end of execution of the for loop, total will be assigned
a Double value of 23.36. The arithmetic operators like += can’t be used with objects.
So why do you think the code compiles? In this example, the Java compiler converted
the preceding code to the following at runtime:
public class Unbox {
public static void main(String args[]) {
ArrayList list = new ArrayList();
list.add(new Double(12.12D));
list.add(new Double(87.98D));
Double total = Double.valueOf(0.0D);
for(Iterator iterator = list.iterator(); iterator.hasNext();)
{
Double d = (Double)iterator.next();
total += total.doubleValue() + d.doubleValue();
}
}

In the previous section, I mentioned that wrapper classes are immutable. So, what
happens when you add a value to the variable total, a Double object? In this case, the
variable total refers to a new Double object.
Wrapper classes are immutable. Adding a primitive value to a
wrapper class variable doesn’t modify the value of the object it refers to.
The wrapper class variable is assigned a new object.

EXAM TIP

Here’s another interesting question. What happens if you pass null as an argument to
the following method?
public int increment(Integer obj) {
return ++i;
}

Because the Java compiler would call obj.intValue() to get obj’s int value, passing
null to method increment() will throw a NullPointerException.
EXAM TIP Unboxing a wrapper reference variable, which refers to null,
will throw a NullPointerException.

Licensed to Mark Watson 

Summary

327

With the preceding exam tip, you’ve completed the coverage of generics and collections topics for the exam. With an understanding of all the related nuances under
your belt, you’ll be able to write better code in real projects. Good luck to you.

4.13 Summary
We started this chapter with a warm-up section on generics, including the need for
their introduction and the benefits and complexities of using them. The chapter covered the creation of generic classes, interfaces, and methods. It included how to
define and use single and multiple type parameters. You can use bounded type parameters to limit the type of objects that can be passed as arguments to generic classes,
interfaces, and methods. We also covered the wildcard ? to declare a type of a variable
or a return type of a method. Bounded wildcards enable you to restrict the types that
can be used as arguments in a parameterized type. You learned how type erasure
removes the type information during the compilation process so that you get only one
class file for each generic class on the interface on compilation. Type erasure also creates bridge methods.
By using type inference, a compiler can determine type arguments if you don’t
specify them while creating instances of generic types. But if it can’t, it’ll throw a warning, an error, or an exception. Mixing of raw and generic types was allowed to use
code that existed before generics were introduced. If not used correctly, you might get
a compiler warning or error, or a runtime exception, when you mix raw with generic
types. With generics, you must follow certain subtyping rules. A generic class is a subtype of its raw type. An object of ArrayList isn’t compatible with a reference
variable of type List.
You also worked with the collections framework, an architecture for representing
and manipulating collections. You worked with the main interfaces, their implementations, and the algorithms used to manipulate collection objects. The Collection interface is extended by the List, Deque, and Set interfaces (but not by the Map interface).
The Collection interface defines methods to manipulate and query its elements.
The List interface models an ordered collection of objects. It returns the objects
to you in the order in which you added them to a List. It allows you to store duplicate
elements. The List implementations on the exam are ArrayList and LinkedList.
A Queue is a linear collection of objects. A Deque is a double-ended queue, a queue
that supports the insertion and deletion of elements at both its ends. The Deque
implementations on the exam are LinkedList and ArrayDeque.
The Set interface models the mathematical Set abstraction. It’s a collection of
objects that doesn’t contain duplicate elements. Set implementations on the exam are
HashSet, LinkedHashSet, and TreeSet.
A Map stores a pool of key-value pairs. It doesn’t allow the addition of duplicate
keys. Items added to a Map aren’t ordered. The retrieval order of items from a Map
object isn’t guaranteed to be the same as its insertion order. Map implementations on
the exam are HashMap, LinkedHashMap, and TreeMap.

Licensed to Mark Watson 

328

CHAPTER 4

Generics and collections

Class HashTable wasn’t a part of the collections framework initially. It was retrofitted to implement the Map interface in Java 2, making it a member of the Java Collection framework. But it’s considered legacy code. It’s roughly equivalent to a HashMap,
with some differences. The operations of a HashMap aren’t synchronized, whereas the
operations of a HashTable are synchronized.
The Comparable interface is used to define the natural order of objects. The
Comparator interface is used to define the custom order for objects when you don’t
want to use their natural order, can’t define or redefine their natural order, or need a
custom order. These interfaces are used by multiple interfaces and classes to sort
objects like TreeSet, TreeMap, Collections.sort(), and Arrays.sort(). Classes
Arrays and Collections define methods to sort and search arrays and lists.
The wrapper classes are used to wrap primitive types so that they can be used with
collection classes. Autoboxing is the automatic conversion of a primitive data type to
an object of the corresponding wrapper class (you box the primitive value). Unboxing
is the reverse process.

REVIEW NOTES
This section lists the main points covered in this chapter.

Creating generic entities
■
You define a generic class, interface, or method by adding one or more type
parameters to it.
■
A class that uses a generic class uses a parameterized type, replacing the formal
parameter with an actual parameter. Also, invalid casts aren’t allowed.
■
Java’s naming conventions limit the use of single uppercase letters for type
parameters. Though not recommended, using any valid identifier name for
type parameters is acceptable code.
■
A generic class can be extended by another generic or nongeneric class.
■
An extended class must be able to pass type arguments to its generic base class.
If it doesn’t, the code won’t compile.
■
When a nongeneric class extends a generic class, the derived class doesn’t
define any type parameters but passes arguments to all type parameters of its
generic base class.
■
A generic interface is defined by including one or more type parameters in its
declaration.
■
When a nongeneric class implements a generic interface, the type parameters
follow the interface name.
■
When a generic class implements a generic interface, the type parameters follow both the class and the interface name.
■
A generic method defines its own formal type parameters. You can define a
generic method in a generic or a nongeneric class.

Licensed to Mark Watson 

Review notes
■

■

■

■
■

■

■

■

■

■

■

■

■

■

■

■

■

■

329

To define a generic method in a nongeneric class or interface, you must define
the type parameters with the method in its type parameter section.
A method’s type parameter list is placed just after its access and nonaccess modifiers and before its return type. Because a type parameter could be used to
define the return type, it should be known before the return type is used.
You can define a generic method in a generic class or interface, defining its
own type parameters.
You can also define a generic constructor in a generic class.
You can specify the bounds to restrict the set of types that can be used as type
arguments to a generic class, interface, or method. It also enables access to the
methods (and variables) defined by the bounds.
For a bounded type parameter, the bound can be a class, an interface, or an enum,
but not an array or a primitive type. All cases use the keyword extends to specify
the bound. If the bound is an interface, the implements keyword isn’t used.
A type parameter can have multiple bounds. The list of bounds consists of one
class or multiple interfaces.
For a type parameter with multiple bounds, the type argument must be a subtype of all bounds.
The wildcard ? represents an unknown type. You can use it to declare the type
of a parameter; a local, instance, or static variable; and a return value of generic
types. But you can’t use it as a type argument to invoke a generic method, create
a generic class instance, or for a supertype.
You can assign an instance of a subclass, say, String, to a variable of its base
class, Object. But you can’t assign ArrayList to a variable of type
List. Inheritance doesn’t apply to type parameters.
When you use a wildcard to declare your variables or method parameters, you
lose the functionality of adding objects to a collection.
To restrict the types that can be used as arguments in a parameterized type, you
can use bounded wildcards.
In upper-bounded wildcards, the keyword extends is used for both a class and
an interface.
For collections defined using upper-bounded wildcards, you can’t add any
objects. You can iterate and read values from such collections.
You can use final classes in upper-bounded wildcards. Although class X
extends String won’t compile,  will compile successfully.
You can restrict the use of type arguments to a type and its supertypes or base
types by using , where Type refers to a class, interface, or enum.
Type information is erased during the compilation process; this is called type
erasure.
When a generic class is compiled, you don’t get multiple versions of the compiled class files.

Licensed to Mark Watson 

330

CHAPTER 4

■

■

Generics and collections

The compiler erases the type information by replacing all type parameters in
generic types with Object (for unbounded parameter types) or their bounds
(for bounded parameter types).
The Java compiler might need to create additional methods, referred to as
bridge methods, as part of the type erasure process.

Using type inference
■

■

■

■

If you don’t specify the type of type arguments to instantiate a generic class or
invoke a generic method, the Java compiler might be able to infer the argument type by examining the declaration of the generic entity and its invocation.
If the type can’t be inferred, you might get a compilation warning, an error, or
an exception.
By throwing an unchecked warning, the compiler states that it can’t ensure type
safety. The term unchecked refers to operations that might result in violating
type safety. This occurs when the compiler doesn’t have enough type information to perform all type checks.
Starting with Java 7, you can drop the type arguments required to invoke the
constructor of a generic class and use a diamond—that is, <>. But an attempt to
drop the diamond will result in a compilation warning.
A Java compiler can’t infer the type parameters by using the diamond in the
case of generic methods. It uses the type of the actual arguments passed to the
method to infer the type parameters.

Understanding interoperability of collections using raw types and
generic types
■
■

■
■

■

■

Raw types exist only for generic types.
You can assign a parameterized type to its raw type, but the reverse will give a
compiler warning.
When you assign a parameterized type to its raw type, you lose the type information.
When you mix raw types with generic types, you might get a compiler warning
or error or a runtime exception.
You can assign an object of a subclass to reference a variable of its base class. But
this subtyping rule doesn’t work when you assign a collection-of-a-derived-class
object to a reference variable of a collection of a base class.
If you declare a reference variable List to a list, whatever you assign to
the list must be of generic type Object. A subclass of Object isn’t allowed.

Working with the Collection interface
■
■

The Collection interface represents a group of objects known as its elements.
There’s no direct implementation of Collection; no concrete class implements
it. It’s extended by more specific interfaces such as Set, List, and Queue.

Licensed to Mark Watson 

Review notes
■

■
■
■

■

331

This collection is used for maximum generality—to work with methods that can
accept objects of, say, Set, List, and Queue.
All collection classes are generic.
The Map interface doesn’t extend the core Collection interface.
The Collection interface implements the Iterable interface, which defines
method iterator(), enabling all the concrete implementations to access an
Iterator to iterate over all the collection objects.
The methods of the Collection interface aren’t marked as synchronized.

Creating and using List, Set, and Deque implementations
■

■

■

■
■

■

■

■

■

■

■

■

The List interface models an ordered collection of objects. It returns the
objects to you in the order in which you added them. It allows you to store
duplicate elements.
In a List you can control the position where you want to store an element. This
is the reason that this interface defines overloaded methods to add, remove,
and retrieve elements at a particular position.
Method listIterator() of List can be used to iterate the complete list or a
part of it.
An ArrayList is a resizable array implementation of the List interface.
An ArrayList uses the size variable to keep track of the number of elements
inserted in it. By default, an element is added to the first available position in
the array. But if you add an element to an earlier location, the rest of the list elements are shifted to the right.
If you remove an element that isn’t the last element in the list, ArrayList shifts
the elements to the left.
An ArrayList maintains a record of its size so that you can’t add elements at
arbitrary locations.
ArrayList’s method remove() sequentially searches the ArrayList to find the target object, using method equals() to compare its elements with the target object.
If a matching element is found, remove(Object) removes the first occurrence
of the match found.
If you’re adding instances of a user-defined class as elements to an ArrayList,
override its method equals() or else its method contains() or remove() might
not behave as expected.
The ArrayList methods clear(), remove(), and removeAll() offer different
functionalities. clear() removes all the elements from an ArrayList. remove
(Object) removes the first occurrence of the specified element, and remove(int)
removes the element at the specified position. removeAll() removes from an
ArrayList all of its elements that are contained in the specified collection.
A Deque is a double-ended queue, a queue that supports the insertion and deletion of elements at both its ends.

Licensed to Mark Watson 

332

CHAPTER 4

■
■

■

■

■
■
■
■
■

■

■

■

■
■

■

■
■

■

■

■

■

Generics and collections

As a double-ended queue, a Deque can work as both a queue and a stack.
The Deque interface defines multiple methods to add, remove, and query the
existence of elements from both its ends.
Methods addFirst(), addLast(), offerFirst(), and offerLast() add and
remove elements from the top and tail.
Deque also defines methods push(), pop(), and peek() to add, remove, and
query elements at its beginning.
ArrayDeque and LinkedList implement the Deque interface.
ArrayDeque is a resizable array implementation of the Deque interface.
Deque’s method peek() only queries elements, it doesn’t remove them.
Deque’s method remove() just removes an element.
Deque’s method poll() returns null when Deque is empty and remove() throws
a runtime exception.
All the insertion methods (add(), addFirst(), addLast(), offer(), offerFirst(), offerLast(), and push()) throw a NullPointerException if you try
to insert a null element into an ArrayDeque.
You can iterate over the elements of Deque by using an Iterator, returned by
methods iterator() and descendingIterator().
Class LinkedList implements both the List and Deque interfaces. So it’s a
double-linked list implementation of the List and Deque interfaces.
Unlike ArrayDeque, LinkedList permits addition of null elements.
A LinkedList is like an ArrayList (ordered by index) but the elements are
double-linked to each other. So besides the methods from List, you get a
bunch of other methods to add or remove at the beginning and end of this list.
So it’s a good choice if you need to implement a queue or a stack. A LinkedList
is useful when you need fast insertion or deletion, but iteration might be slower
than an ArrayList.
Because a LinkedList implements List, Queue, and Deque, it implements
methods from all these interfaces.
The Set interface models the mathematical Set abstraction.
The Set interface doesn’t allow duplicate elements and the elements are returned
in no particular order.
To determine the equality of objects, Set uses their method equals(). For two
elements, say e1 and e2, if e1.equals(e2) returns true, Set doesn’t add both
these elements.
Set defines methods to add and remove its elements. It also defines methods to
query itself for the occurrence of specific objects.
Class HashSet implements the Set interface. It doesn’t allow the addition of
duplicate elements and makes no guarantee to the order of retrieval of its
elements.
HashSet is implemented using a HashMap.

Licensed to Mark Watson 

Review notes
■

■
■
■

■

■

■

■

■

■
■

■

■

■

■

■

333

To store and retrieve its elements, a HashSet uses a hashing method, accessing an object’s hashCode() value to determine the bucket in which it should
be stored.
Method hashCode() doesn’t call method equals().
Method equals() doesn’t call method hashCode().
Classes should override their hashCode() methods efficiently to enable collection classes like HashSet to store them in separate buckets.
A HashSet allows storing of only one null element. All subsequent calls to storing null values are ignored.
Class HashSet uses hashing algorithms to store, remove, and retrieve its elements. So it offers constant time performance for these operations, assuming
that the hash function disperses its elements properly among its buckets.
A LinkedHashSet offers the benefits of a HashSet combined with a LinkedList.
It maintains a double-linked list running through its entries.
As with a LinkedList, you can retrieve objects from a LinkedHashSet in the
order of their insertion.
Like a HashSet, a LinkedHashSet uses hashing to store and retrieve its elements quickly.
A LinkedHashSet permits null values.
LinkedHashSet can be used to create a copy of a Set with the same order as that
of the original set.
LinkedHashSet’s method addAll() accepts a Collection object. So you can
add elements of an ArrayList to a LinkedHashSet. The order of insertion of
objects from ArrayList to LinkedHashSet is determined by the order of objects
returned by ArrayList’s iterator (ArrayList objects can be iterated in the
order of their insertion).
A TreeSet stores all its unique elements in a sorted order. The elements are
ordered either on their natural order (achieved by implementing the Comparable
interface) or by passing a Comparator while instantiating a TreeSet. If you fail
to specify either of these, TreeSet will throw a runtime exception when you try
to add an object to it.
Unlike the other Set implementations like HashSet and LinkedHashSet,
which use equals() to compare objects for equality, a TreeSet uses method
compareTo() (for the Comparable interface) or compare() (for the Comparator
interface) to compare objects for equality and their order.
If two object instances are equal according to their method equals(), but not
according to their method compare() or compareTo(), a Set can exhibit inconsistent behavior.
Classes Enum and File implement the Comparable interface. The natural order
of enum constants is the order in which they’re declared. Classes StringBuffer
and StringBuilder don’t implement the Comparable interface.

Licensed to Mark Watson 

334

CHAPTER 4

Generics and collections

Map and its implementations
■
Unlike the other interfaces from the collections framework, like List and Set,
the Map interface doesn’t extend the Collection interface.
■
A Map defines key-values pairs, where a key can map to a 0 or 1 value.
■
Map objects don’t allow the addition of duplicate keys.
■
The addition of a null value as a key or value depends on a particular Map
implementation. A HashMap and LinkedHashMap allow insertion of null as a key,
but TreeMap doesn’t—it throws an exception.
■
A HashMap is a hash-based Map that uses the hash value of its key (returned by
hashCode()) to store and retrieve keys and their corresponding values. Each
key can refer to a 0 or 1 value. The keys of a HashMap aren’t ordered. The HashMap methods aren’t synchronized, so they aren’t safe to be used in a multithreaded environment.
■
You can create a HashMap by passing its constructor another Map object. Additions of new key-value pairs or deletions of existing key-value pairs in the Map
object passed to the constructor aren’t reflected in the newly created HashMap.
■
Because a HashMap stores objects as its keys and values, it’s common to see code
that stores another collection object (like an ArrayList) as a value in a Map.
■
You can call method get() on a HashMap to retrieve the value for a key.
■
Methods containsKey() and containsValue() check for the existence of a
key or a value in a HashMap, returning a boolean value. Methods get() and
containsKey() rely on appropriate overriding of a key’s methods hashCode()
and equals().
■
Class String and all the wrapper classes override their methods hashCode()
and equals(), so they can be correctly used as keys in a HashMap.
■
HashMap uses hashing functions to add or retrieve key-value pairs. The key must
override both methods equals() and hashCode() so that it can be added to a
HashMap and retrieved from it.
■
When objects of a class that only overrides method equals() (and not method
hashCode()) are used as keys in a HashMap, containsKey() will always return
false.
■
If you add a key-value pair to a HashMap such that the key already exists in the
HashMap, the key’s old value will be replaced with the new value.
■
You can add a value with null as the key in a HashMap.
■
You can use method remove(key) or clear() to remove one or all key-value
pairs of a HashMap.
■
Method remove() can return a null value, irrespective of whether the specified
key exists in a HashMap. It might return null if matching a key isn’t present in
HashMap, or if null is stored as a value for the specified key.
■
For a HashMap, methods that query or search a key use the key’s methods hashCode() and equals().

Licensed to Mark Watson 

Review notes
■

■
■

■

■

■

■

■

■

■

■

■

■

■

■

335

Method remove() removes a maximum of one key-value pair from a HashMap.
Method clear() clears all the entries of a HashMap. Method remove() accepts a
method parameter but clear() doesn’t.
You can use methods size() and isEmpty() to query a HashMap’s size.
You can use method putAll() to copy all the mappings from the specified map
to a HashMap.
Method putAll() accepts an argument of type Map. It copies all the mappings
from the specified map to the map that calls putAll(). For common keys, the
values of map that call putAll() are replaced with the values of the Map object
passed to putAll().
The Map interface defines methods keySet(), values(), and entrySet() to
access keys, values, and key-value pairs of a HashMap.
Method values() returns a Collection object, method keySet() returns a Set
object, and method entrySet() returns a Map.Entry object.
Class HashTable wasn’t a part of the collections framework initially. It was retrofitted to implement the Map interface in Java 2, making it a member of the Java
Collection framework. But it’s considered legacy code. It’s roughly equivalent
to a HashMap with some differences. The operations of a HashMap aren’t synchronized, whereas the operations of a HashTable are synchronized.
The LinkedHashMap IS-A HashMap with a predictable iteration order. Like a
LinkedList, a LinkedHashMap maintains a double-linked list, which runs through
all its entries.
A LinkedHashMap will always iterate over its elements in their order of
insertion.
A TreeMap is sorted according to the natural ordering of its keys or as defined
by a Comparator passed to its constructor.
TreeMap implements the SortedMap interface. Like HashMap and LinkedHashMap, the operations of a TreeMap aren’t synchronized, which makes it unsafe to
be used in a multithreaded environment.
The TreeMap performs all key comparisons by using method compareTo() or
compare(). Two keys are considered equal by a TreeMap if the key’s method
compareTo() or compare() considers them equal.
When you create a TreeMap object, you should specify how its keys should
be ordered. A key might define its natural ordering by implementing the
Comparable interface. If it doesn’t you should pass a Comparator object to
specify the key’s sort order.
The set of values that you retrieve from a TreeMap is sorted on its keys and not
on its values.
You can create a TreeMap without passing it a Comparator object or without
using keys that implement a Comparable interface. But adding key-value pairs
to such a TreeMap will throw a runtime exception, ClassCastException.

Licensed to Mark Watson 

336

CHAPTER 4

■

■

Generics and collections

When you pass a Comparator object to TreeMap constructor, the natural order
of its keys is ignored.
Because a TreeMap uses method compare() or compareTo() to determine the
equality of its keys, it can access the value associated with a key, even though its
key doesn’t override its method equals() or hashCode().

Using java.util.Comparator and java.lang.Comparable
■

■

■

■

■
■

■

■

The Comparable interface is used to define the natural order of the objects of
the class that implements it.
Comparable is a generic interface (using T as type parameter) and defines only
one method, compareTo(T object), which compares the object to the object
passed to it as a method parameter.
Method compareTo() returns a negative integer, zero, or a positive integer if
this object is less than, equal to, or greater than the specified object.
The Comparator interface is used to define the sort order of a collection of
objects, without requiring them to implement this interface.
The Comparator interface defines methods compare() and equals().
You can pass Comparator to sort methods like Arrays.sort() and Collections
.sort().
A Comparator object is also passed to collection classes like TreeSet and TreeMap that require ordered elements.
The Comparator interface is used to specify the sort order for classes that
– Don’t define a natural sort order
– Need to work with an alternate sort order
– Don’t allow modification to their source code so that natural ordering can
be added to them

Sorting and searching arrays and lists
■

■

■

■
■

Class Arrays in the collections framework defines multiple methods to sort
complete or partial arrays of primitive data types and objects.
When method Arrays.sort() accepts fromIndex and toIndex values to sort a
partial array, the element stored at position fromIndex is sorted, but the element stored at position toIndex isn’t.
A space has a lower ASCII or Unicode value than lowercase or uppercase letters.
When arranged in an ascending order, a String value that starts with a space is
placed before the String values that don’t start with a space.
Class Collections defines method sort() to sort objects of List.
Classes Arrays and Collections define method binarySearch() to search a
sorted array or a List for a matching value using the binary search algorithm.
The array or List must be sorted according to the natural order of its elements
or as specified by Comparator. If you pass this method an unsorted list, the

Licensed to Mark Watson 

Review notes

■

337

results are undefined. If more than one value matches the target key value to be
searched, this method can return any of these values.
Method binarySearch() returns the index of the search key if it’s contained in
the list; otherwise it returns (-(insertion point) - 1). The insertion point is
defined as the point at which the key would be inserted into the list: the index
of the first element greater than the key, or list.size() if all elements in the
list are less than the specified key. Note that this guarantees that the return
value will be >= 0 if and only if the key is found.

Using wrapper classes
■
All the wrapper classes are immutable.
■
All the wrapper classes implement the Comparable interface. All these classes
define their natural order.
■
You can create objects of all the wrapper classes in multiple ways:
– Assignment—By assigning a primitive to a wrapper class variable
– Constructor—By using wrapper class constructors
– Static methods—By calling the static method of wrapper classes, like valueOf()
■
All wrapper classes (except Character) define a constructor that accepts a
String argument representing the primitive value that needs to be wrapped.
Watch out for exam questions that include a call to a no-argument constructor
of a wrapper class. None of these classes defines a no-argument constructor.
■
To get a primitive data-type value corresponding to a string value, you can use
the static utility method parseDataType(), where DataType refers to the type of
the return value.
■
Wrapper classes Character, Byte, Short, Integer, and Long cache objects with
values in the range of –128 to 127. These classes define inner static classes that
store objects for the primitive values –128 to 127 in an array. If you request an
object of any of these classes, from this range, method valueOf() returns a reference to a predefined object; otherwise, it creates a new object and returns
its reference.
■
Integer literal values are implicitly converted to Integer objects and decimal literal values are implicitly converted to Double objects.
■
The objects of different wrapper classes with the same values aren’t equal.
■
When arranged in natural sort order, false precedes true.
Autoboxing and Unboxing
■
Autoboxing is the automatic conversion of a primitive data type to an object of
the corresponding wrapper class (you box the primitive value). Unboxing is the
reverse process (you unbox the primitive value).
■
Wrapper classes are immutable. Adding a primitive value to a wrapper class variable doesn’t modify the value of the object it refers to. The wrapper class variable
is assigned a new object.

Licensed to Mark Watson 

338

CHAPTER 4

■

Generics and collections

Unboxing a wrapper reference variable, which refers to null, will throw a NullPointerException.

SAMPLE EXAM QUESTIONS
Q 4-1. Which of the following options creates a generic class that can be passed multiple generic types? (Choose all that apply.)
a

class EJavaMap {}

b

class EJavaMap {}

c

class EJavaMap {
void add(Aa a) {}
void add(Bb a) {}
}

d

class EJavaMap {
void add(Aa a, Bb b) {}
}

Q 4-2. Which of the following statements are true about generic classes, interfaces,
and methods?
a
b
c
d

If you define a generic class, you must define its corresponding raw class explicitly.
On compilation, type information is erased from a generic class.
A generic method can be defined within a generic class or a regular class.
Generic interfaces might not accept multiple generic type parameters.

Q 4-3. Which of the following options when inserted at //INSERT CODE HERE would
compile successfully without any warning? (Choose all that apply.)
class Box {
T t;
Box(T t) {
this.t = t;
}
T getValue() {
return t;
}
}
class Test {
public static void main(String args[]) {
//INSERT CODE HERE
}
}
a
b
c
d

Box box = new Box("abcd");
Box box = new Box<>("String");
Box box = new Box("Object");
Box box = new Box("String");

Licensed to Mark Watson 

339

Sample exam questions

Q 4-4. Consider this pre-generics implementation of method concat() in class MyString:
class MyString {
public static String concat(List list) {
String result = new String();
for (Iterator iter = list.iterator(); iter.hasNext(); ) {
String value = (String)iter.next();
result += value;
}
return result;
}
}

//1
//2
//3
//4
//5

Which three of the following changes together will allow method concat() to be used
with generics without generating unchecked warnings?
a
b
c
d
e
f

Replace line 1 with public static String concat(List list) {.
Replace line 1 with public static String concat(List list) {.
Remove code on line 3.
Remove code on line 4.
Change code on line 3 to for (String value : list) {.
Change code on line 3 to for (String value : list.listIterator()) {.

Q 4-5. What happens when you try to compile and execute the following class with
Java 7? (Choose all that apply.)
class EJava {
public static void main(String args[]) {
ArrayList list = new ArrayList();
list.add("ABCD");
list.add(1);
list.add(new Thread());
for (Object obj:list) System.out.println(obj);
}
}
a
b
c

d

Class EJava fails to compile with Java 7.
Class EJava compiles with a compilation warning when compiled with Java 7.
Class EJava iterates though all the objects of the list and prints their values as
returned by their method toString().
Class EJava prints the first list value and throws a ClassCastException while
trying to print the second list element.

Q 4-6. What is the output of the following code?
import java.util.*;
public class MyHashSet {
public static void main (String [] args) {
Set set = new TreeSet<>();
set.add(new Phone("Harry"));

Licensed to Mark Watson 

340

CHAPTER 4

Generics and collections

set.add(new Phone("Paul"));
set.add(new Phone("Harry"));
set.add(new Phone("Paul"));
Iterator  iterator = set.iterator ();
while (iterator.hasNext()) {
Phone ph = iterator.next();
switch (ph.toString()){
case "Harry": System.out.print("?Harry? ");
break;
case "Paul": System.out.print(" ");
break;
}
}
System.out.print("Set size=" + set.size());
}
}
class Phone{
String manufacturer;
Phone(String value) {
manufacturer = value;
}
public String toString() {
return manufacturer;
}
}
a
b
c
d
e
f
g
h

 ?Harry? ?Harry?  Set size=4
 ?Harry?  ?Harry? Set size=4
?Harry? ?Harry?   Set size=4
 ?Harry? Set size=2
?Harry?  Set size=2

Compilation error
Runtime exception
The output is unpredictable.

Q 4-7. Given the following code, which code options when inserted at //INSERT CODE
HERE will sort the keys in props?
class EMap {
public static void main(String... args) {
HashMap props = new HashMap();
props.put("Harry", "Manth");
props.put("Paul", "Rosen");
props.put("Alm", "Bld");
Set keySet = props.keySet();
//INSERT CODE HERE
}
}

Licensed to Mark Watson 

Sample exam questions
a
b
c
d
e
f

341

Arrays.sort(keySet);
Collections.sort(keySet);
Collection.sort(keySet);
Collections.arrange(keySet);
keySet = new TreeSet(keySet);
keySet = new SortedSet(keySet);

Q 4-8. Which code option(s) when inserted at //INSERT CODE HERE will make class
EJava print Harry?
class EJava {
public static void main(String args[]) {
String myArray[] = {"Harry", "Shreya",
"Selvan", "Paul"};
//INSERT CODE HERE
System.out.println(myArrayList.get(0));
}
}
a
b
c

d
e

List  myArrayList = new LinkedList(Arrays.asList(myArray));
List  myArrayList = new LinkedList<>(Arrays.asList(myArray));
List  myArrayList = new LinkedList<>(Arrays.asList
(myArray));
List myArrayList = new LinkedList(Arrays.asList(myArray));
List myArrayList = new LinkedList(Arrays.asList(myArray));

Q 4-9. Which statements are true about method hashCode()?
a

b

c

d

e

Method hashCode() is used by classes such as HashMap to determine inequality
of objects.
Method hashCode() is used by classes such as HashSet to determine equality
of objects.
Method hashCode() is used by class Collections.sort to order the elements of
a collection.
Method hashCode() is used by classes like HashSet, TreeSet, and HashMap,
which use hashing to group their elements into hash buckets.
An efficient hashCode() method includes use of a particular algorithm recommended by Java.

Q 4-10. What is the output of the following code? (Choose all that apply.)
import java.util.*;
class MyHash {
public static void main(String args[]) {
Person p1 = new Person("Shreya");
Person p2 = new Person("Harry");

Licensed to Mark Watson 

342

CHAPTER 4

Generics and collections

Person p3 = new Person("Paul");
Person p4 = new Person("Paul");
HashSet set = new HashSet<>();
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
System.out.println(set.size());
}
}
class Person {
String name;
Person(String name) {
this.name = name;
}
public int hashCode() {
return 20;
}
public boolean equals(Object obj) {
return true;
}
}
a
b
c
d
e
f
g

0
1
2
3
4
Compilation error
Runtime exception

Q 4-11. Which code option when inserted at //INSERT CODE HERE will enable you to
sort instances of class Student using their natural order and add them to a TreeSet?
class Student implements Comparator {
String id;
String name;
//INSERT CODE HERE
}

e

public boolean compare(Object obj1, Object obj2) {/* relevant code
here */}
public int compare(Object obj1, Object obj2) {/* relevant code here
*/}
public boolean compareTo(Student s1, Student s2) {/* relevant code
here */}
public boolean compare(Object obj1) {/* relevant code here */}
public int compare(Student obj1) {/* relevant code here */}

f

None of the above

a

b

c

d

Licensed to Mark Watson 

Answers to sample exam questions

343

Q 4-12. Select true statements about method hashCode().
a

b
c
d

e

Classes HashSet and HashMap use method hashCode() to store and retrieve
their values.
Class TreeSet can use method hashCode() to store and retrieve its elements.
Method hashCode() is used to test for object equality and inequality for a class.
If hashCode() for two objects of the same class returns the same value, the objects
are considered equal.
For a class, method hashCode() can be used to test for object inequality.

ANSWERS TO SAMPLE EXAM QUESTIONS
A 4-1. a, c, d
[4.1] Create a generic class
Explanation: Though Java recommends using single letters like T or V to specify the
type, using the letters A and B is correct in option (a) as per the syntax.
Option (b) is incorrect because it uses invalid syntax to specify the type parameters
to a class. To specify multiple type parameters in a class declaration, you need to specify a placeholder for only the type—not its variables.
Option (c) and (d) are correct. It’s acceptable to define the type parameters as a
subtype of an existing Java class. Though not recommended, it’s acceptable to use
type parameters with more than one letter: Aa and Bb.
A 4-2. b, c
[4.1] Create a generic class
Explanation: Option (a) is incorrect. A raw type doesn’t include the generic information. For the generic type List, its raw type is List. You don’t need to define a raw
type explicitly for any generic class or interface. You can access the raw type of all the
generic types.
Option (d) is incorrect. Like generic classes, generic interfaces can define any
number of generic type parameters.
A 4-3. b, c
[4.2] Use the diamond for type inference
[4.3] Analyze the interoperability of collections that use raw types and generic types
Explanation: Option (a) generates a compilation warning, because it uses generic
code without its type information.
Options (b) and (c) are correct. The type that you use for declaring a variable of
class Box is String, as in Box box. Class Box defines only one constructor that

Licensed to Mark Watson 

344

CHAPTER 4

Generics and collections

accepts an object of its type parameter. Even though you can just use the angle
brackets and drop the type parameter String from it, you must pass a String object
to the constructor of class Box or a subclass. The following code also compiles without warning (and I pass an instance of a subclass of the generic type parameter into
the constructor):
Box box3 = new Box("Object");

Option (d) fails to compile. Even though class String subclasses class Object, the reference variable box of type Box can’t refer to objects of Box.
A 4-4. a, d, e
[4.3] Analyze the interoperability of collections that use raw types and generic types
Explanation: The options (a), (d), and (e), when implemented together, will allow
method concat() to be used with generics without generating any warnings.
Option (b) is incorrect. Replacing line 1 with public static String concat
(List list) { would generate a ClassCastException at runtime, if a list
other than a list of integer objects is passed to method concat().
Option (c) is incorrect because a for loop is required to iterate through the list
objects.
Options (d) and (e) are correct. With generics, you can use an advanced for loop
to iterate through list elements. Because the object type is already specified (as
String), the advanced for loop returns String objects, which don’t require an
explicit cast.
Option (f) is incorrect and won’t compile.
A 4-5. b, c
[4.3] Analyze the interoperability of collections that use raw types and generic types
Explanation: The code executes, printing all the values of the objects added to the
List. Method toString() is implicitly called when you try to print the value of
an object.
Using a raw type of interface is allowed post-introduction of generics. This is
allowed for backward compatibility with nongenerics code.
But all uses of the add methods with List’s raw type will compile with the following
compilation warning:
warning: [unchecked] unchecked call to add(E) as a member of the raw type
ArrayList
list.add("ABCD");
^
where E is a type-variable:
E extends Object declared in class ArrayList

Licensed to Mark Watson 

Answers to sample exam questions

345

A 4-6. g
[4.5] Create and use List, Set, and Deque implementations
Explanation: The code fails at runtime with the following message because class Phone
doesn’t implement the java.lang.Comparable interface:
Exception in thread "main" java.lang.ClassCastException: Phone
can’t be cast to java.lang.Comparable.

This exception is thrown when the code tries to add a value to set. A TreeSet should be
able to sort its elements either by using their natural order or by using a Comparator
object passed to TreeSet’s constructor. A class defines its natural sort order by implementing the Comparable interface. Class Phone doesn’t define its natural order. Also,
while instantiating set, no Comparator object is passed to TreeSet’s constructor.
You can instantiate a TreeSet that neither uses elements with a natural sort order
nor is passed a Comparator object. But such a TreeSet will throw a runtime exception
when you try to add an element to it.
A 4-7. e
[4.6] Create and use Map implementations
Explanation: Option (a) is incorrect because method sort() of class Arrays sorts only
arrays, not HashMap.
Option (b) is incorrect. Method sort() of class Collections sorts List objects,
not HashMap.
Option (c) is incorrect because Collection isn’t defined in the Java API.
Option (d) is incorrect because method arrange() isn’t defined in class Collections.
Option (f) is incorrect because SortedSet is an interface, which can’t be instantiated.
A 4-8. b, c, d, e
[4.2] Use the diamond for type inference
[4.3] Analyze the interoperability of collections that use raw types and generic types
[4.5] Create and use List, Set, and Deque implementations
Explanation: Option (a) is incorrect. This code won’t compile. When Java runtime
instantiates a LinkedList object, it must know the type of objects that it stores—either
implicitly or explicitly. But, in this option, the type of the LinkedList object is neither
stated explicitly nor can it be inferred.
Option (b) is correct. The wildcard ? is used to refer to any type of object. Here
you’re creating a reference variable myArrayList, which is a List of any type. This reference variable is initialized with LinkedList object, whose type is inferred by the
argument passed to the constructor of LinkedList.

Licensed to Mark Watson 

346

CHAPTER 4

Generics and collections

Option (c) is correct. This option uses the bounded wildcard ,
restricting the unknown type to be either class String or any of its subclasses. Even
though String is a final class, ? extends String is acceptable code.
Option (d) generates a compiler warning because it uses raw types.
Option (e) doesn’t generate a compiler warning because the object creation uses
generics.
A 4-9. a
[4.5] Create and use List, Set, and Deque implementations
Explanation: Option (b) is incorrect. Method equals()is used to determine the
equality of objects.
Option (c) is incorrect. Class Collections defines two overloaded versions of
method sort(). Both accept a List object, with or without a Comparator object.
Method sort() sorts a List passed to it into ascending order, according to the natural
ordering of its elements, or by using the order specified by a Comparator object.
Option (d) is incorrect. Though HashSet and HashMap use hashCode() for hashing, TreeSet doesn’t.
Option (e) is incorrect. Java doesn’t recommend any particular algorithm for
writing an efficient hashCode() method. But Java does recommend writing an efficient algorithm.
A 4-10. b
[4.5] Create and use List, Set, and Deque implementations
Explanation: Method HashSet() uses method hashCode() to determine an appropriate bucket for its element. If it adds a new element to a bucket that already contains
an element, HashSet calls equals on the elements to determine whether they’re
equal. HashSet doesn’t allow duplicate elements. When it adds a Person object, the
same hashCode value makes it land in the same bucket. Calling the equals() method
returns true, signaling that an attempt is being made to add a duplicate object, which
isn’t allowed by HashSet.
A 4-11. f
[4.5] Create and use List, Set, and Deque implementations
[4.7] Use java.util.Comparator and java.lang.Comparable
Explanation: Instances of a class are sorted using its natural order, if the class implements the Comparable interface and not Comparator.
The Comparator interface is used to define how to compare two objects for sorting
(less than, equal to, or greater than). Unlike the Comparable interface, the Comparator

Licensed to Mark Watson 

Answers to sample exam questions

347

interface need not be implemented by the class whose objects are to be sorted. The
Comparator interface can be used to define an order for objects if the objects don’t
define their natural order. It can also be used to define a custom order for objects.
You can use a Comparator object to define an order for objects, the natural order of
which you can’t define or modify. When you pass a Comparator object to the instantiation of a collection class like TreeMap, the TreeMap uses the order as defined by the
Comparator object, ignoring the natural order of its keys.
For example, the following class defines a custom (descending) order for String
objects:
class DescendingStrings implements Comparator {
public int compare(String s1, String s2) {
return s2.name.compareTo(s1.name);
}
}

A 4-12. a, e
[4.5] Create and use List, Set, and Deque implementations
[4.6] Create and use Map implementations
Explanation: In option (a), classes HashSet and HashMap use hashing to store and
retrieve their values. Hashing uses the hashCode value to determine the bucket in
which the values should be stored.
Option (b) is incorrect. TreeSet ignores the hashCode values. A TreeSet stores its
elements based on its key’s natural ordering or the ordering defined by a Comparator.
Options (c) and (d) are incorrect, and (e) is correct. The hashCode value is used
to test for object inequality. If two objects return different hashCode values, they can
never be equal. But if your objects return the same hashCode values, they can be
unequal (if their equals() returns false).

Licensed to Mark Watson 

String processing

Exam objectives covered in this chapter

What you need to know

[5.1] Search, parse, and build strings
(including Scanner, StringTokenizer, StringBuilder,
String, and Formatter)

The methods that can be used to parse
and build strings, from classes Scanner,
StringTokenizer, StringBuilder, and
Formatter.

[5.2] Search, parse, and replace strings by
using regular expressions, using expression
patterns for matching limited to . (dot), *
(star), + (plus), ?, \d, \D, \s, \S, \w, \W,
\b, \B, [], ()

What regular expressions (regex) are and how they’re
used to search, parse, and replace strings. Understand the use of the relevant API classes in the
java.util.regex package.

[5.3] Format strings using the formatting
parameters %b, %c, %d, %f, and %s in format strings

The purpose of formatting parameters to format
strings and other data types.
How to determine code output when invalid
combination of format parameters and data
types is used.

Imagine that after completing a 500-page draft of your novel, you want to change
the name of your main character from Beri to Bery. This should be simple, right?
You can use your word processor’s Find/Replace option and be well on your way.
From a text file, you pull out a list of email addresses of all the publishers to whom
you wish to submit your manuscript. But before using these addresses, you want to
run a quick check to ensure that they’re valid—that they include an @ sign and a
dot (.), followed by a domain name. This should also be simple. You can use your
email client to check them. Now, imagine your novel becomes a best seller, and

348

Licensed to Mark Watson 

Regular expressions

349

your publisher wants to translate it into multiple languages. Wow! Assuming that your
novel includes numbers and decimal numbers, the publisher would also need to
reformat these numbers based on the language that your text is translated into. Various languages might use different separators in decimal numbers. Though not a
straightforward task, it’s feasible.
Finding and replacing text, validating it, and formatting numbers in different ways
are all examples of common requirements. Java applications might need to perform
similar common data manipulation and formatting tasks. To accomplish this, Java
includes flexible and powerful classes and methods to search, parse, replace, and format data. This chapter covers
■

■
■

How classes from the Java API (String, StringBuilder, Scanner, StringTokenizer, Formatter) can help you search, parse, build, and replace strings
Regular expressions and what you can do with them
How to format strings by using format specifiers

Even though you might be familiar with working with the Java programming language, it’s possible that you didn’t get an opportunity to work with regular expressions
(regex) in Java (or in any other programming language). Now available to be used
with most programming languages, either integrated or as an external library, regex is
a powerful and flexible language to describe data and search matching data. But this
chapter’s coverage of regex is limited to the exam topics.
Let’s start with the basic differences between searching text for exact matches and
searching for regex patterns.

5.1

Regular expressions
[5.2] Search, parse, and replace strings by using regular expressions,
using expression patterns for matching limited to . (dot), * (star),
+ (plus), ?, \d, \D, \s, \S, \w, \W, \b, \B, [], ().
As opposed to exact matches, you can use regex to search for data that matches a pattern. Let’s imagine that in addition to changing the name of your main character in
your novel, you want to change all references of sun to moon. Though the solution
might seem as simple as using Find/Replace, how would you go about it if in some
places in the novel, sun is misspelled as sin, son, or sbn?
Figure 5.1 compares searching for a fixed literal value (sun) with finding a regex
pattern (s.n) against a target string value: sun soon son.
As shown in figure 5.1, the literal search string sun finds one match, starting at
position 0, in the target string sun soon son. Unlike the unmatched values, I’ve highlighted the matching value with a dark background. On the other hand, the regex
pattern s.n (the dot is a metacharacter that can match any character) finds two

Licensed to Mark Watson 

350

CHAPTER 5 String processing

s

u

n

0

1

2

s

u

n

0

1

2

3

3

Literal match

Regex match

Target string

Target string

s

o

o

n

4

5

6

7

s
8

o

n

s

u

n

9 10 11

0

1

2

3

s

o

o

n

4

5

6

7

Search string

Search string

sun

s.n

Match result

Match result

s

o

o

n

4

5

6

7

s
8

o

n

s

u

n

9 10 11

0

1

2

3

s

o

o

n

4

5

6

7

s
8

n

9 10 11

s
8

o

o

n

9 10 11

Figure 5.1 Comparing the matching of literal values with finding regex patterns

matches: sun and son, starting at positions 0 and 9 in the target search string sun
soon son.

Similarly, you can use regex to find text that matches a pattern and to perform
operations such as these:
■

■
■

Check method arguments—Determine whether a string value starts with Sh and ends
with either y or a. Determine whether author@manning is a valid email address.
Validate user input—Verify whether 765-981-abc is a correct phone number.
Search usernames in a text file—Find the ones that are exactly 10 characters long,
starting with a letter A and followed by 4 digits and 5 letters.

All of these are common examples of needing regular expressions to describe your target data and find it in a stream (a string, a file, a network connection).
NOTE After you’ve found your target data, you can manipulate it any
way you like: replace it, print it to a file, alert a user about invalid data,
and so forth.

As I move on with this chapter, I’ll show you how to use metacharacters, character
classes, and quantifiers in regex. You’ll see how to use them in code to search for
matching data and manipulate it.
Before I take a plunge into this topic, let me reiterate that regular expressions is a
relatively big topic, and this book limits its coverage to the exam topics. Though I
can’t guarantee that you’ll start writing amazing regex after reading this chapter,

Licensed to Mark Watson 

Regular expressions

351

you’ll definitely be comfortable using them, and of course, ready to answer the relevant exam questions.
Let’s see what a regular expression is and how to evolve one.

5.1.1

What is a regular expression?
The example in the previous section showed you that exact matches might not be able
to find all your matching data. In such cases, you need to define patterns of data (for
example, s.n) that can match your target data. You can define these patterns by using
regular expressions. Regular expressions come with a syntax (which we’ll cover in the
next few pages). With that syntax you can create a pattern to describe target search data.
What’s the difference between describing data and specifying it? When you
describe data, you detail out its attributes or characteristics. When you specify data,
you state the exact data. In the example shown in figure 5.1, the regex s.n describes
the data as follows:
■
■
■

The first character must be s.
Allow any character at the second position.
The third character must be n.
Regular expressions is also referred to as a language because it has its
own syntax. Regex refers to both the language and the data patterns that
it defines.

NOTE

Let’s work with some examples so that this definition makes more sense. First up,
character classes.

5.1.2

Character classes
Character classes aren’t classes defined in the Java API. The term refers to a set of characters that you can enclose within square brackets ([]). When used in a regex pattern,
Java looks for exactly one of the specified characters (not words).
Referring to our example of the novel, imagine that you want to search for all
occurrences of the phrase organized an event. But organized is also written as organised
(in the United Kingdom). Instead of searching your manuscript twice—first for organized and then for organised—you can use the character class [sz] in the search
string organi[sz]ed. [sz] would match either s or z so you can find both organized
and organised.
Let’s work with another example, one you might see on the exam. Figure 5.2 shows
how character class [fdn] is used to find an exact match of f, d, or n. With a target
string I am fine to dine at nine, the regex [fdn]ine matches the words fine, dine, and
nine, at positions 5, 13, and 21.
Let’s see how you can use Java classes Pattern and Matcher (covered in detail later
in the chapter) from the java.util.regex package to work with searching the text
shown in the preceding example.

Licensed to Mark Watson 

352

CHAPTER 5 String processing

Target string
I
0

1

a

m

2

3

4

f

i

n

e

5

6

7

8

t

o

d

i

n

e

a

t

n

i

n

e

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Regex
Match “f” or “d” or “n”
followed by “ine”

[fdn]ine

Match result
I
0

1

a

m

2

3

4

f

i

n

e

5

6

7

8

t

o

d

i

n

e

a

t

n

i

n

e

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Figure 5.2 Character class [fdn] matches exactly one occurrence of f, d, or n.

Listing 5.1 Simple code to work with regex using Pattern and Matcher
import java.util.regex.*;
class UseRegex{
public static void main(String[] args) {
Regex
String targetString = "I am fine to dine at nine";
pattern
String regex = "[fdn]ine";

c

Matcher
created from
pattern
specifying
target string

e

b

Target string to
be searched

d

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(targetString);
while (matcher.find()) {
System.out.println(matcher.group() + " starts at " +
matcher.start() + ", ends at " +
matcher.end());
}

Instantiate Pattern
using factory
method compile().

f

while matches
found

g

Prints matching
text, start and
end position.

}
}

Building a string by using the concatenation operators ( + and +=)
isn’t a recommended practice. Later in this chapter, you’ll see how to use
formatting classes and parameters such as %s and %d to include and format variable values in String literal values.

NOTE

Here’s the output of the preceding code:
fine starts at 5, ends at 9
dine starts at 13, ends at 17
nine starts at 21, ends at 25

The code at B defines the target string to be searched. The code at c defines the
regex pattern. The code at d, class Pattern, a compiled representation of a regex,

Licensed to Mark Watson 

353

Regular expressions

is instantiated. Because this class doesn’t define any public constructor, you must
instantiate it by using its factory method compile(). Method compile() compiles a
regular expression into a Pattern object. At e, class Matcher is instantiated by
calling method matcher() on the Pattern instance. Matcher will match the given
input against this pattern. Class Matcher is an engine that performs match operations on a character sequence by interpreting a regex pattern. It also doesn’t define
a public constructor.
Method find() of class Matcher returns true as long as it can find more
matches of a regex in a target string. At f, the while loop executes for all matches
found. The code at g uses methods group(), start(), and end() to extract the
matched string, its start position in the target string, and its end position in the target string.
Compare the values returned by matcher.start() and matcher.end() in the output shown for the preceding example and in figure 5.2. The substring fine occupies
positions 5, 6, 7, and 8 in the target string I am fine to dine at nine. But matcher.end()
returns the value 9. Beware of this on the exam. It can be combined in a tricky manner with other methods like String’s method substring().
If a matched string occupies index positions 1, 2, and 3 in a
target string, method end() of class Matcher returns the value 4 for the
corresponding call on end(). You can expect trick questions on this
returned value on the exam.
EXAM TIP

Table 5.1 list examples of simple character classes that you can use to create regex patterns and find them in target data.
Table 5.1 Examples of regex patterns that use simple character classes
Class type

Regex pattern

Description

Simple

[agfd]

Match exactly one from a, g, f, or d

Range

[a-f0-7]

Match exactly one from the range a to f (both inclusive) or 0
to 7 (both inclusive)

Negation

[^123k-m]

Match exactly one character that is not 1, 2, or 3 or from the
range k to m (both inclusive)

EXAM TIP If the Java Runtime engine determines that a pattern is
invalid, it throws the runtime exception PatternSyntaxException. On
the exam, when you see a question on the possible output of a string processing code, examine the regex pattern for invalid values.

Licensed to Mark Watson 

354

5.1.3

CHAPTER 5 String processing

Predefined character classes
Java’s regex engine supports predefined character classes for your convenience.
Table 5.2 lists the predefined classes included on this exam. You can test these regex
using the class UseRegex included in listing 5.1.
To use a regex pattern that includes a backslash (\), you must
escape the \ in the pattern by preceding it with another \. The character
literal \ has a special meaning; it’s used as an escape character. To use it
as a literal, it must be escaped.

NOTE

Table 5.2 Predefined character classes on this exam
Character class

Description

.

Any character (may or may not match line terminators)

\d

A digit: [0-9]

\D

A nondigit: [^0-9]

\s

A whitespace character: [ space, \t (tab), \n (new line), \x0B (end of line), \f
(form feed), \r (carriage) ]

\S

A nonwhitespace character: [^\s]

\w

A word character: [a-zA-Z_0-9]

\W

A nonword character: [^\w]

The dot (.) is a metacharacter that matches any character. Metacharacters are special
characters, which have special meanings. The search regex pattern 1.3 is not used to
find 1.3 in the target string. It’ll find the digit 1 followed by any character, followed by
the digit 3. For example, it’ll also find all of these: 123, 1M3, 193, 1)3, 1.3, 1,3, and 1+3.
Table 5.3 lists example target strings, regex patterns, and the result of finding a
regex pattern in the target string. Figure 5.3, a pictorial representation of table 5.3,
shows a target string, the regex pattern that is applied to the target string, and the
results. The regex pattern that could be matched in the target string is highlighted
Table 5.3 Examples of target strings, regex patterns that use predefined character classes, and their
matching results.
Target string

Regex

Match found

Start and end positions of match found

A5C7M%

\d

Yes

5 starts at 1, ends at 2
7 starts at 3, ends at 4

A5C7M%

\D

Yes

A starts at 0, ends at 1
C starts at 2, ends at 3
M starts at 4, ends at 5
% starts at 5, ends at 6

Licensed to Mark Watson 

355

Regular expressions

Table 5.3 Examples of target strings, regex patterns that use predefined character classes, and their
matching results.
Target string

Regex

Match found

Start and end positions of match found

890

\s

Yes

(First space) starts at 1, ends at 2
(Second space) starts at 3, ends at 4
(Third space) starts at 4, ends at 5

A B $890

\S

Yes

A starts at 0, ends at 1
B starts at 2, ends at 3
$ starts at 4, ends at 5
8 starts at 5, ends at 6
9 starts at 6, ends at 7
0 starts at 7, ends at 8

A b$9;

\w

Yes

A starts at 0, ends at 1
b starts at 2, ends at 3
9 starts at 4, ends at 5

A b$9;

\W

Yes

(Space) starts at 1, ends at 2
$ starts at 3, ends at 4
; starts at 5, ends at 6

A B

Target string

Regex

Result

\d
A

5

C

7

M

%

A

5

C

7

M

%

0

1

2

3

4

5

0

1

2

3

4

5

A

5

C

7

M

%

A

5

C

7

M

%

0

1

2

3

4

5

0

1

2

3

4

5

8

9

0

A

4

5

6

7

0

$

8

9

0

A

4

5

6

7

0

b

$

9

;

A

2

3

4

5

0

b

$

9

;

A

2

3

4

5

0

\D

\s
A
0

B
1

2

3

B
1

2

3

8

9

0

4

5

6

7

$

8

9

0

6

7

\S
A
0

B
1

2

3

B
1

2

3

4

5

b

$

9

;

2

3

4

5

b

$

9

;

2

3

4

5

\w
A
0

1

1

\W
A
0

1

1

Figure 5.3 Pictorial representation of target strings, regex pattern applied to them, and the
matches found, including matching positions

Licensed to Mark Watson 

356

CHAPTER 5 String processing

with a colored background. The starting position numbers of the matched string are
marked with an up arrow.
Let’s code an example that uses a predefined character class and replace all the
matching occurrences with a literal string:

Regex
to be
searched

class UsePredefinedCharacterClass{
public static void main(String[] args) {
String targetString = "A b$9;";
String regex = "\\W";

Target
search
string

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(targetString);
String replacedStr = matcher.replaceAll("[]");
System.out.println(replacedStr);
}
}

Prints
A[]b[]9[].

Instantiates Pattern
and compiles regex
pattern.
Creates a matcher that
will match given input
against this pattern.
Replaces all
matches found
with literal [].

The preceding code uses the illustration in figure 5.3. It uses Matcher’s replaceAll()
to replace all matching regex patterns in the target string with a string literal.
Because String objects are immutable, calling replaceAll()
won’t change the contents of String referred to by the variable targetString in the preceding code example. replaceAll() creates and returns
a new String object with the replaced values. Watch out for questions
based on it on the exam.
EXAM TIP

5.1.4

Matching boundaries
Say you want to find all occurrences of the word the in your book. To do that, you’d need
to search for the text the. Well, the same is true when you want to match the, which can
be part of another word, for example, their, leather, or seethe. So you need a way to limit
your searches to the start or end of a word. Matching boundaries can help you with this.
You can match boundaries including the start of a line, a word, a nonword, or the end of
a line by using regex patterns. Table 5.4 lists the boundary constructs that you’re likely
to see on the exam. Even though the boundary constructs ^ (beginning of line) and $
(end of line) aren’t explicitly included in the exam objectives, you might see them in
answer options that are incorrect. To ward off any confusion, I’ve included them in this
section. These constructs also might be helpful in your projects at work.
Table 5.4 Boundary constructs on this exam
Boundary construct

Description

\b

A word boundary

\B

A nonword boundary

^

Beginning of a line

$

End of a line

Licensed to Mark Watson 

357

Regular expressions
Target string
t h e

l e a t h e r

i n

t h e i r

c o a t

m a d e

h e r

s e e t h e

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

Regex
\Bthe

Match word boundary
followed by “the”

Matching result
t h e

l e a t h e r

i n

t h e i r

c o a t

m a d e

h e r

s e e t h e

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

Figure 5.4 Matching regex pattern \Bthe against the string value the leather in their coat
made her seethe. \B is a word boundary. When placed before the text the, it limits searches to
finding words that start with the.

Let’s see what happens when you match the regex pattern \bthe against the literal
string value the leather in their code made her seethe, as shown in figure 5.4.
As shown in figure 5.4, \bthe matches words that start with the, including the and
their. The first match is found at position 0, and the second match is found at position 15, in their. What if you want to find words that include the but don’t start with the?
Let’s modify the regex pattern \bthe used in the preceding example to \Bthe;
instead of matching words that start with the, you’ll match words that don’t start with
the. Figure 5.5 shows the matching values.
The regex pattern \Bthe matches all occurrences of the that aren’t at the beginning of a word. For the match found at position 7 in the word leather, it doesn’t matter
whether the is followed by any other character or a word boundary. What do you think
will be the output of matching the regex patterns ^the and the$ against the literal
string value the leather in their coat made her seethe? Try it out using a simple
code snippet.
Target string
t h e

l e a t h e r

i n

t h e i r

c o a t

m a d e

h e r

s e e t h e

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

Regex
\Bthe

Match a
nonword boundary
followed by “the”

Matching result
t h e

l e a t h e r

i n

t h e i r

c o a t

m a d e

h e r

s e e t h e

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

Figure 5.5 Matching regex pattern \Bthe against the string value the leather in their coat
made her seethe. \B is a nonword boundary. When placed before the text the, it limits its searches
to finding words that don’t start with the.

Licensed to Mark Watson 

358

CHAPTER 5 String processing

It’s time to test your skills in determining matching values for a regex pattern, in the
first “Twist in the Tale” exercise.
Twist in the Tale 5.1

Consider the following string literal value:
String targetString = "The leather in their coat made her seethe";

Which of these options correctly defines a regex pattern for searching the literal
string for the at either the beginning or end of a word, but not in its middle?
a
b
c
d

String regex = "\\Bthe\\B";
String regex = "\\bthe\\B";
String regex = "\\Bthe\\b";
String regex = "\\bthe|the\\b";

It’s interesting to note that none of these regex options is invalid, and each produces
output. Can you also determine the matched the for all these options?

5.1.5

Quantifiers
Imagine you want to search for the word colour or color in your book. A layman would
search the book text first for colour and then again for color. A smart searching tool
could search for both strings using a single search string by using quantifiers in the
search string. You can specify the number of occurrences of a pattern to match in a
target value by using quantifiers. The coverage of quantifiers is limited to greedy
quantifiers on this exam. You could also use possessive or reluctant quantifiers. But
because they aren’t on the exam, I won’t cover them any further. Table 5.5 describes
the greedy quantifiers.
Table 5.5 Greedy quantifiers
Quantifier (Greedy)

Description

X?

Matching X, once or not at all

X*

Matching X, zero or more times

X+

Matching X, one or more times

The greedy quantifiers are so named because they make the matcher read the complete input string before starting to get the first match. If the matcher can’t match the
entire input string, it backs off the input string by one character and attempts again. If
repeats this until a match is found or until no more characters are left. At the end,
depending on whether you asked it to match zero, one, or more occurrences, it’ll try
to match the pattern against zero, one, or more characters from the input string.

Licensed to Mark Watson 

359

Regular expressions

USING ? TO MATCH ZERO OR ONE OCCURRENCE
■

■
■
■

■
■
■

■
■

In all the preceding examples, we tried to search for exactly one occurrence of
a particular digit or character. What if you want to match zero or one occurrence of a letter? For example, color in the United States is spelled colour in the
United Kingdom. How will you match all occurrences of colour or color in a text
file? The metacharacter ? can help: Search string—I am colour in UK and color
in US
Regex—colou?r
Searches for colour or color
In the preceding example, you apply ? to a single letter. You can also apply ? to
a group of characters. How would you search for occurrences of August and Aug
in a text? To do so, you can group the required characters by using parentheses
and place ? right after them: Search string—It can be written as August or Aug
Regex—Aug(ust)?
Searches for August or Aug
Imagine you need to search for the words ball, mall, fall, and all by using a regular expression in the target text: Search string—A ball can fall in a mall with all
Regex—[bmf]?all
Searches for ball, mall, fall, or all

In this example, because ? is applied to the character class [bmf], it can be used to
search for a single occurrence of either b, m, f, or none of these. Note that [bmf] without ? wouldn’t match the text all.
In the sections to follow, I’ll cover working with a combination of ?, square brackets, and curly brackets. The set of combinations and permutations that can be used
with a language feature makes it interesting, and at the same time overwhelming! The
key to conquer such features is to understand a simpler concept before moving on to
the next level.
ZERO LENGTH MATCHES WITH ?

Let’s try to match the regex d? against the target string bday. Following are the target
strings and the corresponding code with its output:
■
■
■

Search string—bday
Regex—d?
Searches for zero or one occurrence of letter d

Following is the relevant code:
class UseQuantifier{
public static void main(String[] args) {
String targetString = "bday";
Instantiates
String regex = "d?";

pattern,
compiles
regex.

Target string to
be searched
Regex with
quantifier

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(targetString);

Licensed to Mark Watson 

Creates a
matcher that
will match
given input
against this
pattern.

360

CHAPTER 5 String processing
while (matcher.find()) {
System.out.printf("Found :%s: starts at %d, ends at %d",
matcher.group(),
matcher.start(),
matcher.end());
System.out.println();
}
}
}

You can use the preceding code (class UseQuantifier) to match a
regex pattern against a string value, listing the start and end positions of
the matches found.

NOTE

Here’s the output of this code:
Found
Found
Found
Found
Found

:: starts at 0, ends at 0
:d: starts at 1, ends at 2
:: starts at 2, ends at 2
:: starts at 3, ends at 3
:: starts at 4, ends at 4

Does this output make you wonder why five matches are found? Remember that ? will
match zero or one occurrence of the letter d. The regex engine found zero matches,
with length 0, at positions 0, 2, 3, and 4. It found one match (with length 1) at position 1. Figure 5.6 illustrates the found matches.

Target string

Regex

Result

d?
b
0

d
1

a
2

y
3

b
4

0

0 Match

d
1

1 Match

a
2

y
3

4

Figure 5.6 Matching regex
pattern d? against the string
value bday

USING * TO MATCH ZERO OR MORE OCCURRENCES
■

■
■
■

You can use the metacharacter * to match zero or more occurrences of a regex.
The regex fo*d will match all occurrences of words in which o occurs zero or
more times: Search string—food, fod, fooodder, fd
Regex—fo*d
Matches food, fod, foood, fd
You can also apply * to a group of characters. How would you search for text
that starts with a letter, ends with a letter, and might contain zero or more digits
in between? Let’s create the regex pattern to search for this pattern. The letters
could be either in lowercase or uppercase. You know that \d or [0-9] can be

Licensed to Mark Watson 

Regular expressions

■
■

361

used to match any digit and that [A-Za-z] can be used to match any letter in
lowercase or uppercase. Because the digit can appear zero or more times, we
evolve the regex as follows: Search string—b234a A6Z abc
Regex—[A-Za-z]\d*[A-Za-z]
Searches for text starting and ending with a letter and containing zero or more
digits in between

The regex [A-Za-z]\d*[A-Za-z] matches b234a, A6Z, and ab in the preceding search
string. It matches ab because \d* asked it to look for zero or more occurrences of digits. Because ab has zero digits between a and b, it’s matched. It won’t match bc because
b was already consumed for matching ab.
■

■
■

Now, what if you need to modify the preceding example, to limit the digits that
appear between the letters to a range of 1 to 5? This is simple: just replace \d*
with [1-5]. Examine the following: Search string—b234a A6Z abc
Regex—[A-Za-z][1-5]*[A-Za-z]
Searches for text starting and ending with a letter, and containing zero or more
digits in the range 1–5 in between

The preceding regex [A-Za-z][1-5]*[A-Za-z] matches only b234a and ab in the target string b234a A6Z abc. It doesn’t match A6Z because 6 isn’t in the range 1–5.
ZERO LENGTH MATCHES WITH *

Since * matches zero or more occurrences of a pattern, it also occurs in zero length
matches, that is, a match wherein the specified pattern can’t be found. Revisit the previous subsection, “Zero length matches with ?”. If you replace the pattern d? with d*,
the code will output the same result because * also matches zero occurrences of a pattern, like the metacharacter ?.
USING + TO MATCH ONE OR MORE OCCURRENCES
■
You can use the metacharacter + to match one or more occurrences of a regex.
For example, the regex fo+d will match all occurrences of words, where o
■
■

occurs one or more times: Search string—food, fod, fooodder, fd
Regex—fo+d
Matches food, fod, foood

You can also apply + to a group of characters. How would you search for text that starts
with a letter and ends with a letter and may contain one or more digits in between? We
know that \d can be used to match any digit and that [A-Za-z] can be used to match
any letter in lowercase or uppercase. Because the digit can appear one or more times,
you can evolve the regex as follows:
■
■
■

■

Search string—b234a A6Z abc
Regex —[A-Za-z]\d+[A-Za-z]
Searches for text starting and ending with a letter and containing one or more
digits in between
Matches b234a A6Z

Licensed to Mark Watson 

362

CHAPTER 5 String processing

The regex matches b234a and A6Z in the preceding search string. It doesn’t match ab
because \d+ asks it to look for one or more digits. Because ab has zero digits between
a and b, it isn’t matched!
Now, what if we need to modify the preceding example to limit the digits that
appear between the letters in the range of 1 to 5? This is simple: just replace \d+ with
[1-5]+. Examine the following:
■
■
■

■

Search string—b234a A6Z abc
Regex—[A-Za-z][1-5]+[A-Za-z]
Searches for text starting and ending with a letter and containing one or more
digits in the range of 1–5 in between
Matches b234a

This regex matches only b234a in the search string. It doesn’t match A6Z because 6
isn’t in the range 1–5. It doesn’t match ab because it doesn’t have a digit in the range
of 1–5 between the letters a and b. Since + looks for one or more occurrences of a pattern, it doesn’t qualify for zero length matches.
NOTE For this exam, you should know how to use the ?, *, and + metacharacters in regex. Metacharacters have a different meaning for the
regex engine.

If you don’t remember how many instances *, +, and ? match, table 5.6 lists a silly but
simple set of questions and answers that might help you remember.
Table 5.6 Questions and answers to help remember the number of matches for *, +, and ?

5.1.6

Metacharacter

Occurrence

Funny question

Silly answer to funny question

*

0 or many

How many stars can you see?

0 in a cloudy sky, many in a
clear sky

?

0 or 1

What can be the answer to one of
the most important questions: Do
you love me?

Yes (1) or no (0)

+

1 or more

How many spouses can you add
in your life?

1 or more

Java’s regex support
Java incorporated regex support by defining the java.util.regex package in version 1.4. Regex in Java supports Unicode as it matches against CharSequence objects.
This package defines classes for matching character sequences against the patterns
specified by regular expressions. For this, you particularly need the Matcher and
Pattern classes.
I’ve used these classes in the coding examples in the previous sections. Class
Pattern is a compiled representation of a regular expression. It doesn’t define a

Licensed to Mark Watson 

Regular expressions

363

public constructor. You can instantiate this class by using its factory method compile().
Here’s an example:
Pattern pattern = Pattern.compile("a*b");

After you create a Pattern object, you must instantiate a Matcher object, which can be
used to find matching patterns in a target string. Class Matcher is referred to as an
engine that scans a target CharSequence for a matching regex pattern. Class Matcher
doesn’t define a public constructor. You can create and access a Matcher object by calling the instance method matcher() on an object of class Pattern:
Matcher m = p.matcher("aaaaab");

After you have access to the Matcher object, you can do the following:
■
■
■
■

Match a complete input sequence against a pattern.
Match the input sequence starting at the beginning.
Find multiple occurrences of the matching pattern.
Retrieve information about the matching groups.
Class Matcher is an engine that interprets a Pattern and matches
it against a character sequence.

NOTE

With the addition of the java.util.regex package, Java also added
methods, like matches(), to existing classes, like String, which matched
string values with the given regular expression. However, behind the scenes,
String.matches() calls method matches() defined in class Pattern. At
times, such methods might also manipulate the values themselves, before
using classes Pattern/ Matcher, to support regex.
In the next section, I’ll continue working with more examples of creating and using
regex patterns, using classes String, StringBuilder, Scanner, and StringTokenizer.
Class String defines multiple methods to search and replace string values based on
exact and regex patterns. But class StringBuilder doesn’t support search or
replace methods based on regex. You’ll see how you can use Scanner and StringTokenizer to parse and tokenize streams (such as text in a file) by using exact text
or regex patterns.
NOTE The use of StringTokenizer is discouraged in new code. This legacy class is retained for backward compatibility. Use classes from the
java.util.regex package or String.split() to get the functionality of
StringTokenizer.

Licensed to Mark Watson 

364

5.2

CHAPTER 5 String processing

Searching, parsing, and building strings
When did you last search the internet for your favorite music, the latest news, or stock
prices? Most people do that every day, every hour. Apart from searching the internet,
people also search printed hardcopies. Searching text, and tokenizing and parsing it,
are important and integral tasks to complete a lot of other tasks. Java includes multiple classes like Scanner, StringTokenizer, StringBuilder, String, and Formatter to
accomplish searching, parsing, and building strings. Let’s get started with how to
search for exact matches and regex patterns.
[5.1] Search, parse, and build strings (including Scanner, StringTokenizer,
StringBuilder, String, and Formatter)

[5.2] Search, parse, and replace strings by using regular expressions,
using expression patterns for matching limited to . (dot), * (star),
+ (plus), ?, \d, \D, \s, \S, \w, \W, \b, \B, [], ()

5.2.1

Searching strings
Class String defines multiple methods to search strings for exact matches of a single
character or string. These methods allow searching from the beginning of a string, or
starting or ending at a specified position.
METHODS INDEXOF() AND LASTINDEXOF()
Both methods indexOf() and lastIndexOf() find a matching character or string in
a string and return the matching position. Method indexOf() returns the first match-

ing position of a character or string, starting from the specified position of the string,
or from its beginning. Method lastIndexOf() returns the last matching position of
a character in the entire string, or its subset (position 0 to the specified position).
Figure 5.7 shows a pictorial representation of a string, use of these methods, and the
positions of the matches found.
p

a

i

n

t

0

1

2

3

4

5

t

h

e

6

7

8

9

c

u

p

10

11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

indexOf("the")

a

n

d

t

h

e

l

a

t

e

lastIndexOf("the")

indexOf('t')
lastIndexOf('t', 15)

p

lastIndexOf('t')
indexOf('t', 15)

Figure 5.7 Showing use of methods indexOf() and lastIndexOf()

Licensed to Mark Watson 

365

Searching, parsing, and building strings

Here’s the code that implements the methods shown in figure 5.7:
String sentence = "paint the cup and the plate";

Prints “4”

System.out.println(sentence.indexOf('t'));
System.out.println(sentence.lastIndexOf('t', 15));

Prints “6”
Prints “6”

System.out.println(sentence.indexOf("the"));
System.out.println(sentence.indexOf('t', 15));

Prints “18”
Prints “18”

System.out.println(sentence.lastIndexOf("the"));
System.out.println(sentence.lastIndexOf('t'));

Prints “25”

Both methods indexOf() and lastIndexOf() differ in the manner in which they
search a target string: indexOf() searches in increasing position numbers, and lastIndexOf() searches backward. Due to this difference, indexOf('a', -100) will search
the complete string, but lastIndexOf('a', -100) won’t. In a similar manner, because
lastIndexOf() searches backward, lastIndexOf('a', 100) will search this string, but
indexOf('a', 0) or indexOf('a', -100) won’t. This is shown in figure 5.8.
Here’s the code that implements the methods shown in figure 5.8:
String sentence = "paint the cup and the plate";
System.out.println(sentence.indexOf('a'));
System.out.println(sentence.indexOf('a', 0));
System.out.println(sentence.indexOf('a', -100));
System.out.println(sentence.indexOf('a', 100));

Search
forward

System.out.println(sentence.lastIndexOf('a'));
System.out.println(sentence.lastIndexOf('a', 0));
System.out.println(sentence.lastIndexOf('a', 100));
System.out.println(sentence.lastIndexOf('a', -100));

Search
backward

indexOf() searches
in direction

p

a

i

n

t

0

1

2

3

4

5

lastIndexOf() searches
in direction

t

h

e

6

7

8

9

indexOf('a')

u

p

a

n

d

t

h

e

p

l

a

t

e

10

11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

lastIndexOf('a')
lastIndexOf('a', 100)

indexOf('a', 0)
indexOf('a', –100)
indexOf('a', 100)

c

–1

lastIndexOf('a', 0)

–1

lastIndexOf('a', –100)

–1

Figure 5.8 Methods indexOf() and lastIndexOf() search a target string in different directions.

Licensed to Mark Watson 

366

CHAPTER 5 String processing

EXAM TIP Methods indexOf() and lastIndexOf() don’t throw a compilation error or runtime exception if the search position is negative
or greater than the length of the string. If no match is found, they
return -1.

METHOD CONTAINS()
Method contains() searches for exact matches in a string and returns true if a match
is found, false otherwise. Because contains() accepts a method parameter of type
CharSequence, you can pass to it both a String or a StringBuilder object:

String
object

String sentence = "paint the cup and the plate";
StringBuilder sb = new StringBuilder("the");
String str = "the";
System.out.println(sentence.contains(sb));
System.out.println(sentence.contains(str));

StringBuilder
object
Searches matching StringBuilder
value in target string; prints “true”.
Searches matching string value
in target string; prints “true”.

METHODS SUBSEQUENCE() AND SUBSTRING()
Both methods subSequence() (uppercase S) and substring() (no uppercase letter)

return a substring of the string. Here’s the signature of these methods:
Returns new character sequence
that’s subsequence of this sequence
CharSequence subSequence(int beginIndex, int endIndex)
String substring(int beginIndex)
String substring(int beginIndex, int endIndex)

Returns new string
that’s substring of
this string

Returns new string that’s
substring of this string

To remember the return types of methods subSequence() and
substring() on the exam, just remember that the names of these methods
can be used to determine their return type. Method subSequence()
returns CharSequence, and method substring() returns String.
EXAM TIP

Method subSequence() simply calls method substring(); it was added to class String
in Java 1.4 to support implementation of the interface CharSequence by class String.
Method substring() defines overloaded versions, which accept one or two int
method parameters to specify the start or the end positions. Method subSequence()
defines only one variant: the one that accepts two int method parameters for the start
and the end position. For the exam, you must remember that these methods don’t
include the character at the end position, as shown in figure 5.9.

Licensed to Mark Watson 

367

Searching, parsing, and building strings
subSequence (2, 7)

sentence

p

a

i

n

t

0

1

2

3

4

5

substring (20)

t

h

e

6

7

8

9

c

u

p

10

11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

sentence.substring(–1, 5);
sentence.subSequence(10, 30);

a

n

d

t

h

e

p

l

a

t

e

StringIndexOutOfBoundsException

Figure 5.9 Methods subSequence() and substring() don’t include the character at the last
position in their return value.

EXAM TIP Methods subSequence() and substring() don’t include the
character at the end position in the result string. Also, unlike methods
indexOf() and lastIndexOf(), they throw the runtime exception StringIndexOutOfBoundsException for invalid start and end positions. The subtraction value from endIndex – beginIndex is the number of chars these
methods will return.

METHOD SPLIT()
Method split(String regex) and method split(String regex, int limit) in class
String search for a matching regex pattern and split a string into an array of string
values. Figure 5.10 shows how the string paint-the-cup-cop-and-cap is split with the
regex pattern c.p. As discussed in the previous section on regex, the dot in regex c.p,

will match exactly one character.
Tokenizing is the process of splitting a string, based on a separator,
into tokens. A separator can be a character, text, or a regex. For example,
if string 1234;J Perry;94.75 is split using a semicolon as the separator,
the tokens that you'll get are 1234, J Perry, and 94.75.

NOTE

Matching string values are
not included in tokens

sentence
tokens

0
1

p

a

i

n

t

-

t

h

e

-

p

a

i

n

t

-

t

h

e

-

c

u

p

-

c

o

p

-

a

n

d

-

-

a

n

d

-

c

a

-

2

String[] tokens = sentence.split("c.p");

Figure 5.10 The String array returned by split() doesn’t contain the values that it matches to
split the target string.

Licensed to Mark Watson 

p

368

CHAPTER 5 String processing

sentence
tokens

p

a

i

n

t

-

t

h

e

-

p

a

i

n

t

-

t

h

e

-

c

u

p

-

c

o

p

-

a

n

d

-

c

a

p

-

c

o

p

-

a

n

d

-

c

a

p

String[] tokens = sentence.split("c.p", 2);

Return only
2 tokens

Figure 5.11

You can limit the maximum number of tokens returned by split().

You can limit the maximum number of tokens that you want to retrieve by using
split(String regex, int limit). Figure 5.11 shows how the target string paint-thecup-cop-and-cap is split with split("c.p", 2). Because the total number of tokens is
limited to two, the regex pattern c.p is matched only once. The remaining string after
the first match is stored as the second array value.
If limit is nonpositive, then the regex pattern will be applied as many times as
possible and the array tokens can have any length. If limit is passed 0, the regex
pattern will be applied as many times as possible, but tokens won’t include trailing
empty strings.

5.2.2

Replacing strings
Finding and replacing characters or text is a common requirement. You can use multiple methods to find and replace text or regex by using class String. As mentioned
previously, the methods that accept the interface CharSequence as a parameter can
accept arguments of all the implementing classes: String, StringBuffer, and StringBuilder. Table 5.7 lists the replacing methods defined in class String.
Table 5.7 Methods to replace string values, using exact matches and regex patterns
Method

Description

replace(char old, char new)

Returns a new string resulting from finding and replacing all
occurrences of old character with new character

replace(CharSequence old,
CharSequence new)

Returns a new string resulting from finding and replacing
each substring of this string that matches the old target
sequence with the specified new replacement sequence

replaceAll(String regex,
String replacement)

Replaces each substring of this string that matches the
given regular expression with the given replacement

replaceFirst(String regex,
String replacement)

Replaces the first substring of this string that matches the
given regular expression with the given replacement

Licensed to Mark Watson 

369

Searching, parsing, and building strings
str.substring (2, 5)

str

newStr

str.subSequence(3, 7)

R

E

N

T

-

T

E

N

T

R

E

N

T

-

T

E

N

T

0

1

2

3

4

5

6

7

8

0

1

2

3

4

5

6

7

8

R

E

T

-

T

E

T

E

N

T

newStr = str.replace(str.substring(2, 5), str.subSequence(3, 7));
Figure 5.12 An example of using replace() to create a new string (newStr) that replaces a
substring of str with another substring of str

METHOD REPLACE()

On the exam, you’re likely to see chained method invocation with String methods.
What happens when method replace() tries to replace a substring of a string with
another substring of the same string? Also, what happens if these string values that are
searched and replaced overlap? Here’s an example:
String str = "RENT-TENT";
String newString = str.replace(
str.substring(2, 5),
str.subSequence(str.indexOf("T"),
str.lastIndexOf('N')));
System.out.println(newString);

Figure 5.12 helps explain this code. To make the code simpler to show and understand, I’ve replaced str.indexOf("T") and str.lastIndexOf('N') with their return
values—that is, 3 and 7—which are passed as arguments to str.subSequence().
As shown in figure 5.12, method replace() creates and returns a new string,
newStr, by replacing the occurrence of the characters it finds at positions 2, 3, and 4
of str, with the characters it finds at positions 3, 4, 5, and 6 of str. The length of the
replacement string can be greater than, equal to, or smaller than the substring that
it replaces.
METHOD REPLACEALL()

This method searches for matching regex patterns in a string and replaces them with
the specified string value. An example follows:
String str = "cat cup copp";
String newString = str.replaceAll("c.p\\B", "()");
System.out.println(newString);

Target string
to be searched
Prints “cat
cup ()p”.

Licensed to Mark Watson 

Finds regex
pattern
c.p\B and
replaces it
with ().

370

CHAPTER 5 String processing

If no match in the target string is found, replaceAll() returns the contents of the
original string.
EXAM TIP Unlike replace(), replaceAll() doesn’t accept method parameters of type CharSequence. Watch out for the passing of objects of class
StringBuilder to replaceAll().

The combination of the overloaded methods replace(), replaceAll(), and replaceFirst() can be confusing on the exam. Take note of the method parameters that can
be passed to each of these methods. Let’s attempt our next “Twist in the Tale” exercise, which should help you get a better grasp of all these string replacement methods
and regex patterns.
Twist in the Tale 5.2

I’ve modified the code used in a previous example for this exercise. Execute this code
on your system and select the correct answer.
class ReplaceString2 {
public static void main(String[] args) {
String str = "cat cup copp";
String newString = str.replaceAll("c.p\\b", "()");
System.out.println(newString);
}
}
a
b
c
d

//line4

The code outputs cat () copp.
The code outputs cat cup ()p.
The code outputs cat cup copp.
If code marked with comment line 4 is replaced with the following code, it’ll
output cat () copp:
String newString = str.replaceFirst("c.p\\b", "()");

e

If code marked with comment line 4 is replaced with the following code, it’ll
output cat () ()p:
String newString = str.replace("c.p", "()");

f

If code marked with comment line 4 is replaced with the following code, it’ll
output cat cup copp:
String newString = str.replace(new StringBuilder("cat"), "()");

Licensed to Mark Watson 

371

Searching, parsing, and building strings

OTHER METHODS

For this exam, you must also know about other commonly used String methods that
you can use to compare string values, match a substring, and determine whether a
string starts or ends with a literal value, as listed in table 5.8.
Table 5.8 Methods for comparing string values
Method

Description

endsWith(String suffix)

Returns true if this string ends with the specified suffix

startsWith(String prefix)

Returns true if this string starts with the specified prefix

startsWith(String prefix,
int offset)

Returns true if the substring of this string beginning at the
specified index starts with the specified prefix

compareTo(String
anotherStr)

Compares this string with anotherStr lexicographically.
Returns a negative, zero, or positive value depending on
whether this string is less than, equal to, or greater than
anotherStr.

compareToIgnoreCase(String
anotherStr)

Compares this string with anotherStr lexicographically,
ignoring case differences. Returns a negative, zero, or positive
value depending on whether this string is less than, equal to,
or greater than anotherStr.

equals(Object object)

Returns true if the object being compared defines the same
sequence of characters

equalsIgnoreCase(String
anotherStr)

Compares this String to anotherStr, ignoring case considerations

You need to be careful with String class methods that accept integer values as index
positions to start or end their searches. Here’s an example of the values returned by
the overloaded method startsWith(), when you pass negative, zero, or positive values to it:
String str = "Start startup, time to start";
System.out.println(str.startsWith("Start"));
System.out.println(str.startsWith("Start", 0));

Prints
“true”

System.out.println(str.startsWith("Start", -1));
System.out.println(str.startsWith("Start", 1));

Prints
“false”

When comparing letters, is a greater than, smaller than, or equal to A? When comparing letters lexicographically, note that a letter in lowercase is greater than its uppercase. The following example outputs a positive value:
String a = "a";
String b = "A";
System.out.println(a.compareTo(b));
System.out.println(b.compareTo(a));

Outputs positive
number
Outputs negative
number

Licensed to Mark Watson 

372

CHAPTER 5 String processing

Lexicographically, a lowercase letter is greater than its equivalent uppercase letter. Method compareTo() returns a negative number
(not necessarily –1) if the String object on which it’s called is lexicographically smaller than the one it’s compared to.

EXAM TIP

Don’t let the simplicity of these methods take you for a ride. When chained, they can
become difficult to answer. What do you think is the output of the following code?
String str = "Start startup, time to start";
System.out.println(str.substring(0,1).compareTo(str.substring(6,7)));

Execute the preceding code on your system and find out the answer yourself.
The next section covers how to parse and tokenize String by using class Scanner.

5.2.3

Parsing and tokenizing strings with Scanner and StringTokenizer
Parsing is the process of analyzing a string to find tokens or items. For example, you
can parse the text 9187 to find the integer value 9187. As you know, the same number
can be stored as text or as an integer value.
SCANNER CLASS
Class Scanner can be used to parse and tokenize streams. (Streams are covered in
chapter 7.) Scanner is a simple text scanner, which can parse primitive types and
strings. Scanner tokenizes its input string by using a pattern, which can be a char-

acter, a string literal, or a regex. You can then use the resulting tokens, converting
them to different data types. Here’s a quick look at the constructors relevant for
this exam:

Scanner(File source)
Scanner(Readable source)
Scanner(String source)

Use File as source
to Scanner.
Use String as
source to Scanner.

FileReader,
BufferedReader
implement Readable.

Now that you know the sources available to Scanner, let’s work with an example of
tokenizing a String with the default delimiter:
Scanner scanner = new Scanner("The \tnew \nProgrammer exam");
while (scanner.hasNext())
System.out.println (scanner.next());

Here’s the output of this code:
The
new
Programmer
exam

Licensed to Mark Watson 

String contains
spaces, tab (\t),
and newline
character (\n).

373

Searching, parsing, and building strings

If no delimiter is specified, a pattern that matches whitespace is used by default for
a Scanner object. You can specify the required regex by calling its method useDelimiter() as follows:
Scanner scanner = new Scanner("The1new22Programmer exam6");
scanner.useDelimiter("[\\d]+");
Uses regex [\d}+
while (scanner.hasNext())
to tokenize text.
System.out.println(scanner.next());

Target string
with letters
and digits

Finds and returns
next token

The output of this code is as follows:
The
new
Programmer exam

What if you want to do the reverse in this example: print out the numbers 1, 22, and
6 and leave the characters? You just change the regex to be used as a delimiter,
as follows:
scanner.useDelimiter("[\\sA-Za-z]+");

Reads regex [\sA-Za-z]+ as a match for 1 or
more occurrences of whitespace, character,
or letter in uppercase or lowercase.

Method next() defined in class Scanner returns an object of type String. This class
defines multiple nextXXX() methods, where XXX refers to a primitive data type. Instead
of returning a string value, these methods return the value as the corresponding primitive type.
Scanner scanner = new Scanner ("Shreya,20,true");
scanner.useDelimiter(",");
System.out.println(scanner.next());
System.out.println(scanner.nextInt());
System.out.println(scanner.nextBoolean());

Retrieves next
string value

Retrieves
next int value

Retrieves next
Boolean value

The following example parses the target string by using the regex [\\sA-Za-z]+,
which is for one or more occurrences of a whitespace or a letter. The tokenized numbers are retrieved using nextInt():
Target string contains combination
of words, numbers, spaces
Scanner scanner = new Scanner ("1 2 4 The new 55 Programmer 44 exam");
scanner.useDelimiter("[\\sA-Za-z]+");
Delimiter is any combination
int total = 0;
of whitespace/letters.
while (scanner.hasNextInt())
total = total + scanner.nextInt();
nextInt returns
System.out.println(total);
int value.

Licensed to Mark Watson 

374

CHAPTER 5 String processing

The output of this code is 106 (1 + 2 + 4 + 55 + 44). For the exam, make note of the
multiple hasNext(), hasNextXxx(), next(), and nextXxx() methods. Methods hasNext() and hasNextXxx() only return true or false but don’t advance. Only methods next() and nextXxx() advance in the input. So you’ll have to be careful or you’ll
end up with a program which runs eternally, like this one:
Scanner scanner = new Scanner ("1 2 4 The new 55 Programmer 44 exam");
scanner.useDelimiter("[\\s]+");
int total = 0;
while (scanner.hasNext())
if (scanner.hasNextInt())
total = total + scanner.nextInt();
System.out.println(total);

Class Scanner also defines method findInLine(), which tries to match the specified
pattern with no regard to delimiters in the input. Here’s an example:
Target string contains ABC, double value,
multiple occurrences of letters, integer.

Find
regex that
matches
text.

Scanner scanner = new Scanner ("ABC 223.2343 Paul 10");
scanner.findInLine("(ABC)+[\\d]+\\.[\\d]+[A-za-z]+[\\d]+");
System.out.println(scanner.next());
System.out.println(scanner.nextDouble());
System.out.println(scanner.next());
System.out.println(scanner.nextInt());

Fourth token is
int value

First token is
string value
Second token is
double value
Third token is
string value

The output of this code is as follows:
ABC
223.2343
Paul
10

The decimal values (float and double) are Locale-specific (Locale
is covered in chapter 12). For decimal numbers, a Locale might use a
decimal comma or a decimal point.
NOTE

What happens when there’s a mismatch in the next token and the method used to
retrieve the data? Does it lead to a compilation error or a runtime exception? Let’s
uncover an important concept in our next “Twist in the Tale” exercise.
Twist in the Tale 5.3

The following code contains a mismatch in the type of token retrieved (a can be
stored as a character or string) and the method (nextInt) used to retrieve this data.
What’s the output of the following code?

Licensed to Mark Watson 

Searching, parsing, and building strings

375

import java.util.*;
public class MyScan {
public static void main(String[] args) {
String in = "1 a 10 . 100 1000";
Scanner s = new Scanner(in);
int accum = 0;
for(int x = 0; x < 4; x++) {
accum += s.nextInt();
}
System.out.println(accum);
}
}
a
b
c
d
e

The code prints 1111.
The code doesn’t compile.
The code throws java.util.InputMismatchException.
The code throws java.util.MismatchException.
The code throws java.util.ParsingException.

STRINGTOKENIZER CLASS
You can use the StringTokenizer class to break a string into tokens. To separate the

tokens you can specify the delimiter to be used either at the time of instantiating
StringTokenizer or on a per-token basis. In the absence of an explicit delimiter, a
whitespace is used.
StringTokenizer st = new StringTokenizer("start your startup");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}

The preceding code uses a whitespace as a delimiter and outputs:
start
your
startup

Methods hasMoreTokens() and hasMoreElements() in class StringTokenizer return
a boolean value indicating whether more tokens are available or not. Methods nextToken() and nextElement() return the next token. The return type of method
nextToken() is String and of method nextElement() is Object.
If the delimiter used to instantiate StringTokenizer is null, the constructor
doesn’t throw an exception. But trying to access the resultant tokens or invoking any
other method on the StringTokenizer instance results in a NullPointerException:
StringTokenizer st = new StringTokenizer("start your startup", null);
System.out.println(st.hasMoreElements());

Throws NullPointerException
because delimiter is null.

Licensed to Mark Watson 

376

CHAPTER 5 String processing

NOTE StringTokenizer is a legacy class that’s retained for compatibility
reasons. Oracle recommends use of method split() of class String or
classes from a regex package to get the same functionality.

Apart from searching, parsing, and manipulating strings, formatting strings is another
frequently used feature. Let’s format some string values.

5.3

Formatting strings
[5.3] Format strings using the formatting parameters %b, %c, %d, %f,
and %s in format strings
How often do you use the operator + to concatenate a String value with the value of
another variable (for example, "name:"+emp+"age:"+age)? Do you find this expression cumbersome to use? This section discusses formatting classes, methods, and
parameters that can replace the + operator for concatenating String and other variable values. You can also write the formatted text to OutputStream, File, or StringBuilder. Let’s start with identifying the classes that you can use to format strings.

5.3.1

Formatting classes
Class java.util.Formatter and I/O classes like PrintStream and PrintWriter define
methods to format strings (coverage of formatting classes is limited to exam topics).
You can use class Formatter to write formatted strings to a file, stream, or StringBuilder objects. Class Formatter is an interpreter for printf-style format strings. This
class provides support for layout justification and alignment; common formats for
numeric, string, and date/time data; and locale-specific output. This type of formatted printing is heavily inspired by C’s printf.
On the exam you’ll be queried on how to write formatted strings to the standard output. You can use class System to access the standard input, standard output, and error
output streams. The standard output in class System is made accessible using a static
variable, out, which is of type PrintStream (remember using System.out.println()).
Class PrintStream defines methods to output formatting strings.

5.3.2

Formatting methods
Class Formatter defines the overloaded method format()to write a formatted string
to its destination using the default or specified Locale (covered in chapter 12), format string, and arguments:
format(String format, Object... args)
format(Locale l,String format, Object... args)

Licensed to Mark Watson 

377

Formatting strings

To output formatted strings to the standard output, you can use class PrintStream
and its overloaded methods format() and printf(), which use the default or specified Locale, format strings, and arguments:
format(String
format(Locale
printf(String
printf(Locale

format, Object... args)
l,String format, Object... args)
format, Object... args)
l, String format, Object... args)

Behind the scenes, method printf() simply calls method format(). Also the number
of method arguments that these methods accept is the same. So you really need to
work with one method to format the strings.
System.out.println() can’t write formatted strings to the
standard output; System.out.format() and System.out.printf() can.

EXAM TIP

Here’s a quick example to write a formatted string to the standard output and to a file:
import java.util.Formatter;
import java.io.File;
class FormattedStrings {
public static void main(String args[]) throws Exception {
String name = "Shreya";

Destination is

file data.txt.
Formatter formatter = new Formatter(new File("data.txt"));
formatter.format("My name is %s", name);
Format
formatter.flush();
parameter %s.
System.out.printf("My name is %s", name);
}
}

Destination is
standard output.

In the preceding code, %s is replaced with the value of variable name. In the next section, let’s see how to define format strings, the formatting parameters (on the exam),
and their elements.
NOTE Because class Locale and Java I/O classes are covered in chapters
12 and 7, I won’t include other examples about these classes here.

5.3.3

Defining format strings
To use methods format() and printf(), you need to define a format string that
defines how to format text and an object argument list that defines what to format. You
can define a combination of fixed text and one or more embedded format specifiers,
to be passed to formatting methods. The format specifier takes the following form:
%[argument_index$][flags][width][.precision]conversion_char

Table 5.9 describes the format specifier elements.

Licensed to Mark Watson 

378

CHAPTER 5 String processing
Table 5.9

Format specifier elements and their purposes

Format specifier element

Optional/ required

What it means

argument_index

Optional

Decimal integer indicating the position of the argument in the argument list. The first argument is referenced by 1$, the second by 2$, and so forth.

flags

Optional

Set of characters that modify the output format. The
set of valid flags depends on the conversion.

width

Optional

A non-negative decimal integer indicating the minimum
number of characters to be written to the output.

precision

Optional

A non-negative decimal integer usually used to
restrict the number of characters. The specific behavior depends on the conversion.

conversion_char

Required

A character indicating how the argument should be
formatted. The set of valid conversions for a given
argument depends on the argument’s data type.

NOTE

The conversion characters on the exam are %b, %c, %d, %f ,and %s.

How to work with format specification
The general syntax is as follows:
%[arg_index$][flags][width][.precision]conversion_char
■

■
■

■

Compulsory element—A format specification must start with a % sign and end
with a conversion character.
Optional elements—arg-index, flags, width, and .precision are all optional.
Anything before a % and after the conversion character is printed as it is. For
example, printf("xxx%1$dyyy%2$dzzz", 10, 20) outputs xxx10yyy20zzz.
You need to remember the following flags:
- Left-justify this argument; must specify width as well.
+ Include a sign (+ or -) with this argument. Applicable only if conversion character is d or f (for numbers).
0 Pad this argument with zeros. Applicable only if conversion character is d or
f (for numbers). Must specify width as well.
, Use locale-specific grouping separators (for example, the comma in 123,456).
Applicable only if conversion character is d or f (for numbers).
( Enclose negative numbers in parentheses. Applicable only if conversion character is d or f (for numbers).

Let’s get started with how to use the formatting parameter %b.

Licensed to Mark Watson 

379

Formatting strings

5.3.4

Formatting parameter %b
If the target argument arg is null, then %b prints the result as false. If arg is boolean
or Boolean, the result is the String returned by String.valueOf(). Otherwise, the
result is true.

String name = "Shreya";
Integer age = null;
boolean isShort = false;

%b prints “true”
for non-null
values.

%b prints
“false” for
null values.

%b evaluates and
outputs boolean
value

System.out.format("Name %b, age %b, isShort %b", name, age, isShort);

The preceding code formats the text as follows:
Name true, age false, isShort false

Now, what happens if there’s a mismatch in the number of arguments passed to
method format() and the number of times %b appears in the format string? If the
number of arguments exceeds the required count, the extra variables are quietly
ignored by the compiler and JVM. In the following example, the extra variables in the
first line are ignored and it outputs Name true. But for the second line because the
number of required arguments falls short, the JVM throws java.util.MissingFormatArgumentException at runtime:
System.out.format("Name %b", name, age, isShort);
System.out.printf("Name %b, age %b", name);

Ignores extra
variables
Throws runtime
exception

If the count of formatting parameters is more than the arguments passed to methods format() or printf(), then java.util.MissingFormatArgumentException is thrown at runtime.
EXAM TIP

This format specifier accepts primitive and reference variables and, hence, you can
pass any type of argument to this format specifier. Examine the following examples,
which use different combinations of flags, width, and precision:
Flag leftjustifies the
text true.

System.out.format("\nName defined %10b.", name);
System.out.format("\nName defined %.1b.", name);
System.out.format("\nName defined %-10b.", name);

Minimum width specified as 10
adds 6 spaces before result true.
Precision 1 truncates
length of result to t.

Here’s the output of the preceding code:
Name defined
Name defined t.
Name defined true

EXAM TIP

true.
.

You can pass any type of primitive variable or object reference

to %b.

Licensed to Mark Watson 

380

5.3.5

Prints
“{“

CHAPTER 5 String processing

Formatting parameter %c
%c outputs the result as a Unicode character. Examine the following examples:
System.out.printf("\nChar %c", new Character('\u007b'));

Prints “(Om)” in
Devanagari script.
Throws runtime exception
if target can’t be converted
to Unicode character.

System.out.printf("\nChar %c", '\u6124');
System.out.printf("\nChar %c", new Boolean(true));

Values with invalid Unicode
values (\affff) won’t compile.

System.out.printf("\nChar %c", '\affff');

If the target can’t be converted to a Unicode character, a runtime exception is thrown:
Exception in thread "main" java.util.IllegalFormatConversionException: c !=
java.lang.Boolean

You can pass only literals and variables that can be converted
to a Unicode character (char, byte, short, int, Character, Byte, Short,
and Integer) to the %c specifier. Passing variables of type boolean, long,
float, Boolean, Long, Float, or any other class will throw IllegalFormatConversionException.
EXAM TIP

5.3.6

Formatting parameters %d and %f
Did you ever notice that the amount displayed in your bank statements is formatted in
a particular manner—say, for example, a maximum width of 20 digits, with exactly 2
digits after the decimal point, and grouped according to the locale-specific information? Let’s see how you can do this by formatting a float or double value, using the
format specifier %f. In the following examples, I’ve used square brackets in the results
displayed ([]) to help you determine how the numbers are formatted with padding
and left justification:
Prints
“[12.123450]”.

Outputs value with width 10, zero
padded; prints “[012.123450]”.

System.out.printf("[%f]", 12.12345);
System.out.printf("[%010f]", 12.12345);
System.out.printf("[%-10f]", 12.12345);
System.out.printf("[%10.2f]", 12.98765);
System.out.printf("[%,f]", 123456789.12345);

Value with width 10, left-justified;
prints “[12.123450 ]”.
Value with width 10, exactly
2 digits after decimal point;
prints “[ 12.99]”.

Locale-specific grouping using ‘,’;
prints “[123,456,789.123450]”.
NOTE Because the formatting of the numbers is specific to your default
locale, you might not see the same output as mentioned in the preceding code.

Licensed to Mark Watson 

381

Formatting strings

Though you can assign an int literal to a float or double variable (float f = 10 or
long d = 10), you can’t use int variables or literal values with %f. The following code
will throw an IllegalFormatConversionException runtime exception:
System.out.printf("[%,f]", 12345);

EXAM TIP By default, %f prints six digits after the decimal. It also rounds
off the last digit. You can pass literal values or variables of type float,
double, Float, and Double to the format specifier %f.

You can format the integers as follows:

Prints
“[12345]”.

System.out.printf("[%d]", 12345);
System.out.printf("[%010d]", 12345);
System.out.printf("%(,d", -123456789);
System.out.printf("[%-10.2d]", 12345);

Throws java.util.IllegalFormatPrecisionException
at runtime; can’t specify precision with integers.

Outputs value with width 10, zero
padded; prints “[0000012345]”.
Negative numbers enclosed
within parentheses; prints
“(-123,456,789)”.

You can pass literal values or variables of type byte, short,
int, long, Byte, Short, Integer, or Long to the %d format specifier. The
EXAM TIP

code throws runtime exceptions for all other types of values.
Also, the flags +, 0, (, and ,(comma) can be specified only with the numeric specifiers
%d and %f. If you try to use them with any other format specifier (%b, %s, or %c), you’ll
get a runtime exception.

5.3.7

Formatting parameter %s
%s is a general-purpose format specifier that can be applied to both primitive variables

and object references. For primitive variables, the value will be displayed; for object
references, method toString() of the object is called:
String name = "Harry";
Integer age = null;
String[] skills = {"Java", "Android"};
System.out.format("Name is %s, age is %s, skills are %s", name,
age,skills);

In the preceding code, format() sends the following to the standard output (the exact
integer value following @ will vary on your system):
Name is Harry, age is null, skills are [Ljava.lang.String;@1d9dc39

You can pass any type of primitive variable or object reference
to the format specifier %s.

EXAM TIP

Licensed to Mark Watson 

382

CHAPTER 5 String processing

It’s interesting to note how you can specify the argument_index to change the arguments used at a particular location. Let’s specify the argument index in the preceding
example to swap the variables name and age:
age = 40;
System.out.format("Name is %2$s, age is %1$s", name, age);

The output of the code is as follows:
Name is 40, age is Harry

EXAM TIP You can specify that the argument_index change the arguments used at a particular location.

5.4

Summary
String processing is a relatively big topic, so we started this chapter with an outline
of what to expect on the exam. The exam covers searching, parsing, replacing, and
formatting strings. It also covers formatting primitive data types and object references.
We covered the classes that you need to know and their respective methods to work
with this functionality. Classes String and StringBuilder are used to work with
strings, to modify, search, and replace their values. Class Scanner is used to tokenize
data and manipulate it.
When you search data, you can look for either exact matches or a pattern of data.
A regular expression (or regex) is a powerful language that can be used to define data
patterns to be searched for. I limited discussion of regex to the patterns included on
the exam. I covered Java’s regex support with the java.util.regex package. At the
end of the chapter, you learned how to control and define the format of your data
(primitive values and objects) by using class Formatter and its utility methods, in a
locale-specific or custom format.

REVIEW NOTES
This section lists the main points covered in this chapter.

Regular expressions
■

■

■

■

■

Regular expressions, or regex, are used to define patterns of data to be found in
a stream.
A regex has a syntax, which can be defined by using regular and special
characters.
As opposed to exact matches, you can use regex to search for data that matches
a pattern.
Character classes aren’t classes defined in the Java API. The term refers to a set
of characters that you can enclose within square brackets [].
Java supports predefined and custom character classes.

Licensed to Mark Watson 

Review notes
■

■

■

■

■

■

■

■

■

■

383

You create a custom character class by enclosing a set of characters within
square brackets []:
– [fdn] can be used to find an exact match of f, d, or n.
– [^fdn] can be used to find a character that doesn’t match either f, d, or n.
– [a-cA-c] can be used to find an exact match of either a, b, c, A, B, or C.
You can use these predefined character classes as follows:
– A dot matches any character (and may or may not match line terminators).
– \d matches any digit: [0-9].
– \D matches a nondigit: [^0-9].
– \s matches a whitespace character: [ (space), \t (tab), \n (new line), \x0B
(end of line), \f (form feed), \r (carriage)]
– \S matches a non-whitespace character: [^\s].
– \w matches a word character: [a-zA-Z_0-9].
– \W matches a nonword character: [^\w].
To use a regex pattern in Java code that includes a backslash, you must escape
the \ by preceding it with another \. The character literal \ has a special meaning; it’s used as an escape character. To use it as a literal, it must be escaped.
For the exam, you’ll need to know these boundary matchers:
– \b indicates a word boundary.
– \B indicates a nonword boundary.
– ^ indicates the beginning of a line.
– $ indicates the end of a line.
You can specify the number of occurrences of a pattern to match in a target
value by using quantifiers.
The coverage of quantifiers on this exam is limited to the following greedy
quantifiers:
– X? matches X, once or not at all.
– X* matches X, zero or more times.
– X+ matches X, one or more times.
– X{min,max} matches X, within the specified range.
Regex in Java supports Unicode, as it matches against the CharSequence
objects.
Class Pattern is a compiled representation of a regular expression. It doesn’t
define a public constructor. You can instantiate this class by using its factory
method compile().
Class Matcher is referred to as an engine that scans a target CharSequence for a
matching regex pattern. Class Matcher doesn’t define a public constructor. You
can create and access a Matcher object by calling the instance method
matcher() on an object of class Pattern.
When you have access to the Matcher object, you can match a complete input
sequence against a pattern, match the input sequence starting at the beginning,

Licensed to Mark Watson 

384

CHAPTER 5 String processing

find multiple occurrences of the matching pattern, or retrieve information
about the matching groups.

Search, parse, and build strings
You can search strings for exact matches of characters or strings, at the beginning of a
string, or starting at a specified position, using String class’s overloaded methods
indexOf (note the capital O).
■

■

■

■

■

■

■

■

■

■

■

■

Method indexOf() returns the first matching position of a character or string,
starting from the specified position of this string, or from its beginning.
Method lastIndexOf() returns the last matching position of a character in the
entire string, or its subset (position 0 to the specified position).
Methods indexOf() and lastIndexOf() differ in the manner that they search a
target string—indexOf() searches in increasing position numbers and lastIndexOf() searches backward. Due to this difference, indexOf('a', -100) will
search the complete string, but lastindexOf('a', -100) won’t. In a similar
manner, because lastIndexOf() searches backwards, lastIndexOf('a', 100)
will search the string, but indexOf('a', 0) or indexOf('a', -100) won’t.
Methods indexOf() and lastIndexOf() don’t throw a compilation error or
runtime exception if the search position is negative or greater than the length
of this string. If no match is found, they return –1.
Method contains() searches for an exact match in this string. Because
contains() accepts a method parameter of interface CharSequence, you can
pass to it both a String and a StringBuilder object.
Methods subSequence (uppercase S) and substring (no uppercase letter)
accept int parameters and return a substring of the target string.
Method substring() defines overloaded versions, which accept one or two int
method parameters to specify the start and end positions.
Method subSequence() defines only one variant, the one that accepts two int
method parameters for the start and end positions.
Methods subSequence() and substring() don’t include the character at the
end position in the result String. Also, unlike methods indexOf() and
lastIndexOf(), they throw the runtime exception StringIndexOutOfBoundsException for invalid start and end positions.
The name of methods subSequence() and substring() can be used to determine their return type. subSequence() returns CharSequence and substring()
returns String.
Methods split(String regex) and split(String regex, int limit) in class
String search for a matching regex pattern and split a String into an array of
string values.
The String array returned by split() doesn’t contain the values that it
matches to split the target string.

Licensed to Mark Watson 

Review notes
■

■

■

■

■

■

■

■

385

You can limit the maximum number of tokens that you want to retrieve by using
split(String regex, int limit).
replace(char oldChar, char newChar) returns a new String resulting from finding and replacing all occurrences of the old character with the new character.
replace(CharSequence oldVal, CharSequence newVal) returns a new String
resulting from finding and replacing each substring of the string that matches
the old target sequence with the specified new replacement sequence.
replaceAll(String regex, String replacement) replaces each substring of the
string that matches the given regular expression with the given replacement.
replaceFirst(String regex, String replacement) replaces the first substring of the string that matches the given regular expression with the given
replacement.
Unlike replace(), replaceAll() doesn’t accept method parameters of type
CharSequence. Watch out for the passing of objects of class StringBuilder to
replaceAll().
The combination of the replace, replaceAll, and replaceFirst overloaded
methods can be confusing on the exam. Take note of the method parameters
that can be passed to each of these methods.
Scanner can be used to parse and tokenize strings.
– If no delimiter is specified, a pattern that matches whitespace is used by
default for a Scanner object.
– You can specify a custom delimiter by calling its method useDelimiter() with
a regex.
– Method next() returns an object of type String.
– Scanner also defines multiple nextXXX methods, where XXX refers to a primitive data type. These methods return the value as the corresponding primitive type.
– Methods hasNext() and hasNextXxx() only return true or false but don’t
advance. Only methods next() and nextXxx() advance in the input.
– Method findInLine() matches the specified pattern with no regard to
delimiters in the input.

Formatting strings
■
■

■
■

■

Class java.util.Formatter is an interpreter for printf-style format strings.
A formatter provides support for layout justification and alignment; common
formats for numeric, string, and date/time data; and locale-specific output.
Formatter’s format() is used to format data.
To use format(), you need to define a format string that defines how to format
text and an object argument list that defines what to format.
You can define a combination of fixed text and one or more embedded format
specifiers, to be passed to the method’s format() first argument.

Licensed to Mark Watson 

386

CHAPTER 5 String processing
■

The format specifier takes the following form:
%[argument_index$][flags][width][.precision]conversion

■

■

■

■

■

■

■

■

A format specification must start with a % sign and end with a conversion character:
– b for boolean
– c for char
– d for int, byte, short, and long
– f for float and double
– s for String
If the number of arguments exceeds the required count, the extra variables are
quietly ignored by the compiler and JVM. But if the number of required arguments falls short, the JVM throws a runtime exception.
The - indicates to left-justify this argument; you must specify width as well. Number flags (only applicable for numbers, conversion chars d and f) are as follows:
– The + indicates to include a sign (+ or -) with this argument.
– 0 indicates to pad this argument with zeros. Must specify width as well.
– , indicates to use locale-specific grouping separators (for example, the
comma in 123,456).
– ( is used to enclose negative numbers in parentheses.
The flags +, 0, (, and , can be specified only with the numeric specifiers %d and
%f. If you try to use them with any other format specifier (%b, %s, or %c), you’ll
get a runtime exception.
Format specifier %b
– You can pass any type of primitive variable or object reference to specifier %b.
– If the target argument arg is null, then %b outputs the result as false. If
arg is boolean or Boolean, the result is the String returned by String
.valueOf(). Otherwise, the result is true.
Format specifier %c
– %c outputs the result as a Unicode character.
– You can pass only literals and variables that can be converted to a Unicode
character (char, byte, short, int, Character, Byte, Short, and Integer) to
the %c specifier. Passing variables of type boolean, long, float, Boolean, Long,
Float, or any other class will throw IllegalFormatConversionException.
Format specifier %f
– You can format decimal numbers (float, Float, double, and Double) by
using the format specifier %f.
– By default, %f prints six digits after the decimal. It also rounds off the last digit.
Format specifier %d
– You can format integers (byte, short, int, long, Byte, Short, Integer, Long)
by using the format specifier %d.
– If you pass literal values or variables of type float, double, Float, or Double
to the format specifier %d, the code will throw a runtime exception.

Licensed to Mark Watson 

Sample exam questions
■

Format specifier %s
– %s outputs the value for a primitive variable. For reference variables, it calls
toString() on objects that are not null and outputs null for null values.
– You can pass any type of primitive variable or object reference to specifier %s.

SAMPLE EXAM QUESTIONS
Q 5-1. What is the output of the following code?
class Format1 {
public static void main(String... args) {
double num1 = 7.12345678;
int num2 = (int)8.12345678;
System.out.printf("num1=%f, num2=%2d, %b", num1, num2, num2);
}
}
a
b
c
d
e
f
g
h

387

num1=7.123456, num2= 8, true
num1=7.123456, num2=8, true
num1=7.123457, num2= 8, true
num1=7.123457, num2=8 , true
num1=7.1234, num2=8, false
num1=7.1234, num2=8.1234, true

Compilation error
Runtime exception

Q 5-2. Given the following command line
java Regex1 \d\d 761cars8 5dogs-total846

what is the output of the following code?
class Regex1 {
public static void main(String[] args) {
Pattern pattern = Pattern.compile(args[0]);
Matcher matcher = pattern.matcher(args[1]);
boolean found = false;
while(found = matcher.find()) {
System.out.println(matcher.group());
}
}
}
a

76
61

b

76

Licensed to Mark Watson 

388

CHAPTER 5 String processing
c

76
61
84
46

d

76
84

e

No output

Q 5-3. Given the following variables, which options will throw exceptions at runtime?
String eJava = "Guru";
Integer start = 100;
boolean win = true;
Float duration = new Float(-1099.9999);
a
b
c
d
e
f

System.out.format("%d", eJava);
System.out.printf("%s", start);
System.out.printf("[%-12b]", win);
System.out.format("%s12", eJava);
System.out.format("%d", duration);
System.out.format("[%+,-(20f]", duration);

Q 5-4. What is the output of the following code?
Scanner scanner = new Scanner("ThemeXtheirXcarpet77");
scanner.useDelimiter("t.*e");
while (scanner.hasNext())
System.out.println (scanner.next());
a

ThemeX
the
t77

b

ThemeX
t77

c

The
the

d

t77

e

Compilation error
Runtime exception

f

Q 5-5. Which options will output the following code?
Hello true 123456
a

System.out.print("%s %b %d", new StringBuilder("Hello"), "false",
123456);

Licensed to Mark Watson 

Sample exam questions
b
c

d

e

f

389

System.out.printf("%s %b %d", new String("Hello"), "false", 123456);
System.out.format("%s %b %d", new StringBuilder("Hello"), "false",
123456);
System.out.println("%s %b %d", new StringBuilder("Hello"), "false",
123456.70);
System.out.printf("%s %b %d", new StringTokenizer("Hello"), "false",
123456);
System.out.format("%s %b %d", ("Hello"), "FALSE", new Integer(123456));

Q 5-6. What is the output of the following code?
class StringCompare {
public static void main(String[] args) {
String mgr1 = "Paul & Harry's new office";
StringBuilder emp = new StringBuilder("Harry");
System.out.println(mgr1.contains(emp));
}
}
a
b
c
d

true
false

Compilation error
Runtime exception
This question includes reading from a file that’s covered in chapter 7. In the exam it’s usual to see questions that are based on multiple
exam topics.

NOTE

Q 5-7. Given that the content of the file data.txt is
Harry;8765,Per[fect

which options are correct for the following code?
public class StrToken {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
String line;
StringTokenizer st;
while ((line = br.readLine()) != null) {
st = new StringTokenizer(line, "[,;]");
//line1
while (st.hasMoreElements())
System.out.println(st.nextElement());
}
br.close();
}
}

Licensed to Mark Watson 

390

CHAPTER 5 String processing

a

The code prints
Harry
8765
Per
fect

b

The code prints
Harry
8765
Per[fect

c

The code prints
Harry;8765
Per[fect

d

If the code on line //line1 is changed to the following, it’ll output the same
results:
st = new StringTokenizer(line, "[,;$]");

e
f

//line1

Code fails to compile.
Code throws a runtime exception.

Q 5-8. What is the output of the following code?
String eJava = "e Java Guru";
if(eJava.matches("u.u")){
String[] tokens = eJava.split("\\Bu");
for (String token : tokens) System.out.println(token);
}
else {
System.out.println(eJava.replace(
eJava.subSequence(3, 4), eJava.substring(11)));
}
a

b
c
d
e

f

e Java G
r
e Java Guru
e Jv Guru
e Juvu Guru
java.lang.StringIndexOutOfBoundsException:
range: 11

String

index

out

of

Code fails to compile.

Q 5-9. Which option, when used as a format specifier, will format the decimal literal
value 14562975.6543 to use at least the locale-specific grouping separator and include
a sign with it (+ or -)? (Choose all that apply.)
a
b

%+,3f
%,+20f

Licensed to Mark Watson 

Answers to sample exam questions
c
d
e
f
g

391

%+,f
%,+f
%()f
%-,f
%0.+f

Q 5-10. Given the following string, which code options use the correct regex to
replace the first letter of all words with A, leave the remaining string unchanged, and
print the replaced string value?
String target = "amita, matinda,shreya,mike, and anthony are arrogant";

f

System.out.println(target.subSequence("[\\b]\\w", "A"));
System.out.println(target.replace("\\b\\w", "A"));
System.out.println(target.replaceAll("\\s\\b\\w", "A"));
System.out.println(target.replace("\\s\\b\\w", "A"));
System.out.println(target.modify("\\b\\w", "A"));
System.out.println(target.replaceAll("\\b\\w", "A"));

g

None of the above

a
b
c
d
e

ANSWERS TO SAMPLE EXAM QUESTIONS
A 5-1. c
[5.3] Format strings using the formatting parameters: %b, %c, %d, %f, and %s in format strings
Explanation: By default, %f prints out six digits after the decimal number. It also
rounds off the last digit. So num1=%f outputs 7.123457 and not 7.123456.
Because the double literal 8.12345678 is explicitly casted to an int value, num2 contains the integer part of the double literal 8.12345678, that is, 8. %2d sets the total
width of the output to 2 digits, padded with spaces and right-aligned by default. It outputs a space preceding the digit 8.
For all non-Boolean primitive values, %b outputs true.
A 5-2. b
[5.2] Search, parse, and replace strings by using regular expressions, using expression
patterns for matching limited to: . (dot), * (star), + (plus), ?, \d, \D, \s, \S, \w, \W, \b,
\B, [], ()
Explanation: The last argument (5dogs-total846) is ignored when you use the following command line because a space precedes it.
java -ea Regex1 \d\d 761cars8 5dogs-total846

Licensed to Mark Watson 

392

CHAPTER 5 String processing

When you pass the regex by using the command-line arguments, you don’t need to
escape the backslashes. It’s required only for literal string values.
\d\d will match two adjacent digits in the literal 761cars8—that is, 76. It won’t
match 61 because the digit 6 was already consumed in finding 76. By default, Java’s
regex engine won’t use characters that have already been consumed.
A 5-3. a, e
[5.3] Format strings using the formatting parameters: %b, %c, %d, %f, and %s in format strings
Explanation: You’ll get runtime exceptions if you use either of the following for a format specifier:
■
■

An invalid data type
An invalid combination of flags

Options (a) and (e) throw runtime exceptions because they use the format specifier
%d, which must be used only with integer literal values or variables, and not with
String or decimal numbers.
Option (b) won’t throw any exception because you can pass any type of primitive
variable or object reference to the format specifier %s. Also, this option doesn’t use
any format flag that’s invalid to be used with %s.
Option (c) won’t throw any exceptions. You can specify the alignment and width
element -12 with %b.
Option (d) won’t throw any exceptions. The value 12 that follows %s is not part of the
width element, but a literal value following %s, so it’s a completely valid format string.
A 5-4. b
[5.1] Search, parse, and build strings (including Scanner, StringTokenizer, StringBuilder, String, and Formatter)
[5.2] Search, parse, and replace strings by using regular expressions, using expression
patterns for matching limited to: . (dot), * (star), + (plus), ?, \d, \D, \s, \S, \w, \W, \b,
\B, [], ().
Explanation: The code compiles successfully and throws no exceptions at runtime. So
options (e) and (f) are incorrect.
In the regex t.*e, the metacharacter . matches any character, and * is a greedy
quantifier. t.*e searches for the letter t and scans all the remaining letters to find the
last occurrence of the letter e. The string theirXcarpe matches this regex pattern. So
theirXcarpe is used as a delimiter to the tokenizer ThemeXtheirXcarpet77. Text preceding and following this delimiter is returned as tokens.

Licensed to Mark Watson 

Answers to sample exam questions

393

A 5-5. b, c, f
[5.3] Format strings using the formatting parameters: %b, %c, %d, %f, and %s in format strings.
Explanation: Options (a) and (d) won’t compile. The type of the variable out in class
System is PrintStream. PrintStream’s methods print() and println() accept just
one method parameter. They’re overloaded to accept parameters of all the primitive
types and object references. But, they don’t accept format strings and arguments.
Option (b) is correct. The format specifier %b returns the value true for non-null
values for object references other than Boolean or boolean.
Option (c) is correct. The format specifier %s calls method toString() on an
object reference to get its String representation. Method toString() of class StringBuilder creates and returns a new String by using the sequence stored by the
StringBuilder. So printing a StringBuilder doesn’t print a value similar to StringBuilder@135d.
Option (e) is incorrect. %s calls StringTokenizer’s method toString() to access
its String representation. This option outputs a value similar to this:
java.util.StringTokenizer@750159 true 123456

Option (f) is correct. Methods printf() and format() provide exactly the same functionality. Behind the scenes, printf() calls format().
A 5-6. a
[5.1] Search, parse, and build strings (including Scanner, StringTokenizer, StringBuilder, String, and Formatter)
Explanation: Method contains() accepts a method parameter of type CharSequence,
and so an object of class StringBuilder is acceptable (class StringBuilder implements the interface CharSequence). Method contains() returns true, if the specified String equivalent can be found in the String object on which this method
is called.
A 5-7. a, d
[5.1] Search, parse, and build strings (including Scanner, StringTokenizer, StringBuilder, String, and Formatter)
[5.2] Search, parse, and replace strings by using regular expressions, using expression
patterns for matching limited to: . (dot), * (star), + (plus), ?, \d, \D, \s, \S, \w, \W, \b,
\B, [], ().
Explanation: On the exam, you’re likely to see questions based on multiple exam
objectives, just as this question covers file handling and string processing.

Licensed to Mark Watson 

394

CHAPTER 5 String processing

Class StringTokenizer doesn’t accept the delimiter as a regex pattern. When
[,;] is passed as a delimiter to class StringTokenizer, the occurrence of either of
these characters acts as a delimiter. So the line read from file data.txt is delimited
using , ; and [.
Because the text in file data.txt doesn’t include $, changing the delimiter text from
[,;] to [,;$] won’t affect the output.
A 5-8. c
[5.1] Search, parse, and build strings (including Scanner, StringTokenizer, StringBuilder, String, and Formatter)
[5.2] Search, parse, and replace strings by using regular expressions, using expression
patterns for matching limited to: . (dot), * (star), + (plus), ?, \d, \D, \s, \S, \w, \W, \b,
\B, [], ().
Explanation: Method matches() doesn’t look for a matching subsequence. It matches
the complete string value against the given regex pattern. Because the regex pattern u.u matches a subsequence and not the entire string value e Java Guru, it
returns false.
The length of string eJava is 11, so eJava.substring(11) doesn’t throw StringIndexOutOfBoundsException. It returns an empty string, which replaces string "a in
string value e Java Guru, resulting in e Jv Guru.
A 5-9. a, b, c, d
[5.3] Format strings using the formatting parameters: %b, %c, %d, %f, and %s in format strings.
Explanation: You must use the following for the required format:
■

%f

■

Flag + to include a sign
Flag , to use locale-specific grouping

■

Options (a), (b), (c), and (d) use the correct format specifier, %f. The flags + and ,
can appear in any order. It’s acceptable to specify the width of the numeric literal.
If the total width specified with the format specifier is less than the width of the
argument passed to it, it’s ignored. If the width specified after the decimal is less than
the number of digits stored by the value to be formatted, it’s rounded off.
Options (e), (f), and (g) are incorrect. They either include an invalid combination
of flags or don’t include the required flags.

Licensed to Mark Watson 

Answers to sample exam questions

395

A 5-10. f
[5.1] Search, parse, and build strings (including Scanner, StringTokenizer, StringBuilder, String, and Formatter)
[5.2] Search, parse, and replace strings by using regular expressions, using expression
patterns for matching limited to: . (dot), * (star), + (plus), ?, \d, \D, \s, \S, \w, \W, \b,
\B, [], ().
Explanation: Option (a) is incorrect because it doesn’t compile. You can only pass
integer values as parameters, not strings. Method subSequence() doesn’t replace
string values. It extracts and returns a matching subsequence of a string.
Options (b) and (d) are incorrect because replace() doesn’t accept a regex pattern. It replaces exact matches of a string value.
Option (c) is incorrect—not all strings have a space in front of the first letter, so
the replace() option isn’t performed on every word in the string. \\s will also match
and replace the spaces (or whitespace, but not a combination of both).
Option (e) is incorrect because modify() doesn’t exist.
Option (f) is correct. replaceAll() can be passed a regex and its replacement. All
matching regex values in the string are replaced with the specified replacement
string. The regex pattern \b\w matches a word boundary (\b) followed by a word
character (\w)— that is, a-zA-Z_0-9.

Licensed to Mark Watson 

Exceptions
and assertions

Exam objectives covered in this chapter

What you need to know

[6.1] Use the throw statement and
throws clause

How to define methods that throw exceptions.
The difference between the throw statement and the
throws clause.
The different combinations of defining overriding methods, when the overridden or overriding methods throw
checked exceptions.

[6.2] Use the try statement with multicatch and finally clauses

How to use this new language feature with Java 7. How
to prevent coding the same set of statements for multiple exception handlers. How to catch multiple and unrelated exceptions in a single catch block.

[6.3] Auto-close resources with a trywith-resources statement

How to use a try-with-resources statement so you
never forget to close resources such as file handlers
and URL connections.
When a try-with-resources statement throws
exceptions, how to handle them. How to define
a try–with-resources statement without a catch
or finally block.

[6.4] Create custom exceptions

How to create your own exceptions, including subclasses. How to use them in your code.

[6.5] Test invariants by using assertions

The need for and purpose of assertions. The correct
syntax of an assert statement. Correct and incorrect
usage of assertions for variants and flow control. How
to enable and disable assertions.

396

Licensed to Mark Watson 

Using the throw statement and the throws clause

397

First let’s talk about exception handling. Imagine that the pilot of an airplane has an
accident just an hour before the flight’s scheduled takeoff. The airline manages the
situation by arranging for an alternate pilot, enabling the flight to take off at the scheduled time. What a relief for both the passengers and the airline. This example illustrates
how an exceptional condition (a pilot’s accident) that was outside the immediate control of the airline can modify the initial flow of an action (the predefined airline flight
operation). It demonstrates the need to handle such unexpected conditions appropriately (the arrangement of an alternate pilot). Can you imagine the inconvenience that
would be caused to all the passengers if the airline had no alternate plan to arrange for
another pilot and recover from this situation?
Similarly, it’s important for a Java application to handle unexpected or exceptional
conditions so it doesn’t fail, exit abruptly, or return invalid values. Java’s exception handling is a mechanism that lets you do just that. It enables you to build robust applications that can recover from exceptional conditions that may arise during the course of
a program, by defining an alternate flow of actions.
Now let’s talk about assertions. Whenever you board an international flight, the
airline staff checks your visa and stamps your passport. Imagine what would happen if
they missed stamping your passport at emigration? Would you be in trouble, at immigration, when you fly back to your own country? As you can see, a failed assumption
that all passports were stamped at emigration can cause issues with immigration. For
such cases, the airline would examine the existing procedures and formulate new
checks so that a similar situation doesn’t occur again.
Just as a failed assumption can help an airline identify an existing flaw in its operations, a failed assumption in an application can help identify and fix errors in the
application logic during its development. It’s difficult to find subtle bugs in the implementation of application logic when a programmer uses predefined assumptions
(believe me—it happens a lot). You must use assertions to check assumptions in the
program logic, variable values, and pre- and postconditions of methods, to avoid bugs
or errors. An assertion offers a way of indicating what should always be true.
Exception handling helps you gracefully handle an exceptional condition by defining an alternate flow of action. On the other hand, assertions help you identify and fix
potential application logic errors.
Exception handling is covered in both the OCA Java SE 7 Programmer I exam
(1Z0-803) and OCP Java SE 7 Programmer II exam (1Z0-804). This chapter covers
exception-handling topics that are specific to the latter exam. Assertions are covered
only by the second exam. This chapter covers
■
■
■
■
■

The throw statement and throws clause
The try statement with multi-catch and finally clauses (new in Java 7)
Auto-closing resources with a try-with-resources statement (new in Java 7)
Custom exceptions
Testing invariants by using assertions

Licensed to Mark Watson 

398

CHAPTER 6

Exceptions and assertions

This chapter assumes that you’re already aware of the basic exception handling covered by the OCA Java SE 7 Programmer I exam. If required, you can refer to OCA Java
SE 7 Programmer I Certification Guide: Prepare for the 1Z0-803 Exam (Manning, 2013),
which covers Java’s basic exception handling.
Let’s get started with the use of the throw statement and the throws clause.

6.1

Using the throw statement and the throws clause
[6.1] Use the throw statement and throws clause
Imagine that you’re assigned the task of finding a specific book, and then reading
and explaining its contents to a class of students. The required sequence looks like
the following:
■
■
■

Get the specified book.
Read aloud its contents.
Explain the contents to a class of students.

But what happens if you can’t find the specified book? You can’t proceed with the rest of
the actions without it, so you need to report back to the person who assigned the task to
you. This unexpected event (missing book) prevents you from completing your task. By
reporting it, you want the originator of this request to take corrective or alternate steps.
Let’s code the preceding task as method teachClass(), as shown in figure 6.1, and
use it to compare the throw statement and the throws clause. This example code is
for demonstration purposes only, because it uses methods locateBook(), readBook(),
and explainContents(), which aren’t defined.

Keyword
throws

void teachClass() throws BookNotFoundException {
boolean bookFound = locateBook();
Keyword

if (!bookFound)
throw new BookNotFoundException();
else {
readbook();
explainContents();
}

throw

}

Figure 6.1 Comparing the throw statement and the throws clause

Licensed to Mark Watson 

Using the throw statement and the throws clause

399

The code in figure 6.1 is simple to follow. On execution of throw new BookNotFoundException, the execution of teachClass() halts. The JVM creates an instance of
BookNotFoundException and sends it off to the caller of teachClass() so alternate
arrangements can be made.
The throw statement is used to throw an instance of BookNotFoundException. The
throws statement is used in the declaration of method teachClass() to signal that it
can throw BookNotFoundException.
Why does a method choose to throw an exception as opposed to handling it itself?
It’s a contract between the calling method and the called method. Referring to method
teachClass() shown in figure 6.1, the caller of teachClass would like to be informed
if teachClass() is unable to find the specified book. Method teachClass() doesn’t
handle BookNotFoundException because its responsibilities don’t include how to work
around a missing book.
The preceding example helps identify a situation when you’d want a method to
throw an exception, rather than handling it itself. It shows you how to use and compare the statement throw and clause throws—to throw exceptions and to signal that a
method might throw an exception. The example also shows that a calling method can
define alternate code, when the called method doesn’t complete successfully and
throws an exception. Apart from testing this logic, the exam will test you on how to
create and use methods that throw checked or unchecked (runtime) exceptions and
errors, along with several other rules.
Before you move forward with the chapter, it’s important to clearly identify all
kinds of exception classes as shown in figure 6.2.
Here’s a list of different kinds of exceptions:
■
■
■
■
■

Exception classes—Refers to Throwable class and all its subclasses
Error classes—Refers to Error class and all its subclasses
Runtime exception classes—Refers to RuntimeException class and all its subclasses
Unchecked exception classes—Refers to runtime exception classes and error classes
Checked exceptions classes—Refers to all exception classes other than the unchecked
exception classes. Class Throwable and any of its subclasses that aren’t a subclass
of either Error or RuntimeException are checked exceptions.

Now let’s create a method that throws checked exceptions.
java.lang.Object

java.lang.Throwable

java.lang.Error

java.lang.Exception

java.lang.RuntimeException

Figure 6.2 Using exception
hierarchy to identify all kinds
of exception classes

Licensed to Mark Watson 

400

6.1.1

CHAPTER 6

Exceptions and assertions

Creating a method that throws a checked exception
Let’s create a simple method that doesn’t handle the checked exception thrown by it,
by using the statement throw and clause throws. Class DemoThrowsException defines
method readFile(), which includes a throws clause in its method declaration. The
actual throwing of an exception is accomplished by the throw statement:
throws statement indicates method
can throw FileNotFoundException
or one of its subclasses

import java.io.FileNotFoundException;
class DemoThrowsException {
public void readFile(String file) throws FileNotFoundException {
boolean found = findFile(file);
if (!found)
throw new FileNotFoundException("Missing file");
If file can’t be
else {
found, code
//code to read file
creates and
}
throws instance
}
of FileNotFoundboolean findFile(String file) {
Exception by using
//code to return true if file can be located
throw statement
}
}

A method can have multiple comma-separated class names of exceptions in its throws
clause. Including runtime exceptions or errors in the method declaration isn’t
required. Including them in the documentation is the preferred way to mention
them. A method can still throw runtime exceptions or errors without including them
in its throws clause.
EXAM TIP

Syntactically, you don’t always need a combination of the

throw statement and throws clause to create a method that throws an
exception (checked or unchecked). You can replace the throw statement

with a method that throws an exception.

6.1.2

Using a method that throws a checked exception
To use a method that throws a checked exception, you must do one of the following:
■

■

■

Handle the exception—Enclose the code within a try block and catch the thrown
exception.
Declare it to be thrown—Declare the exception to be thrown by using the throws
clause.
Handle and declare—Implement both of the preceding options together.
The rule of either handling or declaring an exception is also
referred to as the handle-or-declare rule. To use a method that throws a
checked exception, you must either handle the exception or declare it to
be thrown. But this rule only applies to the checked exceptions and not
to the unchecked exceptions.

EXAM TIP

Licensed to Mark Watson 

401

Using the throw statement and the throws clause

In the following example, method useReadFile() handles FileNotFoundException (a
checked exception) thrown by readFile() by using a try-catch block:
class DemoThrowsException {
public void readFile(String file) throws FileNotFoundException {
//..code
useReadFile uses readFile that
}
throws FileNotFoundException
void useReadFile(String name) {
try {
readFile(name);
Call to readFile should be enclosed
}
in try block because it throws
checked FileNotFoundException
catch (FileNotFoundException e) {
//code
}
}
}

A modified definition of method useReadFile() declares to throw a FileNotFoundException:
class DemoThrowsException {
public void readFile(String file) throws FileNotFoundException {
//..code
}
void useReadFile(String name) throws FileNotFoundException{
useReadFile()
readFile(name);
declares that
}
readFile need not
it could throw
}
be enclosed in
FileNotFound-

try-catch block

Exception.

The compiler doesn’t complain if you mix the preceding approaches. Method useReadFile() can handle FileNotFoundException itself and still declare it to be thrown
(highlighted in bold):
import java.io.*;
class DemoThrowsException {
public void readFile(String file) throws FileNotFoundException {
//..code
}
void useReadFile(String name) throws FileNotFoundException{
try {
readFile(name);
}
catch (FileNotFoundException e) {
//code
}

readFile is enclosed
in try-catch block

}
}

Licensed to Mark Watson 

useReadFile()
declares that
it could throw
FileNotFoundException.

402

CHAPTER 6

Exceptions and assertions

So what happens when FileNotFoundException is thrown by method readFile()?
Will its catch block handle FileNotFoundException, or will readFile() throw the
exception to its calling method? Let’s find out in our first “Twist in the Tale” exercise.
Twist in the Tale 6.1

Let’s modify some of the code used in the previous examples. Which answer correctly
shows the output of class TwistThrowsException?
import java.io.*;
class TwistThrowsException {
public void readFile(String file) throws FileNotFoundException {
System.out.println("readFile");
throw new FileNotFoundException();
}
void useReadFile(String name) throws FileNotFoundException {
System.out.println("useReadFile");
try {
readFile(name);
}
catch (FileNotFoundException e) {
//code
}
}
public static void main(String args[]) {
new TwistThrowsException().useReadFile("a");
}
}
a

useReadFile
readFile
FileNotFoundException thrown at runtime

b

useReadFile
FileNotFoundException thrown at runtime

c

Compilation error

d

FileNotFoundException thrown at runtime

e

useReadFile
readFile

Did you ever happen to debug or fix a piece of code written by someone else? During such a session, have you ever noticed that to avoid coding multiple exception
handlers (prior to Java 7), programmers often caught an overly broad exception,
which often made its way into the production code? The next tip shows why you
must avoid that technique.

Licensed to Mark Watson 

Using the throw statement and the throws clause

403

Practical issues with catching an overly broad exception
Many times (prior to Java 7), developers took a shorter route to handle all checked
exceptions: they defined just one exception handler, java.lang.Exception. Here’s
an example:
class MyCustomException extends Exception {}
class MultiCatch {
void myMethod(Connection con, String fileName) {
try {
// code
Code that might throw MyCustom}
Exception, FileNotFoundException,
catch (Exception e) {
or SQLException
// code
}
Catch all types of checked
}
and runtime exceptions.
}

This, of course, has issues, because java.lang.Exception is the superclass of all
exceptions, including RuntimeException. The preceding catch block will silently
catch all types of runtime exceptions also. It might become difficult to debug such
code—it might log the same message for different exceptions or might try to handle
all types of exceptions in the same way, which might not have been the intent.

6.1.3

Creating and using a method that throws runtime exceptions or errors
While creating a method that throws a runtime exception or error, including the
exception or error name in the throws clause isn’t required. A method that throws a
runtime exception or error isn’t subject to the handle-or-declare rule.
Let’s see this concept in action by modifying the preceding example so method
readFile() throws a NullPointerException (a runtime exception) when a null
value is passed to it (code changes are shown in bold in this example and throughout
the rest of the chapter):
import java.io.FileNotFoundException;
throws clause
indicates that
class DemoThrowsException {
this method
public void readFile(String file) throws FileNotFoundException {
can throw
if (file == null)
FileNotFoundthrow new NullPointerException();
Exception
boolean found = findFile(file);
if (!found)
throw new FileNotFoundException("Missing file");
Code throws
else {
NullPointerException,
//code to read file
but it’s not included
}
in throws clause.
}
boolean findFile(String file) {
//code to return true if file can be located
}
}

Licensed to Mark Watson 

404

CHAPTER 6

Exceptions and assertions

The exam might trick you by including the names of runtime exceptions and errors in
one method’s declaration and leaving them out in another. (You can include the
name of unchecked exceptions in the throws clause, but you don’t have to.) Assuming that the rest of the code remains the same, the following method declaration
is correct:
public void readFile(String file)
throws NullPointerException, FileNotFoundException {
//rest of the code remains same
Though not required,
}

including runtime exceptions
in throws clause is valid

Adding runtime exceptions or errors to a method’s declaration isn’t required. A method can throw a runtime exception or error
irrespective of whether its name is included in its throws clause.

EXAM TIP

Table 6.1 lists common errors and runtime exceptions. All are covered in detail in OCA
Java SE 7 Programmer I Certification Guide.
Table 6.1 Common errors and exceptions
Runtime exceptions

Errors

ArrayIndexOutOfBoundsException

ExceptionInInitializerError

IndexOutOfBoundsException

StackOverflowError

ClassCastException

NoClassDefFoundError

IllegalArgumentException

OutOfMemoryError

IllegalStateException
NullPointerException
NumberFormatException

6.1.4

Points to note while using the throw statement and the throws clause
Apart from understanding the need for throwing exceptions and using their syntax,
you need to know a few rules about throwing exceptions with the throw statement and
the throws clause. These rules are presented in this section.
A METHOD CAN THROW A SUBCLASS OF CHECKED EXCEPTION MENTIONED IN ITS THROWS CLAUSE,
NOT ITS SUPERCLASS

Because class IOException is a superclass of class FileNotFoundException, method
readFile() can’t throw an object of class IOException:
throws clause includes
FileNotFoundException

class DemoThrowsException {
public void readFile(String file) throws FileNotFoundException {
boolean found = findFile(file);

Licensed to Mark Watson 

405

Using the throw statement and the throws clause
if (!found)
throw new IOException("Missing file");
else {
//code to read file
}
}
boolean findFile(String file) {
//code to return true if file can be located
}

Won’t compile; can’t
throw object of
superclass of checked
exception mentioned
in throws clause.

}

Let’s modify the definition of method readFile() by declaring it might throw an
IOException. Because IOException is a superclass of class FileNotFoundException,
readFile() can throw an object of FileNotFoundException:
class DemoThrowsException {
public void readFile(String file) throws IOException {
boolean found = findFile(file);
if (!found)
throw new FileNotFoundException("Missing file");
else {
//code to read file
}
}
boolean findFile(String file) {
//code to return true if file can be located
}
}

throws clause
includes IOException
Will compile;
can throw object
of derived class of
checked exception
mentioned in
throws clause.

Note that this rule doesn’t apply to errors and runtime exceptions.
An overriding method can throw any error or runtime exception,
irrespective of whether they’re thrown by the overridden method or not.

EXAM TIP

A METHOD CAN HANDLE THE EXCEPTION AND STILL DECLARE IT TO BE THROWN

This is usually done by methods whose exception handlers might throw the same
exception. Method useReadFile() handles the FileNotFoundException and also
declares it to be rethrown:
class DemoThrowsException {
public void readFile(String file) throws FileNotFoundException {
//..code
}
void useReadFile(String name) throws FileNotFoundException {
try {
readFile(name);
}
catch (FileNotFoundException e) {
Code is valid;
//..code
useReadFile()
throw e;
handles FileNotFileNotFoundException
}
FoundException
instance will be handed over to
}
from readFile().
method that calls useReadFile()
}

Licensed to Mark Watson 

useReadFile()
could throw
FileNotFoundException.

406

CHAPTER 6

Exceptions and assertions

A METHOD THAT DECLARES A CHECKED EXCEPTION TO BE THROWN MIGHT NOT ACTUALLY THROW IT
Method readFile() includes the name of the checked exception FileNotFoundException in its throws clause, but doesn’t throw it:
Compiles successfully even if readFile()
doesn’t throw FileNotFoundException.
import java.io.FileNotFoundException;
class DemoThrowsException {
public void readFile(String file) throws FileNotFoundException {
System.out.println("readFile:" + file);
}
}

But do you think you can call readFile() as a method that doesn’t throw an exception (without enclosing it within a try-catch block or declaring the exception)? Let’s
check it out in our next “Twist in the Tale” exercise.
Twist in the Tale 6.2

What is the output of class ThrowsException?
import java.io.FileNotFoundException;
class TwistThrowsException {
public void readFile(String file) throws FileNotFoundException {
System.out.println("readFile:" + file);
}
public static void main(String args[]) {
System.out.println("main");
new TwistThrowsException().readFile("Hello.txt");
}
}
a

main
readFile:Hello.txt

b

main
readFile:Hello.txt
FileNotFoundException thrown at runtime

c

Compilation error

d

FileNotFoundException thrown at runtime

YOU CAN RETHROW EXCEPTIONS WITH MORE-INCLUSIVE TYPE CHECKING

Starting with Java 7, the variable type that you use to rethrow an exception can be
more generic in the catch block:
class GenericVariableTypeToRethrowException {
public static void main(String args[])
throws IOException, SQLException {
String source = "DBMS";

Licensed to Mark Watson 

Method declares to
throw exceptions
IOException and
SQLException

407

Using the throw statement and the throws clause
try {
if (source.equals("DBMS"))
throw new SQLException();
Type of variable e in catch block
else
is Exception, more generic than
throw new IOException();
IOException and SQLException
}
catch (Exception e) {
throw e;
Catch block rethrows
}

caught exception

}
}

In the preceding code, the try block can throw two types of checked exceptions:
SQLException or IOException. But the type of the variable e in the catch block is
Exception, which is a superclass of SQLException and IOException. Prior to Java 7,
this code would fail to compile because main() is trying to throw an object of
Exception from its catch block, when its method declaration states that it can
throw an IOException or SQLException. (For checked exceptions, a method can’t
throw a superclass of the exception included in its declaration.) With Java 7, the
preceding code compiles successfully, because the compiler can determine that
the type of the checked exception received by the catch block would always be
either IOException or SQLException. So it’s okay to throw it, even though the
type of the variable e is Exception.
Do you think the code will compile successfully if instead of rethrowing the exception in the catch block, you create a new object of class Exception and throw it? No, it
won’t. It would be a direct violation of the contract between the declarations of exceptions that method main() states to be throwing and what it actually throws. The following modified example won’t compile:
class GenericVariableTypeToRethrowException2 {
public static void main(String args[])
throws IOException, SQLException {
String source = "DBMS";
try {
if (source.equals("DBMS"))
throw new SQLException();
else
throw new IOException();
}
catch (Exception e) {
Won’t compile; catch
throw new Exception();
block creates and throws
Exception instance.
}
}
}

Method declares
to throw
IOException and
SQLException

With Java 7, you can rethrow exceptions with more inclusive
type checking.

EXAM TIP

Licensed to Mark Watson 

408

CHAPTER 6

Exceptions and assertions

A METHOD CAN DECLARE TO THROW ALL TYPES OF EXCEPTIONS, EVEN IF IT DOESN’T
In the following example, class ThrowExceptions defines multiple methods that declare
to throw different exception types. Class ThrowExceptions compiles successfully, even

though its methods don’t include the code that might throw these exceptions:
class ThrowExceptions {
void method1() throws
void method2() throws
void method3() throws
void method4() throws
void method5() throws
}

Error {}
Exception {}
Throwable {}
RuntimeException {}
FileNotFoundException {}

Though a try block can define a handler for unchecked exceptions not thrown by it,
it can’t do so for checked exceptions (other than Exception):
class HandleExceptions {
void method6() {
try {}
catch (Error e) {}
}
void method7() {
try {}
catch (Exception e) {}
}
void method8() {
try {}
catch (Throwable e) {}
}
void method9() {
try {}
catch (RuntimeException e) {}
}
void method10() {
try {}
catch (FileNotFoundException e) {}
}
}

Won’t
compile

In the preceding code, method6(), method7(), method8(), and method9() compile
even though their try blocks don’t define code to throw the exception being handled
by their catch blocks. But method10() won’t compile.
A method can declare to throw any type of exception—checked
or unchecked—even if it doesn’t. But a try block can’t define a catch
block for a checked exception (other than Exception) if the try block
doesn’t throw that checked exception or uses a method that declares to
throw that checked exception.
EXAM TIP

Before moving on to the next section, let’s summarize the points to remember for the
throw statement and the throws clause.

Licensed to Mark Watson 

Creating custom exceptions

409

Rules to remember about the throw statement and the throws clause
■

■

■

■

■

■

■
■

■

■

■
■
■

The throw statement is used within a method to throw an instance of a checked
exception, a runtime exception, or an error.
The throws clause is used with a method's declaration to list the exceptions
that a method can throw.
A method can include multiple comma-separated exception and error class
names in its throws clause.
The rule of either handling or declaring a checked exception is also referred to
as the handle-or-declare rule.
To use a method that throws a checked exception, you must either handle the
exception or declare it to be thrown.
The handle-or-declare rule only applies to the checked exceptions and not to the
unchecked exceptions.
Including runtime exceptions or errors in a throws clause isn’t required.
Adding unchecked exceptions or errors in the throws clause adds no obligations
about handling them in a try-catch block.
A method can throw a more specific exception subclass than the one mentioned
in its throws clause, but not a more generic one (superclass).
A method can handle a checked or unchecked exception and still declare it to
be thrown.
A method that declares a checked exception to be thrown might not throw it.
With Java 7, you can rethrow exceptions with more inclusive type checking.
A method can declare to throw any type of exception—checked or unchecked—
even if it doesn’t. But a try block can’t define a catch block for a checked
exception (other than Exception) if the try block doesn’t throw that checked
exception or uses a method that declares to throw that checked exception.

You can throw your own custom exceptions from methods by using the throw and
throws statements, in the same way you work with exception classes from the Java API.
Why do you need to create custom exception classes, when you can use exception
classes that are already defined in the Java API? Let’s discover in the next section.

6.2

Creating custom exceptions
[6.4] Create custom exceptions
Take a look at figure 6.3, which shows just a few of the existing exceptions from the
Java API.
What information do you gather when a piece of code throws a FileNotFoundException or ArrayIndexOutOfBoundsException? Without reading the code, you can
know that FileNotFoundException was probably thrown by code that couldn’t locate
a specified file. Similarly, ArrayIndexOutOfBoundsException will probably be thrown
by code that tried to access an array element at an index position out of its bounds.

Licensed to Mark Watson 

410

CHAPTER 6

Exceptions and assertions

?

!

?
NullPointer
Exception

FileNotFound
Exception

IOException

!

ArrayIndexOutOf
BoundsException

Figure 6.3 Existing exceptions defined in the Java API

The name of an exception can convey a lot of information to other developers or users,
which is one of the main reasons for defining a custom exception. A custom exception
can also be used to communicate a more descriptive message. For example, assume that
you’re developing an API to enable users to connect to your server to access its services.
How can you communicate failure of the login process to a user, which can arise from
multiple reasons such as a database connectivity issue or inappropriate user access privileges? One of the preferred approaches is to define and use custom exceptions.
Custom exceptions also help you restrict the escalation of implementing specific
exceptions to higher layers. For example, data access code on your server that accesses
persistent data may throw a SQLException. But users connecting to your server should
not be returned this exception. You may catch SQLException and throw another
checked or unchecked exception (the choice of throwing a checked or unchecked
exception is beyond the scope of this book). If you can’t find an appropriate existing
exception, create one!

6.2.1

Creating a custom checked exception
You can subclass java.lang.Exception (or any of its subclasses) to define custom
exceptions. To create custom checked exceptions, subclass java.lang.Exception or
its subclasses (which aren’t subclasses of RuntimeException). Let’s revisit the exception classes in figure 6.4.
java.lang.Object

java.lang.Throwable

java.lang.Error

java.lang.Exception

java.lang.RuntimeException
Figure 6.4 Hierarchy of Exception and Error classes

Licensed to Mark Watson 

411

Creating custom exceptions

A word of caution here: even though you can extend class java.lang.Throwable to
create your own exceptions, it isn’t recommended. Class Throwable is the superclass
of classes java.lang.Error and java.lang.Exception. The exception handler for
this class will catch all types of errors and exceptions! For example, say an OutOfMemoryError brings the JVM into an unstable state. Catching it as a subclass of Error
or Throwable would be undesirable and potentially dangerous. Obviously, you may
not want this behavior.
EXAM TIP Don’t extend class java.lang.Throwable to create your own
exceptions, even though you can. Class Throwable is the superclass of
classes java.lang.Error and java.lang.Exception. The exception handler for this class will catch all types of errors and exceptions.

Often organizations prefix the name of a custom exception with their organization,
project, or module name. Let’s create a custom exception, LoginException, which is a
checked exception:
class LoginException extends Exception {}

LoginException is a
checked exception.

Let’s add constructors to it, a no-argument constructor and one that accepts a description of an exception or problem that occurred, as follows:
class LoginException extends Exception{
public LoginException() {
super();
}
public LoginException(String message) {
super(message);
}
}

No-argument
constructor
Constructor that
accepts String

Note that an exception class is like any other class to which you add your own methods
and variables. Let’s define another class, UserActivity, which defines method
login(). This method creates an instance of LoginException and throws it if a user is
unable to log in successfully. Note that I’ve tried to show how (checked) exceptions
are thrown in real-world applications. Because the main focus here is to throw a
checked exception, I haven’t implemented method findInDatabase(), as it would
have been implemented in the real world. It’s simply reduced to returning a false
value. Examine the following code:
Add throws clause to
method declaration

class UserActivity {
public void login(String user, String pwd) throws LoginException {
if (!findInDatabase(user, pwd))
throw new LoginException("Invalid username or password");
}

Instantiate LoginException
and throw it.

Licensed to Mark Watson 

412

CHAPTER 6

Exceptions and assertions

private boolean findInDatabase(String user, String pwd) {
// code that returns true if user/ pwd
// combination found in database
// false otherwise
return false;
}
}

6.2.2

Creating a custom unchecked exception
Class Error, class RuntimeException, and their derived classes are collectively referred
to as unchecked exception classes.
You can subclass java.lang.RuntimeException to create a custom runtime exception. Here is the modified class LoginException:
class LoginException extends RuntimeException{
public LoginException() {
super();
}
public LoginException(String message) {
super(message);
}
}

Since LoginException
extends RuntimeException,
now it’s an unchecked
exception.

You can throw this exception (now a runtime, or unchecked, exception) in the same
manner as mentioned previously in class UserActivity. The only change is that
(though allowed) now you no longer need to include the throws clause in method
login()’s declaration, as follows:
class UserActivity {
public void login(String username,String pwd) {
if (!findInDatabase(username, pwd))
throw new LoginException("Invalid username or password");
}
private boolean findInDatabase(String username, String pwd) {
// code that returns true if username/ pwd
// combination found in database
// false otherwise
return false;
}
}

No need to
define throws
clause in
method
declaration
for runtime
exception

To create custom Error classes, you can subclass java.lang.Error by extending it.
But Error classes represent serious exceptional conditions, which shouldn’t be thrown
programmatically.
On the exam, you’ll see both custom exceptions and exceptions from the Java API.
Though overriding methods that throw exceptions isn’t explicitly defined as a separate exam topic, you’re likely to be tested on it.

Licensed to Mark Watson 

413

Overriding methods that throw exceptions

Base

Doesn’t
declare to throw
a checked
exception

aMethod()

<>
Derived
Declares to
throw a checked
exception

aMethod()

Figure 6.5 If an overridden method doesn’t declare to throw any checked exception,
the overriding method can’t declare to throw a checked exception.

6.3

Overriding methods that throw exceptions
In this section, you’ll work through compilation issues that occur with overridden and
overriding methods, when either of them declares to throw a checked exception. Multiple combinations exist, as shown in figures 6.5 and 6.6.
With overriding and overridden methods, it's all about which
checked exceptions an overridden method and an overriding method
declare, not about the checked exceptions both actually throw.

EXAM TIP

Base
aMethod()

Declares
to throw
IOException

<>

Derived1

Derived2

Derived3

Derived4

aMethod()

aMethod()

aMethod()

aMethod()

Doesn’t
declare to
throw a checked
exception

Declares
to throw
Exception

Declares
to throw
IOException

Declares
to throw
FileNotFound
Exception

java.lang.
Exception

java.io.
IOException

java.io.
FileNotFound
Exception

Figure 6.6 If an overridden method declares to throw a checked exception, the overriding method
can choose to declare to not throw any checked exception, throw the same exception, or throw a more
specific exception. The overriding method can’t declare to throw a more generic checked exception.

Licensed to Mark Watson 

414

CHAPTER 6

Exceptions and assertions

EXAM TIP Method overriding rules apply only to checked exceptions.
They don’t apply to runtime exceptions or errors. Beware: you’re likely to
be tested on this difference on the exam.

Let’s work with all these combinations by using code snippets.
RULE 1: IF A BASE CLASS METHOD DOESN’T DECLARE TO THROW A CHECKED EXCEPTION, AN
OVERRIDING METHOD IN THE DERIVED CLASS CAN’T DECLARE TO THROW A CHECKED EXCEPTION

Examine the following code:
class Base {
public void aMethod() {}
public void noRuntimeException() {}
}
This line fails
class Derived extends Base {
to compile.
public void aMethod() throws Exception {}
public void noRuntimeException() throws RuntimeException {}
}

This line
compiles
successfully.

RULE 2: IF A BASE CLASS METHOD DECLARES TO THROW A CHECKED EXCEPTION, AN OVERRIDING
METHOD IN THE DERIVED CLASS CAN CHOOSE NOT TO DECLARE TO THROW ANY CHECKED EXCEPTION

Examine the following code:
class Base {
public void aMethod() throws IOException {}
public void withRuntimeException() throws RuntimeException {}
}
class Derived1 extends Base {
public void aMethod() {}
public void withRuntimeException() {}
}

Both lines compile
successfully.

RULE 3: IF A BASE CLASS METHOD DECLARES TO THROW A CHECKED EXCEPTION, AN OVERRIDING
METHOD IN THE DERIVED CLASS CANNOT DECLARE TO THROW A SUPERCLASS OF THE EXCEPTION
THROWN BY THE ONE IN THE BASE CLASS

Examine the following code:
class Base {
public void aMethod() throws IOException {}
public void withRuntimeException() throws NullPointerException {}
}
This line fails
class Derived2 extends Base {
to compile.
public void aMethod() throws Exception {}
public void withRuntimeException() throws RuntimeException{}
}

Licensed to Mark Watson 

Using the try statement with multi-catch and finally clauses

415

RULE 4: IF A BASE CLASS METHOD DECLARES TO THROW A CHECKED EXCEPTION, AN OVERRIDING
METHOD IN THE DERIVED CLASS CAN DECLARE TO THROW THE SAME EXCEPTION

The following code compiles successfully:
class Base {
void aMethod() throws IOException {}
void methodUncheckedEx() throws Error {}
}
class Derived3 extends Base {
void aMethod() throws IOException {}
void methodUncheckedEx() throws NullPointerException {}
}

RULE 5: IF A BASE CLASS METHOD DECLARES TO THROW A CHECKED EXCEPTION, AN OVERRIDING
METHOD IN THE DERIVED CLASS CAN DECLARE TO THROW A DERIVED CLASS OF THE EXCEPTION
THROWN BY THE ONE IN THE BASE CLASS

The following code compiles successfully:
class Base {
void aMethod() throws IOException {}
}
class Derived4 extends Base {
void aMethod() throws FileNotFoundException {}
}

After working with the method overriding rules that include throwing exceptions, let’s
use the try statement with multi-catch and finally clauses. Starting with Java 7, the
try statement can catch multiple exceptions in the same handler, as discussed in
the next section.

6.4

Using the try statement with multi-catch and
finally clauses
[6.2] Use the try statement with multi-catch and finally clauses
Prior to Java 7, if a try block needed to execute the same action for multiple
exceptions thrown, it had to define separate handlers for each of them. Starting
with Java 7, you can catch multiple, unrelated exceptions with one handler, also
called a multi-catch.

6.4.1

Comparing single-catch handlers and multi-catch handlers
The multi-catch comes in handy if you need to execute the same action for handling
multiple, unrelated exceptions. I specifically mention unrelated exceptions, because
an exception handler for, say, MyException, can handle MyException and all of its
subclasses. You can compare this approach of defining separate exception handlers

Licensed to Mark Watson 

416

CHAPTER 6

Exceptions and assertions

Single-catch handler

Multi-catch handler

try {

try {

}
catch (fileNotFoundException e){
//log exception
}
catch (MyCustomException e){
//log exception
}
catch (NumberFormatException e){
//log exception
}

}
catch (FileNotFoundException |
MyCustomException |
NumberFormatException e){
//log exception
}
A single handler
can handle multiple
unrelated exceptions.

Figure 6.7 Comparing the differences between executing the same action with single-catch
and multi-catch exception handlers

(prior to Java 7) with defining only one exception handler to execute the same steps
for multiple unrelated exceptions (starting in Java 7) by using figure 6.7.
Now that you know the difference between a multi-catch and single-catch handler, let’s dive into the details of defining multi-catch handlers with finally clauses.

6.4.2

Handling multiple exceptions in the same exception handler
You should know the basic syntax for creating and using multi-catch blocks, together
with the gotchas that may be used on the exam. So let’s start with the basic syntax of
multi-catch blocks.
BASIC SYNTAX OF MULTI-CATCH BLOCK

To catch multiple exceptions in a single handler, just separate the different exception
types with a vertical bar (|). The following is an example of a try block that defines
code that can handle FileNotFoundException and SQLException (or any of their subclasses) using a multi-catch block:
Line1> class MultiCatch {
Line2>
void myMethod(Connection con, String fileName) {
Line3>
try {
Line4>
File file = new File(fileName);
Line5>
FileInputStream fin = new FileInputStream(file);
Line6>
Line7>
Line8>
Line9>
Line10>
Line11>
Line12>}

Might throw
FileNotFoundException

Statement stmt = con.createStatement();
Might throw
}
SQLException
catch (FileNotFoundException | SQLException e) {
System.out.println(e.toString());
Executes if line 5 throws
}
FileNotFoundException or line
}

6 throws SQLException or any
of their subclasses.

Licensed to Mark Watson 

417

Using the try statement with multi-catch and finally clauses

In the preceding code, the exception handler will execute if the code throws FileNotFoundException, SQLException, or any of their subclasses.
FINALLY BLOCK CAN FOLLOW MULTI-CATCH BLOCK
A multi-catch block can be followed by a finally block. Here’s an example:
class MultiCatchWithFinally {
void myMethod(Connection con, String fileName) {
try {
File file = new File(fileName);
FileInputStream fin = new FileInputStream(file);

}

Statement stmt = con.createStatement();
}
catch (FileNotFoundException | SQLException e) {
System.out.println(e.toString());
}
finally {
System.out.println("finally");
finally block can follow
}
multi-catch block

}

The syntax seems to be simple. So let’s look at some of the gotchas that you need to be
aware of for the exam.
EXCEPTIONS THAT YOU CATCH IN A MULTI-CATCH BLOCK CAN’T SHARE AN INHERITANCE RELATIONSHIP

What happens if you add another line of code in the previous example, which
involves reading from FileInputStream, which might throw an IOException? Let’s
add IOException to the list of exceptions being caught in the multi-catch block:
class MultiCatch {
void myMethod(Connection con, String fileName) {
try {
File file = new File(fileName);
FileInputStream fin = new FileInputStream(file);
fin.read();

May throw
IOException

May throw
FileNotFoundException
May throw
SQLException

Statement stmt = con.createStatement();
}
catch (IOException| FileNotFoundException | SQLException e) {
System.out.println(e.toString());
}

Fails to
compile

}
}

This code fails compilation with the following error message:
MultiCatch.java:13: error: Alternatives in a multi-catch statement cannot
be related by subclassing
catch (IOException | FileNotFoundException | SQLException e) {
^
Alternative FileNotFoundException is a subclass of alternative IOException
1 error

Licensed to Mark Watson 

418

CHAPTER 6

Exceptions and assertions

Looks like the code fails to compile because the IOException is caught before the
FileNotFoundException. In regular catch blocks, if you catch a superclass exception
before a derived class exception, the code won’t compile. So let’s swap the order of
IOException and FileNotFoundException in the preceding code:
class MultiCatch {
void myMethod(Connection con, String fileName) {
try {
File file = new File(fileName);
FileInputStream fin = new FileInputStream(file);
fin.read();

Might throw
IOException

Statement stmt = con.createStatement();

Might throw
FileNotFoundException
Might throw
SQLException

}
catch (FileNotFoundException | IOException| SQLException e) {
System.out.println(e.toString());
}
Swapping exception types

doesn’t make a difference;
code fails to compile.

}
}

The code fails compilation with the following message:
Alternatives in a multi-catch statement cannot be related by subclassing
catch (FileNotFoundException | IOException | SQLException e) {
^
Alternative FileNotFoundException is a subclass of alternative IOException
1 error

So the takeaway from the previous examples is that you can’t use subclasses as alternative types in a multi-catch block. The correct multi-catch block for code that may
throw an IOException, FileNotFoundException, and SQLException is as follows:
class MultiCatch {
void myMethod(Connection con, String fileName) {
try {
..
}
catch (IOException | SQLException e) {
System.out.println(e.toString());
}
}
}

Code that might throw
IOException, FileNotFoundException, or SQLException
Catch IOException (superclass
of FileNotFoundException)
and SQLException or any of
their subclasses.

Because multi-catch blocks are used to execute the same piece of code, when multiple exceptions are thrown, it makes no sense to use subclasses.
COMBINING MULTI-CATCH AND SINGLE-CATCH BLOCKS
You can combine multi-catch and single-catch blocks, as shown in the following code:
class MultiAndSingleCatch {
void myMethod(Connection con, String fileName) {

Licensed to Mark Watson 

Using the try statement with multi-catch and finally clauses

Catch
FileNotFound
Exception or
any of its
subclasses.

}

419

try {
Code that may throw IOException,
..
FileNotFoundException, or SQLException
}
catch (FileNotFoundException e) {}
Catch IOException and SQLException
catch (IOException | SQLException e) {}
or any of their subclasses (except for

FileNotFoundException or its
subclasses).

}

Watch out for a combination of multi-catch and single-catch
exception handlers on the exam. They can get quite tricky.
EXAM TIP

USING A SINGLE EXCEPTION VARIABLE IN THE MULTI-CATCH BLOCK
It’s easy to overlook that even though a multi-catch handler defines multiple exception types, it must use only one variable. Figure 6.8 defines two multi-catch exception
handlers. The latter multi-catch block uses multiple variables, which is incorrect and

won’t compile.

catch (SQLException | IOException e ) {...}

catch (SQLException el | IOException e2 ) {...}

Figure 6.8 A multi-catch
block that uses multiple
exception variables won’t
compile.

IN A MULTI-CATCH BLOCK, VARIABLE E IS IMPLICITLY FINAL
In a multi-catch block, the variable that accepts the exception object is implicitly

final. A final variable can’t be reassigned a value. So if you try to reassign a value to the
variable of the multi-catch exception handler, the code won’t compile:
class MultiCatch {
Code that may throw
void myMethod(Connection con, String fileName) {
IOException or SQLException
try {
..
Catch IOException
}
and SQLException.
catch (IOException| SQLException e) {
e = new FileNotFoundException();
Won’t compile; can’t
}
reassign value to variable e
}
because it’s implicitly final.
}

TYPE OF EXCEPTION VARIABLE IN A MULTI-CATCH BLOCK
In a multi-catch block, the type of the reference variable that accepts the exception
object is a common base class of all the exception types mentioned in a multi-catch
block. In the following code, the type of the reference variable ex is Exception, the

Licensed to Mark Watson 

420

CHAPTER 6

Exceptions and assertions

common base class of Exception1 and Exception2. This is why calling info() on ex
won’t compile:
Custom exception Exception1
extends IOException.

class Exception1 extends IOException{
public String info() {
return "I'm Base Exception";
}

Exception1 defines
method info.

}

Custom exception Exception2
extends Exception.

class Exception2 extends Exception{
public String info() {
return "I'm Derived Exception";
}

Exception2 defines
method info.

}
class TestVariableTypeInMultiCatch {
public static void main(String args[]) {
try {
int a = 10;
if (a <= 10)throw new Exception1();
else throw new Exception2();
}
catch (Exception1 | Exception2 ex) {
System.out.println(ex.info());
}
}

Type of variable ex is
Exception, common
superclass of Exception1
and Exception2.
Won’t compile; class Exception
doesn’t define method info.

}

Okay, let’s modify the code from the previous example so it prints out the value of
variable ex, in the next “Twist in the Tale” exercise.
Twist in the Tale 6.3

Which answer correctly shows the output of class TestMultiCatch?
import java.io.*;
class Exception1 extends IOException{}
class Exception2 extends Exception{}
class TestMultiCatch {
public static void main(String args[]) {
try {
int a = 10;
if (a <= 10)throw new Exception1();
else throw new Exception2();
}
catch (Exception1 | Exception2 ex) {
System.out.println(ex);
}
}
}

// line1

Licensed to Mark Watson 

421

Using the try statement with multi-catch and finally clauses

Value similar to Exception1@96a34
Value similar to Exception2@45a86e

a
b

Exception1
Exception2

c
d

Do you think this code will compile successfully if you comment the code marked with
//line1?

And what would happen if both custom exceptions used in the preceding example
code implement an interface?
interface IEx {
String info();
}
class Exception1 extends IOException implements IEx{
public String info() {
return "I'm Base Exception";
}
}

Custom exception
Exception1 extends
IOException;
implements IEx.

class Exception2 extends Exception implements IEx {
public String info() {
return "I'm Derived Exception";
}
}

Custom exception
Exception2 extends
Exception;
implements IEx.

class TestVariableTypeInMultiCatch {
public static void main(String args[]) {
try {
int a = 10;
if (a <= 10)throw new Exception1();
else throw new Exception2();
}
catch (Exception1 | Exception2 ex) {
System.out.println(ex.info());
}
}

Variable ex is an
intersection type,
with Exception and
IEx as its bounds.
Compiles
successfully.

}

In the preceding code, variable ex is of intersection type with Exception and IEx as its
bounds. You can call methods accessible to class Exception and interface IEx on the
reference variable ex.
The next section covers another major language enhancement in Java: auto-closing
resources by using a try-with-resources statement.

Licensed to Mark Watson 

422

CHAPTER 6

6.5

Auto-closing resources with a try-with-resources
statement

Exceptions and assertions

[6.3] Auto-close resources with a try-with-resources statement
Prior to Java 7, developers used finally blocks to close resources such as file handlers, and database or network connections. Here’s a quick example to show how a
FileInputStream instance was closed prior to Java 7:
try {
FileInputStream fis = /* instantiate */
/* other code */
}
finally {
if (fis != null)
try {
fis.close();
}
catch (Exception e) {/* */}
}

As you can see, closing a resource required a lot of boilerplate code and was errorprone too. What if a developer didn’t close a resource in a finally block?
Starting with Java 7, you can use a try-with-resources statement to auto-close resources
defined with the try statement.

6.5.1

How to use a try-with-resources statement
The try-with-resources statement is a type of try statement that can declare one or
more resources. A resource is an object such as file handlers and database or network
connections, which should be closed after it’s no longer required. If you declare a
resource by using a try-with-resources statement, it automatically closes the resource
by calling its close method, just before the end of the try block. A resource must
implement the java.lang.AutoCloseable interface or any of its subinterfaces to be
eligible to be declared in a try-with-resources statement. Let’s start with an example.
AN EXAMPLE

In the following example, a try-with-resources statement declares and initializes an
object (fin) of type FileInputStream. Because the try-with-resources statement is
supposed to automatically call method close() on fin, the following code doesn’t
explicitly make this call:
class AutoClose {
void readFileContents(String fileName) {
File file = new File(fileName);
try (FileInputStream fin = new FileInputStream(file)){
//.. some code
Some code
}

Licensed to Mark Watson 

Code to open
FileInputStream;
can throw FileNotFoundException

423

Auto-closing resources with a try-with-resources statement
catch (FileNotFoundException e) {
System.out.println(e.toString());
}
}

Catch FileNotFoundException
that might be thrown by code
to initialize fin.

}

But wait! This code doesn’t compile and gives the following compilation error:
AutoClose.java:7: error: unreported exception IOException;
must be caught or declared to be thrown
try (FileInputStream fin = new FileInputStream(file)){
^
exception thrown from implicit call to close() on resource variable 'fin'
1 error

So what went wrong? The try-with-resources statement calls method close() just
before the completion of the try block. Note that if method close() throws any
exception, it should be taken care of by your method; it must either catch it or declare
it to be thrown. Code that handles both FileNotFoundException and IOException
is correct:
class AutoClose {
Code to initialize fin
void readFileContents(String fileName) {
can throw FileNotFile file = new File(fileName);
FoundException;
try (FileInputStream fin = new FileInputStream(file)){
calling close on fin can
//.. some code
throw IOException.
Details of
}
this code not
catch (IOException e) {
required
Catch IOException
System.out.println(e.toString());
(IOException is superclass
}
of FileNotFoundException).
}
}

The following code declares IOException to be thrown, and so it’s also correct:
Declares
IOException
class AutoClose {
to be thrown.
void readFileContents(String fileName) throws IOException {
File file = new File(fileName);
try (FileInputStream fin = new FileInputStream(file)){
//.. some code
}
Initialization of fin may throw
}
FileNotFoundException; calling close on
}
fin may throw IOException.

Did you notice that the try block defined in the preceding code wasn’t followed by
either a catch or a finally block? This is unlike a regular try block, which must be
followed by either a catch or a finally block.

Licensed to Mark Watson 

424

CHAPTER 6

Exceptions and assertions

FileInputStream implements java.io.Closeable, which, starting with Java 7, extends java.lang.AutoCloseable. So FileInputStream
is a valid resource to be used by the try statement.
NOTE

6.5.2

Suppressed exceptions
In a try-with-resources statement, if both the code in the try block and close()
throw an exception, the exception thrown by close() is suppressed by the exception
thrown by the try block.
The resources initialized by the try-with-resources statement are automatically
closed, just before the end of execution of the try block. This happens regardless of
whether any exceptions are thrown. Let’s understand the flow of code by using class
RiverRaft, which implements the AutoCloseable interface:
class RiverRaft implements AutoCloseable {
public RiverRaft() throws Exception {
System.out.println("Start raft");
}
public void crossRapid() throws Exception {
System.out.println("Cross rapid");
throw new Exception("Cross Rapid exception");
}
public void close() throws Exception {
System.out.println("Reach river bank");
}
}

The class SuppressedExceptions initializes an instance of RiverRaft by using a
try-with-resources statement:

b
public class SuppressedExceptions {
public static void main(String[] args) throws Exception {
try ( RiverRaft raft = new RiverRaft(); ) {
raft.crossRapid();

c

}
catch (Exception e) {
System.out.println("Exception caught:" + e);
}

d

}

Instantiate
RiverRaft; no
exceptions
thrown.

Method crossRapid()
throws Exception.

close() called on
raft, before control
is transferred to
exception handler

}

Though not required, I’ve deliberately used a semicolon (;)
after declaring and initializing raft in the preceding code. Watch out for
questions on the exam that include or exclude a semicolon at the end
of the resource defined by a try-with-resources statement. A try-withresources statement can declare multiple resources, which are separated by
a semicolon. After the last resource declaration, a semicolon is optional.

EXAM TIP

For the code at B, the try-with-resources statement creates a RiverRaft instance. For
the code at c, method crossRapid() throws an exception, but the try-with-resources

Licensed to Mark Watson 

Auto-closing resources with a try-with-resources statement

425

statement executes close() d before passing the control to the exception handler.
Here’s the output of the preceding code:
Start raft
Cross rapid
Reach river bank
Exception caught:java.lang.Exception: Cross Rapid exception

Now what happens if close() in RiverRaft also throws an exception? Which exception will be propagated to the exception handler? Will it be the exception from
close() or from crossRapid()? Here’s the modified code:
public class SuppressedExceptions {
public static void main(String[] args) throws Exception {
try ( RiverRaft raft = new RiverRaft(); ) {
raft.crossRapid();
}
catch (Exception e) {
System.out.println("Exception caught:" + e);
}
}
}
class RiverRaft implements AutoCloseable {
public RiverRaft() throws Exception {
System.out.println("Start raft");
}
public void crossRapid() throws Exception {
Method crossRapid()
System.out.println("Cross rapid");
throws Exception.
throw new Exception("Cross Rapid exception");
}
public void close() throws Exception {
Method close() also
System.out.println("Reach river bank");
throws Exception.
throw new Exception("Close exception");
}
}

The exception from method crossRapid() made it the exception handler. Here’s the
output of the preceding code:
Start raft
Cross rapid
Reach river bank
Exception caught:java.lang.Exception: Cross Rapid exception

Because the output of the previous code snippets looks identical, what do you think
happened to the exception thrown by method close()? This exception was suppressed
by the exception thrown by crossRapid(). This is a new automatic resource management feature in Java 7. In a try-with-resources statement, when the code enclosed
within the try body (try block minus its initialization code) throws an exception, followed by an exception thrown by the try-with-resources statement (which implicitly
calls method close()), then the latter is suppressed.
Even though the exception thrown by close() is suppressed in the preceding
code, it’s associated with the exception thrown by crossRapid(). You can retrieve the

Licensed to Mark Watson 

426

CHAPTER 6

Exceptions and assertions

suppressed exceptions by calling getSuppressed() on the exception that has suppressed the other exceptions. The getSuppressed() method returns an array containing all of the exceptions that were suppressed in order to deliver the exception
thrown by crossRapid(). The following modified code shows how you can trace the
exception thrown by method close(). Because only one exception was suppressed,
the code accessed the first element of the array returned by getSuppressed():
public class SuppressedExceptions {
public static void main(String[] args) throws Exception {
try ( RiverRaft raft = new RiverRaft(); ) {
raft.crossRapid();
}
catch (Exception e) {
System.out.println("Exception caught:" + e);
Throwable[] exs = e.getSuppressed();
Retrieves and prints
if (exs.length>0)
first suppressed
exception.
System.out.println(exs[0]);
}
}
}
class RiverRaft implements AutoCloseable {
public RiverRaft() throws Exception {
System.out.println("Start raft");
}
public void crossRapid() throws Exception {
System.out.println("Cross rapid");
throw new Exception("Cross Rapid exception");
}
public void close() throws Exception {
System.out.println("Reach river bank");
throw new Exception("Close exception");
}
}

EXAM TIP In a try-with-resources statement, when the code enclosed
within the try body (try minus its initialization code) throws an exception, followed by an exception thrown by the try-with-resources statement (which implicitly calls method close()), then the latter exception
is suppressed. getSuppressed() never returns null. If there aren’t any
suppressed expressions, the length of the returned array is 0.

Now, let’s take a quick look at the nuts and bolts of using a try-with-resources statement, which you should know for this exam.

6.5.3

The right ingredients
Working with a try-with-resources statement can be tricky because it involves several
points to be taken care of. Let’s start with the declaration of multiple resources in a
try-with-resources statement.

Licensed to Mark Watson 

Auto-closing resources with a try-with-resources statement

427

DECLARING AND INITIALIZING RESOURCES

The variables used to refer to resources are implicitly final variables. You must declare
and initialize resources in the try-with-resources statement. You can’t define un-initialized
resources:
void copyFileContents(String inFile, String outFile) throws IOException{
try (FileInputStream fin;
Won’t compile; resources
FileOutputStream fout;){
must be initialized.
//..rest of the code
}
}

It’s acceptable to the Java compiler to initialize the resources in a try-with-resources
statement to null, only as long as they aren’t being reassigned a value in the try block
(as they are implicitly final):
void copyFileContents(String inFile, String outFile) throws IOException{
try (FileInputStream fin = null;
Will compile successfully,
FileOutputStream fout = null;){
if initialized with null, only
//..rest of the code
as long as it isn’t used.
}
}

But it doesn’t make much sense to initialize a final variable with null. Once initialized
to null, a resource can’t be reassigned a value in a try-with-resources statement. If
you try to do so, the code won’t compile:
void copyFileContents(String inFile, String outFile) throws IOException{
try (FileInputStream fin = null;
Assigned null
FileOutputStream fout = null;){
fin = new FileInputStream(inFile);
//..rest of the code
}
}

to resources.
Won’t compile; you
can’t reassign another
value to fin.

The variables defined in a try-with-resources statement are
implicitly final.

EXAM TIP

SCOPE OF THE RESOURCE DECLARED BY TRY-WITH-RESOURCES IS LIMITED TO IT
The resource declared by try-with-resources is closed just before the completion of
the try block. Also, its scope is limited to the try block. So if you try to access it outside the try block, it won’t compile. Following is an example of a simple method that

tries to copy contents of one file to another:
class AutoClose {
void copyFileContents(String inFile, String outFile)
throws IOException{
try (FileInputStream fin = new FileInputStream(inFile);
FileOutputStream fout = new FileOutputStream(outFile)){

Licensed to Mark Watson 

Scope
of fout
limited to
try block

428

CHAPTER 6

Exceptions and assertions

byte [] buffer = new byte[1024];
int i = 0;
while ((i = fin.read(buffer)) != -1)
fout.write(buffer,0,i);
}
finally {
fout = new FileOutputStream(inFile);
}

Won’t compile; code
outside try block can’t
access variable fout.

}
}

A SEMICOLON MIGHT NOT FOLLOW THE LAST RESOURCE DECLARED BY TRY STATEMENT
You can initialize multiple resources in a try-with-resources statement, separated by a
semicolon (;). It isn’t obligatory for a semicolon to follow the declaration of the last

resource, as shown in figure 6.9.
RESOURCES MUST IMPLEMENT JAVA.IO.AUTOCLOSEABLE OR ITS SUBINTERFACES
(DIRECTLY OR INDIRECTLY)
In the previous examples, I used objects of classes FileInputStream and FileOutputStream in a try-with-resources statement. These classes implement the java.io
.Closeable interface. Starting with Java 7, java.io.Closeable was modified to
extend the java.lang.AutoCloseable interface, so that classes implementing it could
be used with a try-with-resources statement.
class AutoClose {
void copyFileContents(String inFile, String outFile)
throws IOException{
try (FileInputStream fin = new FileInputStream(inFile);
FileOutputStream fout = new FileOutputStream(outFile)){
//..code
}
}
}
Okay to
include or exclude
the semicolon

class AutoClose {
void copyFileContents(String inFile, String outFile)
throws IOException{
try (FileInputStream fin = new FileInputStream(inFile);
FileOutputStream fout = new FileOutputStream(outFile);){
//..code
}
}
}

Figure 6.9 The last resource defined in a try-with-resources statement might not be followed by a
semicolon (;).

Licensed to Mark Watson 

Auto-closing resources with a try-with-resources statement

429

To use instances of your own class with a try-with-resources statement, you can implement the java.lang.AutoCloseable interface:
class MyAutoCloseableRes implements AutoCloseable{
MyAutoCloseableRes() {
System.out.println("Constructor called");
}
public void close() {
System.out.println("Close called");
}
}
class AutoClose2 {
void useCustomResources() {

MyAutoCloseableRes
implements AutoCloseable,
so it can be used in
try-with-resources.
MyAutoCloseableRes implements
the only method, close(), defined
by AutoCloseable.

try (MyAutoCloseableRes res = new MyAutoCloseableRes();){
System.out.println("within try-with-resources");
}
finally {
System.out.println("finally");
Try block doesn’t
}
catch any exception,
}
}
class Test {
public static void main(String args[]) {
new AutoClose2().useCustomResources();
}
}

An object
of MyAutoCloseableRes
can be used
in try-withresources.

because no method of
MyAutoCloseableRes
throws exception

The output of the preceding class Test is as follows:
Constructor called
within try-with-resources
Close called
finally

DEFINITION OF INTERFACES JAVA.LANG.AUTOCLOSEABLE AND JAVA.IO.CLOSEABLE

On the exam, you might get to answer explicit questions on the exceptions that are
thrown by method close() defined in the AutoCloseable and Closeable interfaces.
Following is the definition of the AutoCloseable interface from the Java source code
(minus the comments):
package java.lang;
public interface AutoCloseable {
void close() throws Exception;
}

Method close() of AutoCloseable
throws Exception.

Here’s the definition of the Closeable interface from the Java source code (minus
the comments):
package java.io;
public interface Closeable extends AutoCloseable {
public void close() throws IOException;
}

Method close() of interface
Closeable throws IOException.

Licensed to Mark Watson 

430

CHAPTER 6

Exceptions and assertions

Method close() in the Closeable interface overrides method close() from the
AutoCloseable base interface. Method close(), which implements the Closeable
interface, won’t be able to throw exceptions that are broader than IOException. If
you implement an interface, you must have valid implementations for each method
defined in the interface.
THE RESOURCES DECLARED WITH TRY-WITH-RESOURCES ARE CLOSED IN THE REVERSE ORDER OF
THEIR DECLARATION

Class MyResource implements the AutoCloseable interface. Its constructor accepts
a name for its instance, which is printed when the constructor and method close()
are called:
class MyResource implements AutoCloseable{
MyResource implements
String name;
AutoCloseable so it can be
MyResource(String name) {
used with try-with-resources.
this.name = name;
System.out.println("Created:"+name);
}
public void close() {
Creates MyResource
System.out.println("Closed:"+name);
instance and assigns
}
it to res1.
}
class TestAutoCloseOrder {
Creates another
public static void main(String args[]) {
MyResource instance
try (MyResource res1 = new MyResource("1");
and assigns it to res2.
MyResource res2 = new MyResource("2")){
System.out.println("within try-with-resources");
}
finally {
System.out.println("finally");
}
}

Method close() is
called first on res2
and then on res1,
after execution of
code on this line.

}

Here’s the output of the preceding code:
Created:1
Created:2
within try-with-resources
Closed:2
Closed:1
finally

The resources declared with the try-with-resources are closed
in the reverse order of their declaration. In this and previous sections, we
covered exceptions and worked with how the exception handlers enable
you to recover from exceptional conditions during the execution of your
program. In the next section, you’ll see how the assertions enable you to
test and debug your assumptions and flow control during the development
of your code.

EXAM TIP

Licensed to Mark Watson 

Using assertions

6.6

431

Using assertions
[6.5] Test invariants by using assertions
While testing your code, have you ever come across a situation that made you think
that a variable in your code could be assigned more values than you assumed, or that
your code wasn’t executing as planned for a combination of values?
Assertions help you test your assumptions about the execution of code. For example, you could test your assumption by using a combination of control-flow and logging statements to print the message Error: pages should NOT be < 200, if the value of
pages is greater than 200, as follows:
void printReport() {
int pages = 100;
while (/*some condition*/) {
if (/*some condition*/) {
pages++;
}
}
if (pages<200)
System.out.println("Error: pages should NOT be < 200");
}

When
assumption
is not met,
print an error
message
should not
exceed 200

Because it’s an assumption, it needs to be tested and fixed during the development
phase. So you can use an assert statement to verify the preceding assumption:
void printReport() {
int pages = 100;
while (/*some condition*/) {
if (/*some condition*/) {
pages++;
}
}
assert (pages<200): " Error: pages should NOT be < 200";
}

Assertion
to verify
and test
assumptions

In the preceding example, the programmer’s assumption is coded as an assertion.
An assertion offers a way of indicating what should always be true. An assertion is
implemented using an assert statement that enables you to test your assumptions
about the values assigned to variables and the flow of control in your code. An
assert statement uses a boolean expression, which you believe to be true. If this
boolean expression evaluates to false, your code will halt its execution by throwing
an AssertionError.
Assertions are used for testing and debugging your code. They aren’t for error
checking, which is why they’re off by default. Assertions are disabled by default so they
don’t become a performance liability in deployed applications.
This exam will query you on testing invariants by using assertions, the short and
long form of assertions, and their appropriate and inappropriate use. Assertions were

Licensed to Mark Watson 

432

CHAPTER 6

Exceptions and assertions

Throws AssertionError
if it evaluates to false

Figure 6.10 The assert
statement’s simple form accepts a
boolean expression.

boolean
assert expression ;

introduced in version 1.4, and though powerful, are one of the underused features of
Java. Let’s start with the forms of assertions.

6.6.1

Exploring the forms of assertions
An assertion is defined by using an assert statement that can take two forms. The
simpler form uses only a boolean expression, as shown in figure 6.10.
If the boolean expression used in an assert statement (as shown in figure 6.10)
evaluates to false, the JRE will throw an AssertionError. Assuming that assertions
have been enabled, the following code
public class ThrowAssertionError {
public static void main(String args[]) {
assert false;
}
}

Throws AssertionError
because boolean value
following assert is false.

will throw the following error at runtime:
Exception in thread "main" java.lang.AssertionError
at ThrowAssertionError.main(ThrowAssertionError.java:3)

If the boolean expression used in an assert statement evaluates to false, an AssertionError is thrown (if assertions are enabled
at runtime).

EXAM TIP

As you can see, the preceding output doesn’t include any custom error message. To
include custom details with an AssertionError, you can use the longer form of an
assert statement, as shown in figure 6.11.
The expression used in the longer form of an assert statement is another way to
pass arguments to an AssertionError. If the boolean expression used in the longer
form of the assert statement (as shown in figure 6.11) evaluates to false, the JRE
Colon

boolean
assert expression : Expression ;

Must return a
value of any type

Figure 6.11 An assert statement’s
longer form accepts a boolean
expression and an expression.

Licensed to Mark Watson 

433

Using assertions

creates an object of AssertionError by passing the expression to its constructor. Assuming that assertions have been enabled, the following code uses the longer form of the
assert statement:
public class ThrowDetailedAssertionError {
public static void main(String args[]) {
assert false : "Testing Assertions";
}
}

Throws AssertionError
with message "Testing
Assertions"

The preceding code throws an AssertionError with the message Testing Assertions
at runtime:
Exception in thread "main" java.lang.AssertionError: Testing Assertions
at ThrowDetailedAssertionError.main(ThrowDetailedAssertionError.java:3)

The exam also will test you on the valid expressions used for the assert statement.
The shorter form of the assert statement uses one expression, which must evaluate to
a boolean value. The longer form of the assert statement uses two expressions: the
first one must evaluate to a boolean value, but the second can evaluate to any type of
primitive value or object. Look out for incorrect use of methods for the second
expression, whose return type is void. What do you think is the output of the class
ThrowAssertionError?
class Person {
void getNothing() {}
}
public class ThrowAssertionError {
public static void main(String args[]) {
assert false : new Person().getNothing();
assert true : new ArrayList().clear();
}
}

Line won’t compile
because getNothing
doesn’t return a value.
Line won’t compile
because clear doesn’t
return a value.

Because the methods getNothing() (class Person) and clear() (class ArrayList)
don’t return any value, the class ThrowAssertionError won’t compile. You might find
similar occurrences on the exam.
Class AssertionError uses the String representation of the value passed to its
constructor for its detailed message. A String representation of an object is retrieved
by calling its method toString(). Let’s look at a tricky use of the assert statement.
Class TrickAssert defines an assert statement and uses a boolean assignment for its
first expression and an object for its second expression. Assuming that assertions are
enabled, what do you think is the output of the following code?
class Person {
public String toString() {
return "Pirates of the Caribbean";
}
}

Licensed to Mark Watson 

434

CHAPTER 6

Exceptions and assertions

public class TrickAssert {
public static void main(String args[]) {
boolean b = false;
assert (b = true) : new Person();
}
}

A lot of programmers have answered that the preceding code throws an AssertionError:
Exception in thread "main" java.lang.AssertionError: Pirates of the Caribbean

But it doesn’t, because the expression (b = true) returns true. Revisit the example:
this expression is assigning the boolean literal value true to variable b; it isn’t comparing b with true. Also, the second expression returns an object of class Person.
AssertionError executes toString() to retrieve a String representation of an object
of class Person.
EXAM TIP The longer form of the assert statement uses two expressions: the first one must evaluate to a boolean value, but the second can
evaluate to any type of primitive value or object.

If you use an object of class Throwable for the second expression in an assert statement, it becomes the cause of the thrown AssertionError. Here’s an example:
public class DefineCauseOfAssertionError {
public static void main(String args[]) {
assert (false) : new FileNotFoundException("java.txt missing");
}
}

Assuming that assertions are enabled, the preceding example gives the following
output:
Exception in thread "main" java.lang.AssertionError:
java.io.FileNotFoundException: java.txt missing
at DefineCauseOfAssertionError.main(DefineCauseOfAssertionError.java:4)
Caused by: java.io.FileNotFoundException: java.txt missing

It’s time for our next “Twist in the Tale” exercise. Let me check whether you’ve been
able to retain a few concepts discussed in previous chapters.
Twist in the Tale 6.4

Let’s modify some of the code used in the previous examples. Which answer correctly
shows the output of class AssertionTwist?
public class AssertionTwist {
public static void main(String args[]) {
evenOdd(-11);
}
static void evenOdd(int num) {
if (num%2 == 0)
System.out.println("Even");

Licensed to Mark Watson 

Using assertions

435

else if (num%2 == 1)
System.out.println("Odd");
else {
System.out.println("This should never be printed");
assert false : new Person();
}
}
}
class Person {
private String toString () {
return "Pirates of the Caribbean";
}
}
a

Odd

b

This should never be printed:
AssertionError: Pirates of the Caribbean

c

This should never be printed:
AssertionError: Person@6b97fdOdd

d
e

Compilation error
A runtime exception

Do you think class AssertionTwist will give the same output for executing evenOdd(-10)?

In this section, you examined the nitty-gritty of the syntax for the assert statement. In
the next section, you’ll look at how to test invariants in your code.

6.6.2

Testing invariants in your code
The exam objective specifically states, test invariants by using assertions. You can use
assertions to test your assumptions about multiple types of invariants:
■
■
■

Internal invariants
Control-flow invariants
Class invariants

Let’s start with internal invariants.
INTERNAL INVARIANTS

When you implement the business logic for a method, you can make multiple assumptions about the values assigned to your variables. Here’s an example, which assumes
that a variable of type State can be assigned only the value ON or OFF:
enum State {ON, OFF};
public class InternalAssumption {
private void machineState(State state) {
switch (state) {

Licensed to Mark Watson 

436

CHAPTER 6

Exceptions and assertions

case ON: System.out.println("state is ON");
break;
case OFF: System.out.println("state is OFF");
break;
}
}
}

Let’s modify the code to include this assumption using an assert statement:
enum State {ON, OFF};
public class InternalAssumptionWithAssert {
private void machineState(State state) {
switch (state) {
case ON: System.out.println("state is ON");
break;
case OFF: System.out.println("state is OFF");
break;
default: assert false;
}
}
}

If value other than ON
or OFF is assigned to
state, AssertionError
is thrown

If the control of flow reaches line assert false;, the assumption of the programmer
is invalidated and the code throws an error (AssertionError). One of my colleagues
argued that there is no need for an assert statement here because a variable of type
State can’t be assigned values other than ON or OFF. If you agree, what happens if
more values are added to enum State during enhancement or maintenance of the
project? In this case, the original assumptions won’t hold true.
Let’s take a look at another example, in which a programmer assumes that the
protocol variable within transmitFile() can exist only with values FTP, HTTP,
and HTTPS:
public class InternalAssumption {
void transmitFile(String protocol, String fileName) {
if (protocol.equals("FTP")) {
//..code to transmit file using FTP
}
else if (protocol.equals("HTTP")) {
//..code to transmit file using HTTP
}
Assumption that
else if (protocol.equals("HTTPS")) {
protocol can take only
//..code to transmit file using HTTPS
values FTP, HTTP, HTTPS
}
else {
System.out.println("Control shouldn't reach here");
System.out.println("Only FTP, HTTP, HTTPS supported");
}
}
}

Licensed to Mark Watson 

437

Using assertions

Programmers often document their assumptions (within the code), as shown in the
previous example. A better alternative is to use assertions, so relevant action can be
taken when these assumptions fail. Following is the modified code:
public class InternalAssumption {
void transmitFile(String protocol, String fileName) {
if (protocol.equals("FTP")) {
//..code to transmit file using FTP
}
else if (protocol.equals("HTTP")) {
//..code to transmit file using HTTP
}
else if (protocol.equals("HTTPS")) {
//..code to transmit file using HTTPS
}
else {
assert false:"Only FTP, HTTP, HTTPS supported";
}
}
}

Code throws
AssertionError
(if assertions are
enabled and) if
protocol takes
value other than
FTP, HTTP, or HTTPS

Note that there is an important difference between unreachable code as defined by the
Java specification and code that shouldn’t be reached, as used in the previous example.
Unreachable code won’t compile. For example, in class UnreachableCode, the statement placed after the return statement is unreachable and won’t compile:
class UnreachableCode {
void unreachableStatement() {
return;
System.out.println("code CANNOT reach here");
}
}

Code won’t compile:
unreachable code.

On the other hand, code that shouldn’t be reached is code that shouldn’t execute in a
method if all goes as per a programmer’s assumption. Though it compiles successfully,
its execution represents a flaw in the implementation of programming logic. Here’s
an example:
class CodeThatShouldNotReach {
void unreachableStatement() {
int a = (int)(Math.random() * 4) + 1;
if (a>=2)
return;
else if (a <2)
return;
System.out.println("code SHOULD NOT reach here");
}
}

Licensed to Mark Watson 

Code compiles
successfully, but
its execution
means flaw in
implementation
logic

438

CHAPTER 6

Exceptions and assertions

Unreachable code isn’t the same as a programmer’s assumption
of code that shouldn’t be reached. Unreachable code won’t compile as per
Java’s rules. On the other hand, code that shouldn’t be reached compiles
successfully. But execution of this code means a flaw in the implementation of application logic.
EXAM TIP

Apart from internal invariants, you may make assumptions about the flow of control
in your code, as discussed in the next section.
CONTROL-FLOW INVARIANTS

You can use assertions at locations that you assume control should never be reached.
In method processImage(), a programmer assumes that an image (for an application) can be stored only at a server or on a local disk. The programmer uses a comment to state this assumption:
public class ControlFlowAssumption {
void processImage(String fileName) {
if (/* image stored on server*/) {
// get image from server
return;
}
else if (/* image stored on local disk*/){
// get image from local system
return;
}
// control should never reach here!
// because an image can be stored
// either on server or on local system
}
}

Assumption that code
control will never
reach this line

Replace the preceding comments in bold with an assert statement as follows:
public class ControlFlowAssumption {
void getImage(String fileName) {
if (/* image stored on server*/) {
// get image from server
return;
}
else if (/* image stored on local disk*/){
// get image from local system
return;
Use of assert statement for
}
control-flow invariants if
assert false;
control reaches here
}
}

CLASS INVARIANTS

Class invariants are methods that you use to validate the state of an object. The state of
an object can change due to explicit assignment or reassignment of its fields, or due
to any processing of field values. After each modification, class invariants can be used
to check whether all fields have valid data.

Licensed to Mark Watson 

Using assertions

439

Assume that you create a class to define and operate a data structure that stores a
sorted list of students in a class, sorted on their overall performance in an academic
year. Its methods to add new students or retrieve a student at a particular position
might assume that the existing student list is already sorted. A class invariant might be
that this list is sorted. You can place code to check this assumption in your methods.
For example
import java.util.*;
class Student {}
class SortedStudents {
Class
List students = null;
invariant
private boolean sorted() {
// returns true if list students is sorted
// false otherwise
}
public void addStudent(Student newStudent) {
assert sorted();
// code to add new student to list
Using class
}
invariant to validate
object state
public Student getStudent(String studentID) {
assert sorted();
// code to search list and retrieve matching Student object
}
}

It’s easy to confuse the appropriate and inappropriate use of assertions, and you’re
sure to see questions on it on the exam. In the next section, let’s cover some rules that
will help you answer these questions.

6.6.3

Understanding appropriate and inappropriate uses of assertions
The simple fact that assertions can be turned on or off, and are turned off by default,
makes them inappropriate for all your assumptions. Let’s work with some examples
in detail.
DON’T USE ASSERTIONS TO CHECK METHOD PARAMETERS OF PUBLIC METHODS

You can’t control the values of the arguments that are passed to your public methods;
these values can be called by classes written by other programmers. To validate the
parameters to public methods, you must use code that is guaranteed to execute.
Because assertions can be turned off, you can’t guarantee whether your assertion code
will execute.
In the example that follows, the public method result() in class DemoAssertion1
validates the arguments passed to it and throws an IllegalArgumentException for
invalid values. It doesn’t use an assertion to do so:
public class DemoAssertion1 {
public double result(int score, int maxVal) {
double resultVal = 0;
if (score > 0 && maxVal > 0) {
resultVal = /* some calculation */
}

Licensed to Mark Watson 

440

CHAPTER 6

Exceptions and assertions

else {
throw new IllegalArgumentException();
}
return resultVal;
}

Doesn’t uses
assertion to validate
method parameters
of a public method.

}

USE ASSERTIONS TO CHECK METHOD PARAMETERS OF PRIVATE METHODS

A private method can be called by the methods of the class in which it’s defined.
When a nonprivate method calls a private method, it usually passes validated parameter values to it. You can assert this assumption by using an assert statement. In the following example, method calcResult() uses the assert statement to check that both
values passed to it are greater than zero. If the assertion fails, it will throw an AssertionError (if assertions are enabled), or else return the calculated value:
class DemoAssertion2 {
private double calcResult(int score, int maxVal){
assert(score > 0 && maxVal > 0);
return (score / maxVal * 100);
}
}

Use assert to validate
method parameters of
a private method. Don’t
use Assertions to modify
variable values or state
of an object.

Examine the following code:
public class DemoAssertion3 {
int number = 10;
private void inappropriateAssertCondition(State state) {
assert (++number > 5);
// some other code;
Increments variable value to
}
evaluate assert expression.
}

b

The code in bold at B increments the value of the number variable, and then determines whether its value is greater than 5. This line of code will silently increment the value
of the instance variable number whenever method inappropriateAssertCondition()
executes on any instance of class DemoAssertion3. Depending on whether assertions
are enabled on the host system, the same code might output different values, which
might not be expected.
Similarly, you shouldn’t modify the state of an object in an assert statement:
public class DemoAssertion4 {
int number = 10;
MyClass myClass = new MyClass();
private void inappropriateAssertCondition(State state) {
assert (++number > 5): myClass.modifyDescription
("assertion error");
// some other code;
}
}

Licensed to Mark Watson 

Modifies state of
object myClass.

Using assertions

441

class MyClass {
String description = "No error";
public String modifyDescription(String val) {
description = val;
return description;
}
}

In the preceding example, expression2 (myClass.modifyDescription(..)) passed to
the assert statement modifies the state of object myClass, which it shouldn’t.
DON’T DEPEND ON ASSERTIONS TO MAKE YOUR CODE FUNCTION CORRECTLY

As mentioned previously, assertions can be turned on or off by the host machine on
which a piece of Java code is supposed to execute. Also, assertions are turned off by
default. So it’s never advisable to define code, using assertions, which you must execute for correct functioning of your code.
USE ASSERTIONS FOR SITUATIONS THAT CAN NEVER OCCUR (EVEN IN PUBLIC METHODS)

Use assertions for situations that can never occur, even in public methods. Use assertions to test invariants—internal invariants, control-flow invariants, or class invariants—in your code (discussed in detail in section 6.6.2).
By default, assertions are disabled, because they’re meant to test and verify your
code during the development and testing phases. But they can be easily enabled when
the program starts. You, as a programmer, can test your code during the development
and testing phase and execute the same code without the assertions enabled (and the
related extra overhead) on the production server. Of course, if the code on the production server behaves unexpectedly, you can enable assertions to determine whether
any of your logic is flawed, which is negating your assumptions.
EXAM TIP

Assertions can be turned on or off for specific classes or

packages.
You can enable assertions for a class, say, DemoAssertion, by using either of the following commands:
java –ea DemoAssertion
java –enableassertions DemoAssertion

All your assert statements are equivalent to blank statements, if assertions are disabled. For example, if assertions are disabled, the following method
private static double calcResult(int score, int maxVal) {
assert (score > 0 && maxVal > 0);
return (score/maxVal*100);
}

will execute as if no assert statement were defined in it, as follows:
private static double calcResult(int score, int maxVal) {
return (score/maxVal*100);
}

Licensed to Mark Watson 

442

CHAPTER 6

Exceptions and assertions

Similarly, you can use the command options –da and -disableassertions to disable
assertions.

6.7

Summary
Exception handling is a mechanism for handling errors and exceptional conditions
that arise during the course of a program. It also helps you define exception-handling
code separate from the main application logic. This chapter specifically covers the throw
statement and the throws clause, the try statement with multi-catch and finally
clauses, the try-with-resources statement, and custom exceptions and assertions.
I started this chapter by covering the throw statement and the throws clause. The
throws clause is added to a method declaration to specify that a method can throw
any of the listed exceptions (or its subclasses) during its execution. When an exceptional condition is encountered, a method might handle the exception itself, or throw
it to the calling method, by using the throw statement within a method.
You can create custom exceptions by extending class java.lang.Exception or any
of its derived classes. The name of an exception can convey a lot of information to
other developers or users, and this is one of the main reasons for defining a custom
exception. Custom exceptions also help you restrict the escalation of implementationspecific exceptions to higher layers. For example, you can wrap a data connection
exception in your own custom exception and rethrow the exception. You can create
custom checked exceptions by subclassing java.lang.Exception and a custom runtime exception by subclassing java.lang.RuntimeException. You can throw and handle custom exceptions like regular exceptions.
Starting with Java 7, you can handle multiple exceptions in the same handler by
using a try statement with multi-catch. This prevents a programmer from duplicating
the code required to handle multiple exceptions or to catch a much generalized exception. You need to follow the rules to catch multiple exceptions in a single block:
■
■

■
■

Exception names are separated by a vertical bar in the catch block.
The variable that accepts the exception object in the catch handler is implicitly final.
Exceptions can’t share an inheritance relationship.
You can use multi-catch and single-catch blocks for the same try block.

Prior to Java 7, developers used finally blocks to close resources such as file handlers, databases, or network connections. With the new language feature of the trywith-resources statement, you can declare resources and automatically close them.
The scope of the resources declared with a try-with-resources statement is limited to
it. Resources (classes) that implement the java.lang.AutoCloseable interface or any
of its subinterfaces can be used with the try-with-resources statement. If multiple
resources are declared by a try-with-resources statement, they’re closed in the reverse
order of their declaration.

Licensed to Mark Watson 

Review notes

443

Assertions enable you to test your assumptions about the execution of your code.
Assertions are coded by using the assert statement. They’re used for testing and debugging your code, and so are turned off by default. This also ensures that assert statements don’t become a performance liability when an application is deployed. An assert
statement takes two forms: the simpler form uses just a boolean expression, and the second form uses a boolean expression with an expression, which results in any value. If the
boolean expression in an assert statement evaluates to false, an AssertionError is
thrown. If an assert statement uses its second form, its expression is passed to the constructor of AssertionError, so that its details are displayed in the call stack when this
error is thrown. You can use assertions to test internal invariants, control-flow invariants,
and class invariants. Internal invariants are used to assert values in a loop, conditional
statements, or a switch-case. Control-flow invariants are used to assert the business logic
implemented in code, to assert that a particular line of code won’t execute for a set of values. Class assertions are used to assert the state of all objects of a class, before or after a
method executes on its object. You can also use assertions to validate parameter values
to a private method. You must not use assertions to verify method parameters passed to
public methods or to define code that must execute for correct functioning of your
code. Because assertions can be enabled and disabled, and are usually disabled by
default, you can’t be sure about execution of your assertions code.

REVIEW NOTES
This section lists the main points covered in this chapter.

Using the throw statement and the throws clause
■

■

■

■

■

■

The throws clause is part of a method declaration and lists exceptions that can
be thrown by a method.
The throws clause is used with a method declaration to specify that the method
won’t handle the mentioned exception (or subclasses) and might throw it to
the calling method. The calling method should handle this thrown exception
appropriately or declare it to be rethrown.
The throw statement is used to throw an exception from a method, constructor,
or an initialization block. When an exceptional condition occurs in a method,
that method can handle it (by using a try statement), or throw the exception to
the calling method by using the throw statement.
A method indicates that it throws a checked exception by including its name in
the throws clause, in its method declaration.
When you use a method that throws a checked exception, you must either
enclose the code within a try block or declare it to be rethrown in the calling
method’s declaration. This is also known as the handle-or-declare rule.
A method can throw a runtime exception or error irrespective of whether its
name is included in the throws clause.

Licensed to Mark Watson 

444

CHAPTER 6

■

■
■

Exceptions and assertions

A method can throw a subclass of the exception mentioned in its throws clause
but not a superclass.
A method can handle the exception and still declare it to be thrown.
A method can declare to throw any type of exception, checked or unchecked,
even if it doesn’t. But a try block can’t define a catch block for a checked
exception (other than Exception) if the try block doesn’t throw that checked
exception or use a method that declares to throw that checked exception.

Custom exceptions
■

■

■

■

■

■

■

You can create a custom exception by extending class java.lang.Exception or
any of its subclasses.
You can subclass java.lang.Exception or its subclasses (which don’t subclass
RuntimeException) to create custom checked exceptions.
You can subclass java.lang.RuntimeException or its subclasses to create custom runtime exceptions.
You can add variables and methods to a custom exception class, like a regular class.
The name of an exception can convey a lot of information to other developers or
users, which is one of the main reasons for defining a custom exception. A custom exception can also be used to communicate a more descriptive message.
Custom exceptions help you restrict the escalation of implementation-specific
exceptions to higher layers. For example, SQLException thrown by data access
code can be wrapped within a custom exception and rethrown.
You can throw and catch custom exceptions like the other exception classes.

Overriding methods that throw exceptions
■

■

■

■

■

With overriding and overridden methods, it’s all about which checked exceptions an overridden method and an overriding method declare, not about the
checked exceptions both actually throw.
If a method in the base class doesn’t declare to throw any checked exception,
the overriding method in the derived class can’t throw any checked exception.
If a method in a base class declares to throw a checked exception, the overriding method in the derived class can choose not to declare to throw any
checked exception.
If a method in a base class declares to throw a checked exception, the overriding method in the derived class can declare to throw the same exception or a
subclass of the exception thrown by the method in the base class. An overriding
method in the derived class can’t override a method in the base class, if it
declares to throw a more generic checked exception.
Method overriding rules apply only to checked exceptions. They don’t apply to
runtime (unchecked) exceptions or errors.

Licensed to Mark Watson 

Review notes

445

try statement with multi-catch and finally clauses
■
■

■
■

■
■

■
■

■

■

A multi-catch handler can be used to handle more than one unrelated exception.
To catch multiple exceptions in a single handler, separate the exceptions in a list
by using a vertical bar (|).
A finally block can follow a multi-catch block, like a regular catch block.
The exceptions that you catch in a multi-catch block can’t share an inheritance
relationship. If you try to do so, your code won’t compile.
You can combine multi-catch and single-catch blocks.
The same rules apply when combining multi-catch and single-catch blocks—that
is, more specific exceptions at the top and more general ones at the bottom.
You must define a single exception variable in the multi-catch block.
In a multi-catch block, the variable that accepts the exception object is implicitly final.
In a multi-catch block, the type of variable that accepts the exception object is
the most specific, common super-type of all featured exception classes. Most of
the time it’s likely to be Exception, but it could be more specific (for example,
IOException for classes FileNotFoundException and EOFException). If the
exception classes implement a common interface, then the variable is of an
intersection type, with the exception class and interface as its bounds.
Multi-catch blocks save you from duplicating code, if you need to execute the
same code for handling multiple exceptions.

Auto-close resources with try-with-resources statement
■

■

■

■

■
■

■

■

You can use a try-with-resources statement to define resources with a try statement that will be automatically closed after the try block completes its execution.
The try-with-resources is a type of try statement that can declare one or
more resources.
A resource is an object such as file handlers, databases, or network connections,
which should be closed after it’s no longer required.
A resource must implement the java.lang.AutoCloseable interface or any of
its subinterfaces (directly or indirectly) to be eligible to be declared by a trywith-resources statement.
The java.lang.AutoCloseable interface defines method close().
If declared within the try-with-resources statement, a resource is automatically
closed by calling its close() method at the end of the try block.
If method close() throws any exception, it should be taken care of by the
method that defines the try block; the method must either catch it or declare it
to be thrown.
A try-with-resources block might not be followed by a catch or a finally block.
This is unlike a regular try block, which must be followed by either a catch or a
finally block.

Licensed to Mark Watson 

446

CHAPTER 6

■

■

■

■

■

Exceptions and assertions

The resource declared by try-with-resources is closed immediately after the
completion of the try block. Its scope is limited to the try block, and if you try
to access it outside the try block, your code won’t compile.
The variables used to refer to resources are implicitly final variables. You must
declare and initialize resources in the try-with-resources statement.
It’s acceptable to the Java compiler to initialize the resources in a try-withresources statement to null, only as long as they aren’t being assigned a value
in the try block.
You can initialize multiple resources in a try-with-resources statement, separated by a semicolon (;). The semicolon after the last resource is optional.
Multiple classes like FileInputStream and FileOutputStream from file I/O
implement the java.io.Closeable interface, which extends the java.lang
.AutoCloseable interface.

Assertions
■
■

■

■
■

■

■

■

■

■

■

■

An assertion offers a way of asserting what should always be true.
An assertion is implemented by using an assert statement that enables you to
test your assumptions about the values assigned to variables and the flow of control in your code.
An assert statement uses a boolean expression, which you believe to be true. If
this boolean expression evaluates to false, an AssertionError is thrown (if
assertions are enabled).
Assertions are used for testing and debugging your code. They’re off by default.
Assertions are disabled by default so they don’t become a performance liability
in deployed applications.
An assertion is defined using an assert statement that can take two forms. The
simpler form uses only a boolean expression: assert ;.
The longer form of an assert statement includes an expression with the boolean
expression: assert :;. The second expression used here must return a value (of any type).
In the longer form of an assert statement, when the boolean expression evaluates to false, the JRE creates an object of AssertionError by passing the value
of the second expression to AssertionError’s constructor.
In the longer form of an assert statement, if you use a method with no return
value (void) for the second expression, your code won’t compile.
In the longer form of an assert statement, you can create an object for the second expression. Note that a constructor creates and returns an object, and so it
satisfies the requirement that the second expression must return a value.
You can test multiple types of invariants in your code by using assertions: internal invariants, control-flow invariants, and class invariants.
An assertion is used to verify that code that shouldn’t execute, never executes.

Licensed to Mark Watson 

447

Sample exam questions
■

■

■
■
■
■
■

■
■
■

■

Assertions can’t be used to verify unreachable code, because unreachable code
doesn’t compile.
You can use assertions at locations that you assume control should never
be reached.
You must not use assertions to check method parameters for public methods.
You can use assertions to check method parameters for private methods.
You must not use assertions to modify variable values or the state of an object.
Assertions can be enabled or disabled at the launch of a program.
Because assertions can be enabled or disabled, don’t use them to define code
that must execute in all cases.
Use the command-line option –ea or –enableassertions to enable assertions.
Use the command-line option –da or –disableassertions to disable assertions.
All assert statements are equivalent to blank statements, if the assertions
are disabled.
A generalized –da switch (no assertions enabled) corresponds to the default
JRE behavior.

SAMPLE EXAM QUESTIONS
Q 6-1. Consider the following code and then select the correct options. (Choose all
that apply.)
class ThrowException {
public void readFile(String file) throws IOException {
System.out.println("readFile");
}
void useReadFile(String name) throws IOException{
try {
readFile(name);
}
catch (IOException e) {
System.out.println("catch-IOException");
}
}
public static void main(String args[]) throws Throwable{
new ThrowException().useReadFile("foo");
}
}
a
b
c

// line3

// line4
// line5

Code on both line 1 and line 2 causes compilation failure.
Code on either line 1 or line 2 causes compilation failure.
If code on line 1 is changed to the following, the code will compile successfully:
public void readFile(String file) {d)

d

// line1
// line2

A change in code on line 3 can prevent compilation failure:
void useReadFile(String name) {

Licensed to Mark Watson 

448

CHAPTER 6

e

Exceptions and assertions

Code on either line 4 or line 5 causes compilation failure. If line 4 is changed to
the following, a ThrowException will compile:
public static void main(String args[]) throws Exception {

Q 6-2. Given the following line of code
String s = "assert";

which of the following code options will compile successfully? (Choose all that apply.)
a
b
c
d
e
f
g
h
i

assert(s == null : s = new String());
assert s == null : s = new String();
assert(s == null) : s = new String();
assert(s.equals("assert"));
assert s.equals("assert");
assert s == "assert" ; s.replace('a', 'z');
assert s = new String("Assert") : s.toString();
assert s == new String("Assert") : System.out.println(s);
assert(s = new String("Assert") : System.out.println(s));

Q 6-3. What’s the output of the following code?
class Box implements AutoCloseable {
Box() {
throw new RuntimeException();
}
public void close() throws Exception {
System.out.println("close");
throw new Exception();
}
}
class EJavaFactory{
public static void main(String args[]) {
try (Box box = new Box()) {
box.close();
}
catch (Exception e) {
System.out.println("catch:"+e);
}
finally {
System.out.println("finally");
}
}
}
a

catch:java.lang.RuntimeException
close
finally

b

catch:java.lang.RuntimeException
finally

Licensed to Mark Watson 

Sample exam questions
c

catch:java.lang.RuntimeException
close

d

close
finally

e

Compilation exception

449

Q 6-4. Paul has to modify a method that throws a SQLException when the method
can’t find matching data in a database. Instead of throwing a SQLException, the
method must throw a custom exception, DataException. Assuming the modified
method is defined as follows, which option presents the most appropriate definition
of DataException?
void accessData(String parameters) {
try {
//..code that might throw SQLException
}
catch (SQLException e) {
throw new DataException("Error with Data access");
}
}
a

class DataException extends Exception {
DataException() {super();}
DataException(String msg) { super(msg); }
}

b

class DataException extends RuntimeException {
DataException() {}
DataException(String msg) {}
}

c

class DataException {
DataException() {super();}
DataException(String msg) { super(msg); }
}

d

class DataException extends Throwable {
DataException() {super();}
DataException(String msg) { super(msg); }
}

Q 6-5. Which of the following options show appropriate usage of assertions? (Choose
all that apply.)
// INSERT CODE HERE
assert (b != 0) : "Can't divide with zero";
return (a/b);
}
a
b
c
d

public float divide(int a, int b) {
public static float divide(int a, int b) {
private static float divide(int a, int b) {
private float divide(int a, int b) {

Licensed to Mark Watson 

450

CHAPTER 6

Exceptions and assertions

Q 6-6. Which options can be inserted at //INSERT CODE HERE so the code compiles successfully? (Choose all that apply.)
class BreathingException extends Exception {}
class DivingException extends Exception {}
class Swimmer {
public void swim() throws BreathingException {}
public void dive() throws DivingException {}
}
class Swimming{
public static void main(String args[])throws
BreathingException, DivingException {
try {
Swimmer paul = new Swimmer();
paul.swim();
paul.dive();
}
//INSERT CODE HERE
}
}
a

catch(DivingException | BreathingException e){
throw e;
}

b

catch(Exception e){
throw e;
}

c

catch(DivingException | BreathingException e){
throw new DivingException();
}

d

catch(Exception e){
throw new Exception();
}

e

catch(Exception e){
throw new RuntimeException();
}

Q 6-7. Selvan defines two custom exception classes:
class BaseException extends IOException {}
class DerivedException extends FileNotFoundException {}

When Paul tries to use these exception classes in his own interfaces, the code
doesn’t compile:
interface Base {
void read() throws BaseException;
}
interface Derived extends Base {
void read() throws DerivedException;
}

Licensed to Mark Watson 

Sample exam questions

451

Which definition of read() in the Derived interface compiles successfully?
a
b
c
d
e
f
g

void
void
void
void
void
void
void

read() throws
read() throws
read();
read() throws
read() throws
read() throws
read() throws

FileNotFoundException;
IOException;
Exception;
BaseException;
RuntimeException;
Throwable;

Q 6-8. Given the following code, select the correct options:
class AssertTest {
static int foo = 10;
static boolean calc() {
++foo;
return false;
}
public static void main(String args[]) {
assert (calc());
System.out.println(foo);
}
}
a

If AssertTest is executed using the following command, it will print 11:
java -enable AssertTest

b

If AssertTest is executed using the following command, it will print 10:
java -ea AssertTest

c

If AssertTest is executed using the following command, it will print 10:
java -da AssertTest

d

If AssertTest is executed using the following command, it will throw an
AssertionError and print 11:
java -enableAssertions AssertTest

e

None of the above

Q 6-9. What’s the output of the following code?
class Box implements AutoCloseable {
public void emptyContents() {
System.out.println("emptyContents");
}
public void close() {
System.out.println("close");
}
}

Licensed to Mark Watson 

452

CHAPTER 6

Exceptions and assertions

class EJavaFactory{
public static void main(String args[]) {
try (Box box = new Box()) {
box.close();
box.emptyContents();
}
}
}
a

close
emptyContents
java.lang.RuntimeException

b

close
java.lang.RuntimeException

c

close
emptyContents
close

d

close
java.lang.NullPointerException

Q 6-10. Which of the following statements are correct? (Choose all that apply.)
a
b
c

d

e

You must initialize the resources in the try-with-resources statement.
try-with-resources can be followed only by the finally block.
Method close() on the resource is called irrespective of whether an exception
is thrown during its initialization.
If the close method is called on a resource within a try block, the implicit call
on close() will throw an exception.
The resources declared with try-with-resources are accessible within the catch
and finally blocks, if an exception is thrown during implicit closing of the
resources.

Q 6-11. What’s the output of the following code?
class Box implements AutoCloseable {
public void open() throws Exception {
throw new Exception();
}
public void close() throws Exception {
System.out.println("close");
throw new Exception();
}
}
class EJavaFactory{
public static void main(String args[]) {
try (Box box = new Box()) {
box.open();
}

Licensed to Mark Watson 

Sample exam questions

453

catch (Exception e) {
System.out.println("catch:"+e);
}
finally {
System.out.println("finally");
}
}
}
a

catch:java.lang.Exception
finally

b

catch:java.lang.Exception

c

close
finally

d

close
catch:java.lang.Exception
finally

e

close
catch:java.lang.Exception
catch:java.lang.Exception
finally

Q 6-12. Select the correct statements. (Choose all that apply.)
class Assert {
public void construction(double load){
double maxLoad = 1322976;
// calculations to re-assign a value to maxLoad
try {
if (maxLoad > 18753) assert false;
// line1
}
catch (AssertionError e) {
// log error
// recalculate load
}
}
}
a

b
c
d
e

When assertions fail, you should avoid recalculating variable values in the
error handler.
Code on line1 can throw an AssertionException.
Class Assert shows inappropriate use of assertions.
Class Assert won’t compile.
Class Assert will throw a runtime error.

Q 6-13. What’s the output of the following code?
class Box implements AutoCloseable {
public void close() throws Exception {
System.out.println("close");

Licensed to Mark Watson 

454

CHAPTER 6

Exceptions and assertions

throw new Exception();
}
}
class EJavaFactory{
public static void main(String args[]) {
try (Box box = new Box()) {
box.close();
box.close();
}
catch (Exception e) {
System.out.println("catch:"+e);
}
finally {
System.out.println("finally");
}
}
}
a

catch:java.lang.Exception

b

close
catch:java.lang.Exception
finally

c

close
close
catch:java.lang.Exception
finally

d

close
close
catch:java.lang.Exception
catch:java.lang.Exception
finally

e

close
close
java.lang.CloseException
finally

f

close
java.lang.RuntimeException
finally

Q 6-14. Examine the classes defined as follows and select the correct options. (Choose
all that apply.)
class
class
class
class
a
b

Jumpable extends RuntimeException {}
Closeable extends java.lang.Closeable{}
Thunder extends Throwable {}
Storm extends java.io.FileNotFoundException{}

The classes Jumpable and Closeable are unchecked exceptions.
It isn’t mandatory to include the name of exception Jumpable in the throws
clause of a method.

Licensed to Mark Watson 

Sample exam questions
c

d
e

455

You shouldn’t define a custom exception class the way class Thunder has been
defined.
Class Storm is a checked exception.
All these classes will not compile successfully.

Q 6-15. Assuming that assertions are enabled, what’s the output of the following code?
class MyAssertClass {
public static void main(String... args) {
String name = new String("Shreya");
boolean fail = false;
if (name == "Shreya") {
System.out.println(name);
}
else {
// I explicitly set the name to Shreya
// code cannot reach here
assert false;
}
}
}
a

No output

b

d

java.lang.AssertionError
java.lang.Exception
java.lang.RuntimeException

e

java.lang.AssertionException

f

None of the above

c

Q 6-16. What’s the output of the following code?
class Admission implements AutoCloseable{
String id;
Admission(String id) {
this.id = id;
}
public void close() {
System.out.println("close:"+id);
}
}
class CloseableResources {
public static void main(String... args) {
try (Admission admn1 = new Admission("2765");
Admission admn2 = new Admission("8582");){}
}
}
a

close:8582
close:2765

b

close:2765
close:8582

Licensed to Mark Watson 

456

CHAPTER 6

c
d

Exceptions and assertions

Compilation error
Runtime exception

Q 6-17. Which of the options, when inserted at //INSERT CODE HERE, will print close?
(Choose all that apply.)
//INSERT CODE HERE
public void close() {
System.out.println("close");
}
}
class EJavaFactory2 {
public static void main(String... args) {
try (Carton arton = new Carton()) {}
}
}
a
b
c
d
e

class
class
class
class
class

Carton
Carton
Carton
Carton
Carton

implements
implements
implements
implements
implements

AutoCloseable{
java.lang.Closeable{
java.lang.AutoCloseable{
ImplicitCloseable{
java.io.Closeable{

ANSWERS TO SAMPLE EXAM QUESTIONS
A 6-1. f
[6.1] Use throw and throws statements
Explanation: The code as is (without any changes) compiles successfully. Options (a)
and (b) are incorrect because a method (readFile()) can declare a checked exception (IOException) to be thrown in its throws clause, even if the method doesn’t
throw it.
Option (c) is incorrect. If line 1 is changed so that method readFile() doesn’t
declare to throw an IOException, method useReadFile() won’t compile. The catch
block in method useReadFile() tries to handle IOException, which isn’t thrown by
its try block. This causes compilation error.
Option (d) is incorrect. Code on line 3 will compile as it is. It’s okay for useReadFile() to handle the exception thrown by readFile() using try-catch and still
declare it to be rethrown using the throws clause.
Option (e) is incorrect. The code compiles successfully.
Option (f) is correct. Even though useReadFile() handles IOException and doesn’t
actually throw it, it declares it to be rethrown in its throws clause. So, the method that
uses useReadFile() must either handle the checked exception—IOException (or one
of its superclasses)—or declare it to be rethrown. Because Exception is a superclass of

Licensed to Mark Watson 

Answers to sample exam questions

457

IOException, replacing throws Throwable with throws Exception in the declaration
of method main() enables the code to compile.

A 6-2. b, c, d, e, f
[6.5] Test invariants by using assertions
Explanation: Option (a) is incorrect. For the longer form of the assert statement that
uses two expressions, you can’t enclose both expressions within a single parentheses.
Options (b) and (c) are correct. It’s optional to include the individual expressions
used in the longer form within a single parentheses.
Options (d) and (e) are correct. The shorter form of the assert statement uses
only one expression, which might or might not be included within parentheses.
Option (f) is correct. The semicolon placed after the condition s == "assert"
delimits the assert statement, and the statement following the semicolon is treated as
a separate statement. It’s equivalent to an assert statement using its shorter form followed by another statement, as follows:
assert s == "assert" ;
s.replace('a', 'z');

Option (g) is incorrect because the first expression in an assert statement must
return a boolean value. In this code, the first expression returns an object of class
String.
Option (h) is incorrect because the second expression in an assert statement
must return a value of any type. In this code, the return type of method println()
is void.
Option (i) is incorrect. It incorrectly encloses both expressions of the assert statement within a single pair of parentheses. If parentheses were removed, it’s also an illegal usage of the long form because it uses an expression that doesn’t return a boolean
value for its first expression and its second expression doesn’t return any value.
A 6-3. b
[6.3] Auto-close resources with a try-with-resources statement
Explanation: The constructor of class Box throws a RuntimeException, and so the box
variable isn’t initialized in the try-with-resources statement. Method close() of class
Box isn’t called implicitly because execution didn’t proceed inside the try block.
When try-with-resources throws an exception, the control is transferred to the
catch block. In this case, the exception handler prints catch:java.lang.RuntimeException. The finally block always executes, thereafter printing finally.

Licensed to Mark Watson 

458

CHAPTER 6

Exceptions and assertions

A 6-4. b
[6.4] Create custom exceptions
Explanation: Option (a) is incorrect because it defines custom exception DataException
as a checked exception. To use checked DataException, accessData() must specify it
to be thrown in its throws clause.
Option (b) is correct because it defines custom exception DataException as an
unchecked or runtime exception. Even though method accessData() throws a DataException, a runtime exception, it need not declare its name in its throws clause.
Option (c) is incorrect. Class DataException extends class Object and not
Exception. Also, this code won’t compile because class Object doesn’t define a constructor that matches Object(String).
Option (d) is incorrect. If DataException extends Throwable, accessData() won’t
compile because it’s also a checked exception and therefore must be handled or
declared.
A 6-5. c, d
[6.5] Test invariants by using assertions
Explanation: Options (a) and (b) are incorrect because assertions must not be used to
check method arguments for nonprivate methods. Nonprivate methods can be called
by objects of other classes, and you can’t validate their method arguments by using
assertions. Assertions can be disabled at runtime, so they aren’t the right option to validate method arguments to public methods. You should throw exceptions in this case.
For example, when you come across invalid values that are passed to a nonprivate
method, you can throw an IllegalArgumentException.
A 6-6. a, b, c, e
[6.1] Use throw and throws statements
[6.2] Use the try statement with multi-catch and finally clauses
Explanation: Method swim() throws a BreathingException, and method dive()
throws a DivingException, both checked exceptions. Method main() is declared to
throw both a BreathingException or DivingException.
The code that is inserted at //INSERT CODE HERE must do the following:
■
■

■

The option must use the correct syntax of try with single-catch or multi-catch.
Because method main() throws both a BreathingException and DivingException, the try statement might not handle it.
It must not throw a checked exception more generic than the ones declared by
method main() itself—that is, BreathingException and DivingException.

Licensed to Mark Watson 

Answers to sample exam questions

459

Option (a) is correct. It defines the correct syntax to use try with multi-catch, and
rethrows the caught instance of BreathingException or DivingException.
Option (b) is correct. Though the type of the exception caught in the exception
handler is Exception, the superclass of the exceptions thrown by main(), the compiler knows that the try block can throw only two checked exceptions, BreathingException or DivingException. So this thrown exception is caught and rethrown
with more inclusive type checking.
Option (c) is correct. The multi-catch statement is correct syntactically. Also, the
throw statement throws a checked DivingException, which is declared by method
main()’s throws clause.
Option (d) is incorrect. The catch block throws an instance of checked exception
Exception, the superclass of the exceptions declared to be thrown by main(), which
isn’t acceptable.
Option (e) is correct. It isn’t obligatory to include the names of the runtime exceptions thrown by a method in its throws clause.
A 6-7. c, e, f
[6.1] Use throw and throws statements
[6.4] Create custom exceptions
Explanation: Though the question seems to be testing you on the hierarchy of exception classes IOException and FileNotFoundException, it’s not. This question is about
creating custom exception classes with overridden methods that throw exceptions.
To understand the explanation, note the following class hierarchies:
■
■
■
■

Exception FileNotFoundException extends IOException.
Exception BaseException extends IOException.
Exception DerivedException extends FileNotFoundException.
Exception DerivedException doesn’t extend BaseException.

This class hierarchy implies the following:
■
■

BaseException isn’t a type of DerivedException.
BaseException and FileNotFoundException are unrelated exceptions.

To override read() in the Base interface, read() in the Derived interface must either
declare to throw a BaseException, any derived classes of BaseException, any runtime
exception or errors, or no exception.
Option (a) is incorrect because FileNotFoundException is unrelated to BaseException.
Options (b), (d), and (g) are incorrect because IOException, Exception, and
Throwable are all superclasses of BaseException (and not subclasses) and therefore
invalid when overriding method read().

Licensed to Mark Watson 

460

CHAPTER 6

Exceptions and assertions

A 6-8. c
[6.5] Test invariants by using assertions
Explanation: Options (a) and (d) are incorrect. -enable and –enableAssertions
are invalid switch options. The correct switch options to enable assertions are –ea and
–enableassertions. If you use an invalid argument (like -enable) the program will
not run, but will exit immediately with an “Unrecognized option” error. Option (b) is
incorrect. With assertions enabled, assert(calc()) will evaluate to assert(false)
and throw an AssertionError, exiting the application, before printing any values.
Option (c) is correct. With assertions disabled, the assert (calc()) statement is
equivalent to an empty statement or a nonexistent line of code. So method calc()
isn’t executed, and the value of the static variable foo (10) is printed.
A 6-9. c
[6.3] Auto-close resources with a try-with-resources statement
Explanation: An implicit or explicit call to method close() doesn’t set a resource to
null, and you can call methods on it. So the try block results in the following:
■
■
■

Explicit call to method close()
Explicit call to method emptyContents()
Implicit call to method close()

No exceptions are thrown, and the code prints the output as shown in option (c).
A 6-10. a, b
[6.2] Use the try statement with multi-catch and finally clauses
[6.3] Auto-close resources with a try-with-resources statement
Explanation: Option (c) is incorrect. If a resource couldn’t be initialized because of
an exception, it doesn’t exist. There’s no point in auto-closing such a resource.
Option (d) is incorrect. Subsequent calls to close() don’t throw an exception.
Option (e) is incorrect. The resources declared within a try-with-resources statement are accessible only within the try block.
A 6-11. d
[6.3] Auto-close resources with a try-with-resources statement
Explanation: The code box.open() within the try block throws an Exception. Before
the control is transferred to the exception handler, the resource box is implicitly
closed by calling its close() method, which also throws an Exception.
If an exception is thrown from the try block and one or more exceptions are
thrown from the try-with-resources statement, then those exceptions thrown from

Licensed to Mark Watson 

Answers to sample exam questions

461

the try-with-resources statement are suppressed. So the exception thrown by the
implicit call to box.close() is suppressed. A suppressed exception forms the cause of
the exception that suppresses it. It can be retrieved by using method getSuppressed()
on the exception handler, as follows:
catch (Exception e) {
System.out.println("catch:"+e);
for (Throwable th : e.getSuppressed())
System.out.println(th);
}

It prints the following:
catch:java.lang.Exception
java.lang.Exception

A 6-12. c
[6.5] Test invariants by using assertions
Explanation: Option (a) is incorrect, and (c) is correct. Though verifying the value of
variable maxLoad using the assert statement is valid and appropriate usage of an assertion, handling an AssertionError isn’t. You should never handle an AssertionError.
Assertions enable you to test your assumptions while you’re developing your applications. You must not try to recover from an AssertionError. If an assumption fails, you
must correct the code or logic accordingly. If you foresee exceptional conditions in the
production version of your application, use exceptions instead of assertions.
Option (b) is incorrect because the code on line 1 can throw an AssertionError
and not an AssertionException.
Options (d) and (e) are incorrect. Class Assert will compile successfully. Note that
assert (small a) is a keyword, and not Assert (capital A). When executed with assertions enabled, the assert statement might throw an AssertionError, which will be
handled by the catch block. So class Assert won’t throw a runtime exception and
might throw an AssertionError.
A 6-13. c
[6.3] Auto-close resources with a try-with-resources statement
Explanation: This question tries to trick you with explicit calls of method close()
placed in the try block. Though close() is implicitly called to close a resource, it’s
possible to call it explicitly in the try block. But the explicit call to close() is independent of the implicit call to close() for each resource defined in the try-withresources statement.
The first call to method close() prints close. Because this method call throws an
exception, the control is ready to be transferred to the catch block and thus the second explicit call to method close() doesn’t execute. But before the control is moved

Licensed to Mark Watson 

462

CHAPTER 6

Exceptions and assertions

to the catch block, the implicit call to method close() is made, which again prints
close and throws an exception. The exception thrown by the implicit call of method
close() is suppressed by the exception thrown by the explicit call of method close(),
placed before the end of the try block.
The control is then transferred to the catch block and, last, to the finally block.
A 6-14. b, c, d, e
[6.4] Create custom exceptions
Explanation: Option (a) is incorrect because java.lang.Closeable is undefined. So
class Jumpable won’t compile.
Option (e) is a correct option because class Jumpable won’t compile.
A 6-15. b
[6.5] Test invariants by using assertions
Explanation: When the assertion fails, an instance of AssertionError is thrown. The
condition (name == "Shreya") evaluates to false because it compares the object references and not the String values.
A 6-16. a
[6.3] Auto-close resources with a try-with-resources statement
Explanation: The code compiles successfully, and no runtime exceptions are thrown
during its execution. The resources initialized in a try-with-resources statement are
closed in the reverse order.
A 6-17. a, c, e
[6.3] Auto-close resources with a try-with-resources statement
Explanation: The code will print close, if class Carton implements the java.lang
.AutoCloseable interface or any of its subinterfaces. Options (a) and (c) are correct,
because Carton implements the interface java.lang.AutoCloseable itself.
Option (b) is incorrect. The Closeable interface that extends the java.lang
.AutoCloseable interface is defined in the java.io package.
Option (d) is incorrect. The Java API doesn’t define any interface with the name
ImplicitCloseable in the java.lang package.
Option (e) is correct because java.io.Closeable is a subinterface of java.lang
.AutoCloseable.

Licensed to Mark Watson 

Java I/O fundamentals

Exam objectives covered in this chapter

What you need to know

[7.1] Read and write data from the console

How to access the unique console related
to a JVM
How to read from and write data to the
console

[7.2] Use streams to read from and write to files by
using classes in the java.io package including
BufferedReader, BufferedWriter, File,
FileReader, FileWriter, DataInputStream,
DataOutputStream, ObjectOutputStream,
ObjectInputStream, and PrintWriter

The definitions of byte I/O streams and
character streams, and their similarities
and differences
How to read and write raw bytes, primitive
data, strings, and objects to files by using
one class or a combination of classes

The Java I/O API is powerful and flexible. It enables you to read and write multiple
types of data: raw bytes, characters, and even objects. You can read data from multiple and diverse data sources such as files, network sockets, and memory arrays, and
write them to multiple data destinations. Java I/O also provides the flexibility of
reading or writing buffered and unbuffered data. You can also chain multiple input
and output sources. You can use the same methods to read from an input resource
such as a file, a console, or a network connection.
The Java I/O API is huge and could be a textbook on its own. Coverage in this
chapter is limited to the Java I/O exam topics for the OCP Java SE 7 Programmer II
exam. This chapter assumes no prior knowledge of Java I/O.

463

Licensed to Mark Watson 

464

CHAPTER 7

Java I/O fundamentals

Introduction to I/O concepts is covered in warm-up section 7.1. If
you’re good with the basics of I/O, please skip this section and move to
section 7.2.

NOTE

This chapter doesn’t cover the new file I/O (NIO.2). NIO.2 is covered in the next chapter. This chapter covers
■
■
■
■

■
■
■

Introduction to Java I/O
Types of streams: byte streams and character streams
Buffering data for faster reading and writing
Reading and writing bytes, primitives, and objects using data streams and
object streams
Chaining I/O streams
Writing formatted data to character streams
Reading data from and writing it to the console

Let’s start with the basics of Java I/O in the next section.

7.1

Introducing Java I/O: WARM-UP
Java I/O lets you read your files, data, photos, and videos from multiple sources and
write them to several destinations. You can use it to prepare a formatted report that
you can send to a printer. You can copy your files, create directory structures, delete
them, and do much more.
Java I/O includes a lot of classes and can be intimidating to work with. You must
get the hang of its basics, so you can categorize and work with a group of classes and
methods. Java abstracts all its data sources and data destinations as streams.

7.1.1

Understanding streams
A question that I’m asked quite often is: What is a stream—the data, data source, or
data destination? And it’s a fair question. A stream is a sequence of data. The Java I/O
stream is an abstraction of a data source or a data destination. It represents an object
that can produce data or receive data. An input stream is used to read data from a
data source. An output stream is used to write data to a data destination.
Just as a stream of water represents a continuous flow of water, a Java stream produces or consumes a continuous flow of data. I/O streams are used to move data from
a data source to a Java program, and from a Java program to a data destination.
■
■

An input stream enables you to read data from a data source to a Java application.
An output stream enables you to write data from a Java application to a data
destination.

Licensed to Mark Watson 

465

Introducing Java I/O: WARM-UP

File

File
x

Read from
source

Application

Java
application

1011100 01011001

Write to
destination

x

Application
011100 01011001

Memory

Memory
Input
stream

Network
socket

Output
stream

Data sources

Network
socket

Data destinations

Figure 7.1 Flavors of I/O streams: input streams and output streams. An input stream enables a
Java program to read data from a data source. An output stream enables a Java program to write data
to a data destination.

A Java program can read from multiple data sources and destinations, like a file on
your storage device, another application, an array in memory, a network connection,
and others. Figure 7.1 shows the flavors of I/O streams—input streams and output
streams—and how they’re connected to data sources and destinations.
The exam limits the use of streams to reading from and writing to
files. So the rest of the chapter covers reading and writing of data in multiple formats from and to only files.

NOTE

The exam covers multiple classes that can read and write bytes, characters, and
objects. Before we dive into the details of working with these classes, the next section includes a quick overview of the main types of I/O streams: byte streams and
character streams.

7.1.2

Understanding multiple flavors of data
Java I/O uses separate classes to read and write different types of data. The data is
broadly divided into byte data and character data. The byte streams read and write
byte data. To read and write characters and text data, Java I/O uses readers and writers, referred to as character streams. Byte streams can be used to read and write practically all kinds of data including zip files, image or video files, text or PDF files, and
others. Character streams are used to read and write character data. Figure 7.2 shows
these streams.

Licensed to Mark Watson 

466

CHAPTER 7

Java I/O fundamentals

I/O
streams

Byte streams

Read and write
byte data

.zip files

Character streams

Read and write
character data

Text files

Image files

PDF files

Figure 7.2 Main categories of
Java I/O streams: byte streams
and character streams

Creating files and reading or writing byte data from them or to them
Though you can read and write practically all byte data by using byte streams, you
might need the assistance of specific classes to interpret the read data or written
data in a particular format. For example, to create a PDF file, interpret its data, and
write data to it, you might need to use an API by Adobe or a third party.

Going back to the basics, a computer system reads and writes all data as binary data
(0s and 1s, referred to as bits) from all sources and to all destinations. A byte stream
reads and write bytes (8 bits) to and from data sources. All the other I/O classes build
on the byte streams, adding more functionality. For example, character streams take
into account Unicode characters and the user’s charset. Instead of reading or writing
8-bit data from or to a file, character streams define methods to read or write 16-bit
data. But behind the scenes they use byte streams to get their work done. Figure 7.3
shows the input and output streams for reading and writing byte and character data.
As mentioned earlier, the exam covers writing data to and reading data from only
files. A Java application uses FileOutputStream to write bytes to a file and FileInputStream to read bytes from a file. Similarly, it uses FileReader to read Unicode text
from a file and FileWriter to write Unicode text to a file.

Licensed to Mark Watson 

467

Introducing Java I/O: WARM-UP
(Write bytes to file)

(Write Unicode chars to file)

FileOutputStream

FileWriter

Java
application

File
(images, .zip,
8-bit text)

FileInputStream

FileReader

(Read bytes from file)

(Read Unicode chars from file)

File
(text)

Figure 7.3 A Java application uses FileInputStream and FileOutputStream to read
and write byte data from and to files, respectively. It uses FileReader and FileWriter to
read and write Unicode text from and to files.

Invoking the I/O methods to read and write (very small) basic units of data (byte or
character) isn’t efficient. Buffered streams add functionality to other streams by buffering data. Buffered streams read from a buffer (also referred to as memory or an
internal array), and the native API is called when it’s empty. Similarly, they write
data to a buffer and flush it to the underlying output stream when it’s full. A buffered stream buffers input/output from another input/output stream. Figure 7.4 shows

BufferedOutputStream

BufferedWriter

FileOutputStream

FileWriter

Java
application

File
(images, .zip,
8-bit text,
etc.)

FileInputStream

BufferedInputStream

FileReader

File
(text)

BufferedReader

Figure 7.4 A BufferedInputStream reads and buffers data from an InputStream like
FileInputStream. BufferedOutputStream buffers data before writing it to an
OutputStream like FileOutputStream. Similarly, a BufferedReader buffers input from
a Reader like FileReader. A BufferedWriter buffers data before sending it off to a
Writer like FileWriter.

Licensed to Mark Watson 

468

CHAPTER 7

Java I/O fundamentals

how BufferedInputStream, BufferedOutputStream, BufferedReader, and BufferedWriter buffer data from byte and character streams.
As shown in figure 7.4, a Java application communicates with an object of a buffered stream (like BufferedInputStream), which interacts with the underlying InputStream (like FileInputStream). Notice how figure 7.4 shows that the buffered stream
encloses within it an object of another stream. This pictorial representation will be
handy when you instantiate buffered streams in the next sections.
EXAM TIP To instantiate a BufferedInputStream, you need to pass it an
instance of another InputStream, like FileInputStream. Constructor
wrapping (passing instances to instantiate instances) can become complex in Java I/O.

All these buffered classes are specialized filter classes for the underlying stream. They
modify the way their underlying streams behave, by buffering data. The filter classes
such as BufferedInputStream and BufferedOutputStream are decorator classes. They
add functionality to the existing base classes.
Other InputStream and OutputStream subclasses you need to know for the exam
are classes DataInputStream and DataOutputStream. These classes define methods
for reading and writing primitive values and strings. For example, a method to read
int values, say, readInt, will read 4 bytes from an input stream and return an int
value. Similarly, a method to write an int value, say writeInt(), will write 4 bytes of
data. Figure 7.5 shows how you can read and write primitive values and strings from a
file by using DataInputStream and DataOutputStream. As I mentioned previously,
because the exam covers reading from and writing to files only, the images show the
data source and destination as files.
You can also read and write objects using object streams. Only objects that support
serialization (must implement marker interface Serializable) can be read from and
Read primitive data,
including byte and char

Write primitive data,
including byte and char
Java
application

Source

Destination
FileInputStream

DataInputStream

FileOutputStream

DataOutputStream

Figure 7.5 A DataInputStream can read primitive values (including byte and char) and
strings from a file by using FileInputStream. A DataOutputStream can write primitive
values and strings to a file using FileOutputStream.

Licensed to Mark Watson 

469

Working with class java.io.File

Read objects

Write objects
Java
application

File

File
FileInputStream

FileOutputStream

ObjectInputStream

ObjectOutputStream

Figure 7.6 An ObjectInputStream reads an object from a file by using a
FileInputStream. An ObjectOutputStream writes an object to a file by using a
FileOutputStream.

written to a data source. The object stream classes are ObjectInputStream and ObjectOutputStream. Figure 7.6 shows this arrangement.

To read and write data to a physical file, you need instances of java.io.File. All
classes that read from or write to a file either accept an instance of File or instantiate
one themselves using a path and filename. Before using streams to read data from and
write data to files, let’s work with File in the next section.
Java version 7 has introduced a new interface that offers the existing functionality of class File, addresses its existing issues, and offers
additional functionality: java.nio.file.Path. Because File is on the
exam, we’ll continue to use class File for all examples in this chapter.
Path is covered in chapter 8.
NOTE

7.2

Working with class java.io.File
[7.2] Use streams to read from and write to files by using classes in the
java.io package including BufferedReader, BufferedWriter, File,
FileReader, FileWriter, DataInputStream, DataOutputStream,
ObjectOutputStream, ObjectInputStream, and PrintWriter
To read from and write to files by using java.io classes, you’ll need to work with
class File.
File is an abstract representation of a path to a file or a directory. It can be absolute
or relative. An absolute path can be resolved without any further information. A relative path is interpreted from another path. The name of the class File is rather misleading because it might not be associated with an actual file (or directory) on a system.
You can create objects of class File to store information about the files or directories
on your system. You can use an object of class File to create a new file or directory,

Licensed to Mark Watson 

470

CHAPTER 7

Java I/O fundamentals

delete it, or inquire about or modify its attributes. You can read the contents of a file
or write to it by using byte and character I/O classes.
A File instance can refer to either a file or a directory. But it
might not be necessarily associated with an actual file or directory.

EXAM TIP

To read from or write to a file, you’ll need to instantiate File objects, which can be
used by I/O classes such as FileInputStream, FileOutputStream, FileReader, and
FileWriter. Because a File instance can refer to either a file or a directory, you might
want to verify that you aren’t writing your valuable data to a directory. Let’s work with
an example to instantiate File objects and check whether they represent files or
directories. The first step is to examine the File constructors:
Creates new File instance by converting given
pathname string into abstract pathname
Creates new File instance from
parent abstract pathname and
child pathname string

File(String pathname)
File(File parent, String child)
File(String parent, String child)

Creates new File instance from parent
pathname string and child pathname string

The next section includes an example to instantiate File objects (using the preceding
File constructors) and check whether they’re files or directories.

7.2.1

Instantiating and querying File instances
Say you’re given the directory in the following listing (C:\\temp, with its child files and
directories). Let’s count its (first-level) subdirectories and files. As shown in the following listing, the code should print one directory and two files.
Listing 7.1 Working with File instances

import java.io.*;
public class CountDirFiles {
public static void main(String... args) {
countDirFiles(new File("c:\\temp"));
}
public static void countDirFiles(File dir) {
if (dir.isDirectory()) {
int fileCount = 0;
int dirCount = 0;
String[] list = dir.list();
File item = null;
Creates File
for (String listItem : list) {
object
item = new File(dir, listItem);
if (item.isFile())
++fileCount;
Increments
else
if (item.isDirectory())
fileCount,
++dirCount;
for files
}

If a directory,
proceed
Retrieves files and
subdirectories
Iterates files and
subdirectories
Increments dirCount,
for directories

Licensed to Mark Watson 

Working with class java.io.File
System.out.println ("File(s):"+ fileCount);
System.out.println ("Dir(s):"+ dirCount);
}
else {
throw new IllegalArgumentException("Not a directory");
}
}

471

Throws
exception
if method
argument
isn’t
directory

}

The output of this code is as follows:
File(s):2
Dir(s):1

In this example, I created an object of class File and passed it to method countDirFiles(). This method checks whether the File object represents a directory,
using dir.isDirectory(). If dir.isDirectory() returns true, the method retrieves
a list of the names of its child files and directories, using method list(). Note that the
method returns a list of the names as an array of String. To determine whether an individual array item is a file or directory, we need to first create a File instance using directory dir and the item itself. This is accomplished by using the following line of code:
item = new File(dir, listItem);

You can query a File instance to determine whether it represents a file or a directory,
and increment the variables used to store the respective count, as follows:
if (item.isFile())
++fileCount;
else if (item.isDirectory())
++dirCount;

You can create a File instance that represents a nonexistant
file on your file system. And you can even invoke methods like isFile()
without getting an exception.
EXAM TIP

Revisit the following line of code from listing 7.1:
item = new File(dir, listItem)

This constructor creates a new file instance from a parent abstract pathname and a
child pathname string, using the following constructor defined in class File:
File(File parent, String child)

What happens when you replace new File(dir, listItem) from listing 7.1 with this
constructor: new File(listItem)? The code now prints 0 for both the file and directory count.
Why? If you create a File object, without specifying its complete path, it assumes that
the file that you’re referring to exists in the current directory (the one that contains

Licensed to Mark Watson 

472

CHAPTER 7

Java I/O fundamentals

your .class file). The variable listItem contains just the filename, without its complete
path. In the absence of the complete path name, the variable item refers to a nonexisting file on your system (because it’s looking in the wrong directory). Because the item
variable doesn’t represent a file or directory on your system, methods isFile() and
isDirectory() return false, and neither of the variable values, dirCount or fileCount, is modified.
EXAM TIP The objects of class File are immutable; the pathname represented by a File object can’t be changed.

7.2.2

Creating new files and directories on your physical device
Creating an object of class File won’t create a real file or directory on your system. To
create a new file on your system, use method createNewFile(), and to create new
directories, use methods mkdir() or mkdirs(), as listed in table 7.1.
Table 7.1 Methods used to create new physical files and directories
File method

Exception thrown

Description (from Java API documentation)

boolean
createNewFile()

IOException

Atomically creates a new, empty file named by
this abstract pathname if and only if a file with
this name doesn’t yet exist.

boolean mkdir()

SecurityException

Creates the directory named by this abstract
pathname.

boolean mkdirs()

SecurityException

Creates the directory named by this abstract
pathname, including any necessary but nonexistent parent directories. Note that if this operation fails, it may have succeeded in creating
some of the necessary parent directories.

Here’s an example that creates new files and directories:
import java.io.*;
public class CreateFileAndDirs {
public static void main(String... args) throws IOException {
File dirs = new File("\\usr\\code\\java");
Creates multiple
System.out.println(dirs.mkdirs());
directories in root
File file = new File(dirs, "MyText.txt");
directory
Try to
if (!file.exists())
create file
System.out.println(file.createNewFile());
If file doesn’t
}
exist
}

This code creates a directory tree usr\code\java and a file (MyText.txt) on our system:

Licensed to Mark Watson 

Using byte stream I/O

473

But the results might vary on your system (you might not have the access permission
to create files or directories). Be careful when you create objects of class File on your
system. Even though the following code creates an object of class File, with a weird
name, it compiles successfully:
File f = new File ("*&^%$.yu");

The preceding code doesn’t throw any exception because it won’t create a file on the
physical storage on your system. It creates only an object of class File, which as mentioned before, might not necessarily represent a file or directory. Because creation of
files and directories are system-specific operations, creation of a file with the name
*&^%$.yu might succeed on one system, but fail on another. For example, on a system
running Windows, calling method createNewFile() on this object will throw the following exception at runtime:
Exception in thread "main" java.io.IOException: The filename, directory
name, or volume label syntax is incorrect
at java.io.WinNTFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:883)

With the basic knowledge of using a File object, let’s deep-dive into how to read and
write the contents of a file by using byte streams.

7.3

Using byte stream I/O
[7.2] Use streams to read from and write to files by using classes in the
java.io package including BufferedReader, BufferedWriter, File,
FileReader, FileWriter, DataInputStream, DataOutputStream,
ObjectOutputStream, ObjectInputStream, and PrintWriter
Byte streams work with reading and writing byte data. They’re broadly categorized
as input streams and output streams. All input streams extend the base abstract
class java.io.InputStream, and all output streams extend the base abstract class
java.io.OutputStream. Let’s start with input streams.

7.3.1

Input streams
Class java.io.InputStream is an abstract base class for all the input streams in Java.
It’s extended by all the classes that need to read bytes (for example, image data) from
multiple data sources. Let’s start with the most important method of this class, read(),
used to read data from a source. The class InputStream defines multiple overloaded
versions of method read(), which can be used to read a single byte of data as int, or
multiple bytes into a byte array:
int abstract read()
int read(byte[] b)
int read(byte[] b, int off, int len)

Reads the next,
single byte of data
Reads multiple bytes of
data into a byte array

Licensed to Mark Watson 

474

CHAPTER 7

Java I/O fundamentals

But you wouldn’t use these methods yourself. You’d use method read() by morespecific classes that extend the abstract class InputStream. For example, class FileInputStream extends InputStream and overrides its read() method for you to use.
Watch out for the use of method read() from class InputStream. It returns the next byte of data, or -1 if the end of the stream is
reached. It doesn’t throw an EOFException.

EXAM TIP

Method close() is another important method of class InputStream. Calling close()
on a stream releases the system resources associated with it.
Because InputStream is an abstract class, you can’t create an instance to read from a
data source or destination. You need one of its subclasses to do the work for you. So
why do you need to bother with so much theory about it?

Why do you need to bother with so much theory on InputStream?
On the exam, you’ll be tested on whether you can call a particular method on an
object. For example, you might be tested on whether you can call the method read()
or close() on an object of, say, FileInputStream, a subclass of InputStream.
Because methods read() and close() are defined in class InputStream, they can
be called on objects of any derived classes of InputStream.
These might not be straightforward questions. You need to get the hang of the basics
to answer them correctly.

Table 7.2 contains a list of the main methods in class java.io.InputStream (from the
Java API documentation). Don’t worry about the details; we’ll cover the relevant
details in the subsequent sections.
Table 7.2 List of methods in class java.io.InputStream
Method name

Return type

Description

close()

void

Closes this input stream and releases any system
resources associated with the stream.

abstract read()

int

Reads the next byte of data from the input stream.

read(byte[] b)

int

Reads a number of bytes from the input stream and stores
them into the buffer array b.

read(byte[] b, int
off, int len)

int

Reads up to len bytes of data from the input stream into
an array of bytes.

Licensed to Mark Watson 

475

Using byte stream I/O

InputStream
{abstract}
<>

FileInputStream

BufferedInputStream
Figure 7.7

ObjectInputStream

DataInputStream

Abstract class InputStream and its subclasses (the ones that are on the exam)

Figure 7.7 shows the classes that extend the abstract class java.io.InputStream. In
the next sections, you’ll see how to use all these classes. Don’t panic: because they
share a lot of similarity, they’ll be simple to work with.
EXAM TIP All the classes that include InputStream in their name— FileInputStream, ObjectInputStream, BufferedInputStream, and DataInputStream—extend abstract class InputStream, directly or indirectly.

Apart from image files, you can also read character data by using byte streams. But you
aren’t encouraged to do so because the Java API defines well-defined classes for character I/O. On the exam, you’ll see questions on valid I/O code and recommended I/O
code. Though using a byte stream to read (and write) characters is valid code, it’s not
a recommended practice.
Let’s move on to this class’s counterpart for writing byte data to sources: class
java.io.OutputStream.

7.3.2

Output streams
Class java.io.OutputStream is also an abstract class. It’s the base class for all the output streams in Java. It’s extended by all the classes that need to write bytes (for example, image data) to multiple data destinations. The most important method of this
class is write(), which can be used to write a single byte of data or multiple bytes from
a byte array to a data destination:
Writes single byte
abstract void write(int b)
void write(byte[] b)
void write(byte[] b, int off, int len)

Writes multiple bytes
from a byte array

Methods close() and flush() are other important methods of class OutputStream.
Often data isn’t written directly to the output stream but buffered for an efficient
management of resources. If you want to write data to the output stream right away
without waiting for the buffer to be full, call flush(). Method close() is used to
release system resources being used by this stream.

Licensed to Mark Watson 

476

CHAPTER 7

Java I/O fundamentals

You’d usually work with method write() defined by more specific classes that
extend OutputStream. For example, class FileOutputStream extends OutputStream
and overrides its write() method.
Class OutputStream defines methods write(), flush(), and
close(). So these are valid methods that can be called on any objects of
classes that extend class OutputStream.
EXAM TIP

Table 7.3 contains a list of the methods in class java.io.OutputStream. I’ll cover the
important methods in the subsequent sections.
Table 7.3 Methods in class java.io.OutputStream
Method name

Return type

Description

close()

void

Closes this output stream and releases any system
resources associated with this stream.

flush()

void

Flushes this output stream and forces any buffered output
bytes to be written out.

write(byte[] b)

void

Writes b.length bytes from the specified byte array to this
output stream.

write(byte[] b,
int off, int len)

void

Writes len bytes from the specified byte array, starting at offset off to this output stream.

abstract
write(int b)

void

Writes the specified byte to this output stream.

Figure 7.8 shows abstract class java.io.OutputStream and its subclasses.
All the classes that include OutputStream in their name—
FileOutputStream, ObjectOutputStream, BufferedOutputStream, and
DataOutputStream—extend abstract class OutputStream, directly or indiEXAM TIP

rectly. Let’s start reading from and writing to files with byte streams.

OutputStream
{abstract}
<>

FileOutputStream

BufferedOutputStream

ObjectOutputStream

DataOutputStream

Figure 7.8 Abstract class OutputStream and its subclasses covered by this exam

Licensed to Mark Watson 

Using byte stream I/O

477

(Write bytes to file)
FileOutputStream

Java
application

File
(images, .zip,
8-bit text)

FileInputStream
(Read bytes from file)

7.3.3

Figure 7.9 A Java application
uses FileInputStream and
FileOutputStream to read and
write byte data from and to files.

File I/O with byte streams
Bytes can represent any type of data, including character data. Data in any file type—
Portable Document Format (PDF), zip, Microsoft Word, and others—can be read and
written as byte data. To read and write raw bytes from and to a file, use FileInputStream and FileOutputStream, as shown in figure 7.9.
Before you work with an example of reading from and writing to files, take note of
the constructors of FileInputStream and FileOutputStream. You can instantiate
FileInputStream by passing it the name of a file as a File instance or as a string
value. The constructor opens a connection to a file and might throw a FileNotFoundException, if the specified file can’t be found:
FileInputStream(File file) throws FileNotFoundException {}
FileInputStream(String name) throws FileNotFoundException {}

FileInputStream is instantiated by passing it a File or String
instance. It can’t be instantiated by passing it another InputStream. The
above-mentioned constructors of class FileInputStream throw a checked
exception, FileNotFoundException, which must be handled accordingly.
EXAM TIP

Instantiation of FileOutputStream creates a stream to write to a file specified either as
a File instance or a string value. You can also pass a boolean value specifying whether
to append to the existing file contents. Here are the overloaded constructors of class
FileOutputStream:
FileOutputStream(File file) throws FileNotFoundException
FileOutputStream(File file, boolean append) throws FileNotFoundException
FileOutputStream(String name) throws FileNotFoundException
FileOutputStream(String nm, boolean append) throws FileNotFoundException

The above-mentioned constructors of FileOutputStream
throw a FileNotFoundException, a checked exception. Also, during its
instantiation, you can specify whether to append data to an underlying
file or override its contents.
EXAM TIP

Licensed to Mark Watson 

478

CHAPTER 7

Java I/O fundamentals

Let’s work with an example of reading a PDF file and writing it to another file. A PDF
file never contains just character data. Even if a PDF file contains only characters, it also
contains formatting information, so as a whole, a PDF file can’t be considered character data. You can also use the PDF format for an image file, if you have any doubts.
Even though FileInputStream and FileOutputStream can be
used to write character data, you shouldn’t use them to do so. Unlike byte
I/O streams, character streams take into account a user’s charset and work
with Unicode characters, which are better suited for character data.
EXAM TIP

To read and write to separate PDF files, you need objects of java.io.FileInputStream and java.io.FileOutputStream that can connect to these input and output
PDF files. You need to call at least one method on FileInputStream to read data,
and one method on FileOutputStream to write data. Previously, I mentioned that
InputStream and OutputStream define methods read() and write() to read and
write a single byte of data, respectively. Let’s work with these methods in the following listing.
Listing 7.2 Using FileInputStream and FileOutputStream to read and write bytes
import java.io.*;
public class ReadWriteBytesUsingFiles {
Instantiates
public static void main(String[] args) throws IOException {
FileInputtry (
Stream
FileInputStream in = new FileInputStream("Sample.pdf");
FileOutputStream out = new FileOutputStream("Sample2.pdf");
Instantiates
)
FileOutputStream
Declares variable to store
{
a single byte of data
int data;

b

c

d

while ((data = in.read()) != -1) {
out.write(data);
}

f

}

Writes byte data
to destination file

e

Loops until end of
stream is reached (no
more bytes can be read)

}
}

EXAM TIP

Are you wondering why you need to create a variable of type

int to read byte data from a file in the preceding code? When a stream
exhausts itself and no data can be read from it, method read() returns -1,
which can’t be stored by a variable of type byte.

The code at B and c instantiates FileInputStream and FileOutputStream. The
constructors used in this code accept the filenames as String objects. You can also
pass the constructors’ instances of class File or String. At d, you declare a variable
of type int to store the byte data you read from the file. At e, you call in.read(),
which reads and returns a single byte from the underlying file Sample.pdf. When no
more data can be read from the input file, method read() returns -1, and this is when

Licensed to Mark Watson 

479

Using byte stream I/O

the while loop ends its execution. At f, you write a single byte of data to another file
by using out, an instance of FileOutputStream.
Copying a file’s content might not copy its attributes. To copy a
file, it’s advisable to use methods such as copy from class java.nio
.file.Files.
NOTE

I/O operations that require reading and writing of a single byte from and to a file are
a costly affair. To optimize these operations, you can use a byte array:
import java.io.*;public class ReadWriteBytesUsingFiles2 {
public static void main(String[] args) throws IOException {
try (
FileInputStream in = new FileInputStream(
new File("Sample.pdf"));
FileOutputStream out = new FileOutputStream("Sample2.pdf");
)
Creates byte
{
array of size 1024
int data;
byte[] byteArr = new byte[1024];
while ((data = in.read(byteArr)) != -1) {
out.write(byteArr, 0, data);
}
}
}

read(byteArr) reads
data into array byteArr

Writes only
read bytes to
output stream

}

Programmers are often confused about the correct use of method read() that accepts
a byte array. Unlike read(), read(byte[]) doesn’t return the read bytes. It returns the
count of bytes read, or -1 if no more data can be read. The actual data is read in the byte
array that is passed to it as a method parameter.
So do you think it makes a difference whether I write the complete byte array to
the output stream or write the count of the bytes that were read from the input
device? Let me test you on this concept in the next “Twist in the Tale” exercise.
Twist in the Tale 7.1

Let’s modify some of the code used in the previous example. Select the correct
options for the following code.
import java.io.*;
class Twist {
public static void main(String[] args) throws IOException {
try (
FileInputStream in = new FileInputStream("Twist.java");
FileOutputStream out = new FileOutputStream("Copy.java");
)

Licensed to Mark Watson 

480

CHAPTER 7

Java I/O fundamentals

{
int data;
byte[] byteArr = new byte[2048];
while ((data = in.read(byteArr)) != -1) {
out.write(byteArr);
}
}
}
}
a

b

c
d

Class Twist executes successfully and creates its own copy with the name
Copy.java.
Class Twist executes successfully, doesn’t throw any exceptions, but fails to create its own identical copy.
Class Copy.java fails to compile.
Class Twist doesn’t compile.

Watch out! Class FileOutputStream defines method write() that accepts an int
parameter. But when you use this method to write an int value, it writes only 1 byte to
the output stream. An int data type uses 4 bytes, or 32 bits. write(int) writes the 8
low-order bits of the argument passed to it. The 24 high-order bits are ignored. Here’s
an example that uses binary literals with underscores:
class FileStreamsAlwaysReadWriteBytes {
public static void main(String args[]) throws Exception {
try (
OutputStream os = new FileOutputStream("temp.txt");
Writes 1 byte
InputStream is = new FileInputStream("temp.txt");
(8 lower bits) to
underlying file;
) {
999 is written
int i999 = 0b00000000_00000000_00000011_11100111;
as 231
int i20 = 0b00000000_00000000_00000000_00010100;
os.write(i999);
os.write(i20);
Writes 20 because
System.out.println(i999 + ":" + is.read());
20 can be coded
System.out.println(i20 + ":" + is.read());
in 8 lower bits.
}
}
Prints 999:231
Prints 20:20
}

Method write(int) in class OutputStream writes a byte to
the underlying output stream. If you write an int value by using this
method, only the 8 low-order bits are written to the output stream; the
rest are ignored.
EXAM TIP

In the next section, you’ll see how buffering data reads and writes speeds up the
I/O processes.

Licensed to Mark Watson 

481

Using byte stream I/O

7.3.4

Buffered I/O with byte streams
Imagine you need to transfer a couple of boxes from one room of your home to
another. Would you need less time if you transferred one box at a time, or if you
loaded a couple of them in a container and moved the container? Of course, the latter would need less time. Similarly, buffering stores data in memory before sending a
read or write request to the underlying I/O devices. Buffering drastically reduces the
time required for performing reading and writing I/O operations.
To buffer data with byte streams, you need classes BufferedInputStream and
BufferedOutputStream. You can instantiate a BufferedInputStream by passing it an
InputStream instance. A BufferedOutputStream can be instantiated by passing it
an OutputStream instance. You can also specify a buffer size or use the default size.
Here are their constructors:
public
public
public
public

BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out, int size)

The exam might test you on how to instantiate buffered streams
correctly. To instantiate BufferedInputStream, you must pass it an object
of InputStream. To instantiate BufferedOutputStream, you must pass it
an object of OutputStream.
EXAM TIP

Let’s compare reading and writing a PDF file of considerable size with and without
using buffered streams. To start, let’s read and write the PDF file for the Java 7 Language specification (jls7.pdf):
import java.io.*;
import java.util.Date;
public class NonBufferedBytesReadWrite {
Size of
jls7.pdf is
public static void main(String[] args) throws IOException {
2.96 MB
try (
FileInputStream in = new FileInputStream("jls7.pdf");
FileOutputStream out = new FileOutputStream("jls7-copy.pdf");
)
{
long start = System.currentTimeMillis();
int data;
while ((data = in.read()) != -1) {
out.write(data);
}

Reads, writes
1 byte at a time

long end = System.currentTimeMillis();
System.out.println("MilliSeconds elapsed : " + (end-start));
}
}

Outputs milliseconds
used to copy jls7.pdf to
jls-copy.pdf.

}

Licensed to Mark Watson 

482

CHAPTER 7

Java I/O fundamentals

Let’s see how the same I/O process performs using buffered streams:
Creates BufferedInputStream,
import java.io.*;
passing it object of FileInputStream.
import java.util.Date;
public class BufferedReadWriteBytes{
public static void main(String[] args) throws IOException {
try (
FileInputStream in = new FileInputStream("jls7.pdf");
FileOutputStream out = new FileOutputStream("jls7-copy.pdf");
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos = new BufferedOutputStream(out);
)
{
Creates
long start = System.currentTimeMillis();
BufferedOutputStream,
Reads and
buffers data

passing it object of
FileOutputStream.

int data;
while ((data = bis.read()) != -1) {
bos.write(data);
}

Buffers and
writes data

long end = System.currentTimeMillis();
System.out.println("MilliSeconds elapsed : " + (end-start));
}
}

Outputs milliseconds used to
copy jls7.pdf to jls-copy.pdf.

}

As exhibited by the preceding examples, buffered I/O operations are way faster than
nonbuffered I/O operations.
You can use FileInputStream and FileOutputStream to read and write only byte
data from and to an underlying file. These classes (FileInputStream and FileOutputStream) don’t define methods to work with any other specific primitive data types or
objects, which is what you might need most of the time. In the next section, you’ll see
how to read and write primitive values and strings by using DataInputStream and
DataOutputStream.

7.3.5

Primitive values and strings I/O with byte streams
Data input and output streams let you read and write primitive values and strings from
and to an underlying I/O stream in a machine-independent way. Data written with
DataOutputStream can be read by DataInputStream. You can instantiate a DataInputStream by passing it an InputStream instance. A DataOutputStream can be constructed by passing it an OutputStream instance. Here are their constructors:
DataInputStream(InputStream in)
DataOutputStream(OutputStream out)

Here’s an example of reading and writing char, int, double, and boolean primitive
values and strings using data input and output streams:
import java.io.*;
public class ReadWritePrimitiveData {

Licensed to Mark Watson 

483

Using byte stream I/O
Instantiates
DataOutputStream by
passing
instance of
FileOutputStream

public static void main(String... args) throws IOException {
try (
FileOutputStream fos = new FileOutputStream(
new File("myData.data"));
DataOutputStream dos = new DataOutputStream(fos);
FileInputStream fis = new FileInputStream("myData.data");
DataInputStream dis = new DataInputStream(fis);
) {

Instantiates
DataInputStream by
passing
instance of
FileInputStream

dos.writeChars("OS");
dos.writeInt(999);
dos.writeDouble(45.8);
dos.writeBoolean(true);
dos.writeUTF("Will score 100%");

Writes primitive data
and Unicode String to
output stream

System.out.println(dis.readChar());
System.out.println(dis.readChar());
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
System.out.println(dis.readUTF());

Reads
second
char

Reads first char
Reads int value

//System.out.println(dis.readBoolean());
}
}
}

If uncommented, this line throws
EOFException at runtime.

Reads
double value
Reads Boolean
value
Reads Unicode
string value

The contents of file myData.data are shown
as a screenshot in figure 7.10. As you can
see, this file doesn’t store its data in humanreadable form. It can be reconstructed
using DataInputStream.
Any read operation by DataInput- Figure 7.10 Snapshot of contents of file
Stream past the end of the file will throw myData.data
an EOFException. The data should be read
in the same order as written by DataOutputStream. DataOutputStream writes the byte
data for the corresponding primitive data. Each data type might be interpreted in a different manner and occupy a different number of bytes (for example, int uses 4 bytes,
and double uses 8 bytes). If the data being read doesn’t match the data that was written,
you’ll get unexpected values. For example, if you write a double value and read two int
values instead, you’ll get unexpected values:
import java.io.*;
public class ReadWritePrimitiveData1 {
public static void main(String... args) throws IOException {
try (
FileOutputStream fos = new FileOutputStream(
new File("myData.data"));
DataOutputStream dos = new DataOutputStream(fos);

Licensed to Mark Watson 

484

CHAPTER 7

Java I/O fundamentals

FileInputStream fis = new FileInputStream("myData.data");
DataInputStream dis = new DataInputStream(fis);
) {
dos.writeDouble(45.8);
System.out.println(dis.readInt());
System.out.println(dis.readInt());
}
}

Writes double
value
Reads int value
Reads another
int value

}

The output of the preceding code is:
1078388326
1717986918

In the preceding code, bytes from a double value could be interpreted as int values,
though incorrectly. What happens if you try to read bytes from a char value as int values?
import java.io.*;
public class ReadWritePrimitiveData1 {
public static void main(String... args) throws IOException {
try (
FileOutputStream fos = new FileOutputStream(
new File("myData.data"));
DataOutputStream dos = new DataOutputStream(fos);

Writes 1 byte of
data (Boolean =
1 byte of data)

FileInputStream fis = new FileInputStream("myData.data");
DataInputStream dis = new DataInputStream(fis);
) {
dos.writeBoolean(true);
Writes 2 bytes
dos.writeChar('A');
of data
dos.writeInt(99);
(char = 2 bytes)
System.out.println(dis.readInt());
}

}
}

Reads 4 bytes of data; prints
16793856; interprets first
4 bytes as int value.

Writes 4 bytes of
data (int = 4 bytes)

EXAM TIP If a mismatch occurs in the type of data written by DataOutputStream and the type of data read by DataInputStream, you might

not get a runtime exception. Because data streams read and write bytes,
the read operation constructs the requested data from the available bytes,
though incorrectly.
In the next section, you’ll read and write the state of your objects to a file.

7.3.6

Object I/O with byte streams: reading and writing objects
To read and write objects, you can use object streams. An ObjectOutputStream can write
primitive values and objects to an OutputStream, which can be read by an ObjectInputStream. To write objects to a file, use a file for the stream. Objects of a class
that implements the java.io.Serializable interface can be written to a stream. Serialization is making a deep copy of the object so the whole object graph is written—

Licensed to Mark Watson 

485

Using byte stream I/O

Read objects

Write objects
Java
application

File

File
FileInputStream

FileOutputStream

ObjectInputStream

ObjectOutputStream

Figure 7.11 ObjectOutputStream writes an object to a file by using FileOutputStream. ObjectInputStream can reconstruct saved objects back from a file by using
FileInputStream.

that is, all instance variables, and if some instance variables refer to some other objects,
all the instance variables of the referred objects will be too (only if they implement
Serializable), and so on. The default serialization process doesn’t write the values
of static or transient variables of an object. The reading process, or de-serialization, builds the complete object, including the dependencies.
You can use classes ObjectInputStream and ObjectOutputStream to read and
write objects and primitive values. Figure 7.11 shows this arrangement.
You can instantiate these classes by passing them objects of InputStream or OutputStream. Here are their constructors that read from or write to a specified InputStream
or OutputStream:
public ObjectInputStream(InputStream in)
public ObjectOutputStream(OutputStream out)

You can use ObjectOutputStream and ObjectInputStream to
read and write all serializable objects and primitive values.

EXAM TIP

IMPLEMENT SERIALIZABLE TO READ AND WRITE OBJECTS FROM AND TO A FILE
Let’s start by writing an object of class Car to a file. The following code declares a barebones class Car and writes its object to a file, object.data, by using ObjectOutputStream and FileOutputStream:
import java.io.*;
class Car {}
class WriteObject{
public static void main(String args[]) throws IOException{
try (
FileOutputStream out = new FileOutputStream("object.data");
ObjectOutputStream oos = new ObjectOutputStream(out);
) {
Car c = new Car();
oos.writeObject(c);

Licensed to Mark Watson 

486

CHAPTER 7

Java I/O fundamentals

oos.flush();
}
}
}

Though the preceding code compiles successfully, it throws a java.io.NotSerializableException at runtime. What went wrong? Class Car should implement the Serializable
interface so that it can be written to and read from a file. Here’s the modified code:
import java.io.*;
Car implements
class Car implements Serializable {}
Serializable interface.
class ReadWriteObject{
public static void main(String args[]) throws Exception{
try (
FileOutputStream out = new FileOutputStream("object.data");
ObjectOutputStream oos = new ObjectOutputStream(out);
FileInputStream in = new FileInputStream("object.data");
ObjectInputStream ois = new ObjectInputStream(in);
) {

}
}

Writes object
Car c = new Car();
c to file
oos.writeObject(c);
oos.flush();
Car c2 = (Car)ois.readObject();
readObject() returns
System.out.println(c2);
Object, so needs explicit
cast to Car class.

}

Apart from declaring to throw an IOException, method readObject() might also
throw a ClassNotFoundException, if the JRE fails to retrieve the class information corresponding to the retrieved object.
EXAM TIP To write objects to a file, their classes should implement java
.io.Serializable, or the code will throw a java.io.NotSerializableException.

READ AND WRITE OBJECTS WITH NONSERIALIZABLE PARENT CLASSES
What happens if class Car extends another class, say Vehicle, which doesn’t implement the Serializable interface? Would you be able to write Car objects to a file? In
this case, the variables of the Vehicle class are serialized to a file. For example
import java.io.*;
Base class doesn’t
class Vehicle {
implement Serializable.
String name = "Vehicle";
}
class Car extends Vehicle implements Serializable {
String model = "Luxury";
}
class ParentNotSerializable{
public static void main(String args[]) throws Exception{

Licensed to Mark Watson 

Derived class
implements
Serializable.

487

Using byte stream I/O
try (
FileOutputStream out = new FileOutputStream("object.data");
ObjectOutputStream oos = new ObjectOutputStream(out);
FileInputStream in = new FileInputStream("object.data");
ObjectInputStream ois = new ObjectInputStream(in);
) {
Car c = new Car();
oos.writeObject(c);
oos.flush();
Car c2 = (Car)ois.readObject();
System.out.println(c2.name + ":" + c2.model);

Prints
Vehicle:Luxury

}
}
}

READ AND WRITE OBJECTS WITH NONSERIALIZABLE DATA MEMBERS
Would you be able to write objects to Car to a file, if any of its object fields doesn’t
implement the Serializable interface? In this case, the code will throw a java
.io.NotSerializableException when you attempt to write a Car object to a file.

For example
import java.io.*;
Engine doesn’t
class Engine {
implement Serializable.
String make = "198768";
}
class Car implements Serializable {
Car
String model = "Luxury";
implements
Engine engine = new Engine();
Serializable.
}
class DataMemberNotSerializable{
public static void main(String args[]) throws Exception{
try (
FileOutputStream out = new FileOutputStream("object.data");
ObjectOutputStream oos = new ObjectOutputStream(out);
) {
Car c = new Car();
oos.writeObject(c);
oos.flush();
}

Throws
NotSerializableException.

}
}

A class whose object fields don’t implement the Serializable
interface can’t be serialized even though the class itself implements the
Serializable interface. An attempt to serialize such object fields will
throw a runtime exception.
EXAM TIP

READ AND WRITE OBJECTS ALONG WITH PRIMITIVE VALUES FROM AND TO A FILE
You can use ObjectInputStream and ObjectOutputStream to read and write both

objects and primitive values from and to a file. The data should be retrieved in the
order that it was written. In the following example, class WritePrimAndObjects writes

Licensed to Mark Watson 

488

CHAPTER 7

Java I/O fundamentals

a boolean value and then a Car instance. These values should be retrieved in this
order. An attempt to read mismatching data types will result in throwing runtime
exception OptionalDataException:

Checked
exceptions
that
readObject
can throw:
IOException,
ClassNotFoundException.

class ReadPrimAndObjects{
public static void main(String args[]) throws IOException,
ClassNotFoundException{
try (
FileInputStream in = new FileInputStream("object.data");
ObjectInputStream ois = new ObjectInputStream(in);
) {
System.out.println(ois.readBoolean());
Car c = (Car)ois.readObject();
readObject returns
System.out.println(c.name);
instance of Object and
}
can throw Optional}
DataException
}
class Car implements Serializable{
String name;
Car(String value) {
name = value;
}
}

b

Retrieve the data (primitive and objects) in the order it was
written using object streams, or it might throw a runtime exception.

EXAM TIP

The code at B reads an object from the underlying stream. Method readObject()
returns Object, which is explicitly casted to class Car. For the exam, you should know
that this method can throw multiple exceptions:
■
■

ClassNotFoundException—Class of a serialized object cannot be found
OptionalDataException—Primitive data was found in the stream instead of

objects.
■

IOException—Any of the usual input-/output-related exceptions

THE TRANSIENT AND STATIC VARIABLES AREN’T WRITTEN TO A FILE
When you write objects to a file using ObjectOutputStream, its transient or static

variables aren’t written to the file. For example

static
variable

import java.io.*;
class Car implements Serializable{
String name;
transient String model;
transient
variables
transient int days;
static int carCount;
Car(String value) {
name = value;
model = "some value";
Assign value to
transient variables
days = 98;
++carCount;
}
}

Licensed to Mark Watson 

489

Using character I/O with readers and writers
class ReadWriteCarObjects{
public static void main(String args[]) throws Exception {
try (
FileOutputStream out = new FileOutputStream("object.data");
ObjectOutputStream oos = new ObjectOutputStream(out);
FileInputStream in = new FileInputStream("object.data");
ObjectInputStream ois = new ObjectInputStream(in);
) {
Car c = new Car("AAA");
oos.writeObject(c);
oos.flush();
new Car("BBB");
Car c2 = (Car)ois.readObject();
System.out.println(c2.name);
System.out.println(c2.model + ":" + c2.days);
System.out.println(c2.carCount);

Prints
null:0

Prints 2

}
}
}

In the preceding code, because the value of transient variables model and days wasn’t
written to the file, the deserialization process assigns default values to these variables:
null for objects and 0 for int type.
NOTE

You can also change the serialization process using methods

defaultReadObject() and defaultWriteObject(). But this is beyond

the scope of this exam.
METHODS OF OBJECTINPUTSTREAM AND OBJECTOUTPUTSTREAM

Figure 7.12 shows the methods to read and write byte data, primitive data, objects, and
a few miscellaneous methods of classes ObjectInputStream and ObjectOutputStream.
In this section, we covered how to read and write primitive data, strings, and
objects to an underlying file by using byte I/O classes. Let’s move forward with covering
character I/O with readers and writers.

7.4

Using character I/O with readers and writers
[7.2] Use streams to read from and write to files by using classes in the
java.io package including BufferedReader, BufferedWriter, File,
FileReader, FileWriter, DataInputStream, DataOutputStream,
ObjectOutputStream, ObjectInputStream, and PrintWriter
Reader and Writer are abstract base classes for reading and writing Unicode-

compliant character data. They don’t replace the byte-oriented I/O classes, but supplement them.

Licensed to Mark Watson 

490

CHAPTER 7

Java I/O fundamentals

ObjectInputStream

Byte
data

Primitive
data

ObjectOutputStream

int read()

void write(int)

int read (byte[] buf)

void write(byte[] buf,
int off, int len)

int read(byte[] buf,
int off,
int len)

void write(byte[])

boolean readBoolean()

void writeBoolean(boolean)

byte readByte()

void writeByte(byte)

short readShort()

void writeShort(short)

int readInt()

void writeInt(int)

char readChar()

void writeChar(char)

long readLong()

void writeLong(long)

float readFloat()

void writeFloat(float)

double readDouble()

void writeDouble(double)

object readObject()

void writeObject(object)

Object

void writeBytes(String)
void writeChars(String)
void close()

void close()

int available()

void flush()

Miscellaneous

Figure 7.12 Methods of class ObjectInputStream and ObjectOutputStream to read and
write byte data, primitive data, and objects

Classes Reader and Writer handle 16-bit Unicode well, which isn’t supported by the
byte-oriented InputStream and OutputStream classes. Also note that Java’s primitive
data type char stores 16-bit Unicode values. Even though you can use InputStream
and OutputStream to read and write characters, you should use the character-oriented
Reader and Writer classes to read and write character data. Internationalization is
possible only by using 16-bit Unicode values. Also Reader and Writer classes offer
faster I/O operations.

Licensed to Mark Watson 

491

Using character I/O with readers and writers

7.4.1

Abstract class java.io.Reader
Class Reader defines overloaded read() methods to read character data from an
underlying data stream:
Reads single
character

Reads characters

into array
int read()
int read(char[] cbuf)
abstract int read(char[] cbuf, int off, int len)

Reads
characters into
portion of array

Class Reader implements Closeable (and its parent interface AutoCloseable). So
Reader objects can be declared as resources with a try-with-resources statement.
Compare the overloaded read() methods of class InputStream with the read() methods of class Reader. The read() methods of
InputStream accept an array of byte as their method parameter, and
the read() methods of Reader accept an array of char as their method
EXAM TIP

parameter.
Because class Reader is an abstract class, you won’t use it to read data. Instead you’ll
use one of its concrete subclasses to do the reading for you. Figure 7.13 shows
abstract class Reader and its subclasses (BufferedReader and FileReader) that are
on the exam.

7.4.2

Abstract class java.io.Writer
The abstract class Writer defines overloaded write() methods to write character data
to an underlying data source:
Writes array
of characters

Writes
string

void write(char[] cbuf)
abstract void write(char[] cbuf, int off, int len)
void write(int c)
void write(String str)
void write(String str, int off, int len)

Writes single
character

Writes portion
of string

Reader
{abstract}
<>

BufferedReader
Figure 7.13

FileReader

Abstract class Reader and its subclasses that are on the exam

Licensed to Mark Watson 

Writes portion
of array of
characters

492

CHAPTER 7

Java I/O fundamentals

Writer
{abstract}
<>

BufferedWriter
Figure 7.14

FileWriter

PrintWriter

Abstract class Writer and its subclasses that are on the exam

Class Writer implements Closeable (and its parent interface AutoCloseable). So you
can instantiate and use its objects with a try-with-resources statement, which results in
an implicit call to Writer’s method close(). Reader’s method close() is used to close
the resources associated with the stream. Method flush() is used to write any save