100% found this document useful (1 vote)
106 views636 pages

Java Programming Basics Guide

This document is the introduction to an online textbook about programming in Java. It provides a PDF version of the online book that includes internal and external links but does not include source code, solutions, or quizzes. The book is freely available under a Creative Commons license for non-commercial use and modification as long as it follows the same licensing terms. The corresponding website for this book is provided.

Uploaded by

Victor Adeagbo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
106 views636 pages

Java Programming Basics Guide

This document is the introduction to an online textbook about programming in Java. It provides a PDF version of the online book that includes internal and external links but does not include source code, solutions, or quizzes. The book is freely available under a Creative Commons license for non-commercial use and modification as long as it follows the same licensing terms. The corresponding website for this book is provided.

Uploaded by

Victor Adeagbo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 636

Introduction to Programming Using Java

Version 8.1.1, May 2020


(Minor update of Version 8.0, December 2018;
very minor update of Version 8.1, July 2019)

David J. Eck
Hobart and William Smith Colleges

This is a PDF version of a free on-line book that is available at


http://math.hws.edu/javanotes/. The PDF does not include
source code files, solutions to exercises, or answers to quizzes, but
it does have external links to these resources, shown in blue.
The PDF also has internal links, shown in red. These links can
be used in Acrobat Reader and some other PDF reader programs.
ii

c
1996–2020, David J. Eck

David J. Eck ([email protected])


Department of Mathematics and Computer Science
Hobart and William Smith Colleges
Geneva, NY 14456

This book can be distributed in unmodified form for non-commercial purposes.


Modified versions can be made and distributed for non-commercial purposes
provided they are distributed under the same license as the original. More
specifically: This work is licensed under the Creative Commons Attribution-
NonCommercial-ShareAlike 4.0 License. To view a copy of this license, visit
http://creativecommons.org/licenses/by-nc-sa/4.0/. Other uses require
permission from the author.

The web site for this book is: http://math.hws.edu/javanotes


Contents

Preface xi

1 The Mental Landscape 1


1.1 Machine Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Asynchronous Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 The Java Virtual Machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4 Building Blocks of Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5 Object-oriented Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6 The Modern User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.7 The Internet and Beyond . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Quiz on Chapter 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2 Names and Things 19


2.1 The Basic Java Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Variables and Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.2.2 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.2.3 Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.2.4 Strings and String Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.2.5 Variables in Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.3 Objects and Subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.3.1 Built-in Subroutines and Functions . . . . . . . . . . . . . . . . . . . . . . 30
2.3.2 Classes and Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.3.3 Operations on Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.3.4 Introduction to Enums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.4 Text Input and Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.4.1 Basic Output and Formatted Output . . . . . . . . . . . . . . . . . . . . . 38
2.4.2 A First Text Input Example . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.4.3 Basic TextIO Input Functions . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.4.4 Introduction to File I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.4.5 Other TextIO Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.4.6 Using Scanner for Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.5 Details of Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.5.1 Arithmetic Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.5.2 Increment and Decrement . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.5.3 Relational Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.5.4 Boolean Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.5.5 Conditional Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

i
CONTENTS ii

2.5.6 Assignment Operators and Type Conversion . . . . . . . . . . . . . . . . 52


2.5.7 Precedence Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.6 Programming Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.6.1 Getting JDK and JavaFX . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.6.2 Command Line Environment . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.6.3 JavaFX on the Command Line . . . . . . . . . . . . . . . . . . . . . . . . 59
2.6.4 Eclipse IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.6.5 Using JavaFX in Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2.6.6 BlueJ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.6.7 The Problem of Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
2.6.8 About jshell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Exercises for Chapter 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Quiz on Chapter 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

3 Control 71
3.1 Blocks, Loops, and Branches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.1.1 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.1.2 The Basic While Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.1.3 The Basic If Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
3.1.4 Definite Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
3.2 Algorithm Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.2.1 Pseudocode and Stepwise Refinement . . . . . . . . . . . . . . . . . . . . 78
3.2.2 The 3N+1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.2.3 Coding, Testing, Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.3 while and do..while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
3.3.1 The while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
3.3.2 The do..while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.3.3 break and continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.4 The for Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
3.4.1 For Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
3.4.2 Example: Counting Divisors . . . . . . . . . . . . . . . . . . . . . . . . . . 95
3.4.3 Nested for Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
3.5 The if Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
3.5.1 The Dangling else Problem . . . . . . . . . . . . . . . . . . . . . . . . . . 101
3.5.2 Multiway Branching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
3.5.3 If Statement Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
3.5.4 The Empty Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
3.6 The switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
3.6.1 The Basic switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . 108
3.6.2 Menus and switch Statements . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.6.3 Enums in switch Statements . . . . . . . . . . . . . . . . . . . . . . . . . 112
3.6.4 Definite Assignment and switch Statements . . . . . . . . . . . . . . . . . 112
3.6.5 A New switch Statement Syntax . . . . . . . . . . . . . . . . . . . . . . . 113
3.7 Exceptions and try..catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
3.7.1 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
3.7.2 try..catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
3.7.3 Exceptions in TextIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
CONTENTS iii

3.8 Introduction to Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119


3.8.1 Creating and Using Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.8.2 Arrays and For Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
3.8.3 Random Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
3.8.4 Partially Full Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
3.8.5 Two-dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
3.9 GUI Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
3.9.1 Drawing Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
3.9.2 Drawing in a Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
3.9.3 Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Exercises for Chapter 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Quiz on Chapter 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

4 Subroutines 141
4.1 Black Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4.2 Static Subroutines and Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
4.2.1 Subroutine Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
4.2.2 Calling Subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
4.2.3 Subroutines in Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
4.2.4 Member Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
4.3 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
4.3.1 Using Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
4.3.2 Formal and Actual Parameters . . . . . . . . . . . . . . . . . . . . . . . . 153
4.3.3 Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
4.3.4 Subroutine Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
4.3.5 Array Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
4.3.6 Command-line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
4.3.7 Throwing Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
4.3.8 Global and Local Variables . . . . . . . . . . . . . . . . . . . . . . . . . . 160
4.4 Return Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
4.4.1 The return statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
4.4.2 Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
4.4.3 3N+1 Revisited . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
4.5 Lambda Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
4.5.1 First-class Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
4.5.2 Functional Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
4.5.3 Lambda Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
4.5.4 Method References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.6 APIs, Packages, Modules, and Javadoc . . . . . . . . . . . . . . . . . . . . . . . . 171
4.6.1 Toolboxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
4.6.2 Java’s Standard Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
4.6.3 Using Classes from Packages . . . . . . . . . . . . . . . . . . . . . . . . . 174
4.6.4 About Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
4.6.5 Javadoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
4.6.6 Static Import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
4.7 More on Program Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
4.7.1 Preconditions and Postconditions . . . . . . . . . . . . . . . . . . . . . . . 180
CONTENTS iv

4.7.2 A Design Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181


4.7.3 The Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
4.8 The Truth About Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
4.8.1 Initialization in Declarations . . . . . . . . . . . . . . . . . . . . . . . . . 188
4.8.2 Declaring Variables with var . . . . . . . . . . . . . . . . . . . . . . . . . 189
4.8.3 Named Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
4.8.4 Naming and Scope Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Exercises for Chapter 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Quiz on Chapter 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199

5 Objects and Classes 201


5.1 Objects and Instance Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
5.1.1 Objects, Classes, and Instances . . . . . . . . . . . . . . . . . . . . . . . . 202
5.1.2 Fundamentals of Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
5.1.3 Getters and Setters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
5.1.4 Arrays and Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.2 Constructors and Object Initialization . . . . . . . . . . . . . . . . . . . . . . . . 211
5.2.1 Initializing Instance Variables . . . . . . . . . . . . . . . . . . . . . . . . . 211
5.2.2 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
5.2.3 Garbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
5.3 Programming with Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
5.3.1 Some Built-in Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
5.3.2 The class “Object” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
5.3.3 Writing and Using a Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
5.3.4 Object-oriented Analysis and Design . . . . . . . . . . . . . . . . . . . . . 224
5.4 Programming Example: Card, Hand, Deck . . . . . . . . . . . . . . . . . . . . . . 225
5.4.1 Designing the classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
5.4.2 The Card Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
5.4.3 Example: A Simple Card Game . . . . . . . . . . . . . . . . . . . . . . . . 232
5.5 Inheritance and Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
5.5.1 Extending Existing Classes . . . . . . . . . . . . . . . . . . . . . . . . . . 235
5.5.2 Inheritance and Class Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 237
5.5.3 Example: Vehicles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
5.5.4 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
5.5.5 Abstract Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
5.6 this and super . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
5.6.1 The Special Variable this . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
5.6.2 The Special Variable super . . . . . . . . . . . . . . . . . . . . . . . . . . 248
5.6.3 super and this As Constructors . . . . . . . . . . . . . . . . . . . . . . . . 249
5.7 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
5.7.1 Defining and Implementing Interfaces . . . . . . . . . . . . . . . . . . . . 251
5.7.2 Default Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
5.7.3 Interfaces as Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
5.8 Nested Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
5.8.1 Static Nested Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
5.8.2 Inner Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
5.8.3 Anonymous Inner Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
CONTENTS v

5.8.4 Local Classes and Lambda Expressions . . . . . . . . . . . . . . . . . . . 258


Exercises for Chapter 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Quiz on Chapter 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264

6 Introduction to GUI Programming 267


6.1 A Basic JavaFX Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
6.1.1 JavaFX Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
6.1.2 Stage, Scene, and SceneGraph . . . . . . . . . . . . . . . . . . . . . . . . 270
6.1.3 Nodes and Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
6.1.4 Events and Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . 272
6.2 Some Basic Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
6.2.1 Color and Paint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
6.2.2 Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
6.2.3 Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
6.2.4 Canvas and GraphicsContext . . . . . . . . . . . . . . . . . . . . . . . . . 276
6.2.5 A Bit of CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
6.3 Basic Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
6.3.1 Event Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
6.3.2 Mouse Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
6.3.3 Dragging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
6.3.4 Key Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
6.3.5 AnimationTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
6.3.6 State Machines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
6.3.7 Observable Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
6.4 Basic Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
6.4.1 ImageView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
6.4.2 Label and Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
6.4.3 CheckBox and RadioButton . . . . . . . . . . . . . . . . . . . . . . . . . . 300
6.4.4 TextField and TextArea . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
6.4.5 Slider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
6.5 Basic Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
6.5.1 Do Your Own Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
6.5.2 BorderPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
6.5.3 HBox and VBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
6.5.4 GridPane and TilePane . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
6.6 Complete Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
6.6.1 A Little Card Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
6.6.2 Menus and Menubars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
6.6.3 Scene and Stage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
6.6.4 Creating Jar Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
Exercises for Chapter 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Quiz on Chapter 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332

7 Arrays and ArrayLists 333


7.1 Array Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
7.1.1 For-each Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
7.1.2 Variable Arity Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
7.1.3 Array Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
CONTENTS vi

7.2 Array Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339


7.2.1 Some Processing Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 339
7.2.2 Some Standard Array Methods . . . . . . . . . . . . . . . . . . . . . . . . 342
7.2.3 RandomStrings Revisited . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
7.2.4 Dynamic Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
7.3 ArrayList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
7.3.1 ArrayList and Parameterized Types . . . . . . . . . . . . . . . . . . . . . 349
7.3.2 Wrapper Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
7.3.3 Programming With ArrayList . . . . . . . . . . . . . . . . . . . . . . . . . 352
7.4 Searching and Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
7.4.1 Searching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
7.4.2 Association Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
7.4.3 Insertion Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
7.4.4 Selection Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
7.4.5 Unsorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
7.5 Two-dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
7.5.1 The Truth About 2D Arrays . . . . . . . . . . . . . . . . . . . . . . . . . 366
7.5.2 Conway’s Game Of Life . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
7.5.3 Checkers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Exercises for Chapter 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
Quiz on Chapter 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383

8 Correctness, Robustness, Efficiency 385


8.1 Introduction to Correctness and Robustness . . . . . . . . . . . . . . . . . . . . . 385
8.1.1 Horror Stories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
8.1.2 Java to the Rescue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
8.1.3 Problems Remain in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
8.2 Writing Correct Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
8.2.1 Provably Correct Programs . . . . . . . . . . . . . . . . . . . . . . . . . . 391
8.2.2 Preconditions and Postconditions . . . . . . . . . . . . . . . . . . . . . . . 391
8.2.3 Invariants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
8.2.4 Robust Handling of Input . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
8.3 Exceptions and try..catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
8.3.1 Exceptions and Exception Classes . . . . . . . . . . . . . . . . . . . . . . 401
8.3.2 The try Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
8.3.3 Throwing Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
8.3.4 Mandatory Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . 408
8.3.5 Programming with Exceptions . . . . . . . . . . . . . . . . . . . . . . . . 409
8.4 Assertions and Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
8.4.1 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
8.4.2 Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
8.5 Analysis of Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Exercises for Chapter 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
Quiz on Chapter 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
CONTENTS vii

9 Linked Data Structures and Recursion 429


9.1 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
9.1.1 Recursive Binary Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
9.1.2 Towers of Hanoi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
9.1.3 A Recursive Sorting Algorithm . . . . . . . . . . . . . . . . . . . . . . . . 435
9.1.4 Blob Counting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
9.2 Linked Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
9.2.1 Recursive Linking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
9.2.2 Linked Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
9.2.3 Basic Linked List Processing . . . . . . . . . . . . . . . . . . . . . . . . . 444
9.2.4 Inserting into a Linked List . . . . . . . . . . . . . . . . . . . . . . . . . . 447
9.2.5 Deleting from a Linked List . . . . . . . . . . . . . . . . . . . . . . . . . . 449
9.3 Stacks, Queues, and ADTs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
9.3.1 Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
9.3.2 Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
9.3.3 Postfix Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
9.4 Binary Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
9.4.1 Tree Traversal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
9.4.2 Binary Sort Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
9.4.3 Expression Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
9.5 A Simple Recursive Descent Parser . . . . . . . . . . . . . . . . . . . . . . . . . . 472
9.5.1 Backus-Naur Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
9.5.2 Recursive Descent Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
9.5.3 Building an Expression Tree . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Exercises for Chapter 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
Quiz on Chapter 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484

10 Generic Programming and Collection Classes 487


10.1 Generic Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
10.1.1 Generic Programming in Smalltalk . . . . . . . . . . . . . . . . . . . . . . 488
10.1.2 Generic Programming in C++ . . . . . . . . . . . . . . . . . . . . . . . . 489
10.1.3 Generic Programming in Java . . . . . . . . . . . . . . . . . . . . . . . . . 490
10.1.4 The Java Collection Framework . . . . . . . . . . . . . . . . . . . . . . . . 491
10.1.5 Iterators and for-each Loops . . . . . . . . . . . . . . . . . . . . . . . . . . 493
10.1.6 Equality and Comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
10.1.7 Generics and Wrapper Classes . . . . . . . . . . . . . . . . . . . . . . . . 498
10.2 Lists and Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
10.2.1 ArrayList and LinkedList . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
10.2.2 Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
10.2.3 TreeSet and HashSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
10.2.4 Priority Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
10.3 Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
10.3.1 The Map Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
10.3.2 Views, SubSets, and SubMaps . . . . . . . . . . . . . . . . . . . . . . . . 508
10.3.3 Hash Tables and Hash Codes . . . . . . . . . . . . . . . . . . . . . . . . . 511
10.4 Programming with the JFC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
10.4.1 Symbol Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
CONTENTS viii

10.4.2 Sets Inside a Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515


10.4.3 Using a Comparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
10.4.4 Word Counting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
10.5 Writing Generic Classes and Methods . . . . . . . . . . . . . . . . . . . . . . . . 521
10.5.1 Simple Generic Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
10.5.2 Simple Generic Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
10.5.3 Wildcard Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
10.5.4 Bounded Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
10.6 Introduction the Stream API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
10.6.1 Generic Functional Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . 533
10.6.2 Making Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
10.6.3 Operations on Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
10.6.4 An Experiment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
Exercises for Chapter 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
Quiz on Chapter 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545

11 I/O Streams, Files, and Networking 547


11.1 I/O Streams, Readers, and Writers . . . . . . . . . . . . . . . . . . . . . . . . . . 547
11.1.1 Character and Byte Streams . . . . . . . . . . . . . . . . . . . . . . . . . 548
11.1.2 PrintWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
11.1.3 Data Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
11.1.4 Reading Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
11.1.5 The Scanner Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
11.1.6 Serialized Object I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
11.2 Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
11.2.1 Reading and Writing Files . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
11.2.2 Files and Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
11.2.3 File Dialog Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
11.3 Programming With Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
11.3.1 Copying a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
11.3.2 Persistent Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
11.3.3 Storing Objects in Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
11.4 Networking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575
11.4.1 URLs and URLConnections . . . . . . . . . . . . . . . . . . . . . . . . . . 576
11.4.2 TCP/IP and Client/Server . . . . . . . . . . . . . . . . . . . . . . . . . . 578
11.4.3 Sockets in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
11.4.4 A Trivial Client/Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
11.4.5 A Simple Network Chat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
11.5 A Brief Introduction to XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
11.5.1 Basic XML Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
11.5.2 Working With the DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
Exercises for Chapter 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
Quiz on Chapter 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
CONTENTS ix

12 Threads and Multiprocessing 601


12.1 Introduction to Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
12.1.1 Creating and Running Threads . . . . . . . . . . . . . . . . . . . . . . . . 602
12.1.2 Operations on Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607
12.1.3 Mutual Exclusion with “synchronized” . . . . . . . . . . . . . . . . . . . . 609
12.1.4 Volatile Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
12.1.5 Atomic Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
12.2 Programming with Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
12.2.1 Threads, Timers, and JavaFX . . . . . . . . . . . . . . . . . . . . . . . . . 615
12.2.2 Recursion in a Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
12.2.3 Threads for Background Computation . . . . . . . . . . . . . . . . . . . . 619
12.2.4 Threads for Multiprocessing . . . . . . . . . . . . . . . . . . . . . . . . . . 621
12.3 Threads and Parallel Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
12.3.1 Problem Decomposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
12.3.2 Thread Pools and Task Queues . . . . . . . . . . . . . . . . . . . . . . . . 624
12.3.3 Producer/Consumer and Blocking Queues . . . . . . . . . . . . . . . . . . 627
12.3.4 The ExecutorService Approach . . . . . . . . . . . . . . . . . . . . . . . . 631
12.3.5 Wait and Notify . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
12.4 Threads and Networking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
12.4.1 The Blocking I/O Problem . . . . . . . . . . . . . . . . . . . . . . . . . . 638
12.4.2 An Asynchronous Network Chat Program . . . . . . . . . . . . . . . . . . 639
12.4.3 A Threaded Network Server . . . . . . . . . . . . . . . . . . . . . . . . . . 643
12.4.4 Using a Thread Pool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
12.4.5 Distributed Computing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
12.5 Network Programming Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
12.5.1 The Netgame Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
12.5.2 A Simple Chat Room . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656
12.5.3 A Networked TicTacToe Game . . . . . . . . . . . . . . . . . . . . . . . . 659
12.5.4 A Networked Poker Game . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
Exercises for Chapter 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
Quiz on Chapter 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667

13 GUI Programming Continued 669


13.1 Properties and Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
13.1.1 Observable Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
13.1.2 Bindable Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
13.1.3 Bidirectional Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
13.2 Fancier Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
13.2.1 Fancier Strokes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
13.2.2 Fancier Paints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
13.2.3 Transforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
13.2.4 Stacked Canvasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
13.2.5 Pixel Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
13.2.6 Image I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
13.3 Complex Components and MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
13.3.1 A Simple Custom Component . . . . . . . . . . . . . . . . . . . . . . . . . 689
13.3.2 The MVC Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691
CONTENTS x

13.3.3 ListView and ComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . . 692


13.3.4 TableView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
13.4 Mostly Windows and Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
13.4.1 Dialog Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
13.4.2 WebView and WebEngine . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
13.4.3 Managing Multiple Windows . . . . . . . . . . . . . . . . . . . . . . . . . 705
13.5 Finishing Touches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
13.5.1 The Mandelbrot Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
13.5.2 Design of the Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
13.5.3 Events, Listeners, and Bindings . . . . . . . . . . . . . . . . . . . . . . . . 714
13.5.4 A Few More GUI Details . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
13.5.5 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
13.5.6 Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
Exercises for Chapter 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
Quiz on Chapter 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723

Appendix: Source Files 725

Glossary 735
Preface

Introduction to Programming Using Java is a free introductory computer programming


textbook that uses Java as the language of instruction. It is suitable for use in an introductory
programming course and for people who are trying to learn programming on their own. There
are no prerequisites beyond a general familiarity with the ideas of computers and programs.
There is more than enough material for a full year of college-level programming. Chapters 1
through 7 can be used as a textbook in a one-semester college-level course or in a year-long
high school course. The remaining chapters can be covered in a second course.
The Eighth Edition of the book uses Java 8, with brief coverage of features that were added
to the language in Java 9 and later. JavaFX is used for GUI programming. All sample programs
and exercise solutions have been compiled with Java 11 and with Java 14, as well as with Java 8.
The home web site for this book, where you can always find the latest version, is
http://math.hws.edu/javanotes/. The page at that address contains links for downloading
a copy of the web site and for downloading PDF versions of the book. The web site—and the
web site download—includes source code for the sample programs that are discussed in the
text, answers to end-of-chapter quizzes and a discussion and solution for each end-of-chapter
exercise. Readers are encouraged to download the source code for the examples and to read
and run the programs as they read the book. Readers are also strongly encouraged to read the
exercise solutions if they want to get the most out of this book.
In style, this is a textbook rather than a tutorial. That is, it concentrates on explaining
concepts rather than giving step-by-step how-to-do-it guides. I have tried to use a conversa-
tional writing style that might be closer to classroom lecture than to a typical textbook. This
is certainly not a Java reference book, and it is not a comprehensive survey of all the features
of Java. It is not written as a quick introduction to Java for people who already know another
programming language. Instead, it is directed mainly towards people who are learning program-
ming for the first time, and it is as much about general programming concepts as it is about
Java in particular. I believe that Introduction to Programming using Java is fully competitive
with the conventionally published, printed programming textbooks that are available on the
market. (Well, all right, I’ll confess that I think it’s better.)
There are several approaches to teaching Java. One approach uses graphical user interface
programming from the very beginning. And some people believe that object oriented program-
ming should also be emphasized from the very beginning. These are not the approach that I
take. The approach that I favor starts with the more basic building blocks of programming and
builds from there. After an introductory chapter, I cover procedural programming in Chapters
2, 3, and 4. Object-oriented programming is introduced in Chapter 5. Chapter 6 covers the
closely related topic of event-oriented programming and graphical user interfaces. Arrays are
introduced in Chapter 3 with a full treatment in Chapter 7. Chapter 8 is a short chapter that
marks a turning point in the book, moving beyond the fundamental ideas of programming to
cover more advanced topics. Chapter 8 is about writing robust, correct, and efficient programs.

xi
Preface xii

Chapters 9 and 10 cover recursion and data structures, including generic programming and
the Java Collection Framework. Chapter 11 is about files and networking. Chapter 12 cov-
ers threads and parallel processing. Finally, Chapter 13 returns to the topic of graphical user
interface programming to cover some of JavaFX’s more advanced capabilities.
∗ ∗ ∗
The biggest change from the seventh edition of this textbook is a switch from Swing to
JavaFX for GUI programming. With just a few exceptions in code that the user is not meant
to read, all example code and exercises use JavaFX. (Unfortunately, even as I was working
on the new edition, the future of JavaFX has become unclear because, as of Java 11, JavaFX
is no longer included as part of the basic Java download. However, it is still be available to
be downloaded and installed separately. People who are interested in using Swing instead of
JavaFX might consider using Chapter 6 and Chapter 13 from Version 7 in place of the same
chapters from Version 8.) The eighth edition also incorporates two features that were introduced
in Java 8: lambda expressions and the stream API. Lambda expressions are covered fairly early,
as part of the material on subroutines in Chapter 4. They are used extensively with JavaFX in
Chapter 6 and Chapter 13, as well as with the stream API, which is introduced in Section 10.6.
Aside from these major changes, there are small improvements throughout, such as a short
discussion of loop invariants and class invariants in Section 8.2, increased coverage of Java’s
higher level concurrency support in Chapter 12, and the consistent use of factory methods like
Integer.valueOf(n) in preference to constructors.
Version 8.1 was a fairly small update to Version 8.0, and Version 8.1.1 makes no substantive
changes. The main motivation for the update to 8.1 was to make it clearer how to use the book
with Java 11 and later, but I have also taken the opportunity to briefly discuss a few features
that were introduced after Java 8. The major change to the language itself was the introduction
of modules in Java 9; these were already mentioned in Version 8.0, but that discussion has been
updated. (However, this book does not explain how to create modules.) Aside from that, there
have been few language changes that impact this textbook, but I do introduce the use of var
for declaring variables, several new methods related to the stream API, and the revisions made
to the switch statement in Java 14.
∗ ∗ ∗
The first version of the book was written in 1996, and there have been several editions since
then. All editions are archived (at least until my retirement) at the following Web addresses:
• First edition: http://math.hws.edu/eck/cs124/javanotes1/ (Covers Java 1.0.)
• Second edition: http://math.hws.edu/eck/cs124/javanotes2/ (Covers Java 1.1.)
• Third edition: http://math.hws.edu/eck/cs124/javanotes3/ (Covers Java 1.1.)
• Fourth edition: http://math.hws.edu/eck/cs124/javanotes4/ (Covers Java 1.4.)
• Fifth edition: http://math.hws.edu/eck/cs124/javanotes5/ (Covers Java 5.0.)
• Sixth edition: http://math.hws.edu/eck/cs124/javanotes6/ (Covers Java 5.0, with a bit
of 6.0.)
• Seventh edition: http://math.hws.edu/eck/cs124/javanotes7/ (Covers Java 7.)
• Eighth edition: http://math.hws.edu/eck/cs124/javanotes8/ (Covers Java 8.)
Introduction to Programming using Java is free, but it is not in the pub-
lic domain. Version 8 is published under the terms of the Creative Commons
Attribution-NonCommercial-ShareAlike 4.0 License. To view a copy of this license, visit
http://creativecommons.org/licenses/by-nc-sa/4.0/. For example, you can:
Preface xiii

• Post an unmodified copy of the on-line version on your own Web site (including the parts
that list the author and state the license under which it is distributed!).
• Give away unmodified copies of this book or sell them at cost of production, as long as
they meet the requirements of the license.
• Use the book as a textbook for a course that you are teaching (even though the students
are paying to take that course).
• Make modified copies of the complete book or parts of it and post them on the web or
otherwise distribute them non-commercially, provided that attribution to the author is
given, the modifications are clearly noted, and the modified copies are distributed under
the same license as the original. This includes translations to other languages.
For uses of the book in ways not covered by the license, permission of the author is required.
While it is not actually required by the license, I do appreciate hearing from people who
are using or distributing my work.
∗ ∗ ∗
A technical note on production: The on-line and PDF versions of this book are created
from a single source, which is written largely in XML. To produce the PDF version, the XML
is processed into a form that can be used by the TeX typesetting program. In addition to XML
files, the source includes DTDs, XSLT transformations, Java source code files, image files, a
TeX macro file, and a couple of scripts that are used in processing. The scripts work on Linux
and on Mac OS.
I have made the complete source files available for download at the following
address:
http://math.hws.edu/eck/cs124/downloads/javanotes8-full-source.zip
These files were not originally meant for publication, and therefore are not very cleanly
written. Furthermore, it requires a fair amount of expertise to use them. However, I have had
several requests for the sources and have made them available on an “as-is” basis. For more
information about the sources and how they are used see the README file from the source
download.
∗ ∗ ∗
Professor David J. Eck
Department of Mathematics and Computer Science
Hobart and William Smith Colleges
300 Pulteney Street
Geneva, New York 14456, USA
Email: [email protected]
WWW: http://math.hws.edu/eck/
Chapter 1

Overview: The Mental Landscape

When you begin a journey, it’s a good idea to have a mental map of the terrain you’ll be
passing through. The same is true for an intellectual journey, such as learning to write computer
programs. In this case, you’ll need to know the basics of what computers are and how they
work. You’ll want to have some idea of what a computer program is and how one is created.
Since you will be writing programs in the Java programming language, you’ll want to know
something about that language in particular and about the modern computing environment for
which Java is designed.
As you read this chapter, don’t worry if you can’t understand everything in detail. (In fact,
it would be impossible for you to learn all the details from the brief expositions in this chapter.)
Concentrate on learning enough about the big ideas to orient yourself, in preparation for the
rest of the book. Most of what is covered in this chapter will be covered in much greater detail
later in the book.

1.1 The Fetch and Execute Cycle: Machine Language


A computer is a complex system consisting of many different components. But at the
heart—or the brain, if you want—of the computer is a single component that does the actual
computing. This is the Central Processing Unit, or CPU. In a modern desktop computer,
the CPU is a single “chip” on the order of one square inch in size. The job of the CPU is to
execute programs.
A program is simply a list of unambiguous instructions meant to be followed mechanically
by a computer. A computer is built to carry out instructions that are written in a very simple
type of language called machine language. Each type of computer has its own machine
language, and the computer can directly execute a program only if the program is expressed in
that language. (It can execute programs written in other languages if they are first translated
into machine language.)
When the CPU executes a program, that program is stored in the computer’s main mem-
ory (also called the RAM or random access memory). In addition to the program, memory
can also hold data that is being used or processed by the program. Main memory consists of a
sequence of locations. These locations are numbered, and the sequence number of a location
is called its address. An address provides a way of picking out one particular piece of informa-
tion from among the millions stored in memory. When the CPU needs to access the program
instruction or data in a particular location, it sends the address of that information as a signal
to the memory; the memory responds by sending back the value contained in the specified

1
CHAPTER 1. THE MENTAL LANDSCAPE 2

location. The CPU can also store information in memory by specifying the information to be
stored and the address of the location where it is to be stored.
On the level of machine language, the operation of the CPU is fairly straightforward (al-
though it is very complicated in detail). The CPU executes a program that is stored as a
sequence of machine language instructions in main memory. It does this by repeatedly reading,
or fetching , an instruction from memory and then carrying out, or executing , that instruc-
tion. This process—fetch an instruction, execute it, fetch another instruction, execute it, and
so on forever—is called the fetch-and-execute cycle. With one exception, which will be
covered in the next section, this is all that the CPU ever does. (This is all really somewhat
more complicated in modern computers. A typical processing chip these days contains several
CPU “cores,” which allows it to execute several instructions simultaneously. And access to
main memory is speeded up by memory “caches,” which can be more quickly accessed than
main memory and which are meant to hold data and instructions that the CPU is likely to
need soon. However, these complications don’t change the basic operation.)
A CPU contains an Arithmetic Logic Unit, or ALU, which is the part of the processor
that carries out operations such as addition and subtraction. It also holds a small number of
registers, which are small memory units capable of holding a single number. A typical CPU
might have 16 or 32 “general purpose” registers, which hold data values that are immediately
accessible for processing, and many machine language instructions refer to these registers. For
example, there might be an instruction that takes two numbers from two specified registers,
adds those numbers (using the ALU), and stores the result back into a register. And there
might be instructions for copying a data value from main memory into a register, or from a
register into main memory.
The CPU also includes special purpose registers. The most important of these is the pro-
gram counter , or PC. The CPU uses the PC to keep track of where it is in the program it
is executing. The PC simply stores the memory address of the next instruction that the CPU
should execute. At the beginning of each fetch-and-execute cycle, the CPU checks the PC to
see which instruction it should fetch. During the course of the fetch-and-execute cycle, the
number in the PC is updated to indicate the instruction that is to be executed in the next
cycle. Usually, but not always, this is just the instruction that sequentially follows the current
instruction in the program. Some machine language instructions modify the value that is stored
in the PC. This makes it possible for the computer to “jump” from one point in the program
to another point, which is essential for implementing the program features known as loops and
branches that are discussed in Section 1.4.
∗ ∗ ∗
A computer executes machine language programs mechanically—that is without under-
standing them or thinking about them—simply because of the way it is physically put together.
This is not an easy concept. A computer is a machine built of millions of tiny switches called
transistors, which have the property that they can be wired together in such a way that an
output from one switch can turn another switch on or off. As a computer computes, these
switches turn each other on or off in a pattern determined both by the way they are wired
together and by the program that the computer is executing.
Machine language instructions are expressed as binary numbers. A binary number is made
up of just two possible digits, zero and one. Each zero or one is called a bit. So, a machine
language instruction is just a sequence of zeros and ones. Each particular sequence encodes
some particular instruction. The data that the computer manipulates is also encoded as binary
numbers. In modern computers, each memory location holds a byte, which is a sequence of
CHAPTER 1. THE MENTAL LANDSCAPE 3

eight bits. A machine language instruction or a piece of data generally consists of several bytes,
stored in consecutive memory locations. For example, when a CPU reads an instruction from
memory, it might actually read four or eight bytes from four or eight memory locations; the
memory address of the instruction is the address of the first of those bytes.
A computer can work directly with binary numbers because switches can readily represent
such numbers: Turn the switch on to represent a one; turn it off to represent a zero. Machine
language instructions are stored in memory as patterns of switches turned on or off. When a
machine language instruction is loaded into the CPU, all that happens is that certain switches
are turned on or off in the pattern that encodes that instruction. The CPU is built to respond
to this pattern by executing the instruction it encodes; it does this simply because of the way
all the other switches in the CPU are wired together.
So, you should understand this much about how computers work: Main memory holds ma-
chine language programs and data. These are encoded as binary numbers. The CPU fetches
machine language instructions from memory one after another and executes them. Each in-
struction makes the CPU perform some very small task, such as adding two numbers or moving
data to or from memory. The CPU does all this mechanically, without thinking about or un-
derstanding what it does—and therefore the program it executes must be perfect, complete in
all details, and unambiguous because the CPU can do nothing but execute it exactly as written.
Here is a schematic view of this first-stage understanding of the computer:

Memory

CPU 10001010
00001100
(Location
(Location
0)
1)
10111000 (Location 2)
Registers Data to Memory 01000001 (Location 3)
00001011 (Location 4)
Data from Memory 11011101 (Location 5)
10110000 (Location 6)
01010010 (Location 7)
ALU 11111010 (Location 8)
01001100 (Location 9)
Address for 00100011 (Location 10)
PC
reading/writing
00011010 (Location 11)
data
. .
. .
. .

1.2 Asynchronous Events: Polling Loops and Interrupts


The CPU spends almost all of its time fetching instructions from memory and executing
them. However, the CPU and main memory are only two out of many components in a real
computer system. A complete system contains other devices such as:
• A hard disk or solid state drive for storing programs and data files. (Note that main
memory holds only a comparatively small amount of information, and holds it only as
long as the power is turned on. A hard disk or solid state drive is used for permanent
storage of larger amounts of information, but programs have to be loaded from there into
main memory before they can actually be executed. A hard disk stores data on a spinning
magnetic disk, while a solid state drive is a purely electronic device with no moving parts.)
CHAPTER 1. THE MENTAL LANDSCAPE 4

• A keyboard and mouse for user input.


• A monitor and printer which can be used to display the computer’s output.
• An audio output device that allows the computer to play sounds.
• A network interface that allows the computer to communicate with other computers
that are connected to it on a network, either wirelessly or by wire.
• A scanner that converts images into coded binary numbers that can be stored and
manipulated on the computer.
The list of devices is entirely open ended, and computer systems are built so that they can
easily be expanded by adding new devices. Somehow the CPU has to communicate with and
control all these devices. The CPU can only do this by executing machine language instructions
(which is all it can do, period). The way this works is that for each device in a system, there
is a device driver , which consists of software that the CPU executes when it has to deal
with the device. Installing a new device on a system generally has two steps: plugging the
device physically into the computer, and installing the device driver software. Without the
device driver, the actual physical device would be useless, since the CPU would not be able to
communicate with it.
∗ ∗ ∗
A computer system consisting of many devices is typically organized by connecting those
devices to one or more busses. A bus is a set of wires that carry various sorts of information
between the devices connected to those wires. The wires carry data, addresses, and control
signals. An address directs the data to a particular device and perhaps to a particular register
or location within that device. Control signals can be used, for example, by one device to alert
another that data is available for it on the data bus. A fairly simple computer system might
be organized like this:

CPU Empty Slot


for future
Memory Disk Drive Expansion

Input/ Data
Output Address
Controller Control

Display Keyboard Network


Interface

Now, devices such as keyboard, mouse, and network interface can produce input that needs
to be processed by the CPU. How does the CPU know that the data is there? One simple idea,
which turns out to be not very satisfactory, is for the CPU to keep checking for incoming data
over and over. Whenever it finds data, it processes it. This method is called polling , since
the CPU polls the input devices continually to see whether they have any input data to report.
Unfortunately, although polling is very simple, it is also very inefficient. The CPU can waste
an awful lot of time just waiting for input.
CHAPTER 1. THE MENTAL LANDSCAPE 5

To avoid this inefficiency, interrupts are generally used instead of polling. An interrupt
is a signal sent by another device to the CPU. The CPU responds to an interrupt signal by
putting aside whatever it is doing in order to respond to the interrupt. Once it has handled
the interrupt, it returns to what it was doing before the interrupt occurred. For example, when
you press a key on your computer keyboard, a keyboard interrupt is sent to the CPU. The
CPU responds to this signal by interrupting what it is doing, reading the key that you pressed,
processing it, and then returning to the task it was performing before you pressed the key.
Again, you should understand that this is a purely mechanical process: A device signals an
interrupt simply by turning on a wire. The CPU is built so that when that wire is turned on,
the CPU saves enough information about what it is currently doing so that it can return to
the same state later. This information consists of the contents of important internal registers
such as the program counter. Then the CPU jumps to some predetermined memory location
and begins executing the instructions stored there. Those instructions make up an interrupt
handler that does the processing necessary to respond to the interrupt. (This interrupt handler
is part of the device driver software for the device that signaled the interrupt.) At the end of
the interrupt handler is an instruction that tells the CPU to jump back to what it was doing;
it does that by restoring its previously saved state.
Interrupts allow the CPU to deal with asynchronous events. In the regular fetch-and-
execute cycle, things happen in a predetermined order; everything that happens is “synchro-
nized” with everything else. Interrupts make it possible for the CPU to deal efficiently with
events that happen “asynchronously,” that is, at unpredictable times.
As another example of how interrupts are used, consider what happens when the CPU needs
to access data that is stored on a hard disk. The CPU can access data directly only if it is
in main memory. Data on the disk has to be copied into memory before it can be accessed.
Unfortunately, on the scale of speed at which the CPU operates, the disk drive is extremely
slow. When the CPU needs data from the disk, it sends a signal to the disk drive telling it
to locate the data and get it ready. (This signal is sent synchronously, under the control of
a regular program.) Then, instead of just waiting the long and unpredictable amount of time
that the disk drive will take to do this, the CPU goes on with some other task. When the disk
drive has the data ready, it sends an interrupt signal to the CPU. The interrupt handler can
then read the requested data.
∗ ∗ ∗
Now, you might have noticed that all this only makes sense if the CPU actually has several
tasks to perform. If it has nothing better to do, it might as well spend its time polling for input
or waiting for disk drive operations to complete. All modern computers use multitasking to
perform several tasks at once. Some computers can be used by several people at once. Since the
CPU is so fast, it can quickly switch its attention from one user to another, devoting a fraction
of a second to each user in turn. This application of multitasking is called timesharing . But a
modern personal computer with just a single user also uses multitasking. For example, the user
might be typing a paper while a clock is continuously displaying the time and a file is being
downloaded over the network.
Each of the individual tasks that the CPU is working on is called a thread . (Or a process;
there are technical differences between threads and processes, but they are not important here,
since it is threads that are used in Java.) Many CPUs can literally execute more than one
thread simultaneously—such CPUs contain multiple “cores,” each of which can run a thread—
but there is always a limit on the number of threads that can be executed at the same time.
Since there are often more threads than can be executed simultaneously, the computer has to be
CHAPTER 1. THE MENTAL LANDSCAPE 6

able switch its attention from one thread to another, just as a timesharing computer switches
its attention from one user to another. In general, a thread that is being executed will continue
to run until one of several things happens:
• The thread might voluntarily yield control, to give other threads a chance to run.
• The thread might have to wait for some asynchronous event to occur. For example, the
thread might request some data from the disk drive, or it might wait for the user to press
a key. While it is waiting, the thread is said to be blocked , and other threads, if any, have
a chance to run. When the event occurs, an interrupt will “wake up” the thread so that
it can continue running.
• The thread might use up its allotted slice of time and be suspended to allow other threads
to run. Most computers can “forcibly” suspend a thread in this way; computers that
can do that are said to use preemptive multitasking . To do preemptive multitasking,
a computer needs a special timer device that generates an interrupt at regular intervals,
such as 100 times per second. When a timer interrupt occurs, the CPU has a chance to
switch from one thread to another, whether the thread that is currently running likes it
or not. All modern desktop and laptop computers, and even typical smartphones and
tablets, use preemptive multitasking.
Ordinary users, and indeed ordinary programmers, have no need to deal with interrupts and
interrupt handlers. They can concentrate on the different tasks that they want the computer to
perform; the details of how the computer manages to get all those tasks done are not important
to them. In fact, most users, and many programmers, can ignore threads and multitasking
altogether. However, threads have become increasingly important as computers have become
more powerful and as they have begun to make more use of multitasking and multiprocessing.
In fact, the ability to work with threads is fast becoming an essential job skill for programmers.
Fortunately, Java has good support for threads, which are built into the Java programming
language as a fundamental programming concept. Programming with threads will be covered
in Chapter 12.
Just as important in Java and in modern programming in general is the basic concept of
asynchronous events. While programmers don’t actually deal with interrupts directly, they do
often find themselves writing event handlers, which, like interrupt handlers, are called asyn-
chronously when specific events occur. Such “event-driven programming” has a very different
feel from the more traditional straight-through, synchronous programming. We will begin with
the more traditional type of programming, which is still used for programming individual tasks,
but we will return to threads and events later in the text, starting in Chapter 6
∗ ∗ ∗
By the way, the software that does all the interrupt handling, handles communication with
the user and with hardware devices, and controls which thread is allowed to run is called
the operating system. The operating system is the basic, essential software without which
a computer would not be able to function. Other programs, such as word processors and
Web browsers, are dependent upon the operating system. Common desktop operating systems
include Linux, various versions of Windows, and Mac OS. Operating systems for smartphones
and tablets include Android and iOS.
CHAPTER 1. THE MENTAL LANDSCAPE 7

1.3 The Java Virtual Machine


Machine language consists of very simple instructions that can be executed directly by
the CPU of a computer. Almost all programs, though, are written in high-level programming
languages such as Java, Python, or C++. A program written in a high-level language cannot
be run directly on any computer. First, it has to be translated into machine language. This
translation can be done by a program called a compiler . A compiler takes a high-level-language
program and translates it into an executable machine-language program. Once the translation
is done, the machine-language program can be run any number of times, but of course it can only
be run on one type of computer (since each type of computer has its own individual machine
language). If the program is to run on another type of computer it has to be re-translated,
using a different compiler, into the appropriate machine language.
There is an alternative to compiling a high-level language program. Instead of using a
compiler, which translates the program all at once, you can use an interpreter , which translates
it instruction-by-instruction, as necessary. An interpreter is a program that acts much like a
CPU, with a kind of fetch-and-execute cycle. In order to execute a program, the interpreter
runs in a loop in which it repeatedly reads one instruction from the program, decides what is
necessary to carry out that instruction, and then performs the appropriate machine-language
commands to do so.
(A compiler is like a human translator who translates an entire book from one language to
another, producing a new book in the second language. An interpreter is more like a human
interpreter who translates a speech at the United Nations from one language to another at the
same time that the speech is being given.)
One use of interpreters is to execute high-level language programs. For example, the pro-
gramming language Lisp is usually executed by an interpreter rather than a compiler. However,
interpreters have another purpose: They can let you use a machine-language program meant
for one type of computer on a completely different type of computer. For example, one of the
original home computers was the Commodore 64 or “C64”. While you might not find an actual
C64, you can find programs that run on other computers—or even in a web browser—that
“emulate” one. Such an emulator can run C64 programs by acting as an interpreter for the
C64 machine language.
∗ ∗ ∗
The designers of Java chose to use a combination of compiling and interpreting. Programs
written in Java are compiled into machine language, but it is a machine language for a computer
that doesn’t really exist. This so-called “virtual” computer is known as the Java Virtual Ma-
chine, or JVM. The machine language for the Java Virtual Machine is called Java bytecode.
There is no reason why Java bytecode couldn’t be used as the machine language of a real com-
puter, rather than a virtual computer. But in fact the use of a virtual machine makes possible
one of the main selling points of Java: the fact that it can actually be used on any computer.
All that the computer needs is an interpreter for Java bytecode. Such an interpreter simulates
the JVM in the same way that a C64 emulator simulates a Commodore 64 computer. (The
term JVM is also used for the Java bytecode interpreter program that does the simulation, so
we say that a computer needs a JVM in order to run Java programs. Technically, it would be
more correct to say that the interpreter implements the JVM than to say that it is a JVM.)
Of course, a different Java bytecode interpreter is needed for each type of computer, but
once a computer has a Java bytecode interpreter, it can run any Java bytecode program, and
the same program can be run on any computer that has such an interpreter. This is one of the
CHAPTER 1. THE MENTAL LANDSCAPE 8

essential features of Java: the same compiled program can be run on many different types of
computers.

Java Interpreter
for Mac OS

Java
Java Java Interpreter
Compiler Bytecode
Program for Windows
Program

Java Interpreter
for Linux

Why, you might wonder, use the intermediate Java bytecode at all? Why not just distribute
the original Java program and let each person compile it into the machine language of whatever
computer they want to run it on? There are several reasons. First of all, a compiler has to
understand Java, a complex high-level language. The compiler is itself a complex program.
A Java bytecode interpreter, on the other hand, is a relatively small, simple program. This
makes it easy to write a bytecode interpreter for a new type of computer; once that is done,
that computer can run any compiled Java program. It would be much harder to write a Java
compiler for the same computer.
Furthermore, some Java programs are meant to be downloaded over a network. This leads
to obvious security concerns: you don’t want to download and run a program that will damage
your computer or your files. The bytecode interpreter acts as a buffer between you and the
program you download. You are really running the interpreter, which runs the downloaded
program indirectly. The interpreter can protect you from potentially dangerous actions on the
part of that program.
When Java was still a new language, it was criticized for being slow: Since Java bytecode was
executed by an interpreter, it seemed that Java bytecode programs could never run as quickly
as programs compiled into native machine language (that is, the actual machine language of the
computer on which the program is running). However, this problem has been largely overcome
by the use of just-in-time compilers for executing Java bytecode. A just-in-time compiler
translates Java bytecode into native machine language. It does this while it is executing the
program. Just as for a normal interpreter, the input to a just-in-time compiler is a Java bytecode
program, and its task is to execute that program. But as it is executing the program, it also
translates parts of it into machine language. The translated parts of the program can then be
executed much more quickly than they could be interpreted. Since a given part of a program is
often executed many times as the program runs, a just-in-time compiler can significantly speed
up the overall execution time.
I should note that there is no necessary connection between Java and Java bytecode. A
program written in Java could certainly be compiled into the machine language of a real com-
puter. And programs written in other languages can be compiled into Java bytecode. However,
the combination of Java and Java bytecode is platform-independent, secure, and network-
compatible while allowing you to program in a modern high-level object-oriented language.
In the past few years, it has become fairly common to create new programming languages,
or versions of old languages, that compile into Java bytecode. The compiled bytecode programs
can then be executed by a standard JVM. New languages that have been developed specifically
CHAPTER 1. THE MENTAL LANDSCAPE 9

for programming the JVM include Scala, Groovy, Clojure, and Processing. Jython and JRuby
are versions of older languages, Python and Ruby, that target the JVM. These languages make
it possible to enjoy many of the advantages of the JVM while avoiding some of the technicalities
of the Java language. In fact, the use of other languages with the JVM has become important
enough that several new features have been added to the JVM specifically to add better support
for some of those languages. And this improvement to the JVM has in turn made possible some
new features in Java.
∗ ∗ ∗
I should also note that the really hard part of platform-independence is providing a “Graph-
ical User Interface”—with windows, buttons, etc.—that will work on all the platforms that
support Java. You’ll see more about this problem in Section 1.6.

1.4 Fundamental Building Blocks of Programs


There are two basic aspects of programming: data and instructions. To work with
data, you need to understand variables and types; to work with instructions, you need to
understand control structures and subroutines. You’ll spend a large part of the course
becoming familiar with these concepts.
A variable is just a memory location (or several consecutive locations treated as a unit)
that has been given a name so that it can be easily referred to and used in a program. The
programmer only has to worry about the name; it is the compiler’s responsibility to keep track
of the memory location. As a programmer, you just need to keep in mind that the name refers
to a kind of “box” in memory that can hold data, even though you don’t have to know where
in memory that box is located.
In Java and in many other programming languages, a variable has a type that indicates
what sort of data it can hold. One type of variable might hold integers—whole numbers such as
3, -7, and 0—while another holds floating point numbers—numbers with decimal points such as
3.14, -2.7, or 17.0. (Yes, the computer does make a distinction between the integer 17 and the
floating-point number 17.0; they actually look quite different inside the computer.) There could
also be types for individual characters (’A’, ’;’, etc.), strings (“Hello”, “A string can include
many characters”, etc.), and less common types such as dates, colors, sounds, or any other kind
of data that a program might need to store.
Programming languages always have commands for getting data into and out of variables
and for doing computations with data. For example, the following “assignment statement,”
which might appear in a Java program, tells the computer to take the number stored in the
variable named “principal”, multiply that number by 0.07, and then store the result in the
variable named “interest”:
interest = principal * 0.07;
There are also “input commands” for getting data from the user or from files on the computer’s
disks, and there are “output commands” for sending data in the other direction.
These basic commands—for moving data from place to place and for performing
computations—are the building blocks for all programs. These building blocks are combined
into complex programs using control structures and subroutines.
∗ ∗ ∗
A program is a sequence of instructions. In the ordinary “flow of control,” the computer
executes the instructions in the sequence in which they occur in the program, one after the
CHAPTER 1. THE MENTAL LANDSCAPE 10

other. However, this is obviously very limited: the computer would soon run out of instructions
to execute. Control structures are special instructions that can change the flow of control.
There are two basic types of control structure: loops, which allow a sequence of instructions
to be repeated over and over, and branches, which allow the computer to decide between two
or more different courses of action by testing conditions that occur as the program is running.
For example, it might be that if the value of the variable “principal” is greater than 10000,
then the “interest” should be computed by multiplying the principal by 0.05; if not, then the
interest should be computed by multiplying the principal by 0.04. A program needs some
way of expressing this type of decision. In Java, it could be expressed using the following “if
statement”:
if (principal > 10000)
interest = principal * 0.05;
else
interest = principal * 0.04;
(Don’t worry about the details for now. Just remember that the computer can test a condition
and decide what to do next on the basis of that test.)
Loops are used when the same task has to be performed more than once. For example,
if you want to print out a mailing label for each name on a mailing list, you might say, “Get
the first name and address and print the label; get the second name and address and print
the label; get the third name and address and print the label. . . ” But this quickly becomes
ridiculous—and might not work at all if you don’t know in advance how many names there are.
What you would like to say is something like “While there are more names to process, get the
next name and address, and print the label.” A loop can be used in a program to express such
repetition.
∗ ∗ ∗
Large programs are so complex that it would be almost impossible to write them if there
were not some way to break them up into manageable “chunks.” Subroutines provide one way to
do this. A subroutine consists of the instructions for performing some task, grouped together
as a unit and given a name. That name can then be used as a substitute for the whole set of
instructions. For example, suppose that one of the tasks that your program needs to perform
is to draw a house on the screen. You can take the necessary instructions, make them into
a subroutine, and give that subroutine some appropriate name—say, “drawHouse()”. Then
anyplace in your program where you need to draw a house, you can do so with the single
command:
drawHouse();
This will have the same effect as repeating all the house-drawing instructions in each place.
The advantage here is not just that you save typing. Organizing your program into sub-
routines also helps you organize your thinking and your program design effort. While writing
the house-drawing subroutine, you can concentrate on the problem of drawing a house without
worrying for the moment about the rest of the program. And once the subroutine is written,
you can forget about the details of drawing houses—that problem is solved, since you have a
subroutine to do it for you. A subroutine becomes just like a built-in part of the language which
you can use without thinking about the details of what goes on “inside” the subroutine.
∗ ∗ ∗
Variables, types, loops, branches, and subroutines are the basis of what might be called
“traditional programming.” However, as programs become larger, additional structure is needed
CHAPTER 1. THE MENTAL LANDSCAPE 11

to help deal with their complexity. One of the most effective tools that has been found is object-
oriented programming, which is discussed in the next section.

1.5 Objects and Object-oriented Programming


Programs must be designed. No one can just sit down at the computer and compose a
program of any complexity. The discipline called software engineering is concerned with
the construction of correct, working, well-written programs. The software engineer tries to
use accepted and proven methods for analyzing the problem to be solved and for designing a
program to solve that problem.
During the 1970s and into the 80s, the primary software engineering methodology was
structured programming . The structured programming approach to program design was
based on the following advice: To solve a large problem, break the problem into several pieces
and work on each piece separately; to solve each piece, treat it as a new problem which can itself
be broken down into smaller problems; eventually, you will work your way down to problems
that can be solved directly, without further decomposition. This approach is called top-down
programming .
There is nothing wrong with top-down programming. It is a valuable and often-used ap-
proach to problem-solving. However, it is incomplete. For one thing, it deals almost entirely
with producing the instructions necessary to solve a problem. But as time went on, people
realized that the design of the data structures for a program was at least as important as the
design of subroutines and control structures. Top-down programming doesn’t give adequate
consideration to the data that the program manipulates.
Another problem with strict top-down programming is that it makes it difficult to reuse
work done for other projects. By starting with a particular problem and subdividing it into
convenient pieces, top-down programming tends to produce a design that is unique to that
problem. It is unlikely that you will be able to take a large chunk of programming from another
program and fit it into your project, at least not without extensive modification. Producing
high-quality programs is difficult and expensive, so programmers and the people who employ
them are always eager to reuse past work.
∗ ∗ ∗
So, in practice, top-down design is often combined with bottom-up design. In bottom-up
design, the approach is to start “at the bottom,” with problems that you already know how to
solve (and for which you might already have a reusable software component at hand). From
there, you can work upwards towards a solution to the overall problem.
The reusable components should be as “modular” as possible. A module is a component of a
larger system that interacts with the rest of the system in a simple, well-defined, straightforward
manner. The idea is that a module can be “plugged into” a system. The details of what goes on
inside the module are not important to the system as a whole, as long as the module fulfills its
assigned role correctly. This is called information hiding , and it is one of the most important
principles of software engineering.
One common format for software modules is to contain some data, along with some sub-
routines for manipulating that data. For example, a mailing-list module might contain a list of
names and addresses along with a subroutine for adding a new name, a subroutine for printing
mailing labels, and so forth. In such modules, the data itself is often hidden inside the module;
a program that uses the module can then manipulate the data only indirectly, by calling the
subroutines provided by the module. This protects the data, since it can only be manipulated
CHAPTER 1. THE MENTAL LANDSCAPE 12

in known, well-defined ways. And it makes it easier for programs to use the module, since they
don’t have to worry about the details of how the data is represented. Information about the
representation of the data is hidden.
Modules that could support this kind of information-hiding became common in program-
ming languages in the early 1980s. Since then, a more advanced form of the same idea has
more or less taken over software engineering. This latest approach is called object-oriented
programming , often abbreviated as OOP.
The central concept of object-oriented programming is the object, which is a kind of module
containing data and subroutines. The point-of-view in OOP is that an object is a kind of self-
sufficient entity that has an internal state (the data it contains) and that can respond to
messages (calls to its subroutines). A mailing list object, for example, has a state consisting
of a list of names and addresses. If you send it a message telling it to add a name, it will
respond by modifying its state to reflect the change. If you send it a message telling it to print
itself, it will respond by printing out its list of names and addresses.
The OOP approach to software engineering is to start by identifying the objects involved in
a problem and the messages that those objects should respond to. The program that results is
a collection of objects, each with its own data and its own set of responsibilities. The objects
interact by sending messages to each other. There is not much “top-down” in the large-scale
design of such a program, and people used to more traditional programs can have a hard time
getting used to OOP. However, people who use OOP would claim that object-oriented programs
tend to be better models of the way the world itself works, and that they are therefore easier
to write, easier to understand, and more likely to be correct.
∗ ∗ ∗
You should think of objects as “knowing” how to respond to certain messages. Different
objects might respond to the same message in different ways. For example, a “print” message
would produce very different results, depending on the object it is sent to. This property of
objects—that different objects can respond to the same message in different ways—is called
polymorphism.
It is common for objects to bear a kind of “family resemblance” to one another. Objects
that contain the same type of data and that respond to the same messages in the same way
belong to the same class. (In actual programming, the class is primary; that is, a class is
created and then one or more objects are created using that class as a template.) But objects
can be similar without being in exactly the same class.
For example, consider a drawing program that lets the user draw lines, rectangles, ovals,
polygons, and curves on the screen. In the program, each visible object on the screen could be
represented by a software object in the program. There would be five classes of objects in the
program, one for each type of visible object that can be drawn. All the lines would belong to
one class, all the rectangles to another class, and so on. These classes are obviously related;
all of them represent “drawable objects.” They would, for example, all presumably be able to
respond to a “draw yourself” message. Another level of grouping, based on the data needed to
represent each type of object, is less obvious, but would be very useful in a program: We can
group polygons and curves together as “multipoint objects,” while lines, rectangles, and ovals
are “two-point objects.” (A line is determined by its two endpoints, a rectangle by two of its
corners, and an oval by two corners of the rectangle that contains it. The rectangles that I am
talking about here have sides that are vertical and horizontal, so that they can be specified by
just two points; this is the common meaning of “rectangle” in drawing programs.) We could
diagram these relationships as follows:
CHAPTER 1. THE MENTAL LANDSCAPE 13

DrawableObject

MultipointObject TwoPointObject

Polygon Curve Line Rectangle Oval

DrawableObject, MultipointObject, and TwoPointObject would be classes in the program.


MultipointObject and TwoPointObject would be subclasses of DrawableObject. The class
Line would be a subclass of TwoPointObject and (indirectly) of DrawableObject. A subclass of
a class is said to inherit the properties of that class. The subclass can add to its inheritance and
it can even “override” part of that inheritance (by defining a different response to some message).
Nevertheless, lines, rectangles, and so on are drawable objects, and the class DrawableObject
expresses this relationship.
Inheritance is a powerful means for organizing a program. It is also related to the problem
of reusing software components. A class is the ultimate reusable component. Not only can it
be reused directly if it fits exactly into a program you are trying to write, but if it just almost
fits, you can still reuse it by defining a subclass and making only the small changes necessary
to adapt it exactly to your needs.
So, OOP is meant to be both a superior program-development tool and a partial solution
to the software reuse problem. Objects, classes, and object-oriented programming will be
important themes throughout the rest of this text. You will start using objects that are built
into the Java language in the next chapter, and in Chapter 5 you will begin creating your own
classes and objects.

1.6 The Modern User Interface


When computers were first introduced, ordinary people—including most programmers—
couldn’t get near them. They were locked up in rooms with white-coated attendants who would
take your programs and data, feed them to the computer, and return the computer’s response
some time later. When timesharing—where the computer switches its attention rapidly from
one person to another—was invented in the 1960s, it became possible for several people to
interact directly with the computer at the same time. On a timesharing system, users sit at
“terminals” where they type commands to the computer, and the computer types back its re-
sponse. Early personal computers also used typed commands and responses, except that there
was only one person involved at a time. This type of interaction between a user and a computer
is called a command-line interface.
Today, of course, most people interact with computers in a completely different way. They
use a Graphical User Interface, or GUI. The computer draws interface components on the
screen. The components include things like windows, scroll bars, menus, buttons, and icons.
Usually, a mouse is used to manipulate such components or, on “touchscreens,” your fingers.
Assuming that you have not just been teleported in from the 1970s, you are no doubt already
CHAPTER 1. THE MENTAL LANDSCAPE 14

familiar with the basics of graphical user interfaces!


A lot of GUI interface components have become fairly standard. That is, they have similar
appearance and behavior on many different computer platforms including Mac OS, Windows,
and Linux. Java programs, which are supposed to run on many different platforms without
modification to the program, can use all the standard GUI components. They might vary a
little in appearance from platform to platform, but their functionality should be identical on
any computer on which the program runs.
Shown below is an image of a very simple Java program that demonstrates a few standard
GUI interface components. When the program is run, a window similar to the picture shown
here will open on the computer screen. There are four components in the window with which the
user can interact: a button, a checkbox, a text field, and a pop-up menu. These components
are labeled. There are a few other components in the window. The labels themselves are
components (even though you can’t interact with them). The right half of the window is a
text area component, which can display multiple lines of text. A scrollbar component appears
alongside the text area when the number of lines of text becomes larger than will fit in the
text area. And in fact, in Java terminology, the whole window is itself considered to be a
“component.”

(If you would like to run this program, the source code, GUIDemo.java, is available on line.
For more information on using this and other examples from this textbook, see Section 2.6.)
Now, Java actually has three complete sets of GUI components. One of these, the AWT
or Abstract Windowing Toolkit, was available in the original version of Java. The second,
which is known as Swing , was introduced in Java version 1.2, and was the standard GUI toolkit
for many years. The third GUI toolkit, JavaFX, became a standard part of Java in Version 8
(but but has recently been removed, so that it requires separate installation in some versions
of Java). Although Swing, and even the AWT, can still be used, JavaFX is meant as a more
modern way to write GUI applications. This textbook covers JavaFX exclusively. (If you need
to learn Swing, you can take a look at the previous version of this book.)
When a user interacts with GUI components, “events” are generated. For example, clicking
a push button generates an event, and pressing a key on the keyboard generates an event.
Each time an event is generated, a message is sent to the program telling it that the event has
occurred, and the program responds according to its program. In fact, a typical GUI program
consists largely of “event handlers” that tell the program how to respond to various types of
events. In the above example, the program has been programmed to respond to each event by
displaying a message in the text area. In a more realistic example, the event handlers would
have more to do.
The use of the term “message” here is deliberate. Messages, as you saw in the previous sec-
tion, are sent to objects. In fact, Java GUI components are implemented as objects. Java
includes many predefined classes that represent various types of GUI components. Some of
CHAPTER 1. THE MENTAL LANDSCAPE 15

these classes are subclasses of others. Here is a diagram showing just a few of the JavaFX GUI
classes and their relationships:

Control

Label ButtonBase Slider TextInputControl ComboBox

Button CheckBox TextField TextArea

Don’t worry about the details for now, but try to get some feel about how object-oriented
programming and inheritance are used here. Note that all the GUI classes shown here are
subclasses, directly or indirectly, of a class called Control, which represents general properties
that are shared by many JavaFX components. In the diagram, two of the direct subclasses
of Control themselves have subclasses. The classes TextField and TextArea, which have certain
behaviors in common, are grouped together as subclasses of TextInputControl. Similarly But-
ton and CheckBox are subclasses of ButtonBase, which represents properties common to both
buttons and checkboxes. (ComboBox, by the way, is the class that represents pop-up menus.)
Just from this brief discussion, perhaps you can see how GUI programming can make effec-
tive use of object-oriented design. In fact, GUIs, with their “visible objects,” are probably a
major factor contributing to the popularity of OOP.
Programming with GUI components and events is one of the most interesting aspects of
Java. However, we will spend several chapters on the basics before returning to this topic in
Chapter 6.

1.7 The Internet and Beyond


Computers can be connected together on networks. A computer on a network can
communicate with other computers on the same network by exchanging data and files or by
sending and receiving messages. Computers on a network can even work together on a large
computation.
Today, millions of computers throughout the world are connected to a single huge network
called the Internet. New computers are being connected to the Internet every day, both
by wireless communication and by physical connection using technologies such as DSL, cable
modems, and Ethernet.
There are elaborate protocols for communication over the Internet. A protocol is simply a
detailed specification of how communication is to proceed. For two computers to communicate
at all, they must both be using the same protocols. The most basic protocols on the Internet are
the Internet Protocol (IP), which specifies how data is to be physically transmitted from one
computer to another, and the Transmission Control Protocol (TCP), which ensures that
data sent using IP is received in its entirety and without error. These two protocols, which are
referred to collectively as TCP/IP, provide a foundation for communication. Other protocols
CHAPTER 1. THE MENTAL LANDSCAPE 16

use TCP/IP to send specific types of information such as web pages, electronic mail, and data
files.
All communication over the Internet is in the form of packets. A packet consists of some
data being sent from one computer to another, along with addressing information that indicates
where on the Internet that data is supposed to go. Think of a packet as an envelope with an
address on the outside and a message on the inside. (The message is the data.) The packet
also includes a “return address,” that is, the address of the sender. A packet can hold only
a limited amount of data; longer messages must be divided among several packets, which are
then sent individually over the Net and reassembled at their destination.
Every computer on the Internet has an IP address, a number that identifies it uniquely
among all the computers on the Net. (Actually, the claim about uniqueness is not quite true, but
the basic idea is valid, and the full truth is complicated.) The IP address is used for addressing
packets. A computer can only send data to another computer on the Internet if it knows that
computer’s IP address. Since people prefer to use names rather than numbers, most computers
are also identified by names, called domain names. For example, the main computer of
the Mathematics Department at Hobart and William Smith Colleges has the domain name
math.hws.edu. (Domain names are just for convenience; your computer still needs to know
IP addresses before it can communicate. There are computers on the Internet whose job it
is to translate domain names to IP addresses. When you use a domain name, your computer
sends a message to a domain name server to find out the corresponding IP address. Then, your
computer uses the IP address, rather than the domain name, to communicate with the other
computer.)
The Internet provides a number of services to the computers connected to it (and, of course,
to the users of those computers). These services use TCP/IP to send various types of data over
the Net. Among the most popular services are instant messaging, file sharing, electronic mail,
and the World-Wide Web. Each service has its own protocols, which are used to control
transmission of data over the network. Each service also has some sort of user interface, which
allows the user to view, send, and receive data through the service.
For example, the email service uses a protocol known as SMTP (Simple Mail Transfer
Protocol) to transfer email messages from one computer to another. Other protocols, such as
POP and IMAP, are used to fetch messages from an email account so that the recipient can
read them. A person who uses email, however, doesn’t need to understand or even know about
these protocols. Instead, they are used behind the scenes by computer programs to send and
receive email messages. These programs provide the user with an easy-to-use user interface to
the underlying network protocols.
The World-Wide Web is perhaps the most exciting of network services. The World-Wide
Web allows you to request pages of information that are stored on computers all over the
Internet. A Web page can contain links to other pages on the same computer from which
it was obtained or to other computers anywhere in the world. A computer that stores such
pages of information is called a web server . The user interface to the Web is the type of
program known as a web browser . Common web browsers include Microsoft Edge, Internet
Explorer, Firefox, Chrome, and Safari. You use a Web browser to request a page of information.
The browser sends a request for that page to the computer on which the page is stored, and
when a response is received from that computer, the web browser displays it to you in a neatly
formatted form. A web browser is just a user interface to the Web. Behind the scenes, the
web browser uses a protocol called HTTP (HyperText Transfer Protocol) to send each page
request and to receive the response from the web server.
CHAPTER 1. THE MENTAL LANDSCAPE 17

∗ ∗ ∗
Now just what, you might be thinking, does all this have to do with Java? In fact, Java
is intimately associated with the Internet and the World-Wide Web. When Java was first
introduced, one of its big attractions was the ability to write applets. An applet is a small
program that is transmitted over the Internet and that runs on a web page. Applets made it
possible for a web page to perform complex tasks and have complex interactions with the user.
Alas, applets have suffered from a variety of problems, and they have fallen out of use. There
are now other options for running programs on Web pages.
But applets were only one aspect of Java’s relationship with the Internet. Java can be
used to write complex, stand-alone applications that do not depend on a Web browser. Many
of these programs are network-related. For example many of the largest and most complex
web sites use web server software that is written in Java. Java includes excellent support for
network protocols, and its platform independence makes it possible to write network programs
that work on many different types of computer. You will learn about Java’s network support
in Chapter 11.
Its support for networking is not Java’s only advantage. But many good programming
languages have been invented only to be soon forgotten. Java has had the good luck to ride on
the coattails of the Internet’s immense and increasing popularity.
∗ ∗ ∗
As Java has matured, its applications have reached far beyond the Net. The standard
version of Java already comes with support for many technologies, such as cryptography, data
compression, sound processing, and three-dimensional graphics. And programmers have written
Java libraries to provide additional capabilities. Complex, high-performance systems can be
developed in Java. For example, Hadoop, a system for large scale data processing, is written in
Java. Hadoop is used by Yahoo, Facebook, and other Web sites to process the huge amounts
of data generated by their users.
Furthermore, Java is not restricted to use on traditional computers. Java can be used to
write programs for many smartphones (though not for the iPhone). It is the primary devel-
opment language for Android-based devices. (Android uses Google’s own version of Java and
does not use the same graphical user interface components as standard Java.) Java is also the
programming language for the Amazon Kindle eBook reader and for interactive features on
Blu-Ray video disks.
At this time, Java certainly ranks as one of the most widely used programming languages.
It is a good choice for almost any programming project that is meant to run on more than
one type of computing device, and is a reasonable choice even for many programs that will
run on only one device. It is probably still the most widely taught language at Colleges and
Universities. It is similar enough to other popular languages, such as C++, JavaScript, and
Python, that knowing it will give you a good start on learning those languages as well. Overall,
learning Java is a great starting point on the road to becoming an expert programmer. I hope
you enjoy the journey!
Quiz 18

Quiz on Chapter 1
(answers)

1. One of the components of a computer is its CPU. What is a CPU and what role does it
play in a computer?

2. Explain what is meant by an “asynchronous event.” Give some examples.

3. What is the difference between a “compiler” and an “interpreter”?

4. Explain the difference between high-level languages and machine language.

5. If you have the source code for a Java program, and you want to run that program, you
will need both a compiler and an interpreter. What does the Java compiler do, and what
does the Java interpreter do?

6. What is a subroutine?

7. Java is an object-oriented programming language. What is an object?

8. What is a variable? (There are four different ideas associated with variables in Java. Try
to mention all four aspects in your answer. Hint: One of the aspects is the variable’s
name.)

9. Java is a “platform-independent language.” What does this mean?

10. What is the “Internet”? Give some examples of how it is used. (What kind of services
does it provide?)
Chapter 2

Programming in the Small I:


Names and Things

On a basic level (the level of machine language), a computer can perform only very simple
operations. A computer performs complex tasks by stringing together large numbers of such
operations. Such tasks must be “scripted” in complete and perfect detail by programs. Creating
complex programs will never be really easy, but the difficulty can be handled to some extent by
giving the program a clear overall structure. The design of the overall structure of a program
is what I call “programming in the large.”
Programming in the small, which is sometimes called coding , would then refer to filling in
the details of that design. The details are the explicit, step-by-step instructions for performing
fairly small-scale tasks. When you do coding, you are working “close to the machine,” with some
of the same concepts that you might use in machine language: memory locations, arithmetic
operations, loops and branches. In a high-level language such as Java, you get to work with
these concepts on a level several steps above machine language. However, you still have to
worry about getting all the details exactly right.
This chapter and the next examine the facilities for programming in the small in the Java
programming language. Don’t be misled by the term “programming in the small” into thinking
that this material is easy or unimportant. This material is an essential foundation for all types
of programming. If you don’t understand it, you can’t write programs, no matter how good
you get at designing their large-scale structure.
The last section of this chapter discusses programming environments. That section
contains information about how to compile and run Java programs, and you should take a look
at it before trying to write and use your own programs or trying to use the sample programs
in this book.

2.1 The Basic Java Application


A program is a sequence of instructions that a computer can execute to perform some
task. A simple enough idea, but for the computer to make any use of the instructions, they
must be written in a form that the computer can use. This means that programs have to be
written in programming languages. Programming languages differ from ordinary human
languages in being completely unambiguous and very strict about what is and is not allowed
in a program. The rules that determine what is allowed are called the syntax of the language.
Syntax rules specify the basic vocabulary of the language and how programs can be constructed

19
CHAPTER 2. NAMES AND THINGS 20

using things like loops, branches, and subroutines. A syntactically correct program is one that
can be successfully compiled or interpreted; programs that have syntax errors will be rejected
(hopefully with a useful error message that will help you fix the problem).
So, to be a successful programmer, you have to develop a detailed knowledge of the syntax
of the programming language that you are using. However, syntax is only part of the story. It’s
not enough to write a program that will run—you want a program that will run and produce
the correct result! That is, the meaning of the program has to be right. The meaning of
a program is referred to as its semantics. More correctly, the semantics of a programming
language is the set of rules that determine the meaning of a program written in that language.
A semantically correct program is one that does what you want it to.
Furthermore, a program can be syntactically and semantically correct but still be a pretty
bad program. Using the language correctly is not the same as using it well. For example, a
good program has “style.” It is written in a way that will make it easy for people to read and
to understand. It follows conventions that will be familiar to other programmers. And it has
an overall design that will make sense to human readers. The computer is completely oblivious
to such things, but to a human reader, they are paramount. These aspects of programming are
sometimes referred to as pragmatics. (I will often use the more common term style.)
When I introduce a new language feature, I will explain the syntax, the semantics, and
some of the pragmatics of that feature. You should memorize the syntax; that’s the easy part.
Then you should get a feeling for the semantics by following the examples given, making sure
that you understand how they work, and, ideally, writing short programs of your own to test
your understanding. And you should try to appreciate and absorb the pragmatics—this means
learning how to use the language feature well, with style that will earn you the admiration of
other programmers.
Of course, even when you’ve become familiar with all the individual features of the language,
that doesn’t make you a programmer. You still have to learn how to construct complex programs
to solve particular problems. For that, you’ll need both experience and taste. You’ll find hints
about software development throughout this textbook.
∗ ∗ ∗
We begin our exploration of Java with the problem that has become traditional for such
beginnings: to write a program that displays the message “Hello World!”. This might seem like
a trivial problem, but getting a computer to do this is really a big first step in learning a new
programming language (especially if it’s your first programming language). It means that you
understand the basic process of:
1. getting the program text into the computer,
2. compiling the program, and
3. running the compiled program.
The first time through, each of these steps will probably take you a few tries to get right. I
won’t go into the details here of how you do each of these steps; it depends on the particular
computer and Java programming environment that you are using. See Section 2.6 for informa-
tion about creating and running Java programs in specific programming environments. But in
general, you will type the program using some sort of text editor and save the program in a file.
Then, you will use some command to try to compile the file. You’ll either get a message that the
program contains syntax errors, or you’ll get a compiled version of the program. In the case of
Java, the program is compiled into Java bytecode, not into machine language. Finally, you can
run the compiled program by giving some appropriate command. For Java, you will actually use
CHAPTER 2. NAMES AND THINGS 21

an interpreter to execute the Java bytecode. Your programming environment might automate
some of the steps for you—for example, the compilation step is often done automatically—but
you can be sure that the same three steps are being done in the background.
Here is a Java program to display the message “Hello World!”. Don’t expect to understand
what’s going on here just yet; some of it you won’t really understand until a few chapters from
now:
/** A program to display the message
* "Hello World!" on standard output.
*/
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
} // end of class HelloWorld
The command that actually displays the message is:
System.out.println("Hello World!");
This command is an example of a subroutine call statement. It uses a “built-in subroutine”
named System.out.println to do the actual work. Recall that a subroutine consists of the
instructions for performing some task, chunked together and given a name. That name can be
used to “call” the subroutine whenever that task needs to be performed. A built-in subroutine
is one that is already defined as part of the language and therefore automatically available for
use in any program.
When you run this program, the message “Hello World!” (without the quotes) will be
displayed on standard output. Unfortunately, I can’t say exactly what that means! Java is
meant to run on many different platforms, and standard output will mean different things on
different platforms. However, you can expect the message to show up in some convenient or
inconvenient place. (If you use a command-line interface, like that in Oracle’s Java Development
Kit, you type in a command to tell the computer to run the program. The computer will type
the output from the program, Hello World!, on the next line. In an integrated development
environment such as Eclipse, the output might appear somewhere in one of the environment’s
windows.)
You must be curious about all the other stuff in the above program. Part of it consists of
comments. Comments in a program are entirely ignored by the computer; they are there for
human readers only. This doesn’t mean that they are unimportant. Programs are meant to be
read by people as well as by computers, and without comments, a program can be very difficult
to understand. Java has two types of comments. The first type begins with // and extends to
the end of a line. There is a comment of this form on the last line of the above program. The
computer ignores the // and everything that follows it on the same line. The second type of
comment starts with /* and ends with */, and it can extend over more than one line. The first
three lines of the program are an example of this second type of comment. (A comment that
actually begins with /**, like this one does, has special meaning; it is a “Javadoc” comment
that can be used to produce documentation for the program. See Subsection 4.6.5.)
Everything else in the program is required by the rules of Java syntax. All programming in
Java is done inside “classes.” The first line in the above program (not counting the comment)
says that this is a class named HelloWorld. “HelloWorld,” the name of the class, also serves as
CHAPTER 2. NAMES AND THINGS 22

the name of the program. Not every class is a program. In order to define a program, a class
must include a subroutine named main, with a definition that takes the form:
public static void main(String[] args) {
hstatements i
}
When you tell the Java interpreter to run the program, the interpreter calls this main()
subroutine, and the statements that it contains are executed. These statements make up the
script that tells the computer exactly what to do when the program is executed. The main()
routine can call other subroutines that are defined in the same class or even in other classes,
but it is the main() routine that determines how and in what order the other subroutines are
used.
The word “public” in the first line of main() means that this routine can be called from out-
side the program. This is essential because the main() routine is called by the Java interpreter,
which is something external to the program itself. The remainder of the first line of the routine
is harder to explain at the moment; for now, just think of it as part of the required syntax.
The definition of the subroutine—that is, the instructions that say what it does—consists of
the sequence of “statements” enclosed between braces, { and }. Here, I’ve used hstatementsi as
a placeholder for the actual statements that make up the program. Throughout this textbook,
I will always use a similar format: anything that you see in hthis style of texti (italic in angle
brackets) is a placeholder that describes something you need to type when you write an actual
program.
As noted above, a subroutine can’t exist by itself. It has to be part of a “class”. A program
is defined by a public class that takes the form:
hoptional-package-declaration i
hoptional-imports i
public class hprogram-name i {
hoptional-variable-declarations-and-subroutines i
public static void main(String[] args) {
hstatements i
}
hoptional-variable-declarations-and-subroutines i
}
The first two lines have to do with using packages. A package is a group of classes. You will
start learning about packages in Section 2.4, but our first few example programs will not use
them.
The hprogram-namei in the line that begins “public class” is the name of the program, as
well as the name of the class. (Remember, again, that hprogram-namei is a placeholder for the
actual name!) If the name of the class is HelloWorld, then the class must be saved in a file
called HelloWorld.java. When this file is compiled, another file named HelloWorld.class
will be produced. This class file, HelloWorld.class, contains the translation of the program
into Java bytecode, which can be executed by a Java interpreter. HelloWorld.java is called
the source code for the program. To execute the program, you only need the compiled class
file, not the source code.
The layout of the program on the page, such as the use of blank lines and indentation, is not
part of the syntax or semantics of the language. The computer doesn’t care about layout—you
CHAPTER 2. NAMES AND THINGS 23

could run the entire program together on one line as far as it is concerned. However, layout is
important to human readers, and there are certain style guidelines for layout that are followed
by most programmers.
Also note that according to the above syntax specification, a program can contain other
subroutines besides main(), as well as things called “variable declarations.” You’ll learn more
about these later, but not until Chapter 4.

2.2 Variables and the Primitive Types


Names are fundamental to programming. In programs, names are used to refer to many
different sorts of things. In order to use those things, a programmer must understand the rules
for giving names to them and the rules for using the names to work with them. That is, the
programmer must understand the syntax and the semantics of names.
According to the syntax rules of Java, the most basic names are identifiers. Identifiers
can be used to name classes, variables, and subroutines. An identifier is a sequence of one or
more characters. It must begin with a letter or underscore and must consist entirely of letters,
digits, and underscores. (“Underscore” refers to the character ’ ’.) For example, here are some
legal identifiers:
N n rate x15 quite a long name HelloWorld
No spaces are allowed in identifiers; HelloWorld is a legal identifier, but “Hello World” is
not. Upper case and lower case letters are considered to be different, so that HelloWorld,
helloworld, HELLOWORLD, and hElloWorLD are all distinct names. Certain words are reserved
for special uses in Java, and cannot be used as identifiers. These reserved words include:
class, public, static, if, else, while, and several dozen other words. (Remember that
reserved words are not identifiers, since they can’t be used as names for things.)
Java is actually pretty liberal about what counts as a letter or a digit. Java uses the
Unicode character set, which includes thousands of characters from many different languages
and different alphabets, and many of these characters count as letters or digits. However, I will
be sticking to what can be typed on a regular English keyboard.
The pragmatics of naming includes style guidelines about how to choose names for things.
For example, it is customary for names of classes to begin with upper case letters, while names
of variables and of subroutines begin with lower case letters; you can avoid a lot of confusion by
following this standard convention in your own programs. Most Java programmers do not use
underscores in names, although some do use them at the beginning of the names of certain kinds
of variables. When a name is made up of several words, such as HelloWorld or interestRate,
it is customary to capitalize each word, except possibly the first; this is sometimes referred
to as camel case, since the upper case letters in the middle of a name are supposed to look
something like the humps on a camel’s back.
Finally, I’ll note that in addition to simple identifiers, things in Java can have compound
names which consist of several simple names separated by periods. (Compound names are also
called qualified names.) You’ve already seen an example: System.out.println. The idea
here is that things in Java can contain other things. A compound name is a kind of path to an
item through one or more levels of containment. The name System.out.println indicates that
something called “System” contains something called “out” which in turn contains something
called “println”.
CHAPTER 2. NAMES AND THINGS 24

2.2.1 Variables
Programs manipulate data that are stored in memory. In machine language, data can only be
referred to by giving the numerical address of the location in memory where the data is stored.
In a high-level language such as Java, names are used instead of numbers to refer to data. It
is the job of the computer to keep track of where in memory the data is actually stored; the
programmer only has to remember the name. A name used in this way—to refer to data stored
in memory—is called a variable.
Variables are actually rather subtle. Properly speaking, a variable is not a name for the
data itself but for a location in memory that can hold data. You should think of a variable as
a container or box where you can store data that you will need to use later. The variable refers
directly to the box and only indirectly to the data in the box. Since the data in the box can
change, a variable can refer to different data values at different times during the execution of
the program, but it always refers to the same box. Confusion can arise, especially for beginning
programmers, because when a variable is used in a program in certain ways, it refers to the
container, but when it is used in other ways, it refers to the data in the container. You’ll see
examples of both cases below.
In Java, the only way to get data into a variable—that is, into the box that the variable
names—is with an assignment statement. An assignment statement takes the form:
hvariable i = hexpression i;
where hexpressioni represents anything that refers to or computes a data value. When the
computer comes to an assignment statement in the course of executing a program, it evaluates
the expression and puts the resulting data value into the variable. For example, consider the
simple assignment statement
rate = 0.07;
The hvariablei in this assignment statement is rate, and the hexpressioni is the number 0.07.
The computer executes this assignment statement by putting the number 0.07 in the variable
rate, replacing whatever was there before. Now, consider the following more complicated
assignment statement, which might come later in the same program:
interest = rate * principal;
Here, the value of the expression “rate * principal” is being assigned to the variable
interest. In the expression, the * is a “multiplication operator” that tells the computer
to multiply rate times principal. The names rate and principal are themselves variables,
and it is really the values stored in those variables that are to be multiplied. We see that when
a variable is used in an expression, it is the value stored in the variable that matters; in this
case, the variable seems to refer to the data in the box, rather than to the box itself. When
the computer executes this assignment statement, it takes the value of rate, multiplies it by
the value of principal, and stores the answer in the box referred to by interest. When a
variable is used on the left-hand side of an assignment statement, it refers to the box that is
named by the variable.
(Note, by the way, that an assignment statement is a command that is executed by the
computer at a certain time. It is not a statement of fact. For example, suppose a program
includes the statement “rate = 0.07;”. If the statement “interest = rate * principal;”
is executed later in the program, can we say that the principal is multiplied by 0.07? No!
The value of rate might have been changed in the meantime by another statement. The
CHAPTER 2. NAMES AND THINGS 25

meaning of an assignment statement is completely different from the meaning of an equation


in mathematics, even though both use the symbol ”=”.)

2.2.2 Types
A variable in Java is designed to hold only one particular type of data; it can legally hold that
type of data and no other. The compiler will consider it to be a syntax error if you try to
violate this rule by assigning a value of the wrong type to a variable. We say that Java is a
strongly typed language because it enforces this rule.
There are eight so-called primitive types built into Java. The primitive types are named
byte, short, int, long, float, double, char, and boolean. The first four types hold integers
(whole numbers such as 17, -38477, and 0). The four integer types are distinguished by the
ranges of integers they can hold. The float and double types hold real numbers (such as 3.6 and
-145.99). Again, the two real types are distinguished by their range and accuracy. A variable
of type char holds a single character from the Unicode character set. And a variable of type
boolean holds one of the two logical values true or false.
Any data value stored in the computer’s memory must be represented as a binary number,
that is as a string of zeros and ones. A single zero or one is called a bit. A string of eight
bits is called a byte. Memory is usually measured in terms of bytes. Not surprisingly, the byte
data type refers to a single byte of memory. A variable of type byte holds a string of eight
bits, which can represent any of the integers between -128 and 127, inclusive. (There are 256
integers in that range; eight bits can represent 256—two raised to the power eight—different
values.) As for the other integer types,
• short corresponds to two bytes (16 bits). Variables of type short have values in the range
-32768 to 32767.
• int corresponds to four bytes (32 bits). Variables of type int have values in the range
-2147483648 to 2147483647.
• long corresponds to eight bytes (64 bits). Variables of type long have values in the range
-9223372036854775808 to 9223372036854775807.
You don’t have to remember these numbers, but they do give you some idea of the size of
integers that you can work with. Usually, for representing integer data you should just stick to
the int data type, which is good enough for most purposes.
The float data type is represented in four bytes of memory, using a standard method for
encoding real numbers. The maximum value for a float is about 10 raised to the power 38.
A float can have about 7 significant digits. (So that 32.3989231134 and 32.3989234399 would
both have to be rounded off to about 32.398923 in order to be stored in a variable of type
float.) A double takes up 8 bytes, can range up to about 10 to the power 308, and has about
15 significant digits. Ordinarily, you should stick to the double type for real values.
A variable of type char occupies two bytes in memory. The value of a char variable is a
single character such as A, *, x, or a space character. The value can also be a special character
such a tab or a carriage return or one of the many Unicode characters that come from different
languages. Values of type char are closely related to integer values, since a character is actually
stored as a 16-bit integer code number. In fact, we will see that chars in Java can actually be
used like integers in certain situations.
It is important to remember that a primitive type value is represented using only a certain,
finite number of bits. So, an int can’t be an arbitrary integer; it can only be an integer
CHAPTER 2. NAMES AND THINGS 26

in a certain finite range of values. Similarly, float and double variables can only take on
certain values. They are not true real numbers in the mathematical sense. For example, the
mathematical constant π can only be approximated by a value of type float or double, since
it would require an infinite number of decimal places to represent it exactly. For that matter,
many simple numbers such as 1/3 can only be approximated by floats and doubles.

2.2.3 Literals
A data value is stored in the computer as a sequence of bits. In the computer’s memory, it
doesn’t look anything like a value written on this page. You need a way to include constant
values in the programs that you write. In a program, you represent constant values as literals.
A literal is something that you can type in a program to represent a value. It is a kind of name
for a constant value.
For example, to type a value of type char in a program, you must surround it with a pair
of single quote marks, such as ’A’, ’*’, or ’x’. The character and the quote marks make up a
literal of type char. Without the quotes, A would be an identifier and * would be a multiplication
operator. The quotes are not part of the value and are not stored in the variable; they are just
a convention for naming a particular character constant in a program. If you want to store the
character A in a variable ch of type char, you could do so with the assignment statement
ch = ’A’;
Certain special characters have special literals that use a backslash, \, as an “escape character.”
In particular, a tab is represented as ’\t’, a carriage return as ’\r’, a linefeed as ’\n’, the
single quote character as ’\’’, and the backslash itself as ’\\’. Note that even though you
type two characters between the quotes in ’\t’, the value represented by this literal is a single
tab character.
Numeric literals are a little more complicated than you might expect. Of course, there
are the obvious literals such as 317 and 17.42. But there are other possibilities for expressing
numbers in a Java program. First of all, real numbers can be represented in an exponential
form such as 1.3e12 or 12.3737e-108. The “e12” and “e-108” represent powers of 10, so that
1.3e12 means 1.3 times 1012 and 12.3737e-108 means 12.3737 times 10−108 . This format can be
used to express very large and very small numbers. Any numeric literal that contains a decimal
point or exponential is a literal of type double. To make a literal of type float, you have to
append an “F” or “f” to the end of the number. For example, “1.2F” stands for 1.2 considered
as a value of type float. (Occasionally, you need to know this because the rules of Java say that
you can’t assign a value of type double to a variable of type float, so you might be confronted
with a ridiculous-seeming error message if you try to do something like “x = 1.2;” if x is a
variable of type float. You have to say “x = 1.2F;". This is one reason why I advise sticking
to type double for real numbers.)
Even for integer literals, there are some complications. Ordinary integers such as 177777
and -32 are literals of type byte, short, or int, depending on their size. You can make a literal
of type long by adding “L” as a suffix. For example: 17L or 728476874368L. As another
complication, Java allows binary, octal (base-8), and hexadecimal (base-16) literals. I don’t
want to cover number bases in detail, but in case you run into them in other people’s programs,
it’s worth knowing a few things: Octal numbers use only the digits 0 through 7. In Java, a
numeric literal that begins with a 0 is interpreted as an octal number; for example, the octal
literal 045 represents the number 37, not the number 45. Octal numbers are rarely used, but
you need to be aware of what happens when you start a number with a zero. Hexadecimal
CHAPTER 2. NAMES AND THINGS 27

numbers use 16 digits, the usual digits 0 through 9 and the letters A, B, C, D, E, and F. Upper
case and lower case letters can be used interchangeably in this context. The letters represent
the numbers 10 through 15. In Java, a hexadecimal literal begins with 0x or 0X, as in 0x45
or 0xFF7A. Finally, binary literals start with 0b or 0B and contain only the digits 0 and 1; for
example: 0b10110.
As a final complication, numeric literals can include the underscore character (“ ”), which
can be used to separate groups of digits. For example, the integer constant for two billion could
be written 2 000 000 000, which is a good deal easier to decipher than 2000000000. There is
no rule about how many digits have to be in each group. Underscores can be especially useful
in long binary numbers; for example, 0b1010 1100 1011.
I will note that hexadecimal numbers can also be used in character literals to represent
arbitrary Unicode characters. A Unicode literal consists of \u followed by four hexadecimal
digits. For example, the character literal ’\u00E9’ represents the Unicode character that is an
“e” with an acute accent.
For the type boolean, there are precisely two literals: true and false. These literals are
typed just as I’ve written them here, without quotes, but they represent values, not variables.
Boolean values occur most often as the values of conditional expressions. For example,
rate > 0.05
is a boolean-valued expression that evaluates to true if the value of the variable rate is greater
than 0.05, and to false if the value of rate is less than or equal to 0.05. As you’ll see in
Chapter 3, boolean-valued expressions are used extensively in control structures. Of course,
boolean values can also be assigned to variables of type boolean. For example, if test is a
variable of type boolean, then both of the following assignment statements are legal:
test = true;
test = rate > 0.05;

2.2.4 Strings and String Literals


Java has other types in addition to the primitive types, but all the other types represent objects
rather than “primitive” data values. For the most part, we are not concerned with objects for
the time being. However, there is one predefined object type that is very important: the type
String. (String is a type, but not a primitive type; it is in fact the name of a class, and we will
return to that aspect of strings in the next section.)
A value of type String is a sequence of characters. You’ve already seen a string literal:
"Hello World!". The double quotes are part of the literal; they have to be typed in the
program. However, they are not part of the actual String value, which consists of just the
characters between the quotes. A string can contain any number of characters, even zero. A
string with no characters is called the empty string and is represented by the literal "", a pair
of double quote marks with nothing between them. Remember the difference between single
quotes and double quotes! Single quotes are used for char literals and double quotes for String
literals! There is a big difference between the String "A" and the char ’A’.
Within a string literal, special characters can be represented using the backslash notation.
Within this context, the double quote is itself a special character. For example, to represent
the string value
I said, "Are you listening!"
with a linefeed at the end, you would have to type the string literal:
CHAPTER 2. NAMES AND THINGS 28

"I said, \"Are you listening!\"\n"


You can also use \t, \r, \\, and Unicode sequences such as \u00E9 to represent other
special characters in string literals.

2.2.5 Variables in Programs


A variable can be used in a program only if it has first been declared . A variable declaration
statement is used to declare one or more variables and to give them names. When the computer
executes a variable declaration, it sets aside memory for the variable and associates the variable’s
name with that memory. A simple variable declaration takes the form:
htype-name i hvariable-name-or-names i;
The hvariable-name-or-namesi can be a single variable name or a list of variable names sepa-
rated by commas. (We’ll see later that variable declaration statements can actually be some-
what more complicated than this.) Good programming style is to declare only one variable in
a declaration statement, unless the variables are closely related in some way. For example:
int numberOfStudents;
String name;
double x, y;
boolean isFinished;
char firstInitial, middleInitial, lastInitial;
It is also good style to include a comment with each variable declaration to explain its
purpose in the program, or to give other information that might be useful to a human reader.
For example:
double principal; // Amount of money invested.
double interestRate; // Rate as a decimal, not percentage.
In this chapter, we will only use variables declared inside the main() subroutine of a pro-
gram. Variables declared inside a subroutine are called local variables for that subroutine.
They exist only inside the subroutine, while it is running, and are completely inaccessible from
outside. Variable declarations can occur anywhere inside the subroutine, as long as each vari-
able is declared before it is used in any way. Some people like to declare all the variables at
the beginning of the subroutine. Others like to wait to declare a variable until it is needed. My
preference: Declare important variables at the beginning of the subroutine, and use a comment
to explain the purpose of each variable. Declare “utility variables” which are not important to
the overall logic of the subroutine at the point in the subroutine where they are first used. Here
is a simple program using some variables and assignment statements:
/**
* This class implements a simple program that
* will compute the amount of interest that is
* earned on $17,000 invested at an interest
* rate of 0.027 for one year. The interest and
* the value of the investment after one year are
* printed to standard output.
*/
public class Interest {
public static void main(String[] args) {
CHAPTER 2. NAMES AND THINGS 29

/* Declare the variables. */


double principal; // The value of the investment.
double rate; // The annual interest rate.
double interest; // Interest earned in one year.
/* Do the computations. */
principal = 17000;
rate = 0.027;
interest = principal * rate; // Compute the interest.
principal = principal + interest;
// Compute value of investment after one year, with interest.
// (Note: The new value replaces the old value of principal.)
/* Output the results. */
System.out.print("The interest earned is $");
System.out.println(interest);
System.out.print("The value of the investment after one year is $");
System.out.println(principal);
} // end of main()
} // end of class Interest
This program uses several subroutine call statements to display information to the user of the
program. Two different subroutines are used: System.out.print and System.out.println.
The difference between these is that System.out.println adds a linefeed after the end of the
information that it displays, while System.out.print does not. Thus, the value of interest,
which is displayed by the subroutine call “System.out.println(interest);”, follows on the
same line as the string displayed by the previous System.out.print statement. Note that the
value to be displayed by System.out.print or System.out.println is provided in parentheses
after the subroutine name. This value is called a parameter to the subroutine. A parameter
provides a subroutine with information it needs to perform its task. In a subroutine call state-
ment, any parameters are listed in parentheses after the subroutine name. Not all subroutines
have parameters. If there are no parameters in a subroutine call statement, the subroutine
name must be followed by an empty pair of parentheses.
All the sample programs for this textbook are available in separate source code files in the
on-line version of this text at http://math.hws.edu/javanotes/source. They are also included
in the downloadable archives of the web site, in a folder named source. The source code for
the Interest program, for example, can be found in the file Interest.java in subfolder named
chapter2 inside the source folder.

2.3 Strings, Classes, Objects, and Subroutines


The previous section introduced the eight primitive data types and the type String. There
is a fundamental difference between the primitive types and String : Values of type String
are objects. While we will not study objects in detail until Chapter 5, it will be useful for
you to know a little about them and about a closely related topic: classes. This is not just
because strings are useful but because objects and classes are essential to understanding another
important programming concept, subroutines.
CHAPTER 2. NAMES AND THINGS 30

2.3.1 Built-in Subroutines and Functions


Recall that a subroutine is a set of program instructions that have been chunked together and
given a name. A subroutine is designed to perform some task. To get that task performed
in a program, you can “call” the subroutine using a subroutine call statement. In Chapter 4,
you’ll learn how to write your own subroutines, but you can get a lot done in a program just
by calling subroutines that have already been written for you. In Java, every subroutine is
contained either in a class or in an object. Some classes that are standard parts of the Java
language contain predefined subroutines that you can use. A value of type String, which is an
object, contains subroutines that can be used to manipulate that string. These subroutines
are “built into” the Java language. You can call all these subroutines without understanding
how they were written or how they work. Indeed, that’s the whole point of subroutines: A
subroutine is a “black box” which can be used without knowing what goes on inside.
Let’s first consider subroutines that are part of a class. One of the purposes of a class is
to group together some variables and subroutines, which are contained in that class. These
variables and subroutines are called static members of the class. You’ve seen one example:
In a class that defines a program, the main() routine is a static member of the class. The parts
of a class definition that define static members are marked with the reserved word “static”,
such as the word “static” in public static void main...
When a class contains a static variable or subroutine, the name of the class is part of the full
name of the variable or subroutine. For example, the standard class named System contains
a subroutine named exit. To use that subroutine in your program, you must refer to it as
System.exit. This full name consists of the name of the class that contains the subroutine,
followed by a period, followed by the name of the subroutine. This subroutine requires an
integer as its parameter, so you would actually use it with a subroutine call statement such as
System.exit(0);
Calling System.exit will terminate the program and shut down the Java Virtual Machine. You
could use it if you had some reason to terminate the program before the end of the main routine.
(The parameter tells the computer why the program was terminated. A parameter value of 0
indicates that the program ended normally. Any other value indicates that the program was
terminated because an error was detected, so you could call System.exit(1) to indicate that
the program is ending because of an error. The parameter is sent back to the operating system;
in practice, the value is usually ignored by the operating system.)
System is just one of many standard classes that come with Java. Another useful class
is called Math. This class gives us an example of a class that contains static variables: It
includes the variables Math.PI and Math.E whose values are the mathematical constants π
and e. Math also contains a large number of mathematical “functions.” Every subroutine
performs some specific task. For some subroutines, that task is to compute or retrieve some
data value. Subroutines of this type are called functions. We say that a function returns a
value. Generally, the returned value is meant to be used somehow in the program that calls
the function.
You are familiar with the mathematical function that computes the square root of a number.
The corresponding function in Java is called Math.sqrt. This function is a static member
subroutine of the class named Math. If x is any numerical value, then Math.sqrt(x) computes
and returns the square root of that value. Since Math.sqrt(x) represents a value, it doesn’t
make sense to put it on a line by itself in a subroutine call statement such as
Math.sqrt(x); // This doesn’t make sense!
CHAPTER 2. NAMES AND THINGS 31

What, after all, would the computer do with the value computed by the function in this case?
You have to tell the computer to do something with the value. You might tell the computer to
display it:
System.out.print( Math.sqrt(x) ); // Display the square root of x.
or you might use an assignment statement to tell the computer to store that value in a variable:
lengthOfSide = Math.sqrt(x);
The function call Math.sqrt(x) represents a value of type double, and it can be used anyplace
where a numeric literal of type double could be used. The x in this formula represents the
parameter to the subroutine; it could be a variable named “x”, or it could be replaced by any
expression that represents a numerical value. For example, Math.sqrt(2) computes the square
root of 2, and Math.sqrt(a*a+b*b) would be legal as long as a and b are numeric variables.
The Math class contains many static member functions. Here is a list of some of the more
important of them:
• Math.abs(x), which computes the absolute value of x.
• The usual trigonometric functions, Math.sin(x), Math.cos(x), and Math.tan(x). (For
all the trigonometric functions, angles are measured in radians, not degrees.)
• The inverse trigonometric functions arcsin, arccos, and arctan, which are written as:
Math.asin(x), Math.acos(x), and Math.atan(x). The return value is expressed in radi-
ans, not degrees.
• The exponential function Math.exp(x) for computing the number e raised to the power
x, and the natural logarithm function Math.log(x) for computing the logarithm of x in
the base e.
• Math.pow(x,y) for computing x raised to the power y.
• Math.floor(x), which rounds x down to the nearest integer value that is less than or
equal to x. Even though the return value is mathematically an integer, it is returned
as a value of type double, rather than of type int as you might expect. For example,
Math.floor(3.76) is 3.0, and Math.floor(-4.2) is -5. The function Math.round(x)
returns the integer that is closest to x, and Math.ceil(x) rounds x up to an integer.
(“Ceil” is short for “ceiling”, the opposite of “floor.”)
• Math.random(), which returns a randomly chosen double in the range 0.0 <=
Math.random() < 1.0. (The computer actually calculates so-called “pseudorandom”
numbers, which are not truly random but are effectively random enough for most pur-
poses.) We will find a lot of uses for Math.random in future examples.
For these functions, the type of the parameter—the x or y inside the parentheses—can be
any value of any numeric type. For most of the functions, the value returned by the function
is of type double no matter what the type of the parameter. However, for Math.abs(x), the
value returned will be the same type as x; if x is of type int, then so is Math.abs(x). So, for
example, while Math.sqrt(9) is the double value 3.0, Math.abs(9) is the int value 9.
Note that Math.random() does not have any parameter. You still need the parentheses,
even though there’s nothing between them. The parentheses let the computer know that this is
a subroutine rather than a variable. Another example of a subroutine that has no parameters
is the function System.currentTimeMillis(), from the System class. When this function is
executed, it retrieves the current time, expressed as the number of milliseconds that have passed
CHAPTER 2. NAMES AND THINGS 32

since a standardized base time (the start of the year 1970, if you care). One millisecond is one-
thousandth of a second. The return value of System.currentTimeMillis() is of type long (a
64-bit integer). This function can be used to measure the time that it takes the computer to
perform a task. Just record the time at which the task is begun and the time at which it is
finished and take the difference. For more accurate timing, you can use System.nanoTime()
instead. System.nanoTime() returns the number of nanoseconds since some arbitrary starting
time, where one nanosecond is one-billionth of a second. However, you should not expect the
time to be truly accurate to the nanosecond.
Here is a sample program that performs a few mathematical tasks and reports the time that
it takes for the program to run.
/**
* This program performs some mathematical computations and displays the
* results. It also displays the value of the constant Math.PI. It then
* reports the number of seconds that the computer spent on this task.
*/
public class TimedComputation {
public static void main(String[] args) {
long startTime; // Starting time of program, in nanoseconds.
long endTime; // Time when computations are done, in nanoseconds.
long compTime; // Run time in nanoseconds.
double seconds; // Time difference, in seconds.
startTime = System.nanoTime();
double width, height, hypotenuse; // sides of a triangle
width = 42.0;
height = 17.0;
hypotenuse = Math.sqrt( width*width + height*height );
System.out.print("A triangle with sides 42 and 17 has hypotenuse ");
System.out.println(hypotenuse);
System.out.println("\nMathematically, sin(x)*sin(x) + "
+ "cos(x)*cos(x) - 1 should be 0.");
System.out.println("Let’s check this for x = 100:");
System.out.print(" sin(100)*sin(100) + cos(100)*cos(100) - 1 is: ");
System.out.println( Math.sin(100)*Math.sin(100)
+ Math.cos(100)*Math.cos(100) - 1 );
System.out.println("(There can be round-off errors when"
+ " computing with real numbers!)");
System.out.print("\nHere is a random number: ");
System.out.println( Math.random() );
System.out.print("\nThe value of Math.PI is ");
System.out.println( Math.PI );
endTime = System.nanoTime();
compTime = endTime - startTime;
seconds = compTime / 1000000000.0;
System.out.print("\nRun time in nanoseconds was: ");
System.out.println(compTime);
System.out.println("(This is probably not perfectly accurate!");
System.out.print("\nRun time in seconds was: ");
CHAPTER 2. NAMES AND THINGS 33

System.out.println(seconds);
} // end main()
} // end class TimedComputation

2.3.2 Classes and Objects


Classes can be containers for static variables and subroutines. However classes also have another
purpose. They are used to describe objects. In this role, the class is a type, in the same way
that int and double are types. That is, the class name can be used to declare variables. Such
variables can only hold one type of value. The values in this case are objects. An object is
a collection of variables and subroutines. Every object has an associated class that tells what
“type” of object it is. The class of an object specifies what subroutines and variables that
object contains. All objects defined by the same class are similar in that they contain similar
collections of variables and subroutines. For example, an object might represent a point in the
plane, and it might contain variables named x and y to represent the coordinates of that point.
Every point object would have an x and a y, but different points would have different values for
these variables. A class, named Point for example, could exist to define the common structure
of all point objects, and all such objects would then be values of type Point.
As another example, let’s look again at System.out.println. System is a class, and out
is a static variable within that class. However, the value of System.out is an object, and
System.out.println is actually the full name of a subroutine that is contained in the object
System.out. You don’t need to understand it at this point, but the object referred to by
System.out is an object of the class PrintStream. PrintStream is another class that is a standard
part of Java. Any object of type PrintStream is a destination to which information can be
printed; any object of type PrintStream has a println subroutine that can be used to send
information to that destination. The object System.out is just one possible destination, and
System.out.println is a subroutine that sends information to that particular destination.
Other objects of type PrintStream might send information to other destinations such as files
or across a network to other computers. This is object-oriented programming: Many different
things which have something in common—they can all be used as destinations for output—can
all be used in the same way—through a println subroutine. The PrintStream class expresses
the commonalities among all these objects.
The dual role of classes can be confusing, and in practice most classes are designed to
perform primarily or exclusively in only one of the two possible roles. Fortunately, you will not
need to worry too much about it until we start working with objects in a more serious way, in
Chapter 5.
By the way, since class names and variable names are used in similar ways, it might be hard
to tell which is which. Remember that all the built-in, predefined names in Java follow the rule
that class names begin with an upper case letter while variable names begin with a lower case
letter. While this is not a formal syntax rule, I strongly recommend that you follow it in your
own programming. Subroutine names should also begin with lower case letters. There is no
possibility of confusing a variable with a subroutine, since a subroutine name in a program is
always followed by a left parenthesis.
As one final general note, you should be aware that subroutines in Java are often referred
to as methods. Generally, the term “method” means a subroutine that is contained in a class
or in an object. Since this is true of every subroutine in Java, every subroutine in Java is a
method. The same is not true for other programming languages, and for the time being, I will
CHAPTER 2. NAMES AND THINGS 34

prefer to use the more general term, “subroutine.” However, I should note that some people
prefer to use the term “method” from the beginning.

2.3.3 Operations on Strings


String is a class, and a value of type String is an object. That object contains data, namely
the sequence of characters that make up the string. It also contains subroutines. All of these
subroutines are in fact functions. For example, every string object contains a function named
length that computes the number of characters in that string. Suppose that advice is a
variable that refers to a String. For example, advice might have been declared and assigned a
value as follows:
String advice;
advice = "Seize the day!";
Then advice.length() is a function call that returns the number of characters in the string
“Seize the day!”. In this case, the return value would be 14. In general, for any variable str
of type String, the value of str.length() is an int equal to the number of characters in the
string. Note that this function has no parameter; the particular string whose length is being
computed is the value of str. The length subroutine is defined by the class String, and it
can be used with any value of type String. It can even be used with String literals, which are,
after all, just constant values of type String. For example, you could have a program count the
characters in “Hello World” for you by saying
System.out.print("The number of characters in ");
System.out.print("the string \"Hello World\" is ");
System.out.println( "Hello World".length() );
The String class defines a lot of functions. Here are some that you might find useful. Assume
that s1 and s2 are variables of type String :
• s1.equals(s2) is a function that returns a boolean value. It returns true if s1 consists
of exactly the same sequence of characters as s2, and returns false otherwise.
• s1.equalsIgnoreCase(s2) is another boolean-valued function that checks whether s1
is the same string as s2, but this function considers upper and lower case letters
to be equivalent. Thus, if s1 is “cat”, then s1.equals("Cat") is false, while
s1.equalsIgnoreCase("Cat") is true.
• s1.length(), as mentioned above, is an integer-valued function that gives the number of
characters in s1.
• s1.charAt(N), where N is an integer, returns a value of type char. It returns the Nth
character in the string. Positions are numbered starting with 0, so s1.charAt(0) is
actually the first character, s1.charAt(1) is the second, and so on. The final position is
s1.length() - 1. For example, the value of "cat".charAt(1) is ’a’. An error occurs if
the value of the parameter is less than zero or is greater than or equal to s1.length().
• s1.substring(N,M), where N and M are integers, returns a value of type String. The
returned value consists of the characters of s1 in positions N, N+1,. . . , M-1. Note that the
character in position M is not included. The returned value is called a substring of s1. The
subroutine s1.substring(N) returns the substring of s1 consisting of characters starting
at position N up until the end of the string.
• s1.indexOf(s2) returns an integer. If s2 occurs as a substring of s1, then the returned
value is the starting position of that substring. Otherwise, the returned value is -1. You
CHAPTER 2. NAMES AND THINGS 35

can also use s1.indexOf(ch) to search for a char, ch, in s1. To find the first occurrence
of x at or after position N, you can use s1.indexOf(x,N). To find the last occurrence of
x in s1, use s1.lastIndexOf(x).
• s1.compareTo(s2) is an integer-valued function that compares the two strings. If the
strings are equal, the value returned is zero. If s1 is less than s2, the value returned is
a number less than zero, and if s1 is greater than s2, the value returned is some number
greater than zero. There is also a function s1.compareToIgnoreCase(s2). (If both of
the strings consist entirely of lower case letters, or if they consist entirely of upper case
letters, then “less than” and “greater than” refer to alphabetical order. Otherwise, the
ordering is more complicated.)
• s1.toUpperCase() is a String -valued function that returns a new string that is equal to s1,
except that any lower case letters in s1 have been converted to upper case. For example,
"Cat".toUpperCase() is the string "CAT". There is also a function s1.toLowerCase().
• s1.trim() is a String -valued function that returns a new string that is equal to s1 except
that any non-printing characters such as spaces and tabs have been trimmed from the
beginning and from the end of the string. Thus, if s1 has the value "fred ", then
s1.trim() is the string "fred", with the spaces at the end removed.
For the functions s1.toUpperCase(), s1.toLowerCase(), and s1.trim(), note that the
value of s1 is not changed. Instead a new string is created and returned as the value of
the function. The returned value could be used, for example, in an assignment statement
such as “smallLetters = s1.toLowerCase();”. To change the value of s1, you could use an
assignment “s1 = s1.toLowerCase();”.
∗ ∗ ∗
Here is another extremely useful fact about strings: You can use the plus operator, +, to
concatenate two strings. The concatenation of two strings is a new string consisting of all the
characters of the first string followed by all the characters of the second string. For example,
"Hello" + "World" evaluates to "HelloWorld". (Gotta watch those spaces, of course—if you
want a space in the concatenated string, it has to be somewhere in the input data, as in
"Hello " + "World".)
Let’s suppose that name is a variable of type String and that it already refers to the name
of the person using the program. Then, the program could greet the user by executing the
statement:
System.out.println("Hello, " + name + ". Pleased to meet you!");
Even more surprising is that you can actually concatenate values of any type onto a String
using the + operator. The value is converted to a string, just as it would be if you printed it to
the standard output, and then that string is concatenated with the other string. For example,
the expression "Number" + 42 evaluates to the string "Number42". And the statements
System.out.print("After ");
System.out.print(years);
System.out.print(" years, the value is ");
System.out.print(principal);
can be replaced by the single statement:
System.out.print("After " + years +
" years, the value is " + principal);
CHAPTER 2. NAMES AND THINGS 36

Obviously, this is very convenient. It would have shortened some of the examples presented
earlier in this chapter.

2.3.4 Introduction to Enums


Java comes with eight built-in primitive types and a huge collection of types that are defined
by classes, such as String. But even this large collection of types is not sufficient to cover all the
possible situations that a programmer might have to deal with. So, an essential part of Java,
just like almost any other programming language, is the ability to create new types. For the
most part, this is done by defining new classes; you will learn how to do that in Chapter 5. But
we will look here at one particular case: the ability to define enums (short for enumerated
types).
Technically, an enum is considered to be a special kind of class, but that is not important
for now. In this section, we will look at enums in a simplified form. In practice, most uses of
enums will only need the simplified form that is presented here.
An enum is a type that has a fixed list of possible values, which is specified when the enum
is created. In some ways, an enum is similar to the boolean data type, which has true and
false as its only possible values. However, boolean is a primitive type, while an enum is not.
The definition of an enum type has the (simplified) form:
enum henum-type-name i { hlist-of-enum-values i }
This definition cannot be inside a subroutine. You can place it outside the main() routine
of the program (or it can be in a separate file). The henum-type-namei can be any simple
identifier. This identifier becomes the name of the enum type, in the same way that “boolean”
is the name of the boolean type and “String” is the name of the String type. Each value in the
hlist-of-enum-valuesi must be a simple identifier, and the identifiers in the list are separated by
commas. For example, here is the definition of an enum type named Season whose values are
the names of the four seasons of the year:
enum Season { SPRING, SUMMER, FALL, WINTER }
By convention, enum values are given names that are made up of upper case letters, but that
is a style guideline and not a syntax rule. An enum value is a constant; that is, it represents
a fixed value that cannot be changed. The possible values of an enum type are usually referred
to as enum constants.
Note that the enum constants of type Season are considered to be “contained in” Season,
which means—following the convention that compound identifiers are used for things that are
contained in other things—the names that you actually use in your program to refer to them
are Season.SPRING, Season.SUMMER, Season.FALL, and Season.WINTER.
Once an enum type has been created, it can be used to declare variables in exactly the same
ways that other types are used. For example, you can declare a variable named vacation of
type Season with the statement:
Season vacation;
After declaring the variable, you can assign a value to it using an assignment statement. The
value on the right-hand side of the assignment can be one of the enum constants of type Season.
Remember to use the full name of the constant, including “Season”! For example:
vacation = Season.SUMMER;
CHAPTER 2. NAMES AND THINGS 37

You can print out an enum value with an output statement such as System.out.print(vacation).
The output value will be the name of the enum constant (without the “Season.”). In this case,
the output would be “SUMMER”.
Because an enum is technically a class, the enum values are technically objects. As ob-
jects, they can contain subroutines. One of the subroutines in every enum value is named
ordinal(). When used with an enum value, it returns the ordinal number of the value in
the list of values of the enum. The ordinal number simply tells the position of the value in
the list. That is, Season.SPRING.ordinal() is the int value 0, Season.SUMMER.ordinal() is
1, Season.FALL.ordinal() is 2, and Season.WINTER.ordinal() is 3. (You will see over and
over again that computer scientists like to start counting at zero!) You can, of course, use the
ordinal() method with a variable of type Season, such as vacation.ordinal().
Using enums can make a program more readable, since you can use meaningful names for
the values. And it can prevent certain types of errors, since a compiler can check that the values
assigned to an enum variable are in fact legal values for that variable. For now, you should just
appreciate them as the first example of an important concept: creating new types. Here is a
little example that shows enums being used in a complete program:
public class EnumDemo {
// Define two enum types -- remember that the definitions
// go OUTSIDE the main() routine!
enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }
public static void main(String[] args) {
Day tgif; // Declare a variable of type Day.
Month libra; // Declare a variable of type Month.
tgif = Day.FRIDAY; // Assign a value of type Day to tgif.
libra = Month.OCT; // Assign a value of type Month to libra.
System.out.print("My sign is libra, since I was born in ");
System.out.println(libra); // Output value will be: OCT
System.out.print("That’s the ");
System.out.print( libra.ordinal() );
System.out.println("-th month of the year.");
System.out.println(" (Counting from 0, of course!)");
System.out.print("Isn’t it nice to get to ");
System.out.println(tgif); // Output value will be: FRIDAY
System.out.println( tgif + " is the " + tgif.ordinal()
+ "-th day of the week.");
}
}
(As I mentioned, an enum can actually be defined in a separate file. The sample program
SeparateEnumDemo.java is identical to EnumDemo.java, except that the enum types that it
uses are defined in files named Month.java and Day.java.)
CHAPTER 2. NAMES AND THINGS 38

2.4 Text Input and Output


We have seen that it is very easy to display text to the user with the functions
System.out.print and System.out.println. But there is more to say on the topic of out-
putting text. Furthermore, most programs use data that is input to the program at run time
rather than built into the program. So you need to know how to do input as well as output.
This section explains how to get data from the user, and it covers output in more detail than
we have seen so far. It also has a section on using files for input and output.

2.4.1 Basic Output and Formatted Output


The most basic output function is System.out.print(x), where x can be a value or expression
of any type. If the parameter, x, is not already a string, it is converted to a value of type String,
and the string is then output to the destination called standard output. (Generally, this
means that the string is displayed to the user; however, in GUI programs, it outputs to a place
where a typical user is unlikely to see it. Furthermore, standard output can be “redirected”
to write to a different output destination. Nevertheless, for the type of program that we are
working with now, the purpose of System.out is to display text to the user.)
System.out.println(x) outputs the same text as System.out.print, but it follows that
text by a line feed, which means that any subsequent output will be on the next line. It is
possible to use this function with no parameter, System.out.println(), which outputs nothing
but a line feed. Note that System.out.println(x) is equivalent to
System.out.print(x);
System.out.println();
You might have noticed that System.out.print outputs real numbers with as many digits
after the decimal point as necessary, so that for example π is output as 3.141592653589793, and
numbers that are supposed to represent money might be output as 1050.0 or 43.575. You might
prefer to have these numbers output as, for example, 3.14159, 1050.00, and 43.58. Java has a
“formatted output” capability that makes it easy to control how real numbers and other values
are printed. A lot of formatting options are available. I will cover just a few of the simplest
and most commonly used possibilities here.
The function System.out.printf can be used to produce formatted output. (The name
“printf,” which stands for “print formatted,” is copied from the C and C++ programming
languages, where this type of output originated.) System.out.printf takes one or more pa-
rameters. The first parameter is a String that specifies the format of the output. This parameter
is called the format string . The remaining parameters specify the values that are to be out-
put. Here is a statement that will print a number in the proper format for a dollar amount,
where amount is a variable of type double:
System.out.printf( "%1.2f", amount );
The output format for a value is give by a format specifier in the format string. In this
example, the format specifier is %1.2f. The format string (in the simple cases that I cover
here) contains one format specifier for each of the values that is to be output. Some typical
format specifiers are %d, %12d, %10s, %1.2f, %15.8e and %1.8g. Every format specifier begins
with a percent sign (%) and ends with a letter, possibly with some extra formatting information
in between. The letter specifies the type of output that is to be produced. For example, in
%d and %12d, the “d” specifies that an integer is to be written. The “12” in %12d specifies the
CHAPTER 2. NAMES AND THINGS 39

minimum number of spaces that should be used for the output. If the integer that is being
output takes up fewer than 12 spaces, extra blank spaces are added in front of the integer to
bring the total up to 12. We say that the output is “right-justified in a field of length 12.” A
very large value is not forced into 12 spaces; if the value has more than 12 digits, all the digits
will be printed, with no extra spaces. The specifier %d means the same as %1d—that is, an
integer will be printed using just as many spaces as necessary. (The “d,” by the way, stands
for “decimal”—that is, base-10—numbers. You can replace the “d” with an “x” to output an
integer value in hexadecimal form.)
The letter “s” at the end of a format specifier can be used with any type of value. It
means that the value should be output in its default format, just as it would be in unformatted
output. A number, such as the “20” in %20s, can be added to specify the (minimum) number
of characters. The “s” stands for “string,” and it can be used for values of type String. It can
also be used for values of other types; in that case the value is converted into a String value in
the usual way.
The format specifiers for values of type double are more complicated. An “f”, as in %1.2f,
is used to output a number in “floating-point” form, that is with digits after a decimal point. In
%1.2f, the “2” specifies the number of digits to use after the decimal point. The “1” specifies
the (minimum) number of characters to output; a “1” in this position effectively means that
just as many characters as are necessary should be used. Similarly, %12.3f would specify a
floating-point format with 3 digits after the decimal point, right-justified in a field of length 12.
Very large and very small numbers should be written in exponential format, such as
6.00221415e23, representing “6.00221415 times 10 raised to the power 23.” A format speci-
fier such as %15.8e specifies an output in exponential form, with the “8” telling how many
digits to use after the decimal point. If you use “g” instead of “e”, the output will be in ex-
ponential form for very small values and very large values and in floating-point form for other
values. In %1.8g, the 8 gives the total number of digits in the answer, including both the digits
before the decimal point and the digits after the decimal point.
For numeric output, the format specifier can include a comma (“,”), which will cause the
digits of the number to be separated into groups, to make it easier to read big numbers. In
the United States, groups of three digits are separated by commas. For example, if x is one
billion, then System.out.printf("%,d",x) will output 1,000,000,000. In other countries, the
separator character and the number of digits per group might be different. The comma should
come at the beginning of the format specifier, before the field width; for example: %,12.3f.
If you want the output to be left-justified instead of right justified, add a minus sign to the
beginning of the format specifier: for example, %-20s.
In addition to format specifiers, the format string in a printf statement can include other
characters. These extra characters are just copied to the output. This can be a convenient way
to insert values into the middle of an output string. For example, if x and y are variables of
type int, you could say
System.out.printf("The product of %d and %d is %d", x, y, x*y);
When this statement is executed, the value of x is substituted for the first %d in the string, the
value of y for the second %d, and the value of the expression x*y for the third, so the output
would be something like “The product of 17 and 42 is 714” (quotation marks not included in
output!).
To output a percent sign, use the format specifier %% in the format string. You can use %n
to output a line feed. You can also use a backslash, \, as usual in strings to output special
characters such as tabs and double quote characters.
CHAPTER 2. NAMES AND THINGS 40

2.4.2 A First Text Input Example


For some unfathomable reason, Java has traditionally made it difficult to read data typed
in by the user of a program. You’ve already seen that output can be displayed to the user
using the subroutine System.out.print. This subroutine is part of a predefined object called
System.out. The purpose of this object is precisely to display output to the user. There is
a corresponding object called System.in that exists to read data input by the user, but it
provides only very primitive input facilities, and it requires some advanced Java programming
skills to use it effectively.
Java 5.0 finally made input a little easier with a new Scanner class. However, it requires
some knowledge of object-oriented programming to use this class, so it’s not ideal for use here
at the beginning of this course. Java 6 introduced the Console class for communicating with
the user, but Console has its own problems. (It is not always available, and it can only read
strings, not numbers.) Furthermore, in my opinion, Scanner and Console still don’t get things
quite right. Nevertheless, I will introduce Scanner briefly at the end of this section, in case you
want to start using it now. However, we start with my own version of text input.
Fortunately, it is possible to extend Java by creating new classes that provide subroutines
that are not available in the standard part of the language. As soon as a new class is available,
the subroutines that it contains can be used in exactly the same way as built-in routines. Along
these lines, I’ve written a class named TextIO that defines subroutines for reading values typed
by the user. The subroutines in this class make it possible to get input from the standard input
object, System.in, without knowing about the advanced aspects of Java that are needed to
use Scanner or to use System.in directly.
TextIO is defined in a “package” named textio. This means that when you look for the
file TextIO.java, you will find it inside a folder named textio. Furthermore, it means that a
program that uses TextIO must “import” it from the textio package. This is done with the
import directive
import textio.TextIO;
This directive must come before the “public class” that begins your program. Most of Java’s
standard classes are defined in packages and are imported into programs in the same way.
To use the TextIO class, you must make sure that the class is available to your program.
What this means depends on the Java programming environment that you are using. In general,
you just have to add the folder textio to the same folder that contains your main program. This
folder contains the file TextIO.java. See Section 2.6 for information about how to use TextIO.
The input routines in the TextIO class are static member functions. (Static member func-
tions were introduced in the previous section.) Let’s suppose that you want your program
to read an integer typed in by the user. The TextIO class contains a static member function
named getlnInt that you can use for this purpose. Since this function is contained in the
TextIO class, you have to refer to it in your program as TextIO.getlnInt. The function has
no parameters, so a complete call to the function takes the form “TextIO.getlnInt()”. This
function call represents the int value typed by the user, and you have to do something with
the returned value, such as assign it to a variable. For example, if userInput is a variable
of type int (created with a declaration statement “int userInput;”), then you could use the
assignment statement
userInput = TextIO.getlnInt();
When the computer executes this statement, it will wait for the user to type in an integer value.
The user must type a number and press return before the program can continue. The value
CHAPTER 2. NAMES AND THINGS 41

that the user typed will then be returned by the function, and it will be stored in the variable,
userInput. Here is a complete program that uses TextIO.getlnInt to read a number typed
by the user and then prints out the square of that number. Note the import directive on the
first line:
import textio.TextIO;
/**
* A program that reads an integer that is typed in by the
* user and computes and prints the square of that integer.
*/
public class PrintSquare {
public static void main(String[] args) {
int userInput; // The number input by the user.
int square; // The userInput, multiplied by itself.
System.out.print("Please type a number: ");
userInput = TextIO.getlnInt();
square = userInput * userInput;
System.out.println();
System.out.println("The number that you entered was " + userInput);
System.out.println("The square of that number is " + square);
System.out.println();
} // end of main()
} //end of class PrintSquare
When you run this program, it will display the message “Please type a number:” and will pause
until you type a response, including a carriage return after the number. Note that it is good
style to output a question or some other prompt to the user before reading input. Otherwise,
the user will have no way of knowing exactly what the computer is waiting for, or even that it
is waiting for the user to do something.

2.4.3 Basic TextIO Input Functions


TextIO includes a variety of functions for inputting values of various types. Here are the
functions that you are most likely to use:
j = TextIO.getlnInt(); // Reads a value of type int.
y = TextIO.getlnDouble(); // Reads a value of type double.
a = TextIO.getlnBoolean(); // Reads a value of type boolean.
c = TextIO.getlnChar(); // Reads a value of type char.
w = TextIO.getlnWord(); // Reads one "word" as a value of type String.
s = TextIO.getln(); // Reads an entire input line as a String.
For these statements to be legal, the variables on the left side of each assignment statement
must already be declared and must be of the same type as that returned by the function on
the right side. Note carefully that these functions do not have parameters. The values that
they return come from outside the program, typed in by the user as the program is running.
To “capture” that data so that you can use it in your program, you have to assign the return
value of the function to a variable. You will then be able to refer to the user’s input value by
using the name of the variable.
CHAPTER 2. NAMES AND THINGS 42

When you call one of these functions, you are guaranteed that it will return a legal value of
the correct type. If the user types in an illegal value as input—for example, if you ask for an
int and the user types in a non-numeric character or a number that is outside the legal range
of values that can be stored in a variable of type int—then the computer will ask the user to
re-enter the value, and your program never sees the first, illegal value that the user entered. For
TextIO.getlnBoolean(), the user is allowed to type in any of the following: true, false, t, f, yes,
no, y, n, 1, or 0. Furthermore, they can use either upper or lower case letters. In any case, the
user’s input is interpreted as a true/false value. It’s convenient to use TextIO.getlnBoolean()
to read the user’s response to a Yes/No question.
You’ll notice that there are two input functions that return Strings. The first, getlnWord(),
returns a string consisting of non-blank characters only. When it is called, it skips over any
spaces and carriage returns typed in by the user. Then it reads non-blank characters until
it gets to the next space or carriage return. It returns a String consisting of all the non-
blank characters that it has read. The second input function, getln(), simply returns a string
consisting of all the characters typed in by the user, including spaces, up to the next carriage
return. It gets an entire line of input text. The carriage return itself is not returned as part of
the input string, but it is read and discarded by the computer. Note that the String returned
by TextIO.getln() might be the empty string , "", which contains no characters at all. You
will get this return value if the user simply presses return, without typing anything else first.
TextIO.getln() does not skip blanks or end-of-lines before reading a value. But the
input functions getlnInt(), getlnDouble(), getlnBoolean(), and getlnChar() behave like
getlnWord() in that they will skip past any blanks and carriage returns in the input before
reading a value. When one of these functions skips over an end-of-line, it outputs a ’ ?’ to let
the user know that more input is expected.
Furthermore, if the user types extra characters on the line after the input value, all the
extra characters will be discarded, along with the carriage return at the end of the
line. If the program executes another input function, the user will have to type in another line
of input, even if they had typed more than one value on the previous line. It might not sound
like a good idea to discard any of the user’s input, but it turns out to be the safest thing to do
in most programs.
∗ ∗ ∗
Using TextIO for input and output, we can now improve the program from Section 2.2 for
computing the value of an investment. We can have the user type in the initial value of the
investment and the interest rate. The result is a much more useful program—for one thing, it
makes sense to run it more than once! Note that this program uses formatted output to print
out monetary values in their correct format.
import textio.TextIO;
/**
* This class implements a simple program that will compute
* the amount of interest that is earned on an investment over
* a period of one year. The initial amount of the investment
* and the interest rate are input by the user. The value of
* the investment at the end of the year is output. The
* rate must be input as a decimal, not a percentage (for
* example, 0.05 rather than 5).
*/
public class Interest2 {
CHAPTER 2. NAMES AND THINGS 43

public static void main(String[] args) {


double principal; // The value of the investment.
double rate; // The annual interest rate.
double interest; // The interest earned during the year.
System.out.print("Enter the initial investment: ");
principal = TextIO.getlnDouble();
System.out.print("Enter the annual interest rate (as a decimal): ");
rate = TextIO.getlnDouble();
interest = principal * rate; // Compute this year’s interest.
principal = principal + interest; // Add it to principal.
System.out.printf("The amount of interest is $%1.2f%n", interest);
System.out.printf("The value after one year is $%1.2f%n", principal);
} // end of main()
} // end of class Interest2
(You might be wondering why there is only one output routine, System.out.println, which
can output data values of any type, while there is a separate input routine for each data type.
For the output function, the computer can tell what type of value is being output by looking
at the parameter. However, the input routines don’t have parameters, so the different input
routines can only be distinguished by having different names.)

2.4.4 Introduction to File I/O


System.out sends its output to the output destination known as “standard output.” But stan-
dard output is just one possible output destination. For example, data can be written to a file
that is stored on the user’s hard drive. The advantage to this, of course, is that the data is saved
in the file even after the program ends, and the user can print the file, email it to someone else,
edit it with another program, and so on. Similarly, System.in has only one possible source for
input data.
TextIO has the ability to write data to files and to read data from files. TextIO includes
output functions TextIO.put, TextIO.putln, and TextIO.putf. Ordinarily, these functions
work exactly like System.out.print, System.out.println, and System.out.printf and are
interchangeable with them. However, they can also be used to output text to files and to other
destinations.
When you write output using TextIO.put, TextIO.putln, or TextIO.putf, the output
is sent to the current output destination. By default, the current output destination is
standard output. However, TextIO has subroutines that can be used to change the current
output destination. To write to a file named “result.txt”, for example, you would use the
statement:
TextIO.writeFile("result.txt");
After this statement is executed, any output from TextIO output statements will be sent to the
file named “result.txt” instead of to standard output. The file will be created if it does not
already exist. Note that if a file with the same name already exists, its previous contents will
be erased without any warning!
CHAPTER 2. NAMES AND THINGS 44

When you call TextIO.writeFile, TextIO remembers the file and automatically sends any
output from TextIO.put or other output functions to that file. If you want to go back to
writing to standard output, you can call
TextIO.writeStandardOutput();
Here is a simple program that asks the user some questions and outputs the user’s responses to
a file named “profile.txt.” As an example, it uses TextIO for output to standard output as well
as to the file, but System.out could also have been used for the output to standard output.
import textio.TextIO;
public class CreateProfile {
public static void main(String[] args) {
String name; // The user’s name.
String email; // The user’s email address.
double salary; // the user’s yearly salary.
String favColor; // The user’s favorite color.
TextIO.putln("Good Afternoon! This program will create");
TextIO.putln("your profile file, if you will just answer");
TextIO.putln("a few simple questions.");
TextIO.putln();
/* Gather responses from the user. */
TextIO.put("What is your name? ");
name = TextIO.getln();
TextIO.put("What is your email address? ");
email = TextIO.getln();
TextIO.put("What is your yearly income? ");
salary = TextIO.getlnDouble();
TextIO.put("What is your favorite color? ");
favColor = TextIO.getln();
/* Write the user’s information to the file named profile.txt. */
TextIO.writeFile("profile.txt"); // subsequent output goes to file
TextIO.putln("Name: " + name);
TextIO.putln("Email: " + email);
TextIO.putln("Favorite Color: " + favColor);
TextIO.putf( "Yearly Income: %,1.2f%n", salary);
/* Print a final message to standard output. */
TextIO.writeStandardOutput();
TextIO.putln("Thank you. Your profile has been written to profile.txt.");
}
}
In many cases, you want to let the user select the file that will be used for output. You
could ask the user to type in the file name, but that is error-prone, and users are more familiar
with selecting a file from a file dialog box. The statement
TextIO.writeUserSelectedFile();
CHAPTER 2. NAMES AND THINGS 45

will open a typical graphical-user-interface file selection dialog where the user can specify the
output file. This also has the advantage of alerting the user if they are about to replace
an existing file. It is possible for the user to cancel the dialog box without selecting a file.
TextIO.writeUserSelectedFile is a function that returns a boolean value. The return value
is true if the user selected a file, and is false if the user canceled the dialog box. Your program
can check the return value if it needs to know whether it is actually going to write to a file or
not.
∗ ∗ ∗
TextIO can also read from files, as an alternative to reading from standard input. You can
specify an input source for TextIO’s various “get” functions. The default input source is standard
input. You can use the statement TextIO.readFile("data.txt") to read from a file named
“data.txt” instead, or you can let the user select the input file with a GUI-style dialog box by
saying TextIO.readUserSelectedFile(). After you have done this, any input will come from
the file instead of being typed by the user. You can go back to reading the user’s input with
TextIO.readStandardInput().
When your program is reading from standard input, the user gets a chance to correct any
errors in the input. This is not possible when the program is reading from a file. If illegal data
is found when a program tries to read from a file, an error occurs that will crash the program.
(Later, we will see that it is possible to “catch” such errors and recover from them.) Errors can
also occur, though more rarely, when writing to files.
A complete understanding of input/output in Java requires a knowledge of object oriented
programming. We will return to the topic later, in Chapter 11. The file I/O capabilities in
the TextIO class are rather primitive by comparison. Nevertheless, they are sufficient for many
applications, and they will allow you to get some experience with files sooner rather than later.

2.4.5 Other TextIO Features


The TextIO input functions that we have seen so far can only read one value from a line of
input. Sometimes, however, you do want to read more than one value from the same line of
input. For example, you might want the user to be able to type something like “42 17” to input
the two numbers 42 and 17 on the same line. TextIO provides the following alternative input
functions to allow you to do this:
j = TextIO.getInt(); // Reads a value of type int.
y = TextIO.getDouble(); // Reads a value of type double.
a = TextIO.getBoolean(); // Reads a value of type boolean.
c = TextIO.getChar(); // Reads a value of type char.
w = TextIO.getWord(); // Reads one "word" as a value of type String.
The names of these functions start with “get” instead of “getln”. “Getln” is short for “get line”
and should remind you that the functions whose names begin with “getln” will consume an
entire line of data. A function without the “ln” will read an input value in the same way, but
will then save the rest of the input line in a chunk of internal memory called the input buffer .
The next time the computer wants to read an input value, it will look in the input buffer before
prompting the user for input. This allows the computer to read several values from one line
of the user’s input. Strictly speaking, the computer actually reads only from the input buffer.
The first time the program tries to read input from the user, the computer will wait while the
user types in an entire line of input. TextIO stores that line in the input buffer until the data
CHAPTER 2. NAMES AND THINGS 46

on the line has been read or discarded (by one of the “getln” functions). The user only gets to
type when the buffer is empty.
Note, by the way, that although the TextIO input functions will skip past blank spaces and
carriage returns while looking for input, they will not skip past other characters. For example,
if you try to read two ints and the user types “42,17”, the computer will read the first number
correctly, but when it tries to read the second number, it will see the comma. It will regard this
as an error and will force the user to retype the number. If you want to input several numbers
from one line, you should make sure that the user knows to separate them with spaces, not
commas. Alternatively, if you want to require a comma between the numbers, use getChar()
to read the comma before reading the second number.
There is another character input function, TextIO.getAnyChar(), which does not skip past
blanks or carriage returns. It simply reads and returns the next character typed by the user,
even if it’s a blank or carriage return. If the user typed a carriage return, then the char returned
by getAnyChar() is the special linefeed character ’\n’. There is also a function, TextIO.peek(),
that lets you look ahead at the next character in the input without actually reading it. After
you “peek” at the next character, it will still be there when you read the next item from input.
This allows you to look ahead and see what’s coming up in the input, so that you can take
different actions depending on what’s there.
The TextIO class provides a number of other functions. To learn more about them, you can
look at the comments in the source code file, TextIO.java.
Clearly, the semantics of input is much more complicated than the semantics of output!
Fortunately, for the majority of applications, it’s pretty straightforward in practice. You only
need to follow the details if you want to do something fancy. In particular, I strongly advise
you to use the “getln” versions of the input routines, rather than the “get” versions, unless you
really want to read several items from the same line of input, precisely because the semantics
of the “getln” versions is much simpler.

2.4.6 Using Scanner for Input


TextIO makes it easy to get input from the user. However, since it is not a standard class, you
have to remember to make TextIO.java available to any program that uses it. Another option
for input is the Scanner class. One advantage of using Scanner is that it’s a standard part of
Java and so is always there when you want it.
It’s not that hard to use a Scanner for user input, and it has some nice features, but using
it requires some syntax that will not be introduced until Chapter 4 and Chapter 5. I’ll tell you
how to do it here, without explaining why it works. You won’t understand all the syntax at
this point. (Scanners will be covered in more detail in Subsection 11.1.5.)
First, since Scanner is defined in the package java.util, you should add the following import
directive to your program at the beginning of the source code file, before the “public class. . . ”:
import java.util.Scanner;
Then include the following statement at the beginning of your main() routine:
Scanner stdin = new Scanner( System.in );
This creates a variable named stdin of type Scanner. (You can use a different name for the
variable if you want; “stdin” stands for “standard input.”) You can then use stdin in your
program to access a variety of subroutines for reading user input. For example, the function
stdin.nextInt() reads one value of type int from the user and returns it. It is almost the
CHAPTER 2. NAMES AND THINGS 47

same as TextIO.getInt() except for two things: If the value entered by the user is not a legal
int, then stdin.nextInt() will crash rather than prompt the user to re-enter the value. And
the integer entered by the user must be followed by a blank space or by an end-of-line, whereas
TextIO.getInt() will stop reading at any character that is not a digit.
There are corresponding methods for reading other types of data, including
stdin.nextDouble(), stdin.nextLong(), and stdin.nextBoolean(). (stdin.nextBoolean()
will only accept “true” or “false” as input.) These subroutines can read more than one value
from a line, so they are more similar to the “get” versions of TextIO subroutines rather than
the “getln” versions. The method stdin.nextLine() is equivalent to TextIO.getln(), and
stdin.next(), like TextIO.getWord(), returns a string of non-blank characters.
As a simple example, here is a version of the sample program Interest2.java that uses Scanner
instead of TextIO for user input:
import java.util.Scanner;
public class Interest2WithScanner {
public static void main(String[] args) {
Scanner stdin = new Scanner( System.in ); // Create the Scanner.
double principal; // The value of the investment.
double rate; // The annual interest rate.
double interest; // The interest earned during the year.
System.out.print("Enter the initial investment: ");
principal = stdin.nextDouble();
System.out.print("Enter the annual interest rate (as a decimal): ");
rate = stdin.nextDouble();
interest = principal * rate; // Compute this year’s interest.
principal = principal + interest; // Add it to principal.
System.out.printf("The amount of interest is $%1.2f%n", interest);
System.out.printf("The value after one year is $%1.2f%n", principal);
} // end of main()
} // end of class Interest2WithScanner
Note the inclusion of the two lines given above to import Scanner and create stdin.
Also note the substitution of stdin.nextDouble() for TextIO.getlnDouble(). (In fact,
stdin.nextDouble() is really equivalent to TextIO.getDouble() rather than to the “getln”
version, but this will not affect the behavior of the program as long as the user types just one
number on each line of input.)
I will continue to use TextIO for input for the time being, but I will give a few more examples
of using Scanner in the on-line solutions to the end-of-chapter exercises. There will be more
detailed coverage of Scanner later in the book.

2.5 Details of Expressions


This section takes a closer look at expressions. Recall that an expression is a piece of
program code that represents or computes a value. An expression can be a literal, a variable,
a function call, or several of these things combined with operators such as + and >. The value
CHAPTER 2. NAMES AND THINGS 48

of an expression can be assigned to a variable, used as a parameter in a subroutine call, or


combined with other values into a more complicated expression. (The value can even, in some
cases, be ignored, if that’s what you want to do; this is more common than you might think.)
Expressions are an essential part of programming. So far, this book has dealt only informally
with expressions. This section tells you the more-or-less complete story (leaving out some of
the less commonly used operators).
The basic building blocks of expressions are literals (such as 674, 3.14, true, and ’X’),
variables, and function calls. Recall that a function is a subroutine that returns a value. You’ve
already seen some examples of functions, such as the input routines from the TextIO class and
the mathematical functions from the Math class.
The Math class also contains a couple of mathematical constants that are useful in
mathematical expressions: Math.PI represents π (the ratio of the circumference of a cir-
cle to its diameter), and Math.E represents e (the base of the natural logarithms). These
“constants” are actually member variables in Math of type double. They are only ap-
proximations for the mathematical constants, which would require an infinite number of
digits to specify exactly. The standard class Integer contains a couple of constants re-
lated to the int data type: Integer.MAX VALUE is the largest possible int, 2147483647, and
Integer.MIN VALUE is the smallest int, -2147483648. Similarly, the class Double contains some
constants related to type double. Double.MAX VALUE is the largest value of type double, and
Double.MIN VALUE is the smallest positive value. It also has constants to represent infinite
values, Double.POSITIVE INFINITY and Double.NEGATIVE INFINITY, and the special value
Double.NaN to represent an undefined value. For example, the value of Math.sqrt(-1) is
Double.NaN.
Literals, variables, and function calls are simple expressions. More complex expressions
can be built up by using operators to combine simpler expressions. Operators include + for
adding two numbers, > for comparing two values, and so on. When several operators appear
in an expression, there is a question of precedence, which determines how the operators are
grouped for evaluation. For example, in the expression “A + B * C”, B*C is computed first
and then the result is added to A. We say that multiplication (*) has higher precedence
than addition (+). If the default precedence is not what you want, you can use parentheses to
explicitly specify the grouping you want. For example, you could use “(A + B) * C” if you
want to add A to B first and then multiply the result by C.
The rest of this section gives details of operators in Java. The number of operators in Java
is quite large. I will not cover them all here, but most of the important ones are here.

2.5.1 Arithmetic Operators


Arithmetic operators include addition, subtraction, multiplication, and division. They are
indicated by +, -, *, and /. These operations can be used on values of any numeric type: byte,
short, int, long, float, or double. (They can also be used with values of type char, which
are treated as integers in this context; a char is converted into its Unicode code number when
it is used with an arithmetic operator.) When the computer actually calculates one of these
operations, the two values that it combines must be of the same type. If your program tells
the computer to combine two values of different types, the computer will convert one of the
values from one type to another. For example, to compute 37.4 + 10, the computer will convert
the integer 10 to a real number 10.0 and will then compute 37.4 + 10.0. This is called a type
conversion. Ordinarily, you don’t have to worry about type conversion in expressions, because
the computer does it automatically.
CHAPTER 2. NAMES AND THINGS 49

When two numerical values are combined (after doing type conversion on one of them, if
necessary), the answer will be of the same type. If you multiply two ints, you get an int; if you
multiply two doubles, you get a double. This is what you would expect, but you have to be
very careful when you use the division operator /. When you divide two integers, the answer
will always be an integer; if the quotient has a fractional part, it is discarded. For example, the
value of 7/2 is 3, not 3.5. If N is an integer variable, then N/100 is an integer, and 1/N is equal
to zero for any N greater than one! This fact is a common source of programming errors. You
can force the computer to compute a real number as the answer by making one of the operands
real: For example, when the computer evaluates 1.0/N, it first converts N to a real number in
order to match the type of 1.0, so you get a real number as the answer.
Java also has an operator for computing the remainder when one number is divided by
another. This operator is indicated by %. If A and B are integers, then A % B represents the
remainder when A is divided by B. (However, for negative operands, % is not quite the same as
the usual mathematical “modulus” operator, since if one of A or B is negative, then the value
of A % B will be negative.) For example, 7 % 2 is 1, while 34577 % 100 is 77, and 50 % 8 is
2. A common use of % is to test whether a given integer is even or odd: N is even if N % 2 is
zero, and it is odd if N % 2 is 1. More generally, you can check whether an integer N is evenly
divisible by an integer M by checking whether N % M is zero.
The % operator also works with real numbers. In general, A % B is what is left over after
you remove as many copies of B as possible from A. For example, 7.52 % 0.5 is 0.02.
Finally, you might need the unary minus operator, which takes the negative of a number.
For example, -X has the same value as (-1)*X. For completeness, Java also has a unary plus
operator, as in +X, even though it doesn’t really do anything.
By the way, recall that the + operator can also be used to concatenate a value of any type
onto a String. When you use + to combine a string with a value of some other type, it is another
example of type conversion, since any type can be automatically converted into type String.

2.5.2 Increment and Decrement


You’ll find that adding 1 to a variable is an extremely common operation in programming.
Subtracting 1 from a variable is also pretty common. You might perform the operation of
adding 1 to a variable with assignment statements such as:
counter = counter + 1;
goalsScored = goalsScored + 1;
The effect of the assignment statement x = x + 1 is to take the old value of the variable
x, compute the result of adding 1 to that value, and store the answer as the new value of
x. The same operation can be accomplished by writing x++ (or, if you prefer, ++x). This
actually changes the value of x, so that it has the same effect as writing “x = x + 1”. The two
statements above could be written
counter++;
goalsScored++;
Similarly, you could write x-- (or --x) to subtract 1 from x. That is, x-- performs the same
computation as x = x - 1. Adding 1 to a variable is called incrementing that variable,
and subtracting 1 is called decrementing . The operators ++ and -- are called the increment
operator and the decrement operator, respectively. These operators can be used on variables
belonging to any of the numerical types and also on variables of type char. (If ch is ’A’ then
ch++ changes the value of ch to ’B’.)
CHAPTER 2. NAMES AND THINGS 50

Usually, the operators ++ or -- are used in statements like “x++;” or “x--;”. These state-
ments are commands to change the value of x. However, it is also legal to use x++, ++x, x--,
or --x as expressions, or as parts of larger expressions. That is, you can write things like:
y = x++;
y = ++x;
TextIO.putln(--x);
z = (++x) * (y--);
The statement “y = x++;” has the effects of adding 1 to the value of x and, in addition, assigning
some value to y. The value assigned to y is defined to be the old value of x, before the 1 is
added. Thus, if the value of x is 6, the statement “y = x++;” will change the value of x to 7,
but it will change the value of y to 6, because the value assigned to y is the old value of x. On
the other hand, the value of ++x is defined to be the new value of x, after the 1 is added. So if
x is 6, then the statement “y = ++x;” changes the values of both x and y to 7. The decrement
operator, --, works in a similar way.
Note in particular that the statement x = x++; does not change the value of x! This is
because the value that is being assigned to x is the old value of x, the one that it had before the
statement was executed. The net result is that x is incremented but then immediately changed
back to its previous value! You also need to remember that x++ is not the same as x + 1. The
expression x++ changes the value of x; the expression x + 1 does not.
This can be confusing, and I have seen many bugs in student programs resulting from the
confusion. My advice is: Don’t be confused. Use ++ and -- only as stand-alone statements,
not as expressions. I will follow this advice in almost all examples in these notes.

2.5.3 Relational Operators


Java has boolean variables and boolean-valued expressions that can be used to express con-
ditions that can be either true or false. One way to form a boolean-valued expression is
to compare two values using a relational operator . Relational operators are used to test
whether two values are equal, whether one value is greater than another, and so forth. The
relational operators in Java are: ==, !=, <, >, <=, and >=. The meanings of these operators are:
A == B Is A "equal to" B?
A != B Is A "not equal to" B?
A < B Is A "less than" B?
A > B Is A "greater than" B?
A <= B Is A "less than or equal to" B?
A >= B Is A "greater than or equal to" B?
These operators can be used to compare values of any of the numeric types. They can also be
used to compare values of type char. For characters, < and > are defined according the numeric
Unicode values of the characters. (This might not always be what you want. It is not the same
as alphabetical order because all the upper case letters come before all the lower case letters.)
When using boolean expressions, you should remember that as far as the computer is con-
cerned, there is nothing special about boolean values. In the next chapter, you will see how to
use them in loop and branch statements. But you can also assign boolean-valued expressions
to boolean variables, just as you can assign numeric values to numeric variables. And functions
can return boolean values.
By the way, the operators == and != can be used to compare boolean values too. This is
occasionally useful. For example, can you figure out what this does:
CHAPTER 2. NAMES AND THINGS 51

boolean sameSign;
sameSign = ((x > 0) == (y > 0));
One thing that you cannot do with the relational operators <, >, <=, and >= is to use them
to compare values of type String. You can legally use == and != to compare Strings, but
because of peculiarities in the way objects behave, they might not give the results you want.
(The == operator checks whether two objects are stored in the same memory location, rather
than whether they contain the same value. Occasionally, for some objects, you do want to
make such a check—but rarely for strings. I’ll get back to this in a later chapter.) Instead,
you should compare strings using subroutines such as equals() and compareTo(), which were
described in Subsection 2.3.3.
Another place where == and != don’t work as you would expect is with Double.NaN, the
constant that represents an undefined value of type double. The values of x == Double.NaN and
x != Double.NaN are both defined to be false in all cases, whether or not x is Double.NaN! To
test whether a real value x is the undefined value Double.NaN, use the boolean-valued function
Double.isNaN(x).

2.5.4 Boolean Operators


In English, complicated conditions can be formed using the words “and”, “or”, and “not.” For
example, “If there is a test and you did not study for it. . . ”. “And”, “or”, and “not” are
boolean operators, and they exist in Java as well as in English.
In Java, the boolean operator “and” is represented by &&. The && operator is used to
combine two boolean values. The result is also a boolean value. The result is true if both
of the combined values are true, and the result is false if either of the combined values is
false. For example, “(x == 0) && (y == 0)” is true if and only if both x is equal to 0 and
y is equal to 0.
The boolean operator “or” is represented by ||. (That’s supposed to be two of the vertical
line characters, |.) The expression “A || B” is true if either A is true or B is true, or if both
are true. “A || B” is false only if both A and B are false.
The operators && and || are said to be short-circuited versions of the boolean operators.
This means that the second operand of && or || is not necessarily evaluated. Consider the test
(x != 0) && (y/x > 1)
Suppose that the value of x is in fact zero. In that case, the division y/x is undefined math-
ematically. However, the computer will never perform the division, since when the computer
evaluates (x != 0), it finds that the result is false, and so it knows that ((x != 0) && any-
thing) has to be false. Therefore, it doesn’t bother to evaluate the second operand. The
evaluation has been short-circuited and the division by zero is avoided. (This may seem like a
technicality, and it is. But at times, it will make your programming life a little easier.)
The boolean operator “not” is a unary operator. In Java, it is indicated by ! and is written
in front of its single operand. For example, if test is a boolean variable, then
test = ! test;
will reverse the value of test, changing it from true to false, or from false to true.

2.5.5 Conditional Operator


Any good programming language has some nifty little features that aren’t really necessary but
that let you feel cool when you use them. Java has the conditional operator. It’s a ternary
CHAPTER 2. NAMES AND THINGS 52

operator—that is, it has three operands—and it comes in two pieces, ? and :, that have to be
used together. It takes the form
hboolean-expression i ? hexpression1 i : hexpression2 i
The computer tests the value of hboolean-expressioni. If the value is true, it evaluates
hexpression1 i; otherwise, it evaluates hexpression2 i. For example:
next = (N % 2 == 0) ? (N/2) : (3*N+1);
will assign the value N/2 to next if N is even (that is, if N % 2 == 0 is true), and it will assign
the value (3*N+1) to next if N is odd. (The parentheses in this example are not required, but
they do make the expression easier to read.)

2.5.6 Assignment Operators and Type Conversion


You are already familiar with the assignment statement, which uses the symbol “=” to assign
the value of an expression to a variable. In fact, = is really an operator in the sense that an
assignment can itself be used as an expression or as part of a more complex expression. The
value of an assignment such as A=B is the same as the value that is assigned to A. So, if you
want to assign the value of B to A and test at the same time whether that value is zero, you
could say:
if ( (A=B) == 0 )...
Usually, I would say, don’t do things like that!
In general, the type of the expression on the right-hand side of an assignment statement
must be the same as the type of the variable on the left-hand side. However, in some cases,
the computer will automatically convert the value computed by the expression to match the
type of the variable. Consider the list of numeric types: byte, short, int, long, float, double.
A value of a type that occurs earlier in this list can be converted automatically to a value that
occurs later. For example:
int A;
double X;
short B;
A = 17;
X = A; // OK; A is converted to a double
B = A; // illegal; no automatic conversion
// from int to short
The idea is that conversion should only be done automatically when it can be done without
changing the semantics of the value. Any int can be converted to a double with the same
numeric value. However, there are int values that lie outside the legal range of shorts. There
is simply no way to represent the int 100000 as a short, for example, since the largest value of
type short is 32767.
In some cases, you might want to force a conversion that wouldn’t be done automatically.
For this, you can use what is called a type cast. A type cast is indicated by putting a type
name, in parentheses, in front of the value you want to convert. For example,
int A;
short B;
A = 17;
B = (short)A; // OK; A is explicitly type cast
// to a value of type short
CHAPTER 2. NAMES AND THINGS 53

You can do type casts from any numeric type to any other numeric type. However, you should
note that you might change the numeric value of a number by type-casting it. For example,
(short)100000 is -31072. (The -31072 is obtained by taking the 4-byte int 100000 and throwing
away two of those bytes to obtain a short—you’ve lost the real information that was in those
two bytes.)
When you type-cast a real number to an integer, the fractional part is discarded. For
example, (int)7.9453 is 7. As another example of type casts, consider the problem of get-
ting a random integer between 1 and 6. The function Math.random() gives a real number
between 0.0 and 0.9999. . . , and so 6*Math.random() is between 0.0 and 5.999. . . . The type-
cast operator, (int), can be used to convert this to an integer: (int)(6*Math.random()).
Thus, (int)(6*Math.random()) is one of the integers 0, 1, 2, 3, 4, and 5. To get a number
between 1 and 6, we can add 1: “(int)(6*Math.random()) + 1”. (The parentheses around
6*Math.random() are necessary because of precedence rules; without the parentheses, the type
cast operator would apply only to the 6.)
The type char is almost an integer type. You can assign char values to int variables, and you
can assign integer constants in the range 0 to 65535 to char variables. You can also use explicit
type-casts between char and the numeric types. For example, (char)97 is ’a’, (int)’+’ is
43, and (char)(’A’ + 2) is ’C’.
∗ ∗ ∗
Type conversion between String and other types cannot be done with type-casts. One way to
convert a value of any type into a string is to concatenate it with an empty string. For example,
"" + 42 is the string "42". But a better way is to use the function String.valueOf(x), a static
member function in the String class. String.valueOf(x) returns the value of x, converted into
a string. For example, String.valueOf(42) is the string "42", and if ch is a char variable,
then String.valueOf(ch) is a string of length one containing the single character that is the
value of ch.
It is also possible to convert certain strings into values of other types. For example, the
string "10" should be convertible into the int value 10, and the string "17.42e-2" into the
double value 0.1742. In Java, these conversions are handled by built-in functions.
The standard class Integer contains a static member function for converting from String to
int. In particular, if str is any expression of type String, then Integer.parseInt(str) is a
function call that attempts to convert the value of str into a value of type int. For example, the
value of Integer.parseInt("10") is the int value 10. If the parameter to Integer.parseInt
does not represent a legal int value, then an error occurs.
Similarly, the standard class Double includes a function Double.parseDouble. If str is a
String, then the function call Double.parseDouble(str) tries to convert str into a value of
type double. An error occurs if str does not represent a legal double value.
∗ ∗ ∗
Getting back to assignment statements, Java has several variations on the assignment
operator, which exist to save typing. For example, “A += B” is defined to be the same as
“A = A + B”. Every operator in Java that applies to two operands, except for the relational
operators, gives rise to a similar assignment operator. For example:
x -= y; // same as: x = x - y;
x *= y; // same as: x = x * y;
x /= y; // same as: x = x / y;
x %= y; // same as: x = x % y;
q &&= p; // same as: q = q && p; (for booleans q and p)
CHAPTER 2. NAMES AND THINGS 54

The combined assignment operator += even works with strings. Recall that when the + operator
is used with a string as one of the operands, it represents concatenation. Since str += x is
equivalent to str = str + x, when += is used with a string on the left-hand side, it appends
the value on the right-hand side onto the string. For example, if str has the value “tire”, then
the statement str += ’d’; changes the value of str to “tired”.

2.5.7 Precedence Rules


If you use several operators in one expression, and if you don’t use parentheses to explicitly
indicate the order of evaluation, then you have to worry about the precedence rules that deter-
mine the order of evaluation. (Advice: don’t confuse yourself or the reader of your program;
use parentheses liberally.)
Here is a listing of the operators discussed in this section, listed in order from highest
precedence (evaluated first) to lowest precedence (evaluated last):
Unary operators: ++, --, !, unary -, unary +, type-cast
Multiplication and division: *, /, %
Addition and subtraction: +, -
Relational operators: <, >, <=, >=
Equality and inequality: ==, !=
Boolean and: &&
Boolean or: ||
Conditional operator: ?:
Assignment operators: =, +=, -=, *=, /=, %=
Operators on the same line have the same precedence. When operators of the same precedence
are strung together in the absence of parentheses, unary operators and assignment operators are
evaluated right-to-left, while the remaining operators are evaluated left-to-right. For example,
A*B/C means (A*B)/C, while A=B=C means A=(B=C). (Can you see how the expression A=B=C
might be useful, given that the value of B=C as an expression is the same as the value that is
assigned to B?)

2.6 Programming Environments


Although the Java language is highly standardized, the procedures for creating, compil-
ing, and editing Java programs vary widely from one programming environment to another.
There are two basic approaches: a command line environment, where the user types com-
mands and the computer responds, and an integrated development environment (IDE),
where the user uses the keyboard and mouse to interact with a graphical user interface. While
there is essentially just one command line environment for Java programming, there are several
common IDEs, including Eclipse, NetBeans, IntelliJ IDEA, and BlueJ. I cannot give complete
or definitive information on Java programming environments in this section, but I will try to
give enough information to let you compile and run the examples from this textbook using the
command line, Eclipse, or BlueJ. (Readers are strongly encouraged to read, compile, and run
the examples. Source code for sample programs and solutions to end-of-chapter exercises can
be downloaded from the book’s web page, http://math.hws.edu/javanotes.)
One thing to keep in mind is that you do not have to pay any money to do Java programming
(aside from buying a computer, of course). Everything that you need can be downloaded for
free on the Internet.
CHAPTER 2. NAMES AND THINGS 55

This textbook covers Java 8, but as of the release of Version 8.1.1 of the book in May
2020, the current version of Java is Java 14, with version 15 due to be released in September
2020. Significant changes both in the Java language and in the Oracle corporation’s policies
concerning Java have been made since the release of Java 8. Unfortunately, the changes have
made it somewhat more difficult to install Java and get it up and running. In this section, I
will try to give you enough information to make it possible to install Java and use it with this
textbook.
One of the changes made to Java was to the release schedule. New versions of Java will
be released much more frequently than in the past, about twice a year, but only some of the
releases are “long-term support” (LTS) releases that will continue to receive bug fixes and
security updates over an extended period of time. Java 8 and Java 11 are long-term support
releases, but Java 9, 10, 12, 13, and 14 are not. In fact, only Java 8, 11, and 14 are currently
officially supported (although for the purposes of this textbook, using another version that is
no longer receiving support is not a real problem).

2.6.1 Getting JDK and JavaFX


The basic development system for Java programming is usually referred to as a JDK (Java
Development Kit). For this textbook, you need a JDK for Java 8 or later. Note that Java comes
in two versions: a Development Kit version (the JDK) and a Runtime Environment version (the
JRE). A Runtime Environment can be used to run Java programs, but it does not allow you
to compile your own Java programs. A Development Kit includes the Runtime Environment
but also lets you compile programs. (Since the release of Java 11 it has become harder find a
separate JRE download, but you will still see the term used.) A JDK will include the command
line environment that you need to work with Java on the command line. Even if you decide to
use an IDE, you will still need to download a JDK first.
Java was developed by Sun Microsystems, Inc., which was acquired by the Oracle corpo-
ration. It is possible to download a JDK directly from Oracle’s web site, but starting with
Java 11, the Oracle JDK is meant mostly for commercial use. For personal and educational
use, it is probably preferable to use OpenJDK, which has the same functionality as the version
available from Oracle and is distributed under a fully free, open-source license. I will assume
here that you will use the OpenJDK. If you get a JDK from the Oracle web site, you can find
installation instructions there. OpenJDK can be downloaded from
https://jdk.java.net/
which is also owned by Oracle. That web site has limited options. OpenJDKs can also be
downloaded from AdoptOpenJDK at this address:
https://adoptopenjdk.net/
This site has OpenJDKs for a wider range of platforms, and it provides installers for Mac OS
and Windows that make it easier to set up Java on those platforms. It might be your best
option for getting a JDK. (For Linux, you likely also have the option of installing a JDK from
Linux software repositories.) As of May 2020, the current version is Java 14. It is advisable
to use either the most recent version or a long-term support release. However, the sample
programs and exercises in this textbook will work with JDK versions as old as Java 8.
The GUI programs in this book use a programming library known as JavaFX , which must
be downloaded separately from the OpenJDK. For the Oracle JDKs, JavaFX is included in
versions prior to Java 11, but for Java 11 and later, JavaFX must be downloaded separately.
You can download the JavaFX library from
https://gluonhq.com/products/javafx/
CHAPTER 2. NAMES AND THINGS 56

You should get the JavaFX “SDK”—not the “jmods”—for your operating system (Linux,
Mac OS, or Windows). You should get the version number that matches the version number
of the OpenJDK that you downloaded.
If you downloaded a JDK installer for Windows or Mac OS from Oracle or AdoptOpenJDK,
you can just double-click the installer file to start the installation, if it does not start automat-
ically. Otherwise, when you download OpenJDK or the JavaFX SDK, it will be in the form of
a compressed archive file. You will need to extract the contents of the archive. Usually, simply
double-clicking the icon of the archive file will either extract the contents or open a program
that you can use to extract the contents. You will get a directory with a name something like
jdk-11.0.7 or or javafx-sdk-11.0.2. You can put the directories anywhere on your computer, but
you will need to know where they are located.
Note that if you are using Linux, then a recent OpenJDK and JavaFX should be available in
the software repositories for your version of Linux, and all that you need to do is use your Linux
software installer tool to install them. (Exactly what that means depends on the version of
Linux that you are using. Search for packages with names containing “openjdk” and “openjfx”.)
You will still need to know where the JavaFX SDK directory is located; on my computer, it’s
/usr/share/openjfx.
And if you are using Mac OS and downloaded an OpenJDK as an archive file, then
when you extract the OpenJDK archive, you will get a directory that is named something
like jdk-11.0.7.jdk. It is advisable to move that JDK directory into the directory /Li-
brary/Java/JavaVirtualMachines/. (This will require administrative privileges on your Mac.)
Mac OS should automatically use the most recent JDK in /Library/Java/JavaVirtualMachines/
as the default version of Java.
As for Windows, the usual location for JDKs is in C:\Program Files\Java, so you might
consider moving the OpenJDK directory there.

2.6.2 Command Line Environment


Many modern computer users find the command line environment to be pretty alien and unin-
tuitive. It is certainly very different from the graphical user interfaces that most people are used
to. However, it takes only a little practice to learn the basics of the command line environment
and to become productive using it. It is useful to know how to use the command line, and it is
particularly important for computer science students, but you can skip this section if you plan
to do all of your programming in an IDE.
To use a command line programming environment, you will have to open a window where
you can type in commands. In Windows, you can open such a command window by running a
program named cmd . In Mac OS, you want to run the Terminal program, which can be found
in the Utilities folder inside the Applications folder. In Linux, there are several possibilities,
including a very old program called xterm; but try looking for “Terminal” in your Applications
menu.
No matter what type of computer you are using, when you open a command window, it
will display a prompt of some sort. Type in a command at the prompt and press return. The
computer will carry out the command, displaying any output in the command window, and
will then redisplay the prompt so that you can type another command. One of the central
concepts in the command line environment is the current directory or working directory ,
which contains files that can be used by the commands that you type. (The words “directory”
and “folder” mean the same thing.) Often, the name of the current directory is part of the
command prompt. You can get a list of the files in the current directory by typing in the
CHAPTER 2. NAMES AND THINGS 57

command dir (on Windows) or ls (on Linux and Mac OS). When the window first opens,
the current directory is your home directory , where your personal files are stored. You can
change the current directory using the cd command with the name of the directory that you
want to use. For example, if the current directory is your home directory, then you can change
into your Desktop directory by typing the command cd Desktop (and then pressing return).
You might want to create a directory (that is, a folder) to hold your Java work. For example,
you might create a directory named javawork in your home directory. You can do this using
your computer’s GUI; another way is to use the command line: Open a command window. If
you want to put your work directory in a different folder from your home directory, cd into
the directory where you want to put it. Then enter the command mkdir javawork to make
the directory. When you want to work on programming, open a command window and use the
cd command to change into your Java work directory. Of course, you can have more than one
working directory for your Java work; you can organize your files any way you like.
∗ ∗ ∗
The most basic commands for using Java on the command line are javac and java. The
javac command is used to compile Java source code, and java is used to run Java programs.
These commands, and other commands for working with Java, can be found in a directory
named bin inside the JDK directory. If you set things up correctly on your computer, it should
recognize these commands when you type them on the command line. Try typing the commands
java -version and javac -version. The output from these commands should tell you which
version of Java is being used. If you get a message such as “Command not found,” then Java
is not correctly configured.
Java should already be configured correctly on Linux, if you have installed Java from
the Linux software repositories. The same is true on Mac OS, if you have used an in-
staller from AdoptOpenJDK or if you have manually moved the OpenJDK directory into
/Library/Java/JavaVirtualMachines/ — on Mac OS, javac and java will use the most re-
cent JDK in that directory. For Windows, an AdoptOpenJDK installer should set things up
correctly by default. If you use Oracle JDK or a JDK from a compressed archive file, you
need to add the JDK bin directory to the system path by hand. The installation instructions
for the Oracle JDK for Windows have information about how to do that; see the “Installation
Instructions” link on the following page, and look for “Setting the Path Environment Variable”
in the instructions for installing the JDK on Windows:
https://www.oracle.com/technetwork/java/javase/downloads/index.html
∗ ∗ ∗
To test the javac command, place a copy of HelloWorld.java into your working directory.
(If you downloaded the Web site of this book, you can find it in the directory named source;
you can use your computer’s GUI to copy-and-paste this file into your working directory. Al-
ternatively, you can navigate to HelloWorld.java on the book’s Web site and use the “Save As”
command in your Web browser to save a copy of the file into your working directory.) Type
the command:
javac HelloWorld.java
This will compile HelloWorld.java and will create a bytecode file named HelloWorld.class
in the same directory. Note that if the command succeeds, you will not get any response from
the computer; it will just redisplay the command prompt to tell you it’s ready for another
command. You will then be able to run the program using the java command:
java HelloWorld
CHAPTER 2. NAMES AND THINGS 58

The computer should respond by outputting the message “Hello World!”. Note that although
the program is stored in a file named HelloWorld.class, the java command uses the name of
the class, HelloWorld, not the name of the file.
Many of the sample programs for this book use TextIO to read input from the user (see
Subsection 2.4.3). Since TextIO is not a standard part of Java, you must make it available to any
program that uses it. This means that your working directory should contain a folder named
textio, and inside that folder should be the file TextIO.java. You can copy TextIO.java from
this book’s source directory, or your can download it from the web site, but you should be sure
to place it inside a folder named textio in the same directory as the program that uses TextIO.
Once you have TextIO.java you can run a sample program such as Interest2.java to test
user input. First, compile the program with the command
javac Interest2.java
If successful, this will create the compiled file named Interest2.class. But you will also notice
that it creates the file TextIO.class inside the textio folder, if that file does not already exist.
More generally, the javac command will compile not just the file that you specify but also any
additional Java files that are needed. Once you have Interest2.class, you can run it using
the command
java Interest2
You will be asked to enter some information, and you will respond by typing your answers into
the command window, pressing return at the end of each line. When the program ends, you
will see the command prompt, and you can enter another command. (Note, by the way, that
“java TextIO” would not make sense, since TextIO does not have a main() routine, and so it
is not possible to execute it as a program.)
You can follow a similar procedure to run all of the examples in this book that do not use
JavaFX. For running JavaFX programs, see the next subsection below.
∗ ∗ ∗
To create your own programs, you will need a text editor . A text editor is a computer
program that allows you to create and save documents that contain plain text. It is important
that the documents be saved as plain text, that is without any special encoding or formatting
information. Word processor documents are not appropriate, unless you can get your word
processor to save as plain text. A good text editor can make programming a lot more pleasant.
Linux comes with several text editors. On Windows, you can use notepad in a pinch, but you
will probably want something better. For Mac OS, you might download the BBEdit application,
which can be used for free. One possibility that will work on any platform is to use jedit, a
programmer’s text editor that is itself written in Java and that can be downloaded for free from
www.jedit.org. Another popular cross-platform programming editor is Atom, available from
atom.io.
To work on your programs, you can open a command line window and cd into the working
directory where you will store your source code files. Start up your text editor program, such
as by double-clicking its icon or selecting it from a Start menu. Type your code into the editor
window, or open an existing source code file that you want to modify. Save the file into your
working directory. Remember that the name of a Java source code file must end in “.java”, and
the rest of the file name must match the name of the class that is defined in the file. Once the
file is saved in your working directory, go to the command window and use the javac command
to compile it, as discussed above. If there are syntax errors in the code, they will be listed
in the command window. Each error message contains the line number in the file where the
CHAPTER 2. NAMES AND THINGS 59

computer found the error. Go back to the editor and try to fix one or more errors, save your
changes, and then try the javac command again. (It’s usually a good idea to just work on
the first few errors; sometimes fixing those will make other errors go away.) Remember that
when the javac command finally succeeds, you will get no message at all, or possibly just some
“warnings” that do not stop the program from running. Then you can use the java command
to run your program, as described above. Once you’ve compiled the program, you can run it
as many times as you like without recompiling it.
That’s really all there is to it: Keep both editor and command-line window open. Edit,
save, and compile until you have eliminated all the syntax errors. (Always remember to save
the file before compiling it—the compiler only sees the saved file, not the version in the editor
window.) When you run the program, you might find that it has semantic errors that cause it
to run incorrectly. In that case, you have to go back to the edit/save/compile loop to try to
find and fix the problem.

2.6.3 JavaFX on the Command Line


JavaFX is a collection of Java classes that can be used for making GUI programs. In this book,
it is first used in Section 3.9 and is covered extensively in Chapter 6 and Chapter 13. It is
also used in example programs in several other chapters. JavaFX was a standard part of the
Oracle JDK for Java 8. If you are using that JDK, you don’t need to do anything special to
work with JavaFX. However with OpenJDK and with any JDK for Java 11 and later, JavaFX
requires some special treatment. This subsection explains how to use JavaFX on the command
line with Java 11 or later. It assumes that you have already downloaded the JavaFX SDK,
as described above. Starting with Java 11, JavaFX is distributed as a set of “modules.” (See
Subsection 4.6.4) The modules are stored in .jar files in the lib subdirectory of the JavaFX
SDK. When using the javac and java commands on a program that uses JavaFX, you need
to tell the command where to find the JavaFX modules.
The modules are specified for the javac and java commands using two command options:
--module-path and --add-modules. The first option specifies the directory that contains the
module .jar files, and the second says which modules you actually want to use. For the purposes
of this textbook, you can set the value of --add-modules to ALL-MODULE-PATH, which makes
all of JavaFX available to your program. The value of --module-path is a path to the lib
directory that contains the JavaFX .jar files. For example, let’s say that the JavaFX directory
is named openjfx-sdk-11 and that it is in my home directory, /home/eck. Then the full path to
the lib directory is /home/eck/openjfx-sdk-11/lib, and the javac command for compiling
JavaFX programs would be:
javac --module-path=/home/eck/openjfx-sdk-11/lib --add-modules=ALL-MODULE-PATH
followed by the .java files that you want to compile. Exactly the same options would be used
with the java command to run JavaFX programs. The option --module-path can also be
abbreviated to -p, with no equals sign. So this can also be written
javac -p /home/eck/openjfx-sdk-11/lib --add-modules=ALL-MODULE-PATH
If you don’t know the full path to the JavaFX SDK, open a command window and use the
cd command to move to the SDK’s lib directory. On Mac or Linux, enter the command pwd
to print out the full path of the working directory. On windows, use the command cd with no
directory specified to print out the path. Use the output for the value of --module-path. On
windows, a typical java command for use with javafx might look something like this:
CHAPTER 2. NAMES AND THINGS 60

java -p C:\Users\eck\openjfx-sdk-11\lib --add-modules=ALL-MODULE-PATH


If the path name includes a space, or certain other special characters, it must be enclosed in
quotation marks.
Of course, this is very verbose, and it would be nice not to have to retype it all the time.
On Mac OS or Linux, it is easy to define aliases, which are shortcuts for long commands. On
my computer, I used an alias to define a jfxc command for compiling JavaFX programs. In
the alias, I used the full path name of the javac command as well as the full path name of
the JavaFX lib directory. This allowed me to use a JDK that was not officially installed on the
computer. The alias is defined as follows, except that this must all be typed on one line:
alias jfxc=’/home/eck/jdk-11.0.7/bin/javac
--module-path=/home/eck/javafx-sdk-11/lib
--add-modules=ALL-MODULE-PATH’
Similarly, I defined an alias for running JavaFX programs (again, all on one line):
alias jfx=’/home/eck/jdk-11.0.7/bin/java
--module-path=/home/eck/javafx-sdk-11/lib
--add-modules=ALL-MODULE PATH’
To make these alias definitions permanent, I added them to a file named .bashrc in my home
directory. On Mac OS, I would put them in a file named .bash profile in my home directory. The
file might or might not already exist; if it doesn’t exsit, you can create it. The file is executed
whenever you open a Terminal window. (In particular, changes do not become effective until
you open a new Terminal.) Note that the file name begins with a period, which makes it a
“hidden file.” That means that it won’t show up in a usual directory listing or file browser.
(On Linux, it’s usually easy to set a file browser window to show hidden files; try looking in
a “View” menu. On Mac, it’s not so easy. In any case, on the command line, you can get a
directory listing that includes hidden files using the command ls -a — with a space after ls. If
you have trouble working with a hidden file, one option is to rename it to be a non-hidden file
that you can edit, and then rename it back. The mv command can be used to rename files.
For example: mv .bash profile temp and mv temp .bash profile.)
Unfortunately, Windows currently does not have an equivalent of a .bashrc or .bash profile
file for its cmd command window. (An improved version of cmd has been promised.) One
option is to make a batch script file to run the command. For compilation, you could create a
file named jfxc.bat containing just one line similar to
javac -p C:\Users\eck\openjfx-sdk-11\lib --add-modules=ALL-MODULES-PATH $*
but, of course, using the appropriate JavaFX location for your own computer. The “$*” at the
end represents the inputs to the javac command. The file can be in the current directory or
somewhere on the system path, such as the JDK bin directory. Then you can use jfxc as a
command for compiling JavaFX programs:
jfxc MyJavaFXProgram.java
You can handle the java command in the same way.

2.6.4 Eclipse IDE


In an Integrated Development Environment, everything you need to create, compile, and run
programs is integrated into a single package, with a graphical user interface that will be familiar
to most computer users. There are a number of different IDEs for Java program development,
CHAPTER 2. NAMES AND THINGS 61

ranging from fairly simple wrappers around the JDK to highly complex applications with a
multitude of features. For a beginning programmer, there is a danger in using an IDE, since
the difficulty of learning to use the IDE, on top of the difficulty of learning to program, can be
daunting. However, for my own programming, I generally use the Eclipse IDE, and I introduce
my students to it after they have had some experience with the command line. I will discuss
Eclipse in some detail and a much simpler alternative, BlueJ, more briefly. IDEs have features
that are very useful even for a beginning programmer, although a beginner will want to ignore
many of their advanced features.
Unless you are using Oracle’s JDK for Java 8, 9, or 10, using Eclipse for JavaFX programs
will require some extra configuration. The next subsection discusses using JavaFX in Eclipse.
This subsection tells you how to use it for programs that use only standard Java classes.
You can download an Eclipse IDE from eclipse.org. When I install Eclipse, I get the “Eclipse
IDE for Java Developers” package from this web page:
https://www.eclipse.org/downloads/packages/
For Windows and Linux, the download is a compressed archive file. You can simply extract
the contents of the archive and place the resulting directory wherever you want it on your
computer. You will find the Eclipse application in that directory, and you can start Eclipse by
double-clicking the application icon. For Mac OS, the download is a .dmg file that contains the
Eclipse application. You can open the .dmg file and drag the application to any location that
you prefer (probably the Applications folder).
Eclipse is a free program. It is itself written in Java. It requires a JDK for Java 8 or later.
Before trying to run Eclipse, you should first install a JDK. (Note for Mac OS users: As of
June 2019, a bug in Eclipse stops it from running on JDK 12. Until that is fixed, you can stick
to JDK 11 as your default version of Java, if you want to use Eclipse.)
The first time you start Eclipse, you will be asked to specify a workspace, which is the
directory where your work will be stored. You can accept the default name, or provide one
of your own. You can use multiple workspaces and select the one that you want to use at
startup. When a new workspace is first opened, the Eclipse window will be filled by a large
“Welcome” screen that includes links to extensive documentation and tutorials. You should
close this screen, by clicking the “X” next to the word “Welcome”; you can get back to it later
by choosing “Welcome” from the “Help” menu.
The Eclipse GUI consists of one large window that is divided into several sections. Each
section contains one or more views. For example, a view can be a text editor, it can be a place
where a program can do I/O, or it can contain a list of your projects. If there are several views
in one section of the window, then there will be tabs at the top of the section to select the view
that is displayed in that section. Each view displays a different type of information. The whole
set of views is called a perspective. Eclipse uses different perspectives, that is, different sets of
views of different types of information, for different tasks. For compiling and running programs,
the only perspective that you will need is the “Java Perspective,” which is the default. As you
become more experienced, you might want to use the “Debug Perspective,” which has features
designed to help you find semantic errors in programs. There are small buttons in the Eclipse
toolbar that can be used to switch between perspectives.
The Java Perspective includes a large area in the center of the window that contains text
editor views. This is where you will create and edit your programs. To the left of this is the
Package Explorer view, which will contain a list of your Java projects and source code files. To
the right are some other views that I don’t find very useful; I suggest that you close them by
clicking the small “X” next to the name of each one. Several other views that will be useful
CHAPTER 2. NAMES AND THINGS 62

appear in a section of the window below the editing area. If you accidently close one of the
important views, such as the Package Explorer, you can get it back by selecting it from the
“Show View” submenu of the “Window” menu. You can also reset the whole window to its
default contents by selecting “Reset Perspective” from the “Window” menu.
∗ ∗ ∗
To do any work in Eclipse, you need a project. To start a Java project, go to the “New”
submenu in the “File” menu, and select the “Java Project” command. In the window that pops
up, it is only necessary to fill in a “Project Name” for the project and click the “Finish” button.
(In some circumstances, it might be necessary to select the JDK that you want to use for the
project.) The project name can be anything you like. Eclipse might ask you if you want to
create “module-info.java” — you should click “Don’t Create”; the programs in this book
do not use modules. (See Subsection 4.6.4 for information about modules.) The project should
appear in the “Package Explorer” view. Click on the small triangle or plus sign next to the
project name to see the contents of the project. Assuming that you use the default settings,
there should be a directory named “src,” which is where your Java source code files will go. It
also contains the “JRE System Library”; this is the collection of standard built-in classes that
come with Java.
To run any of the sample Java programs from this textbook, you need to copy the source
code file into your Eclipse Java project. Assuming that you have downloaded the source code
file onto your computer, you can copy-and-paste it into the Eclipse window. (Right-click the
file icon (or control-click on Mac OS); select “Copy” from the pop-up menu; then right-click
the project’s src folder in the Eclipse window, and select “Paste”. Be sure to paste it into the
src folder, not into the project itself; files outside the src folder are not treated as Java source
code files.) Alternatively, you can try dragging the file icon from a file browser window onto
the src folder in the Eclipse window.
To use the TextIO based examples from this textbook, you must add the source code file
TextIO.java to your project. This file has to be in a “package” named textio. If you have
downloaded TextIO.java and placed it into a folder named “textio,” as described above, then
you can simply copy-and-paste the textio folder into the “src” folder of your project. Alterna-
tively, you can create the textio package using the “New/Package” command from the “File”
menu. This will make a folder named “textio” in your project, and you can then copy-and-paste
TextIO.java into that folder. In any case, package textio should appear under “src” in your
project, with TextIO.java inside it. (You can drag files from one location to another in the
Package Explorer view, if you accidently put a file in the wrong location.)
Once a Java program is in the project, you can open it in an editor by double-clicking the
file name in the “Package Explorer” view. To run the program, right-click in the editor window,
or on the file name in the Package Explorer view (or control-click in Mac OS). In the menu that
pops up, go to the “Run As” submenu, and select “Java Application”. The program will be
executed. If the program writes to standard output, the output will appear in the “Console”
view, in the area of the Eclipse window below the editing area. If the program uses TextIO
or Scanner for input, you will have to type the required input into the “Console” view—click
the “Console” view before you start typing so that the characters that you type will be
sent to the correct part of the window. (For an easier way to run a program, find and click
the small “Run” button in Eclipse’s tool bar. This will run either the program in the editor
window, the program selected in the Package Explorer view, or the program that was run most
recently, depending on context.) Note that when you run a program in Eclipse, it is compiled
automatically. There is no separate compilation step.
CHAPTER 2. NAMES AND THINGS 63

You can have more than one program in the same Eclipse project, or you can create addi-
tional projects to organize your work better. Remember to place a copy of TextIO.java, inside
a folder named textio, in any project that requires it.
∗ ∗ ∗
To create a new Java program in Eclipse, you must create a new Java class. To do that,
right-click the Java project name in the “Project Explorer” view. Go to the “New” submenu
of the popup menu, and select “Class”. (Alternatively, there is a small icon in the toolbar at
the top of the Eclipse window that you can click to create a new Java class.) In the window
that opens, type in the name of the class that you want to create. The class name must be
a legal Java identifier. Note that you want the name of the class, not the name of the source
code file, so don’t add “.java” at the end of the name. The window also includes an input box
labeled “Package” where you can specify the name of a package to contain the class. Most
examples in this book use the “default package,” but you can create your own programs in any
package. To use the default package, the “Package” input box should be empty. Finally, click
the “Finish” button to create the class. The class should appear inside the “src” folder, in a
folder corresponding to its package. The new file should automatically open in the editing area
so that you can start typing in your program.
Eclipse has several features that aid you as you type your code. It will underline any syntax
error with a jagged red line, and in some cases will place an error marker in the left border
of the edit window. If you hover the mouse cursor over the error marker or over the error
itself, a description of the error will appear. Note that you do not have to get rid of every
error immediately as you type; some errors will go away as you type in more of the program.
If an error marker displays a small “light bulb,” Eclipse is offering to try to fix the error for
you. Click the light bulb—or simply hover your mouse over the actual error—to get a list of
possible fixes, then click the fix that you want to apply. For example, if you use an undeclared
variable in your program, Eclipse will offer to declare it for you. You can actually use this
error-correcting feature to get Eclipse to write certain types of code for you! Unfortunately,
you’ll find that you won’t understand a lot of the proposed fixes until you learn more about
the Java language, and it is not a good idea to apply a fix that you don’t understand—often
that will just make things worse in the end.
Eclipse will also look for spelling errors in comments and will underline them with jagged
red lines. Hover your mouse over the error to get a list of possible correct spellings.
Another essential Eclipse feature is content assist. Content assist can be invoked by typing
Control-Space. It will offer possible completions of whatever you are typing at the moment. For
example, if you type part of an identifier and hit Control-Space, you will get a list of identifiers
that start with the characters that you have typed; use the up and down arrow keys to select one
of the items in the list, and press Return or Enter. (You can also click an item with the mouse
to select it, or hit Escape to dismiss the list.) If there is only one possible completion when
you hit Control-Space, it will be inserted automatically. By default, Content Assist will also
pop up automatically, after a short delay, when you type a period or certain other characters.
For example, if you type “TextIO.” and pause for just a fraction of a second, you will get a
list of all the subroutines in the TextIO class. Personally, I find this auto-activation annoying.
You can disable it in the Eclipse Preferences. (Look under Java / Editor / Content Assist, and
turn off the “Enable auto activation” option.) You can still call up Code Assist manually with
Control-Space.
Once you have an error-free program, you can run it as described above. If you find a
problem when you run it, it’s very easy to go back to the editor, make changes, and run it
CHAPTER 2. NAMES AND THINGS 64

again.

2.6.5 Using JavaFX in Eclipse


You are probably using a JDK that does not include JavaFX, which means that you need to
configure your Eclipse projects to use it. There are many ways to do that. My goal here is not
to present the most “correct” or general way to do it; I just want to make it possible to easily
work with basic JavaFX programs like the ones that come with this textbook. I will discuss a
one-time configuration of an Eclipse workspace that seems to work reliably for Java 11 in the
version of Eclipse released in June 2019. It has also worked with older versions of Eclipse and
hopefully will work with newer versions of Java and Eclipse as well.
To begin, open the Eclipse preferences, using the “Preferences” command (which is in the
“Windows” menu on Linux and Windows and in the “Eclipse” menu on Mac OS). Expand the
Java section in the list on the left, by clicking the small triangle or plus sign next to the word
“Java.” Click on “Installed JREs.” You will see a list of the Java environments that Eclipse
knows about. You can either add a new JRE by clicking the “Add” button, or you can select
one from the list and click “Edit.” If you are adding a new JRE, you will see a dialog box that
asks you to specify a “JRE Type”; choose “Standard VM” and click “Next”. You should see
a dialog box similar to the following. If you are editing an existing JRE, the “JRE home” and
“JRE name” will already be filled in, and there will be an entry under “JRE system libraries”;
aside from that, the box will be empty.

If you are adding a new JRE, you need to specify the “JRE home.” This will be the directory
that contains the bin directory of the JDK that you want to use. Click the “Directory” button
to open a file browser, and navigate to that directory. This can be a JDK that you have simply
downloaded and have not installed officially on your computer, or it can be an installed JDK.
In the example, the JDK was officially installed on Linux. (On Windows or Linux, you want
CHAPTER 2. NAMES AND THINGS 65

the main JDK directory. For Mac OS, you need to navigate to the main JDK directory, then
go into the “Content” folder and finally into the “Home” folder; the JRE home will end with
/Content/Home.) If you enter a valid JDK directory, Eclipse will fill in a JRE name and system
library.
Remember that to use JavaFX, you need to make it available to your program both at
compile time and at run time. To make it available at compile time, you can add JavaFX
.jar files to the system libraries. Click the “Add External JARs” button, and navigate to the
lib directory in the JavaFX SDK that you downloaded or installed. You should see the seven
JavaFX .jar files. Select them all, and click “OK”. They should appear in the “JRE system
libraries” list. Optionally, make sure that all of the JavaFX jar files are selected in that list, and
click the “Source Attachment” button. In the “Source Attachment Configuration” dialog box,
select “External Location”, and click “External File”. Select the file src.zip from the JavaFX
lib directory, and click “OK”. This optional step will allow Eclipse to find the documentation
for JavaFX, which can be very useful when you are writing programs.
To make JavaFX available to your programs at run time, you need to fill in the “Default
VM arguments” box. You should type in the arguments by hand. This box contains options
that will be added to the java command every time you run a program in Eclipse using this
JDK. You should type the same options that you would use on the java command line, as
discussed above. The value of the -p (or --module-path) option is the location of the JavaFX
lib directory, and the value for --add-modules can be ALL-MODULE-PATH, as shown.
When everything is set up, click “Finish” and then click “Apply and Close” in the main
“Preferences” dialog box. When you create a new Java project in the workspace, make sure
that the JRE that you have added or edited is selected in the project creation dialog box. Then
you should be able to use JavaFX in that project. If not, check your JRE configuration.
It is, by the way, harmless to use a JRE that is configured for JavaFX even for standard
programs that do not use JavaFX. In particular, you can use the same configuration for all the
programs in this textbook.

2.6.6 BlueJ
Finally, I will mention BlueJ, a small IDE that is designed specifically for people who are
learning to program. It is much less complex than Eclipse, but it does have some features that
make it useful for education. BlueJ can be downloaded from bluej.org. The current version
requires at least Java 11 and JavaFX 11. You need to have both before you can run BlueJ. (An
earlier version that works with Java 8 is also available.) The first time you start BlueJ, it will
ask you to enter the locations of the JDK and of JavaFX. You will only have to do that once,
and once it is done, you will not have to take any special actions to compile and run JavaFX
programs in the IDE.
In BlueJ, you can begin a project with the “New Project” command in the “Project” menu.
A BlueJ project is simply a folder. When you create a project, you will have to select a folder
name that does not already exist. The folder will be created and a window will be opened to
show the contents of the folder. Files are shown as icons in the BlueJ window. You can drag
.java files from the file system onto that window to add files to the project; they will be copied
into the project folder as well as shown in the window. You can also copy files directly into
the project folder, but BlueJ won’t see them until the next time you open the project. When
you restart BlueJ, it should show the last project you were working on, but you can open any
project with a command from the “Project” menu.
CHAPTER 2. NAMES AND THINGS 66

There is a button in the project window for creating a new class. An icon for the class is
added to the window, and a .java source code file is created in the project folder. The file is not
automatically opened for editing. To edit a file, double-click its icon in the project window. An
editor will be opened in a separate window. (A newly created class will contain some default
code that you probably don’t want; you can erase it and add a main() routine instead.) The
BlueJ editor does not show errors as you type. Errors will be reported when you compile the
program. Also, it does not offer automatic fixes for errors. It has a less capable version of
Eclipse’s Content Assist, which seems only to work for getting a list of available subroutines in
a class or object; call up the list by hitting Control-Space after typing the period following the
name of a class or object.
An editor window contains a button for compiling the program in the window. There is
also a compile button in the project window, which compiles all the classes in the project.
To run a program, it must already be compiled. Right-click the icon of a compiled program.
In the menu that pops up, you will see “void main(String[] args)”. Select that option from
the menu to run the program. Just click “OK” in the dialog box that pops up. A separate
window will open for input/output.
One of the neatest features of BlueJ is that you can actually use it to run any subroutine,
not just main. If a class contains other subroutines, you will see them in the list that you get
by right-clicking its icon. A pop-up dialog allows you to enter any parameters required by the
routine, and if the routine is a function, you will get another dialog box after the routine has
been executed to tell you its return value. This allows easy testing of individual subroutines.
Furthermore, you can also use BlueJ to create new objects from a class. An icon for the object
will be added at the bottom of the project window, and you can right-click that icon to get
a list of subroutines in the object. This will, of course, not be useful to you until we get to
object-oriented programming in Chapter 5.

2.6.7 The Problem of Packages


Every class in Java is contained in something called a package. Classes that are not explicitly
put into a package are in the “default” package. All of Java’s standard classes are in named
packages. This includes even classes like String and System, which are in a package named
java.lang. Classes in java.lang are automatically imported into any Java file, but classes in
other packages must be imported using an import directive. My TextIO class is in a package
named textio, and it must be imported into a program that wants to use it. I will discuss
packages in greater detail in Section 4.6. For now, you just need to know some basic facts.
Although most of my examples are in the default package, in fact, the use of the default
package is discouraged, according to official Java style guidelines. Nevertheless, I have chosen
to use it, since it seems easier for beginning programmers to avoid packages as much as possible,
at least at first. If Eclipse tries to put a class into a package, you can delete the package name
from the class-creation dialog to get it to use the default package instead. But if you do create
a class in a package, the source code starts with a line that specifies which package the class is
in. For example, if the class is in a package named test.pkg, then the first line of the source
code will be
package test.pkg;
For example, the source code for TextIO begins with “package textio;”. I put TextIO in a
package because a class that is in a non-default package cannot use a class from the default
package. That is, if TextIO were in the default package, then it could only be used by programs
CHAPTER 2. NAMES AND THINGS 67

that are also in the default package. (In fact, in earlier versions of this textbook, TextIO was
in the default package. I have moved it to package textio for Version 8 of the book.)
When packages are used in a command-line environment, some complications arise. For
example, if a program is in a package named text.pkg, then the source code file must be in
a subdirectory named “pkg” inside a directory named “test” that is in turn inside your main
Java working directory. Nevertheless, when you compile or execute the program, you should
be working in the main directory, not in the subdirectory. When you compile the source
code file, you have to include the name of the directory in the command: For example, for a
program in package test.pkg use “javac test/pkg/ClassName.java” on Linux or Mac OS, or
“javac test\pkg\ClassName.java” on Windows. The command for executing the program is
then “java test.pkg.ClassName”, with a period separating the package name from the class
name.

2.6.8 About jshell


I will mention one more command-line tool for working with Java: jshell. The jshell command
is a standard part of the JDK for Java 9 or later. If you have Java 9 or later, and if you can
use the javac and java commands on the command line, then you can also use jshell. (On
Mac OS—at least for older versions—you may need to use the full path to the jshell executable
in the bin directory of the JDK.) The purpose of jshell is to let you type in and execute Java
code without the bother of creating a .java file and writing a main program. To start jshell,
just enter the command on a line by itself. You will get a jshell prompt where you can enter
either a Java statement or a Java expression. If you enter a statement, it will be executed. If
you enter an expression, its value will be printed. You do not have to place a semicolon at the
end of a line. Here is a short example of a jshell session.
$ jshell
| Welcome to JShell -- Version 11.0.7
| For an introduction type: /help intro
jshell> System.out.println("Hello World")
Hello World
jshell> int x = 42
x ==> 42
jshell> x * x
$3 ==> 1764
jshell> /exit
| Goodbye

Using jshell can be a great way to learn Java and to experiment with its features. I won’t
give any more detailed information about it in this book, but you can learn more at
https://docs.oracle.com/en/java/javase/11/jshell/introduction-jshell.html
Exercises 68

Exercises for Chapter 2

1. Write a program that will print your initials to standard output in letters that are nine (solution)
lines tall. Each big letter should be made up of a bunch of *’s. For example, if your initials
were “DJE”, then the output would look something like:
****** ************* **********
** ** ** **
** ** ** **
** ** ** **
** ** ** ********
** ** ** ** **
** ** ** ** **
** ** ** ** **
***** **** **********

2. Write a program that simulates rolling a pair of dice. You can simulate rolling one die by (solution)
choosing one of the integers 1, 2, 3, 4, 5, or 6 at random. The number you pick represents
the number on the die after it is rolled. As pointed out in Section 2.5, the expression
(int)(Math.random()*6) + 1
does the computation to select a random integer between 1 and 6. You can assign this
value to a variable to represent one of the dice that are being rolled. Do this twice and
add the results together to get the total roll. Your program should report the number
showing on each die as well as the total roll. For example:
The first die comes up 3
The second die comes up 5
Your total roll is 8

3. Write a program that asks the user’s name, and then greets the user by name. Before (solution)
outputting the user’s name, convert it to upper case letters. For example, if the user’s
name is Fred, then the program should respond “Hello, FRED, nice to meet you!”.

4. Write a program that helps the user count his change. The program should ask how many (solution)
quarters the user has, then how many dimes, then how many nickels, then how many
pennies. Then the program should tell the user how much money he has, expressed in
dollars.

5. If you have N eggs, then you have N/12 dozen eggs, with N%12 eggs left over. (This is (solution)
essentially the definition of the / and % operators for integers.) Write a program that asks
the user how many eggs she has and then tells the user how many dozen eggs she has and
how many extra eggs are left over.
A gross of eggs is equal to 144 eggs. Extend your program so that it will tell the user
how many gross, how many dozen, and how many left over eggs she has. For example, if
the user says that she has 1342 eggs, then your program would respond with
Your number of eggs is 9 gross, 3 dozen, and 10
Exercises 69

since 1342 is equal to 9*144 + 3*12 + 10.

6. This exercise asks you to write a program that tests some of the built-in subroutines for (solution)
working with Strings. The program should ask the user to enter their first name and their
last name, separated by a space. Read the user’s response using TextIO.getln(). Break
the input string up into two strings, one containing the first name and one containing the
last name. You can do that by using the indexOf() subroutine to find the position of the
space, and then using substring() to extract each of the two names. Also output the
number of characters in each name, and output the user’s initials. (The initials are the
first letter of the first name together with the first letter of the last name.) A sample run
of the program should look something like this:
Please enter your first name and last name, separated by a space.
? Mary Smith
Your first name is Mary, which has 4 characters
Your last name is Smith, which has 5 characters
Your initials are MS

7. Suppose that a file named “testdata.txt” contains the following information: The first (solution)
line of the file is the name of a student. Each of the next three lines contains an integer.
The integers are the student’s scores on three exams. Write a program that will read
the information in the file and display (on standard output) a message that contains the
name of the student and the student’s average grade on the three exams. The average is
obtained by adding up the individual exam grades and then dividing by the number of
exams.
Quiz 70

Quiz on Chapter 2
(answers)

1. Briefly explain what is meant by the syntax and the semantics of a programming language.
Give an example to illustrate the difference between a syntax error and a semantics error.

2. What does the computer do when it executes a variable declaration statement. Give an
example.

3. What is a type, as this term relates to programming?

4. One of the primitive types in Java is boolean. What is the boolean type? Where are
boolean values used? What are its possible values?

5. Give the meaning of each of the following Java operators:


a) ++
b) &&
c) !=

6. Explain what is meant by an assignment statement, and give an example. What are
assignment statements used for?

7. What is meant by precedence of operators?

8. What is a literal ?

9. In Java, classes have two fundamentally different purposes. What are they?

10. What is the difference between the statement “x = TextIO.getDouble();” and the state-
ment “x = TextIO.getlnDouble();”

11. Explain why the value of the expression 2 + 3 + "test" is the string "5test" while the
value of the expression "test" + 2 + 3 is the string "test23". What is the value of
"test" + 2 * 3 ?

12. Integrated Development Environments such as Eclipse often use syntax coloring , which
assigns various colors to the characters in a program to reflect the syntax of the language.
A student notices that Eclipse colors the word String differently from int, double, and
boolean. The student asks why String should be a different color, since all these words
are names of types. What’s the answer to the student’s question?

13. What is the purpose of an import directive, such as import textio.TextIO or import
java.util.Scanner?

14. Write a complete program that asks the user to enter the number of “widgets” they want
to buy and the cost per widget. The program should then output the total cost for all
the widgets. Use System.out.printf to print the cost, with two digits after the decimal
point. You do not need to include any comments in the program.
Chapter 3

Programming in the Small II:


Control

The basic building blocks of programs—variables, expressions, assignment statements, and


subroutine call statements—were covered in the previous chapter. Starting with this chapter,
we look at how these building blocks can be put together to build complex programs with more
interesting behavior.
Since we are still working on the level of “programming in the small” in this chapter, we are
interested in the kind of complexity that can occur within a single subroutine. On this level,
complexity is provided by control structures. The two types of control structures, loops and
branches, can be used to repeat a sequence of statements over and over or to choose among two
or more possible courses of action. Java includes several control structures of each type, and
we will look at each of them in some detail.
Program complexity can be seen not just in control structures but also in data structures.
A data structure is an organized collection of data, chunked together so that it can be treated
as a unit. Section 3.8 in this chapter includes an introduction to one of the most common data
structures: arrays.
The chapter will also begin the study of program design. Given a problem, how can you
come up with a program to solve that problem? We’ll look at a partial answer to this question
in Section 3.2. Finally, Section 3.9 is a very brief first look at GUI programming.

3.1 Blocks, Loops, and Branches


The ability of a computer to perform complex tasks is built on just a few ways of
combining simple commands into control structures. In Java, there are just six such structures
that are used to determine the normal flow of control in a program—and, in fact, just three
of them would be enough to write programs to perform any task. The six control structures
are: the block , the while loop, the do..while loop, the for loop, the if statement, and the
switch statement. Each of these structures is considered to be a single “statement,” but a
structured statement that can contain one or more other statements inside itself.

3.1.1 Blocks
The block is the simplest type of structured statement. Its purpose is simply to group a
sequence of statements into a single statement. The format of a block is:

71
CHAPTER 3. CONTROL 72

{
hstatements i
}
That is, it consists of a sequence of statements enclosed between a pair of braces, “{” and “}”.
In fact, it is possible for a block to contain no statements at all; such a block is called an empty
block , and can actually be useful at times. An empty block consists of nothing but an empty
pair of braces. Block statements usually occur inside other statements, where their purpose is
to group together several statements into a unit. However, a block can be legally used wherever
a statement can occur. There is one place where a block is required: As you might have already
noticed in the case of the main subroutine of a program, the definition of a subroutine is a
block, since it is a sequence of statements enclosed inside a pair of braces.
I should probably note again at this point that Java is what is called a free-format language.
There are no syntax rules about how the language has to be arranged on a page. So, for example,
you could write an entire block on one line if you want. But as a matter of good programming
style, you should lay out your program on the page in a way that will make its structure as
clear as possible. In general, this means putting one statement per line and using indentation
to indicate statements that are contained inside control structures. This is the format that I
will use in my examples.
Here are two examples of blocks:
{
System.out.print("The answer is ");
System.out.println(ans);
}

{ // This block exchanges the values of x and y


int temp; // A temporary variable for use in this block.
temp = x; // Save a copy of the value of x in temp.
x = y; // Copy the value of y into x.
y = temp; // Copy the value of temp into y.
}
In the second example, a variable, temp, is declared inside the block. This is perfectly legal,
and it is good style to declare a variable inside a block if that variable is used nowhere else
but inside the block. A variable declared inside a block is completely inaccessible and invisible
from outside that block. When the computer executes the variable declaration statement, it
allocates memory to hold the value of the variable (at least conceptually). When the block
ends, that memory is discarded (that is, made available for reuse). The variable is said to be
local to the block. There is a general concept called the “scope” of an identifier. The scope of
an identifier is the part of the program in which that identifier is valid. The scope of a variable
defined inside a block is limited to that block, and more specifically to the part of the block
that comes after the declaration of the variable.

3.1.2 The Basic While Loop


The block statement by itself really doesn’t affect the flow of control in a program. The five
remaining control structures do. They can be divided into two classes: loop statements and
branching statements. You really just need one control structure from each category in order to
have a completely general-purpose programming language. More than that is just convenience.
CHAPTER 3. CONTROL 73

In this section, I’ll introduce the while loop and the if statement. I’ll give the full details of
these statements and of the other three control structures in later sections.
A while loop is used to repeat a given statement over and over. Of course, it’s not likely
that you would want to keep repeating it forever. That would be an infinite loop, which is
generally a bad thing. (There is an old story about computer pioneer Grace Murray Hopper,
who read instructions on a bottle of shampoo telling her to “lather, rinse, repeat.” As the
story goes, she claims that she tried to follow the directions, but she ran out of shampoo. (In
case you don’t get it, she was making a joke about the way that computers mindlessly follow
instructions.))
To be more specific, a while loop will repeat a statement over and over, but only so long
as a specified condition remains true. A while loop has the form:
while (hboolean-expression i)
hstatement i
Since the statement can be, and usually is, a block, most while loops have the form:
while (hboolean-expression i) {
hstatements i
}
Some programmers think that the braces should always be included as a matter of style, even
when there is only one statement between them, but I don’t always follow that advice myself.
The semantics of the while statement go like this: When the computer comes to a while
statement, it evaluates the hboolean-expressioni, which yields either true or false as its value.
If the value is false, the computer skips over the rest of the while loop and proceeds to the
next command in the program. If the value of the expression is true, the computer executes
the hstatementi or block of hstatementsi inside the loop. Then it returns to the beginning of
the while loop and repeats the process. That is, it re-evaluates the hboolean-expressioni, ends
the loop if the value is false, and continues it if the value is true. This will continue over and
over until the value of the expression is false when the computer evaluates it; if that never
happens, then there will be an infinite loop.
Here is an example of a while loop that simply prints out the numbers 1, 2, 3, 4, 5:
int number; // The number to be printed.
number = 1; // Start with 1.
while ( number < 6 ) { // Keep going as long as number is < 6.
System.out.println(number);
number = number + 1; // Go on to the next number.
}
System.out.println("Done!");
The variable number is initialized with the value 1. So when the computer evaluates the
expression “number < 6” for the first time, it is asking whether 1 is less than 6, which is
true. The computer therefore proceeds to execute the two statements inside the loop. The
first statement prints out “1”. The second statement adds 1 to number and stores the result
back into the variable number; the value of number has been changed to 2. The computer has
reached the end of the loop, so it returns to the beginning and asks again whether number is
less than 6. Once again this is true, so the computer executes the loop again, this time printing
out 2 as the value of number and then changing the value of number to 3. It continues in this
way until eventually number becomes equal to 6. At that point, the expression “number < 6”
evaluates to false. So, the computer jumps past the end of the loop to the next statement
CHAPTER 3. CONTROL 74

and prints out the message “Done!”. Note that when the loop ends, the value of number is 6,
but the last value that was printed was 5.
By the way, you should remember that you’ll never see a while loop standing by itself
in a real program. It will always be inside a subroutine which is itself defined inside some
class. As an example of a while loop used inside a complete program, here is a little program
that computes the interest on an investment over several years. This is an improvement over
examples from the previous chapter that just reported the results for one year:
import textio.TextIO;
/**
* This class implements a simple program that will compute the amount of
* interest that is earned on an investment over a period of 5 years. The
* initial amount of the investment and the interest rate are input by the
* user. The value of the investment at the end of each year is output.
*/
public class Interest3 {

public static void main(String[] args) {


double principal; // The value of the investment.
double rate; // The annual interest rate.
/* Get the initial investment and interest rate from the user. */
System.out.print("Enter the initial investment: ");
principal = TextIO.getlnDouble();
System.out.println();
System.out.println("Enter the annual interest rate.");
System.out.print("Enter a decimal, not a percentage: ");
rate = TextIO.getlnDouble();
System.out.println();
/* Simulate the investment for 5 years. */
int years; // Counts the number of years that have passed.
years = 0;
while (years < 5) {
double interest; // Interest for this year.
interest = principal * rate;
principal = principal + interest; // Add it to principal.
years = years + 1; // Count the current year.
System.out.print("The value of the investment after ");
System.out.print(years);
System.out.print(" years is $");
System.out.printf("%1.2f", principal);
System.out.println();
} // end of while loop
} // end of main()
} // end of class Interest3

You should study this program, and make sure that you understand what the computer does
step-by-step as it executes the while loop.
CHAPTER 3. CONTROL 75

3.1.3 The Basic If Statement


An if statement tells the computer to take one of two alternative courses of action, depending
on whether the value of a given boolean-valued expression is true or false. It is an example of
a “branching” or “decision” statement. An if statement has the form:
if ( hboolean-expression i )
hstatement1 i
else
hstatement2 i
When the computer executes an if statement, it evaluates the boolean expression. If the value
is true, the computer executes the first statement and skips the statement that follows the
“else”. If the value of the expression is false, then the computer skips the first statement and
executes the second one. Note that in any case, one and only one of the two statements inside
the if statement is executed. The two statements represent alternative courses of action; the
computer decides between these courses of action based on the value of the boolean expression.
In many cases, you want the computer to choose between doing something and not doing
it. You can do this with an if statement that omits the else part:
if ( hboolean-expression i )
hstatement i
To execute this statement, the computer evaluates the expression. If the value is true, the
computer executes the hstatementi that is contained inside the if statement; if the value is
false, the computer skips over that hstatementi. In either case, the computer then continues
with whatever follows the if statement in the program.
Sometimes, novice programmers confuse while statements with simple if statements (with
no else part), although their meanings are quite different. The hstatementi in an if is executed
at most once, while the hstatementi in a while can be executed any number of times. It can
be helpful to look at diagrams of the flow of control for while and simple if statements:

While Loop Flow of Control If Statement Flow of Control

Is condition true? No Is condition true?


No

Yes Yes

Do statement Do statement
CHAPTER 3. CONTROL 76

In these diagrams, the arrows represent the flow of time as the statement is executed. Control
enters the diagram at the top and leaves at the bottom. Similarly, a flow control diagram for an
if..else statement makes it clear that exactly one of the two nested statements is executed:

If..Else Flow of Control

Yes No
Is condition true?

Do statement

∗ ∗ ∗
Of course, either or both of the hstatementsi in an if statement can be a block, and again
many programmers prefer to add the braces even when they contain just a single statement.
So an if statement often looks like:
if ( hboolean-expression i ) {
hstatements i
}
else {
hstatements i
}
or:
if ( hboolean-expression i ) {
hstatements i
}
As an example, here is an if statement that exchanges the value of two variables, x and y,
but only if x is greater than y to begin with. After this if statement has been executed, we
can be sure that the value of x is definitely less than or equal to the value of y:
if ( x > y ) {
int temp; // A temporary variable for use in this block.
temp = x; // Save a copy of the value of x in temp.
x = y; // Copy the value of y into x.
y = temp; // Copy the value of temp into y.
}
CHAPTER 3. CONTROL 77

Finally, here is an example of an if statement that includes an else part. See if you can
figure out what it does, and why it would be used:
if ( years > 1 ) { // handle case for 2 or more years
System.out.print("The value of the investment after ");
System.out.print(years);
System.out.print(" years is $");
}
else { // handle case for 1 year
System.out.print("The value of the investment after 1 year is $");
} // end of if statement
System.out.printf("%1.2f", principal); // this is done in any case
I’ll have more to say about control structures later in this chapter. But you already know
the essentials. If you never learned anything more about control structures, you would already
know enough to perform any possible computing task. Simple looping and branching are all
you really need!

3.1.4 Definite Assignment


I will finish this introduction to control structures with a somewhat technical issue that you
might not fully understand the first time you encounter it. Consider the following two code
segments, which seem to be entirely equivalent:
int y; int y;
if (x < 0) { if (x < 0) {
y = 1; y = 1;
} }
else { if (x >= 0) {
y = 2; y = 2;
} }
System.out.println(y); System.out.println(y);
In the version on the left, y is assigned the value 1 if x < 0 and it is assigned the value 2
otherwise, that is, if x >= 0. Exactly the same is true of the version on the right. However, there
is a subtle difference. In fact, the Java compiler will report an error for the System.out.println
statement in the code on the right, while the code on the left is perfectly fine!
The problem is that in the code on the right, the computer can’t tell that the variable y has
definitely been assigned a value. When an if statement has no else part, the statement inside
the if might or might not be executed, depending on the value of the condition. The compiler
can’t tell whether it will be executed or not, since the condition will only be evaluated when
the program is running. For the code on the right above, as far as the compiler is concerned, it
is possible that neither statement, y = 1 or y = 2, will be evaluated, so it is possible that the
output statement is trying to print an undefined value. The compiler considers this to be an
error. The value of a variable can only be used if the compiler can verify that the variable will
have been assigned a value at that point when the program is running. This is called definite
assignment. (It doesn’t matter that you can tell that y will always be assigned a value in
this example. The question is whether the compiler can tell.)
Note that in the code on the left above, y is definitely assigned a value, since in an if..else
statement, one of the two alternatives will be executed no matter what the value of the condition
in the if. It is important that you understand that there is a difference between an if..else
CHAPTER 3. CONTROL 78

statement and a pair of plain if statements. Here is another pair of code segments that might
seem to do the same thing, but don’t. What’s the value of x after each code segment is executed?
int x; int x;
x = -1; x = -1;
if (x < 0) if (x < 0)
x = 1; x = 1;
else if (x >= 0)
x = 2; x = 2;

After the code on the left is executed, x is 1; after the code on the right, x is 2. If you don’t
believe this, work though the code step-by-step, doing exactly what the computer does when it
executes each step.

3.2 Algorithm Development


Programming is difficult (like many activities that are useful and worthwhile—and like
most of those activities, it can also be rewarding and a lot of fun). When you write a program,
you have to tell the computer every small detail of what to do. And you have to get everything
exactly right, since the computer will blindly follow your program exactly as written. How,
then, do people write any but the most simple programs? It’s not a big mystery, actually. It’s
a matter of learning to think in the right way.
A program is an expression of an idea. A programmer starts with a general idea of a task
for the computer to perform. Presumably, the programmer has some idea of how to perform
the task by hand, at least in general outline. The problem is to flesh out that outline into a
complete, unambiguous, step-by-step procedure for carrying out the task. Such a procedure is
called an “algorithm.” (Technically, an algorithm is an unambiguous, step-by-step procedure
that always terminates after a finite number of steps. We don’t want to count procedures that
might go on forever.) An algorithm is not the same as a program. A program is written in some
particular programming language. An algorithm is more like the idea behind the program, but
it’s the idea of the steps the program will take to perform its task, not just the idea of what the
task needs to accomplish in the end. When describing an algorithm, the steps don’t necessarily
have to be specified in complete detail, as long as the steps are unambiguous and it’s clear that
carrying out the steps will accomplish the assigned task. An algorithm can be expressed in
any language, including English. Of course, an algorithm can only be expressed as an actual
program if all the details have been filled in.
So, where do algorithms come from? Usually, they have to be developed, often with a lot of
thought and hard work. Skill at algorithm development is something that comes with practice,
but there are techniques and guidelines that can help. I’ll talk here about some techniques and
guidelines that are relevant to “programming in the small,” and I will return to the subject
several times in later chapters.

3.2.1 Pseudocode and Stepwise Refinement


When programming in the small, you have a few basics to work with: variables, assignment
statements, and input/output routines. You might also have some subroutines, objects, or
other building blocks that have already been written by you or someone else. (Input/output
routines fall into this class.) You can build sequences of these basic instructions, and you can
also combine them into more complex control structures such as while loops and if statements.
CHAPTER 3. CONTROL 79

Suppose you have a task in mind that you want the computer to perform. One way to
proceed is to write a description of the task, and take that description as an outline of the
algorithm you want to develop. Then you can refine and elaborate that description, gradually
adding steps and detail, until you have a complete algorithm that can be translated directly
into programming language. This method is called stepwise refinement, and it is a type of
top-down design. As you proceed through the stages of stepwise refinement, you can write out
descriptions of your algorithm in pseudocode—informal instructions that imitate the structure
of programming languages without the complete detail and perfect syntax of actual program
code.
As an example, let’s see how one might develop the program from the previous section, which
computes the value of an investment over five years. The task that you want the program to
perform is: “Compute and display the value of an investment for each of the next five years,
where the initial investment and interest rate are to be specified by the user.” You might then
write—or more likely just think—that this can be expanded as:
Get the user’s input
Compute the value of the investment after 1 year
Display the value
Compute the value after 2 years
Display the value
Compute the value after 3 years
Display the value
Compute the value after 4 years
Display the value
Compute the value after 5 years
Display the value
This is correct, but rather repetitive. And seeing that repetition, you might notice an
opportunity to use a loop. A loop would take less typing. More important, it would be more
general: Essentially the same loop will work no matter how many years you want to process.
So, you might rewrite the above sequence of steps as:
Get the user’s input
while there are more years to process:
Compute the value after the next year
Display the value
Following this algorithm would certainly solve the problem, but for a computer we’ll have
to be more explicit about how to “Get the user’s input,” how to “Compute the value after the
next year,” and what it means to say “there are more years to process.” We can expand the
step, “Get the user’s input” into
Ask the user for the initial investment
Read the user’s response
Ask the user for the interest rate
Read the user’s response
To fill in the details of the step “Compute the value after the next year,” you have to
know how to do the computation yourself. (Maybe you need to ask your boss or professor for
clarification?) Let’s say you know that the value is computed by adding some interest to the
previous value. Then we can refine the while loop to:
CHAPTER 3. CONTROL 80

while there are more years to process:


Compute the interest
Add the interest to the value
Display the value
As for testing whether there are more years to process, the only way that we can do that is
by counting the years ourselves. This displays a very common pattern, and you should expect
to use something similar in a lot of programs: We have to start with zero years, add one each
time we process a year, and stop when we reach the desired number of years. This is sometimes
called a counting loop. So the while loop becomes:
years = 0
while years < 5:
years = years + 1
Compute the interest
Add the interest to the value
Display the value
We still have to know how to compute the interest. Let’s say that the interest is to be
computed by multiplying the interest rate by the current value of the investment. Putting
this together with the part of the algorithm that gets the user’s inputs, we have the complete
algorithm:
Ask the user for the initial investment
Read the user’s response
Ask the user for the interest rate
Read the user’s response
years = 0
while years < 5:
years = years + 1
Compute interest = value * interest rate
Add the interest to the value
Display the value
Finally, we are at the point where we can translate pretty directly into proper programming-
language syntax. We still have to choose names for the variables, decide exactly what we want
to say to the user, and so forth. Having done this, we could express our algorithm in Java as:
double principal, rate, interest; // declare the variables
int years;
System.out.print("Type initial investment: ");
principal = TextIO.getlnDouble();
System.out.print("Type interest rate: ");
rate = TextIO.getlnDouble();
years = 0;
while (years < 5) {
years = years + 1;
interest = principal * rate;
principal = principal + interest;
System.out.println(principal);
}
This still needs to be wrapped inside a complete program, it still needs to be commented,
and it really needs to print out more information in a nicer format for the user. But it’s
essentially the same program as the one in the previous section. (Note that the pseudocode
CHAPTER 3. CONTROL 81

algorithm used indentation to show which statements are inside the loop. In Java, indentation
is completely ignored by the computer, so you need a pair of braces to tell the computer which
statements are in the loop. If you leave out the braces, the only statement inside the loop would
be “years = years + 1;". The other statements would only be executed once, after the loop
ends. The nasty thing is that the computer won’t notice this error for you, like it would if you
left out the parentheses around “(years < 5)”. The parentheses are required by the syntax of
the while statement. The braces are only required semantically. The computer can recognize
syntax errors but not semantic errors.)
One thing you should have noticed here is that my original specification of the problem—
“Compute and display the value of an investment for each of the next five years”—was far from
being complete. Before you start writing a program, you should make sure you have a complete
specification of exactly what the program is supposed to do. In particular, you need to know
what information the program is going to input and output and what computation it is going
to perform. Here is what a reasonably complete specification of the problem might look like in
this example:
“Write a program that will compute and display the value of
an investment for each of the next five years. Each year, interest
is added to the value. The interest is computed by multiplying
the current value by a fixed interest rate. Assume that the initial
value and the rate of interest are to be input by the user when the
program is run.”

3.2.2 The 3N+1 Problem


Let’s do another example, working this time with a program that you haven’t already seen. The
assignment here is an abstract mathematical problem that is one of my favorite programming
exercises. This time, we’ll start with a more complete specification of the task to be performed:
“Given a positive integer, N, define the ’3N+1’ sequence start-
ing from N as follows: If N is an even number, then divide N by
two; but if N is odd, then multiply N by 3 and add 1. Continue
to generate numbers in this way until N becomes equal to 1. For
example, starting from N = 3, which is odd, we multiply by 3 and
add 1, giving N = 3*3+1 = 10. Then, since N is even, we divide
by 2, giving N = 10/2 = 5. We continue in this way, stopping
when we reach 1. The complete sequence is: 3, 10, 5, 16, 8, 4, 2,
1.
“Write a program that will read a positive integer from the
user and will print out the 3N+1 sequence starting from that
integer. The program should also count and print out the number
of terms in the sequence.”
A general outline of the algorithm for the program we want is:
Get a positive integer N from the user.
Compute, print, and count each number in the sequence.
Output the number of terms.
The bulk of the program is in the second step. We’ll need a loop, since we want to keep
computing numbers until we get 1. To put this in terms appropriate for a while loop, we need
CHAPTER 3. CONTROL 82

to know when to continue the loop rather than when to stop it: We want to continue as long
as the number is not 1. So, we can expand our pseudocode algorithm to:
Get a positive integer N from the user;
while N is not 1:
Compute N = next term;
Output N;
Count this term;
Output the number of terms;
In order to compute the next term, the computer must take different actions depending on
whether N is even or odd. We need an if statement to decide between the two cases:
Get a positive integer N from the user;
while N is not 1:
if N is even:
Compute N = N/2;
else
Compute N = 3 * N + 1;
Output N;
Count this term;
Output the number of terms;
We are almost there. The one problem that remains is counting. Counting means that you
start with zero, and every time you have something to count, you add one. We need a variable
to do the counting. The variable must be set to zero once, before the loop starts, and it must
be incremented within the loop. (Again, this is a common pattern that you should expect to
see over and over.) With the counter added, we get:
Get a positive integer N from the user;
Let counter = 0;
while N is not 1:
if N is even:
Compute N = N/2;
else
Compute N = 3 * N + 1;
Output N;
Add 1 to counter;
Output the counter;
We still have to worry about the very first step. How can we get a positive integer from the
user? If we just read in a number, it’s possible that the user might type in a negative number
or zero. If you follow what happens when the value of N is negative or zero, you’ll see that the
program will go on forever, since the value of N will never become equal to 1. This is bad. In
this case, the problem is probably no big deal, but in general you should try to write programs
that are foolproof. One way to fix this is to keep reading in numbers until the user types in a
positive number:
Ask user to input a positive number;
Let N be the user’s response;
while N is not positive:
Print an error message;
Read another value for N;
Let counter = 0;
while N is not 1:
CHAPTER 3. CONTROL 83

if N is even:
Compute N = N/2;
else
Compute N = 3 * N + 1;
Output N;
Add 1 to counter;
Output the counter;
The first while loop will end only when N is a positive number, as required. (A common
beginning programmer’s error is to use an if statement instead of a while statement here: “If
N is not positive, ask the user to input another value.” The problem arises if the second number
input by the user is also non-positive. The if statement is only executed once, so the second
input number is never tested, and the program proceeds into an infinite loop. With the while
loop, after the second number is input, the computer jumps back to the beginning of the loop
and tests whether the second number is positive. If not, it asks the user for a third number,
and it will continue asking for numbers until the user enters an acceptable input. After the
while loop ends, we can be absolutely sure that N is a positive number.)
Here is a Java program implementing this algorithm. It uses the operators <= to mean “is
less than or equal to” and != to mean “is not equal to.” To test whether N is even, it uses
“N % 2 == 0”. All the operators used here were discussed in Section 2.5.
import textio.TextIO;
/**
* This program prints out a 3N+1 sequence starting from a positive
* integer specified by the user. It also counts the number of
* terms in the sequence, and prints out that number.
*/
public class ThreeN1 {
public static void main(String[] args) {
int N; // for computing terms in the sequence
int counter; // for counting the terms
System.out.print("Starting point for sequence: ");
N = TextIO.getlnInt();
while (N <= 0) {
System.out.print(
"The starting point must be positive. Please try again: " );
N = TextIO.getlnInt();
}
// At this point, we know that N > 0
counter = 0;
while (N != 1) {
if (N % 2 == 0)
N = N / 2;
else
N = 3 * N + 1;
System.out.println(N);
counter = counter + 1;
}
System.out.println();
System.out.print("There were ");
CHAPTER 3. CONTROL 84

System.out.print(counter);
System.out.println(" terms in the sequence.");
} // end of main()
} // end of class ThreeN1
Two final notes on this program: First, you might have noticed that the first term of the
sequence—the value of N input by the user—is not printed or counted by this program. Is
this an error? It’s hard to say. Was the specification of the program careful enough to decide?
This is the type of thing that might send you back to the boss/professor for clarification. The
problem (if it is one!) can be fixed easily enough. Just replace the line “counter = 0” before
the while loop with the two lines:
System.out.println(N); // print out initial term
counter = 1; // and count it
Second, there is the question of why this problem might be interesting. Well, it’s interesting
to mathematicians and computer scientists because of a simple question about the problem that
they haven’t been able to answer: Will the process of computing the 3N+1 sequence finish after
a finite number of steps for all possible starting values of N? Although individual sequences are
easy to compute, no one has been able to answer the general question. To put this another
way, no one knows whether the process of computing 3N+1 sequences can properly be called
an algorithm, since an algorithm is required to terminate after a finite number of steps! (Note:
This discussion really applies to integers, not to values of type int! That is, it assumes that the
value of N can take on arbitrarily large integer values, which is not true for a variable of type
int in a Java program. When the value of N in the program becomes too large to be represented
as a 32-bit int, the values output by the program are no longer mathematically correct. So
the Java program does not compute the correct 3N+1 sequence if N becomes too large. See
Exercise 8.2.)

3.2.3 Coding, Testing, Debugging


It would be nice if, having developed an algorithm for your program, you could relax, press a
button, and get a perfectly working program. Unfortunately, the process of turning an algorithm
into Java source code doesn’t always go smoothly. And when you do get to the stage of a working
program, it’s often only working in the sense that it does something. Unfortunately not what
you want it to do.
After program design comes coding: translating the design into a program written in Java
or some other language. Usually, no matter how careful you are, a few syntax errors will creep
in from somewhere, and the Java compiler will reject your program with some kind of error
message. Unfortunately, while a compiler will always detect syntax errors, it’s not very good
about telling you exactly what’s wrong. Sometimes, it’s not even good about telling you where
the real error is. A spelling error or missing “{” on line 45 might cause the compiler to choke
on line 105. You can avoid lots of errors by making sure that you really understand the syntax
rules of the language and by following some basic programming guidelines. For example, I
never type a “{” without typing the matching “}”. Then I go back and fill in the statements
between the braces. A missing or extra brace can be one of the hardest errors to find in a large
program. Always, always indent your program nicely. If you change the program, change the
indentation to match. It’s worth the trouble. Use a consistent naming scheme, so you don’t
have to struggle to remember whether you called that variable interestrate or interestRate.
CHAPTER 3. CONTROL 85

In general, when the compiler gives multiple error messages, don’t try to fix the second error
message from the compiler until you’ve fixed the first one. Once the compiler hits an error in
your program, it can get confused, and the rest of the error messages might just be guesses.
Maybe the best advice is: Take the time to understand the error before you try to fix it.
Programming is not an experimental science.
When your program compiles without error, you are still not done. You have to test the
program to make sure it works correctly. Remember that the goal is not to get the right output
for the two sample inputs that the professor gave in class. The goal is a program that will
work correctly for all reasonable inputs. Ideally, when faced with an unreasonable input, it
should respond by gently chiding the user rather than by crashing. Test your program on a
wide variety of inputs. Try to find a set of inputs that will test the full range of functionality
that you’ve coded into your program. As you begin writing larger programs, write them in
stages and test each stage along the way. You might even have to write some extra code to
do the testing—for example to call a subroutine that you’ve just written. You don’t want to
be faced, if you can avoid it, with 500 newly written lines of code that have an error in there
somewhere.
The point of testing is to find bugs—semantic errors that show up as incorrect behavior
rather than as compilation errors. And the sad fact is that you will probably find them. Again,
you can minimize bugs by careful design and careful coding, but no one has found a way to
avoid them altogether. Once you’ve detected a bug, it’s time for debugging . You have to
track down the cause of the bug in the program’s source code and eliminate it. Debugging is a
skill that, like other aspects of programming, requires practice to master. So don’t be afraid of
bugs. Learn from them. One essential debugging skill is the ability to read source code—the
ability to put aside preconceptions about what you think it does and to follow it the way the
computer does—mechanically, step-by-step—to see what it really does. This is hard. I can still
remember the time I spent hours looking for a bug only to find that a line of code that I had
looked at ten times had a “1” where it should have had an “i”, or the time when I wrote a
subroutine named WindowClosing which would have done exactly what I wanted except that
the computer was looking for windowClosing (with a lower case “w”). Sometimes it can help
to have someone who doesn’t share your preconceptions look at your code.
Often, it’s a problem just to find the part of the program that contains the error. Most
programming environments come with a debugger , which is a program that can help you find
bugs. Typically, your program can be run under the control of the debugger. The debugger
allows you to set “breakpoints” in your program. A breakpoint is a point in the program where
the debugger will pause the program so you can look at the values of the program’s variables.
The idea is to track down exactly when things start to go wrong during the program’s execution.
The debugger will also let you execute your program one line at a time, so that you can watch
what happens in detail once you know the general area in the program where the bug is lurking.
I will confess that I only occasionally use debuggers myself. A more traditional approach to
debugging is to insert debugging statements into your program. These are output statements
that print out information about the state of the program. Typically, a debugging statement
would say something like
System.out.println("At start of while loop, N = " + N);
You need to be able to tell from the output where in your program the output is coming from,
and you want to know the value of important variables. Sometimes, you will find that the
computer isn’t even getting to a part of the program that you think it should be executing.
Remember that the goal is to find the first point in the program where the state is not what
CHAPTER 3. CONTROL 86

you expect it to be. That’s where the bug is.


And finally, remember the golden rule of debugging: If you are absolutely sure that every-
thing in your program is right, and if it still doesn’t work, then one of the things that you are
absolutely sure of is wrong.

3.3 The while and do..while Statements


Statements in Java can be either simple statements or compound statements. Simple
statements, such as assignment statements and subroutine call statements, are the basic building
blocks of a program. Compound statements, such as while loops and if statements, are used to
organize simple statements into complex structures, which are called control structures because
they control the order in which the statements are executed. The next five sections explore
the details of control structures that are available in Java, starting with the while statement
and the do..while statement in this section. At the same time, we’ll look at examples of
programming with each control structure and apply the techniques for designing algorithms
that were introduced in the previous section.

3.3.1 The while Statement


The while statement was already introduced in Section 3.1. A while loop has the form
while ( hboolean-expression i )
hstatement i
The hstatementi can, of course, be a block statement consisting of several statements grouped
together between a pair of braces. This statement is called the body of the loop. The body
of the loop is repeated as long as the hboolean-expressioni is true. This boolean expression is
called the continuation condition, or more simply the test, of the loop. There are a few
points that might need some clarification. What happens if the condition is false in the first
place, before the body of the loop is executed even once? In that case, the body of the loop is
never executed at all. The body of a while loop can be executed any number of times, including
zero. What happens if the condition is true, but it becomes false somewhere in the middle of
the loop body? Does the loop end as soon as this happens? It doesn’t, because the computer
continues executing the body of the loop until it gets to the end. Only then does it jump back
to the beginning of the loop and test the condition, and only then can the loop end.
Let’s look at a typical problem that can be solved using a while loop: finding the average
of a set of positive integers entered by the user. The average is the sum of the integers, divided
by the number of integers. The program will ask the user to enter one integer at a time. It
will keep count of the number of integers entered, and it will keep a running total of all the
numbers it has read so far. Here is a pseudocode algorithm for the program:
Let sum = 0 // The sum of the integers entered by the user.
Let count = 0 // The number of integers entered by the user.
while there are more integers to process:
Read an integer
Add it to the sum
Count it
Divide sum by count to get the average
Print out the average
CHAPTER 3. CONTROL 87

But how can we test whether there are more integers to process? A typical solution is to
tell the user to type in zero after all the data have been entered. This will work because we
are assuming that all the data are positive numbers, so zero is not a legal data value. The zero
is not itself part of the data to be averaged. It’s just there to mark the end of the real data.
A data value used in this way is sometimes called a sentinel value. So now the test in the
while loop becomes “while the input integer is not zero”. But there is another problem! The
first time the test is evaluated, before the body of the loop has ever been executed, no integer
has yet been read. There is no “input integer” yet, so testing whether the input integer is zero
doesn’t make sense. So, we have to do something before the while loop to make sure that the
test makes sense. Setting things up so that the test in a while loop makes sense the first time
it is executed is called priming the loop. In this case, we can simply read the first integer
before the beginning of the loop. Here is a revised algorithm:
Let sum = 0
Let count = 0
Read an integer
while the integer is not zero:
Add the integer to the sum
Count it
Read an integer
Divide sum by count to get the average
Print out the average
Notice that I’ve rearranged the body of the loop. Since an integer is read before the loop, the
loop has to begin by processing that integer. At the end of the loop, the computer reads a new
integer. The computer then jumps back to the beginning of the loop and tests the integer that
it has just read. Note that when the computer finally reads the sentinel value, the loop ends
before the sentinel value is processed. It is not added to the sum, and it is not counted. This
is the way it’s supposed to work. The sentinel is not part of the data. The original algorithm,
even if it could have been made to work without priming, was incorrect since it would have
summed and counted all the integers, including the sentinel. (Since the sentinel is zero, the sum
would still be correct, but the count would be off by one. Such so-called off-by-one errors
are very common. Counting turns out to be harder than it looks!)
We can easily turn the algorithm into a complete program. Note that the program cannot
use the statement “average = sum/count;” to compute the average. Since sum and count
are both variables of type int, the value of sum/count is an integer. The average should be
a real number. We’ve seen this problem before: we have to convert one of the int values to
a double to force the computer to compute the quotient as a real number. This can be done
by type-casting one of the variables to type double. The type cast “(double)sum” converts
the value of sum to a real number, so in the program the average is computed as “average =
((double)sum) / count;”. Another solution in this case would have been to declare sum to
be a variable of type double in the first place.
One other issue is addressed by the program: If the user enters zero as the first input value,
there are no data to process. We can test for this case by checking whether count is still equal
to zero after the while loop. This might seem like a minor point, but a careful programmer
should cover all the bases.
Here is the full source code for the program (with comments added, of course!):
import textio.TextIO;
/**
CHAPTER 3. CONTROL 88

* This program reads a sequence of positive integers input


* by the user, and it will print out the average of those
* integers. The user is prompted to enter one integer at a
* time. The user must enter a 0 to mark the end of the
* data. (The zero is not counted as part of the data to
* be averaged.) The program does not check whether the
* user’s input is positive, so it will actually add up
* both positive and negative input values.
*/
public class ComputeAverage {
public static void main(String[] args) {
int inputNumber; // One of the integers input by the user.
int sum; // The sum of the positive integers.
int count; // The number of positive integers.
double average; // The average of the positive integers.
/* Initialize the summation and counting variables. */
sum = 0;
count = 0;
/* Read and process the user’s input. */
System.out.print("Enter your first positive integer: ");
inputNumber = TextIO.getlnInt();
while (inputNumber != 0) {
sum += inputNumber; // Add inputNumber to running sum.
count++; // Count the input by adding 1 to count.
System.out.print("Enter your next positive integer, or 0 to end: ");
inputNumber = TextIO.getlnInt();
}
/* Display the result. */
if (count == 0) {
System.out.println("You didn’t enter any data!");
}
else {
average = ((double)sum) / count;
System.out.println();
System.out.println("You entered " + count + " positive integers.");
System.out.printf("Their average is %1.3f.\n", average);
}
} // end main()
} // end class ComputeAverage

3.3.2 The do..while Statement


Sometimes it is more convenient to test the continuation condition at the end of a loop, instead
of at the beginning, as is done in the while loop. The do..while statement is very similar
to the while statement, except that the word “while,” along with the condition that it tests,
CHAPTER 3. CONTROL 89

has been moved to the end. The word “do” is added to mark the beginning of the loop. A
do..while statement has the form
do
hstatement i
while ( hboolean-expression i );
or, since, as usual, the hstatementi can be a block,
do {
hstatements i
} while ( hboolean-expression i );
Note the semicolon, ’;’, at the very end. This semicolon is part of the statement, just as
the semicolon at the end of an assignment statement or declaration is part of the statement.
Omitting it is a syntax error. (More generally, every statement in Java ends either with a
semicolon or a right brace, ’}’.)
To execute a do loop, the computer first executes the body of the loop—that is, the statement
or statements inside the loop—and then it evaluates the boolean expression. If the value of
the expression is true, the computer returns to the beginning of the do loop and repeats the
process; if the value is false, it ends the loop and continues with the next part of the program.
Since the condition is not tested until the end of the loop, the body of a do loop is always
executed at least once.
For example, consider the following pseudocode for a game-playing program. The do loop
makes sense here instead of a while loop because with the do loop, you know there will be at
least one game. Also, the test that is used at the end of the loop wouldn’t even make sense at
the beginning:
do {
Play a Game
Ask user if he wants to play another game
Read the user’s response
} while ( the user’s response is yes );
Let’s convert this into proper Java code. Since I don’t want to talk about game playing at the
moment, let’s say that we have a class named Checkers, and that the Checkers class contains
a static member subroutine named playGame() that plays one game of checkers against the
user. Then, the pseudocode “Play a game” can be expressed as the subroutine call statement
“Checkers.playGame();”. We need a variable to store the user’s response. The TextIO class
makes it convenient to use a boolean variable to store the answer to a yes/no question. The
input function TextIO.getlnBoolean() allows the user to enter the value as “yes” or “no”
(among other acceptable responses). “Yes” is considered to be true, and “no” is considered to
be false. So, the algorithm can be coded as
boolean wantsToContinue; // True if user wants to play again.
do {
Checkers.playGame();
System.out.print("Do you want to play again? ");
wantsToContinue = TextIO.getlnBoolean();
} while (wantsToContinue == true);
When the value of the boolean variable is set to false, it is a signal that the loop should end.
When a boolean variable is used in this way—as a signal that is set in one part of the program
CHAPTER 3. CONTROL 90

and tested in another part—it is sometimes called a flag or flag variable (in the sense of a
signal flag).
By the way, a more-than-usually-pedantic programmer would sneer at the test
“while (wantsToContinue == true)”. This test is exactly equivalent to “while
(wantsToContinue)”. Testing whether “wantsToContinue == true” is true amounts to the
same thing as testing whether “wantsToContinue” is true. A little less offensive is an expression
of the form “flag == false”, where flag is a boolean variable. The value of “flag == false”
is exactly the same as the value of “!flag”, where ! is the boolean negation operator. So
you can write “while (!flag)” instead of “while (flag == false)”, and you can write
“if (!flag)” instead of “if (flag == false)”.
Although a do..while statement is sometimes more convenient than a while statement,
having two kinds of loops does not make the language more powerful. Any problem that can be
solved using do..while loops can also be solved using only while statements, and vice versa.
In fact, if hdoSomethingi represents any block of program code, then
do {
hdoSomething i
} while ( hboolean-expression i );
has exactly the same effect as
hdoSomething i
while ( hboolean-expression i ) {
hdoSomething i
}
Similarly,
while ( hboolean-expression i ) {
hdoSomething i
}
can be replaced by
if ( hboolean-expression i ) {
do {
hdoSomething i
} while ( hboolean-expression i );
}
without changing the meaning of the program in any way.

3.3.3 break and continue


The syntax of the while and do..while loops allows you to test the continuation condition at
either the beginning of a loop or at the end. Sometimes, it is more natural to have the test
in the middle of the loop, or to have several tests at different places in the same loop. Java
provides a general method for breaking out of the middle of any loop. It’s called the break
statement, which takes the form
break;
When the computer executes a break statement in a loop, it will immediately jump out
of the loop. It then continues on to whatever follows the loop in the program. Consider for
example:
CHAPTER 3. CONTROL 91

while (true) { // looks like it will run forever!


System.out.print("Enter a positive number: ");
N = TextIO.getlnInt();
if (N > 0) // the input value is OK, so jump out of loop
break;
System.out.println("Your answer must be > 0.");
}
// continue here after break
If the number entered by the user is greater than zero, the break statement will be executed
and the computer will jump out of the loop. Otherwise, the computer will print out “Your
answer must be > 0.” and will jump back to the start of the loop to read another input value.
The first line of this loop, “while (true)” might look a bit strange, but it’s perfectly
legitimate. The condition in a while loop can be any boolean-valued expression. The computer
evaluates this expression and checks whether the value is true or false. The boolean literal
“true” is just a boolean expression that always evaluates to true. So “while (true)” can be
used to write an infinite loop, or one that will be terminated by a break statement.
A break statement terminates the loop that immediately encloses the break statement. It
is possible to have nested loops, where one loop statement is contained inside another. If you
use a break statement inside a nested loop, it will only break out of that loop, not out of
the loop that contains the nested loop. There is something called a labeled break statement
that allows you to specify which loop you want to break. This is not very common, so I will
go over it quickly. Labels work like this: You can put a label in front of any loop. A label
consists of a simple identifier followed by a colon. For example, a while with a label might
look like “mainloop: while...”. Inside this loop you can use the labeled break statement
“break mainloop;” to break out of the labeled loop. For example, here is a code segment that
checks whether two strings, s1 and s2, have a character in common. If a common character is
found, the value of the flag variable nothingInCommon is set to false, and a labeled break is
used to end the processing at that point:
boolean nothingInCommon;
nothingInCommon = true; // Assume s1 and s2 have no chars in common.
int i,j; // Variables for iterating through the chars in s1 and s2.
i = 0;
bigloop: while (i < s1.length()) {
j = 0;
while (j < s2.length()) {
if (s1.charAt(i) == s2.charAt(j)) { // s1 and s2 have a common char...
nothingInCommon = false; // so nothingInCommon is actually false.
break bigloop; // break out of BOTH loops
}
j++; // Go on to the next char in s2.
}
i++; //Go on to the next char in s1.
}

∗ ∗ ∗
The continue statement is related to break, but less commonly used. A continue state-
ment tells the computer to skip the rest of the current iteration of the loop. However, instead
of jumping out of the loop altogether, it jumps back to the beginning of the loop and continues
with the next iteration (including evaluating the loop’s continuation condition to see whether
CHAPTER 3. CONTROL 92

any further iterations are required). As with break, when a continue is in a nested loop, it
will continue the loop that directly contains it; a “labeled continue” can be used to continue
the containing loop instead.
break and continue can be used in while loops and do..while loops. They can also be
used in for loops, which are covered in the next section. In Section 3.6, we’ll see that break can
also be used to break out of a switch statement. A break can occur inside an if statement,
but only if the if statement is nested inside a loop or inside a switch statement. In that case,
it does not mean to break out of the if. Instead, it breaks out of the loop or switch statement
that contains the if statement. The same consideration applies to continue statements inside
ifs.

3.4 The for Statement


We turn in this section to another type of loop, the for statement. Any for loop is
equivalent to some while loop, so the language doesn’t get any additional power by having for
statements. But for a certain type of problem, a for loop can be easier to construct and easier
to read than the corresponding while loop. It’s quite possible that in real programs, for loops
actually outnumber while loops (and I know of at least one person who only uses for loops).

3.4.1 For Loops


The for statement makes a common type of while loop easier to write. Many while loops have
the general form:
hinitialization i
while ( hcontinuation-condition i ) {
hstatements i
hupdate i
}
For example, consider this example, copied from an example in Section 3.2:
years = 0; // initialize the variable years
while ( years < 5 ) { // condition for continuing loop
interest = principal * rate; //
principal += interest; // do three statements
System.out.println(principal); //
years++; // update the value of the variable, years
}
This loop can be written as the following equivalent for statement:
for ( years = 0; years < 5; years++ ) {
interest = principal * rate;
principal += interest;
System.out.println(principal);
}
The initialization, continuation condition, and updating have all been combined in the first line
of the for loop. This keeps everything involved in the “control” of the loop in one place, which
helps make the loop easier to read and understand. The for loop is executed in exactly the
same way as the original code: The initialization part is executed once, before the loop begins.
CHAPTER 3. CONTROL 93

The continuation condition is executed before each execution of the loop (including the first
execution), and the loop ends when this condition is false. The update part is executed at
the end of each execution of the loop, just before jumping back to check the condition.
The formal syntax of the for statement is as follows:
for ( hinitialization i; hcontinuation-condition i; hupdate i )
hstatement i
or, using a block statement:
for ( hinitialization i; hcontinuation-condition i; hupdate i ) {
hstatements i
}
The hcontinuation-conditioni must be a boolean-valued expression. The hinitializationi is usu-
ally a declaration or an assignment statement, but it can be any expression that would be
allowed as a statement in a program. The hupdatei can be any simple statement, but is usually
an increment, a decrement, or an assignment statement. Any of the three parts can be empty.
If the continuation condition is empty, it is treated as if it were “true,” so the loop will be
repeated forever or until it ends for some other reason, such as a break statement. (Some
people like to begin an infinite loop with “for (;;)” instead of “while (true)”.) Here’s a
flow control diagram for a for statement:
For Loop Flow of Control

Is condition true?
No

Yes

Do statement

Usually, the initialization part of a for statement assigns a value to some variable, and the
update changes the value of that variable with an assignment statement or with an increment
or decrement operation. The value of the variable is tested in the continuation condition, and
the loop ends when this condition evaluates to false. A variable used in this way is called a
loop control variable. In the example given above, the loop control variable was years.
Certainly, the most common type of for loop is the counting loop, where a loop control
variable takes on all integer values between some minimum and some maximum value. A
counting loop has the form
CHAPTER 3. CONTROL 94

for ( hvariable i = hmin i; hvariable i <= hmax i; hvariable i++ ) {


hstatements i
}
where hmini and hmax i are integer-valued expressions (usually constants). The hvariablei takes
on the values hmini, hmini+1, hmini+2, . . . , hmax i. The value of the loop control variable is
often used in the body of the loop. The for loop at the beginning of this section is a counting
loop in which the loop control variable, years, takes on the values 1, 2, 3, 4, 5. Here is an even
simpler example, in which the numbers 1, 2, . . . , 10 are displayed on standard output:
for ( N = 1 ; N <= 10 ; N++ )
System.out.println( N );
For various reasons, Java programmers like to start counting at 0 instead of 1, and they tend
to use a “<” in the condition, rather than a “<=”. The following variation of the above loop
prints out the ten numbers 0, 1, 2, . . . , 9:
for ( N = 0 ; N < 10 ; N++ )
System.out.println( N );
Using < instead of <= in the test, or vice versa, is a common source of off-by-one errors in
programs. You should always stop and think, Do I want the final value to be processed or not?
It’s easy to count down from 10 to 1 instead of counting up. Just start with 10, decrement
the loop control variable instead of incrementing it, and continue as long as the variable is
greater than or equal to one.
for ( N = 10 ; N >= 1 ; N-- )
System.out.println( N );
Now, in fact, the official syntax of a for statement actually allows both the initialization
part and the update part to consist of several expressions, separated by commas. So we can
even count up from 1 to 10 and count down from 10 to 1 at the same time!
for ( i=1, j=10; i <= 10; i++, j-- ) {
System.out.printf("%5d", i); // Output i in a 5-character wide column.
System.out.printf("%5d", j); // Output j in a 5-character column.
System.out.println(); // and end the line.
}
As a final introductory example, let’s say that we want to use a for loop that prints out
just the even numbers between 2 and 20, that is: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20. There are
several ways to do this. Just to show how even a very simple problem can be solved in many
ways, here are four different solutions (three of which would get full credit):
(1) // There are 10 numbers to print.
// Use a for loop to count 1, 2,
// ..., 10. The numbers we want
// to print are 2*1, 2*2, ... 2*10.
for (N = 1; N <= 10; N++) {
System.out.println( 2*N );
}

(2) // Use a for loop that counts


// 2, 4, ..., 20 directly by
// adding 2 to N each time through
CHAPTER 3. CONTROL 95

// the loop.
for (N = 2; N <= 20; N = N + 2) {
System.out.println( N );
}

(3) // Count off all the numbers


// 2, 3, 4, ..., 19, 20, but
// only print out the numbers
// that are even.
for (N = 2; N <= 20; N++) {
if ( N % 2 == 0 ) // is N even?
System.out.println( N );
}

(4) // Irritate the professor with


// a solution that follows the
// letter of this silly assignment
// while making fun of it.
for (N = 1; N <= 1; N++) {
System.out.println("2 4 6 8 10 12 14 16 18 20");
}

Perhaps it is worth stressing one more time that a for statement, like any statement except
for a variable declaration, never occurs on its own in a real program. A statement must be
inside the main routine of a program or inside some other subroutine. And that subroutine
must be defined inside a class. I should also remind you that every variable must be declared
before it can be used, and that includes the loop control variable in a for statement. In all
the examples that you have seen so far in this section, the loop control variables should be
declared to be of type int. It is not required that a loop control variable be an integer. Here,
for example, is a for loop in which the variable, ch, is of type char, using the fact that the ++
operator can be applied to characters as well as to numbers:
// Print out the alphabet on one line of output.
char ch; // The loop control variable;
// one of the letters to be printed.
for ( ch = ’A’; ch <= ’Z’; ch++ )
System.out.print(ch);
System.out.println();

3.4.2 Example: Counting Divisors


Let’s look at a less trivial problem that can be solved with a for loop. If N and D are positive
integers, we say that D is a divisor of N if the remainder when D is divided into N is zero.
(Equivalently, we could say that N is an even multiple of D.) In terms of Java programming, D
is a divisor of N if N % D is zero.
Let’s write a program that inputs a positive integer, N, from the user and computes how
many different divisors N has. The numbers that could possibly be divisors of N are 1, 2, . . . , N.
To compute the number of divisors of N, we can just test each possible divisor of N and count
CHAPTER 3. CONTROL 96

the ones that actually do divide N evenly. (This is a correct solution, but is certainly not the
most efficient way to perform this task.) In pseudocode, the algorithm takes the form
Get a positive integer, N, from the user
Let divisorCount = 0
for each number, testDivisor, in the range from 1 to N:
if testDivisor is a divisor of N:
Count it by adding 1 to divisorCount
Output the count
This algorithm displays a common programming pattern that is used when some, but not all,
of a sequence of items are to be processed. The general pattern is
for each item in the sequence:
if the item passes the test:
process it
The for loop in our divisor-counting algorithm can be translated into Java code as
for (testDivisor = 1; testDivisor <= N; testDivisor++) {
if ( N % testDivisor == 0 )
divisorCount++;
}
On a modern computer, this loop can be executed very quickly. It is not impossible to run
it even for the largest legal int value, 2147483647. (If you wanted to run it for even larger
values, you could use variables of type long rather than int.) However, it does take a significant
amount of time for very large numbers. So when I implemented this algorithm, I decided to
output a dot every time the computer has tested ten million possible divisors. In the improved
version of the program, there are two types of counting going on. We have to count the number
of divisors and we also have to count the number of possible divisors that have been tested.
So the program needs two counters. When the second counter reaches 10000000, the program
outputs a ’.’ and resets the counter to zero so that we can start counting the next group of ten
million. Reverting to pseudocode, the algorithm now looks like
Get a positive integer, N, from the user
Let divisorCount = 0 // Number of divisors found.
Let numberTested = 0 // Number of possible divisors tested
// since the last period was output.
for each number, testDivisor, in the range from 1 to N:
if testDivisor is a divisor of N:
Count it by adding 1 to divisorCount
Add 1 to numberTested
if numberTested is 10000000:
print out a ’.’
Reset numberTested to 0
Output the count
Finally, we can translate the algorithm into a complete Java program:
import textio.TextIO;
/**
* This program reads a positive integer from the user.
* It counts how many divisors that number has, and
* then it prints the result.
*/
CHAPTER 3. CONTROL 97

public class CountDivisors {


public static void main(String[] args) {
int N; // A positive integer entered by the user.
// Divisors of this number will be counted.
int testDivisor; // A number between 1 and N that is a
// possible divisor of N.
int divisorCount; // Number of divisors of N that have been found.
int numberTested; // Used to count how many possible divisors
// of N have been tested. When the number
// reaches 10000000, a period is output and
// the value of numberTested is reset to zero.
/* Get a positive integer from the user. */
while (true) {
System.out.print("Enter a positive integer: ");
N = TextIO.getlnInt();
if (N > 0)
break;
System.out.println("That number is not positive. Please try again.");
}
/* Count the divisors, printing a "." after every 10000000 tests. */
divisorCount = 0;
numberTested = 0;
for (testDivisor = 1; testDivisor <= N; testDivisor++) {
if ( N % testDivisor == 0 )
divisorCount++;
numberTested++;
if (numberTested == 10000000) {
System.out.print(’.’);
numberTested = 0;
}
}
/* Display the result. */
System.out.println();
System.out.println("The number of divisors of " + N
+ " is " + divisorCount);
} // end main()
} // end class CountDivisors

3.4.3 Nested for Loops


Control structures in Java are statements that contain other, simpler statements. In particular,
control structures can contain control structures. You’ve already seen several examples of
if statements inside loops, and one example of a while loop inside another while, but any
CHAPTER 3. CONTROL 98

combination of one control structure inside another is possible. We say that one structure is
nested inside another. You can even have multiple levels of nesting, such as a while loop
inside an if statement inside another while loop. The syntax of Java does not set a limit on
the number of levels of nesting. As a practical matter, though, it’s difficult to understand a
program that has more than a few levels of nesting.
Nested for loops arise naturally in many algorithms, and it is important to understand how
they work. Let’s look at a couple of examples. First, consider the problem of printing out a
multiplication table like this one:
1 2 3 4 5 6 7 8 9 10 11 12
2 4 6 8 10 12 14 16 18 20 22 24
3 6 9 12 15 18 21 24 27 30 33 36
4 8 12 16 20 24 28 32 36 40 44 48
5 10 15 20 25 30 35 40 45 50 55 60
6 12 18 24 30 36 42 48 54 60 66 72
7 14 21 28 35 42 49 56 63 70 77 84
8 16 24 32 40 48 56 64 72 80 88 96
9 18 27 36 45 54 63 72 81 90 99 108
10 20 30 40 50 60 70 80 90 100 110 120
11 22 33 44 55 66 77 88 99 110 121 132
12 24 36 48 60 72 84 96 108 120 132 144
The data in the table are arranged into 12 rows and 12 columns. The process of printing them
out can be expressed in a pseudocode algorithm as
for each rowNumber = 1, 2, 3, ..., 12:
Print the first twelve multiples of rowNumber on one line
Output a carriage return
The first step in the for loop can itself be expressed as a for loop. We can expand “Print the
first twelve multiples of rowNumber on one line” as:
for N = 1, 2, 3, ..., 12:
Print N * rowNumber
so a refined algorithm for printing the table has one for loop nested inside another:
for each rowNumber = 1, 2, 3, ..., 12:
for N = 1, 2, 3, ..., 12:
Print N * rowNumber
Output a carriage return
We want to print the output in neat columns, with each output number taking up four spaces.
This can be done using formatted output with format specifier %4d. Assuming that rowNumber
and N have been declared to be variables of type int, the algorithm can be expressed in Java as
for ( rowNumber = 1; rowNumber <= 12; rowNumber++ ) {
for ( N = 1; N <= 12; N++ ) {
// print in 4-character columns
System.out.printf( "%4d", N * rowNumber ); // No carriage return !
}
System.out.println(); // Add a carriage return at end of the line.
}
CHAPTER 3. CONTROL 99

This section has been weighed down with lots of examples of numerical processing. For our
next example, let’s do some text processing. Consider the problem of finding which of the 26
letters of the alphabet occur in a given string. For example, the letters that occur in “Hello
World” are D, E, H, L, O, R, and W. More specifically, we will write a program that will list all
the letters contained in a string and will also count the number of different letters. The string
will be input by the user. Let’s start with a pseudocode algorithm for the program.
Ask the user to input a string
Read the response into a variable, str
Let count = 0 (for counting the number of different letters)
for each letter of the alphabet:
if the letter occurs in str:
Print the letter
Add 1 to count
Output the count
Since we want to process the entire line of text that is entered by the user, we’ll use
TextIO.getln() to read it. The line of the algorithm that reads “for each letter of the al-
phabet” can be expressed as “for (letter=’A’; letter<=’Z’; letter++)”. But the if
statement inside the for loop needs still more thought before we can write the program. How
do we check whether the given letter, letter, occurs in str? One idea is to look at each
character in the string in turn, and check whether that character is equal to letter. We can
get the i-th character of str with the function call str.charAt(i), where i ranges from 0 to
str.length() - 1.
One more difficulty: A letter such as ’A’ can occur in str in either upper or lower case, ’A’
or ’a’. We have to check for both of these. But we can avoid this difficulty by converting str
to upper case before processing it. Then, we only have to check for the upper case letter. We
can now flesh out the algorithm fully:
Ask the user to input a string
Read the response into a variable, str
Convert str to upper case
Let count = 0
for letter = ’A’, ’B’, ..., ’Z’:
for i = 0, 1, ..., str.length()-1:
if letter == str.charAt(i):
Print letter
Add 1 to count
break // jump out of the loop, to avoid counting letter twice
Output the count
Note the use of break in the nested for loop. It is required to avoid printing or counting a given
letter more than once (in the case where it occurs more than once in the string). The break
statement breaks out of the inner for loop, but not the outer for loop. Upon executing the
break, the computer continues the outer loop with the next value of letter. You should try
to figure out exactly what count would be at the end of this program, if the break statement
were omitted. Here is the complete program:
import textio.TextIO;
/**
* This program reads a line of text entered by the user.
* It prints a list of the letters that occur in the text,
* and it reports how many different letters were found.
CHAPTER 3. CONTROL 100

*/
public class ListLetters {
public static void main(String[] args) {
String str; // Line of text entered by the user.
int count; // Number of different letters found in str.
char letter; // A letter of the alphabet.
System.out.println("Please type in a line of text.");
str = TextIO.getln();
str = str.toUpperCase();
count = 0;
System.out.println("Your input contains the following letters:");
System.out.println();
System.out.print(" ");
for ( letter = ’A’; letter <= ’Z’; letter++ ) {
int i; // Position of a character in str.
for ( i = 0; i < str.length(); i++ ) {
if ( letter == str.charAt(i) ) {
System.out.print(letter);
System.out.print(’ ’);
count++;
break;
}
}
}
System.out.println();
System.out.println();
System.out.println("There were " + count + " different letters.");
} // end main()
} // end class ListLetters

In fact, there is actually an easier way to determine whether a given letter occurs in a string,
str. The built-in function str.indexOf(letter) will return -1 if letter does not occur in
the string. It returns a number greater than or equal to zero if it does occur. So, we could
check whether letter occurs in str simply by checking “if (str.indexOf(letter) >= 0)”.
If we used this technique in the above program, we wouldn’t need a nested for loop. This gives
you a preview of how subroutines can be used to deal with complexity.

3.5 The if Statement


The first of the two branching statements in Java is the if statement, which you
have already seen in Section 3.1. It takes the form
if (hboolean-expression i)
hstatement-1 i
else
hstatement-2 i
CHAPTER 3. CONTROL 101

As usual, the statements inside an if statement can be blocks. The if statement represents
a two-way branch. The else part of an if statement—consisting of the word “else” and the
statement that follows it—can be omitted.

3.5.1 The Dangling else Problem


Now, an if statement is, in particular, a statement. This means that either hstatement-1 i
or hstatement-2 i in the above if statement can itself be an if statement. A problem arises,
however, if hstatement-1 i is an if statement that has no else part. This special case is
effectively forbidden by the syntax of Java. Suppose, for example, that you type
if ( x > 0 )
if (y > 0)
System.out.println("First case");
else
System.out.println("Second case");
Now, remember that the way you’ve indented this doesn’t mean anything at all to the computer.
You might think that the else part is the second half of your “if (x > 0)” statement, but
the rule that the computer follows attaches the else to “if (y > 0)”, which is closer. That
is, the computer reads your statement as if it were formatted:
if ( x > 0 )
if (y > 0)
System.out.println("First case");
else
System.out.println("Second case");
You can force the computer to use the other interpretation by enclosing the nested if in a
block:
if ( x > 0 ) {
if (y > 0)
System.out.println("First case");
}
else
System.out.println("Second case");
These two if statements have different meanings: In the case when x <= 0, the first statement
doesn’t print anything, but the second statement prints “Second case”.

3.5.2 Multiway Branching


Much more interesting than this technicality is the case where hstatement-2 i, the else part
of the if statement, is itself an if statement. The statement would look like this (perhaps
without the final else part):
if (hboolean-expression-1 i)
hstatement-1 i
else
if (hboolean-expression-2 i)
hstatement-2 i
else
hstatement-3 i
CHAPTER 3. CONTROL 102

However, since the computer doesn’t care how a program is laid out on the page, this is almost
always written in the format:
if (hboolean-expression-1 i)
hstatement-1 i
else if (hboolean-expression-2 i)
hstatement-2 i
else
hstatement-3 i
You should think of this as a single statement representing a three-way branch. When the
computer executes this, one and only one of the three statements—hstatement-1 i, hstatement-
2 i, or hstatement-3 i—will be executed. The computer starts by evaluating hboolean-expression-
1 i. If it is true, the computer executes hstatement-1 i and then jumps all the way to the end of
the outer if statement, skipping the other two hstatementsi. If hboolean-expression-1 i is false,
the computer skips hstatement-1 i and executes the second, nested if statement. To do this,
it tests the value of hboolean-expression-2 i and uses it to decide between hstatement-2 i and
hstatement-3 i.
Here is an example that will print out one of three different messages, depending on the
value of a variable named temperature:
if (temperature < 50)
System.out.println("It’s cold.");
else if (temperature < 80)
System.out.println("It’s nice.");
else
System.out.println("It’s hot.");
If temperature is, say, 42, the first test is true. The computer prints out the message “It’s
cold”, and skips the rest—without even evaluating the second condition. For a temperature of
75, the first test is false, so the computer goes on to the second test. This test is true, so
the computer prints “It’s nice” and skips the rest. If the temperature is 173, both of the tests
evaluate to false, so the computer says “It’s hot” (unless its circuits have been fried by the
heat, that is).
You can go on stringing together “else-if’s” to make multiway branches with any number
of cases:
if (htest-1 i)
hstatement-1 i
else if (htest-2 i)
hstatement-2 i
else if (htest-3 i)
hstatement-3 i
.
. // (more cases)
.
else if (htest-N i)
hstatement-N i
else
hstatement-(N+1) i
The computer evaluates the tests, which are boolean expressions, one after the other until it
comes to one that is true. It executes the associated statement and skips the rest. If none
of the boolean expressions evaluate to true, then the statement in the else part is executed.
CHAPTER 3. CONTROL 103

This statement is called a multiway branch because one and only one of the statements will be
executed. The final else part can be omitted. In that case, if all the boolean expressions are
false, none of the statements are executed. Of course, each of the statements can be a block,
consisting of a number of statements enclosed between { and }. Admittedly, there is lot of
syntax here; as you study and practice, you’ll become comfortable with it. It might be useful
to look at a flow control diagram for the general “if..else if” statement shown above:

Yes No

Yes No
Do statemen

Yes
Do statement-2

Yes No

Do statement-N Do statement-(N+1)

3.5.3 If Statement Examples


As an example of using if statements, let’s suppose that x, y, and z are variables of type int,
and that each variable has already been assigned a value. Consider the problem of printing out
the values of the three variables in increasing order. For example, if the values are 42, 17, and
20, then the output should be in the order 17, 20, 42.
One way to approach this is to ask, where does x belong in the list? It comes first if it’s
less than both y and z. It comes last if it’s greater than both y and z. Otherwise, it comes in
the middle. We can express this with a 3-way if statement, but we still have to worry about
the order in which y and z should be printed. In pseudocode,
if (x < y && x < z) {
output x, followed by y and z in their correct order
}
else if (x > y && x > z) {
output y and z in their correct order, followed by x
}
else {
output x in between y and z in their correct order
}
CHAPTER 3. CONTROL 104

Determining the relative order of y and z requires another if statement, so this becomes
if (x < y && x < z) { // x comes first
if (y < z)
System.out.println( x + " " + y + " " + z );
else
System.out.println( x + " " + z + " " + y );
}
else if (x > y && x > z) { // x comes last
if (y < z)
System.out.println( y + " " + z + " " + x );
else
System.out.println( z + " " + y + " " + x );
}
else { // x in the middle
if (y < z)
System.out.println( y + " " + x + " " + z);
else
System.out.println( z + " " + x + " " + y);
}
You might check that this code will work correctly even if some of the values are the same. If
the values of two variables are the same, it doesn’t matter which order you print them in.
Note, by the way, that even though you can say in English “if x is less than y and z,” you
can’t say in Java “if (x < y && z)”. The && operator can only be used between boolean
values, so you have to make separate tests, x<y and x<z, and then combine the two tests with
&&.
There is an alternative approach to this problem that begins by asking, “which order should
x and y be printed in?” Once that’s known, you only have to decide where to stick in z. This
line of thought leads to different Java code:
if ( x < y ) { // x comes before y
if ( z < x ) // z comes first
System.out.println( z + " " + x + " " + y);
else if ( z > y ) // z comes last
System.out.println( x + " " + y + " " + z);
else // z is in the middle
System.out.println( x + " " + z + " " + y);
}
else { // y comes before x
if ( z < y ) // z comes first
System.out.println( z + " " + y + " " + x);
else if ( z > x ) // z comes last
System.out.println( y + " " + x + " " + z);
else // z is in the middle
System.out.println( y + " " + z + " " + x);
}
Once again, we see how the same problem can be solved in many different ways. The two
approaches to this problem have not exhausted all the possibilities. For example, you might
start by testing whether x is greater than y. If so, you could swap their values. Once you’ve
done that, you know that x should be printed before y.
∗ ∗ ∗
CHAPTER 3. CONTROL 105

Finally, let’s write a complete program that uses an if statement in an interesting way. I
want a program that will convert measurements of length from one unit of measurement to
another, such as miles to yards or inches to feet. So far, the problem is extremely under-
specified. Let’s say that the program will only deal with measurements in inches, feet, yards,
and miles. It would be easy to extend it later to deal with other units. The user will type in
a measurement in one of these units, such as “17 feet” or “2.73 miles”. The output will show
the length in terms of each of the four units of measure. (This is easier than asking the user
which units to use in the output.) An outline of the process is
Read the user’s input measurement and units of measure
Express the measurement in inches, feet, yards, and miles
Display the four results
The program can read both parts of the user’s input from the same line by using
TextIO.getDouble() to read the numerical measurement and TextIO.getlnWord() to read
the unit of measure. The conversion into different units of measure can be simplified by first
converting the user’s input into inches. From there, the number of inches can easily be con-
verted into feet, yards, and miles. Before converting into inches, we have to test the input to
determine which unit of measure the user has specified:
Let measurement = TextIO.getDouble()
Let units = TextIO.getlnWord()
if the units are inches
Let inches = measurement
else if the units are feet
Let inches = measurement * 12 // 12 inches per foot
else if the units are yards
Let inches = measurement * 36 // 36 inches per yard
else if the units are miles
Let inches = measurement * 12 * 5280 // 5280 feet per mile
else
The units are illegal!
Print an error message and stop processing
Let feet = inches / 12.0
Let yards = inches / 36.0
Let miles = inches / (12.0 * 5280.0)
Display the results
Since units is a String, we can use units.equals("inches") to check whether the spec-
ified unit of measure is “inches”. However, it would be nice to allow the units to be spec-
ified as “inch” or abbreviated to “in”. To allow these three possibilities, we can check if
(units.equals("inches") || units.equals("inch") || units.equals("in")). It would
also be nice to allow upper case letters, as in “Inches” or “IN”. We can do this by converting
units to lower case before testing it or by substituting the function units.equalsIgnoreCase
for units.equals.
In my final program, I decided to make things more interesting by allowing the user to
repeat the process of entering a measurement and seeing the results of the conversion for each
measurement. The program will end only when the user inputs 0. To program that, I just had
to wrap the above algorithm inside a while loop, and make sure that the loop ends when the
user inputs a 0. Here’s the complete program:
import textio.TextIO;
CHAPTER 3. CONTROL 106

/**
* This program will convert measurements expressed in inches,
* feet, yards, or miles into each of the possible units of
* measure. The measurement is input by the user, followed by
* the unit of measure. For example: "17 feet", "1 inch", or
* "2.73 mi". Abbreviations in, ft, yd, and mi are accepted.
* The program will continue to read and convert measurements
* until the user enters an input of 0.
*/
public class LengthConverter {
public static void main(String[] args) {
double measurement; // Numerical measurement, input by user.
String units; // The unit of measure for the input, also
// specified by the user.
double inches, feet, yards, miles; // Measurement expressed in
// each possible unit of
// measure.
System.out.println("Enter measurements in inches, feet, yards, or miles.");
System.out.println("For example: 1 inch 17 feet 2.73 miles");
System.out.println("You can use abbreviations: in ft yd mi");
System.out.println("I will convert your input into the other units");
System.out.println("of measure.");
System.out.println();
while (true) {
/* Get the user’s input, and convert units to lower case. */
System.out.print("Enter your measurement, or 0 to end: ");
measurement = TextIO.getDouble();
if (measurement == 0)
break; // Terminate the while loop.
units = TextIO.getlnWord();
units = units.toLowerCase(); // convert units to lower case
/* Convert the input measurement to inches. */
if (units.equals("inch") || units.equals("inches")
|| units.equals("in")) {
inches = measurement;
}
else if (units.equals("foot") || units.equals("feet")
|| units.equals("ft")) {
inches = measurement * 12;
}
else if (units.equals("yard") || units.equals("yards")
|| units.equals("yd")) {
inches = measurement * 36;
}
else if (units.equals("mile") || units.equals("miles")
|| units.equals("mi")) {
inches = measurement * 12 * 5280;
}
CHAPTER 3. CONTROL 107

else {
System.out.println("Sorry, but I don’t understand \""
+ units + "\".");
continue; // back to start of while loop
}
/* Convert measurement in inches to feet, yards, and miles. */
feet = inches / 12;
yards = inches / 36;
miles = inches / (12*5280);
/* Output measurement in terms of each unit of measure. */
System.out.println();
System.out.println("That’s equivalent to:");
System.out.printf("%14.5g inches%n", inches);
System.out.printf("%14.5g feet%n", feet);
System.out.printf("%14.5g yards%n", yards);
System.out.printf("%14.5g miles%n", miles);
System.out.println();
} // end while
System.out.println();
System.out.println("OK! Bye for now.");
} // end main()
} // end class LengthConverter

(Note that this program uses formatted output with the “g” format specifier. In this pro-
gram, we have no control over how large or how small the numbers might be. It could easily
make sense for the user to enter very large or very small measurements. The “g” format will
print a real number in exponential form if it is very large or very small, and in the usual decimal
form otherwise. Remember that in the format specification %14.5g, the 5 is the total number
of significant digits that are to be printed, so we will always get the same number of significant
digits in the output, no matter what the size of the number. If we had used an “f” format
specifier such as %14.5f, the output would be in decimal form with 5 digits after the decimal
point. This would print the number 0.000000000745482 as 0.00000, with no significant digits
at all! With the “g” format specifier, the output would be 7.4549e-10.)

3.5.4 The Empty Statement


As a final note in this section, I will mention one more type of statement in Java: the empty
statement. This is a statement that consists simply of a semicolon and which tells the computer
to do nothing. The existence of the empty statement makes the following legal, even though
you would not ordinarily see a semicolon after a } :
if (x < 0) {
x = -x;
};
The semicolon is legal after the }, but the computer considers it to be an empty statement,
not part of the if statement. Occasionally, you might find yourself using the empty statement
when what you mean is, in fact, “do nothing.” For example, the rather contrived if statement
CHAPTER 3. CONTROL 108

if ( done )
; // Empty statement
else
System.out.println( "Not done yet.");
does nothing when the boolean variable done is true, and prints out “Not done yet” when
it is false. You can’t just leave out the semicolon in this example, since Java syntax requires
an actual statement between the if and the else. I prefer, though, to use an empty block,
consisting of { and } with nothing between, for such cases.
Occasionally, stray empty statements can cause annoying, hard-to-find errors in a program.
For example, the following program segment prints out “Hello” just once, not ten times:
for (i = 0; i < 10; i++);
System.out.println("Hello");
Why? Because the “;” at the end of the first line is a statement, and it is this empty statement
that is executed ten times. The System.out.println statement is not really inside the for
statement at all, so it is executed just once, after the for loop has completed. The for loop
just does nothing, ten times!

3.6 The switch Statement


The second branching statement in Java is the switch statement, which is introduced
in this section. The switch statement is used far less often than the if statement, but it is
sometimes useful for expressing a certain type of multiway branch.

3.6.1 The Basic switch Statement


A switch statement allows you to test the value of an expression and, depending on that value,
to jump directly to some location within the switch statement. Only expressions of certain
types can be used. The value of the expression can be one of the primitive integer types int,
short, or byte. It can be the primitive char type. It can be String. Or it can be an enum type
(see Subsection 2.3.4 for an introduction to enums). In particular, note that the expression
cannot be a double or float value.
The positions within a switch statement to which it can jump are marked with case labels
that take the form: “case hconstanti:”. The hconstanti here is a literal of the same type as
the expression in the switch. A case label marks the position the computer jumps to when
the expression evaluates to the given hconstanti value. You can also use the label “default:”
in a switch statement; this provides a default jump point that is used when the value of the
expression is not listed in any case label.
A switch statement, as it is most often used, has the form:
switch (hexpression i) {
case hconstant-1 i:
hstatements-1 i
break;
case hconstant-2 i:
hstatements-2 i
break;
.
. // (more cases)
CHAPTER 3. CONTROL 109

.
case hconstant-N i:
hstatements-N i
break;
default: // optional default case
hstatements-(N+1) i
} // end of switch statement
This has exactly the same effect as the following multiway if statement, but the switch
statement can be more efficient because the computer can evaluate one expression and jump
directly to the correct case, whereas in the if statement, the computer must evaluate up to N
expressions before it knows which set of statements to execute:
if (hexpression i == hconstant-1 i) { // but use .equals for String!!
hstatements-1 i
}
else if (hexpression i == hconstant-2 i) {
hstatements-2 i
}
.
.
.
else if (hexpression i == hconstant-N i) {
hstatements-N i
}
else {
hstatements-(N+1) i
}
The break statements in the switch are not actually required by the syntax of the switch
statement. The effect of a break is to make the computer jump past the end of the switch state-
ment, skipping over all the remaining cases. If you leave out the break statement, the computer
will just forge ahead after completing one case and will execute the statements associated with
the next case label. This is rarely what you want, but it is legal. (I will note here—although
you won’t understand it until you get to the next chapter—that inside a subroutine, the break
statement is sometimes replaced by a return statement, which terminates the subroutine as
well as the switch.)
Note that you can leave out one of the groups of statements entirely (including the break).
You then have two case labels in a row, containing two different constants. This just means
that the computer will jump to the same place and perform the same action for each of the two
constants.
Here is an example of a switch statement. This is not a useful example, but it should be
easy for you to follow. Note, by the way, that the constants in the case labels don’t have to be
in any particular order, but they must all be different:
switch ( N ) { // (Assume N is an integer variable.)
case 1:
System.out.println("The number is 1.");
break;
case 2:
case 4:
case 8:
System.out.println("The number is 2, 4, or 8.");
System.out.println("(That’s a power of 2!)");
CHAPTER 3. CONTROL 110

break;
case 3:
case 6:
case 9:
System.out.println("The number is 3, 6, or 9.");
System.out.println("(That’s a multiple of 3!)");
break;
case 5:
System.out.println("The number is 5.");
break;
default:
System.out.println("The number is 7 or is outside the range 1 to 9.");
}
The switch statement is pretty primitive as control structures go, and it’s easy to make mis-
takes when you use it. Java takes all its control structures directly from the older programming
languages C and C++. The switch statement is certainly one place where the designers of Java
should have introduced some improvements.

3.6.2 Menus and switch Statements


One application of switch statements is in processing menus. A menu is a list of options.
The user selects one of the options. The computer has to respond to each possible choice in a
different way. If the options are numbered 1, 2, . . . , then the number of the chosen option can
be used in a switch statement to select the proper response.
In a command-line program, the menu can be presented as a numbered list of options, and
the user can choose an option by typing in its number. Here is an example that could be used
in a variation of the LengthConverter example from the previous section:
int optionNumber; // Option number from menu, selected by user.
double measurement; // A numerical measurement, input by the user.
// The unit of measurement depends on which
// option the user has selected.
double inches; // The same measurement, converted into inches.
/* Display menu and get user’s selected option number. */
System.out.println("What unit of measurement does your input use?");
System.out.println();
System.out.println(" 1. inches");
System.out.println(" 2. feet");
System.out.println(" 3. yards");
System.out.println(" 4. miles");
System.out.println();
System.out.println("Enter the number of your choice: ");
optionNumber = TextIO.getlnInt();
/* Read user’s measurement and convert to inches. */
switch ( optionNumber ) {
case 1:
System.out.println("Enter the number of inches: ");
measurement = TextIO.getlnDouble();
inches = measurement;
break;
CHAPTER 3. CONTROL 111

case 2:
System.out.println("Enter the number of feet: ");
measurement = TextIO.getlnDouble();
inches = measurement * 12;
break;
case 3:
System.out.println("Enter the number of yards: ");
measurement = TextIO.getlnDouble();
inches = measurement * 36;
break;
case 4:
System.out.println("Enter the number of miles: ");
measurement = TextIO.getlnDouble();
inches = measurement * 12 * 5280;
break;
default:
System.out.println("Error! Illegal option number! I quit!");
System.exit(1);
} // end switch
/* Now go on to convert inches to feet, yards, and miles... */
This example could instead be written using a String in the switch statement:
String units; // Unit of measurement, entered by user.
double measurement; // A numerical measurement, input by the user.
double inches; // The same measurement, converted into inches.
/* Read the user’s unit of measurement. */
System.out.println("What unit of measurement does your input use?");
System.out.print("Legal responses: inches, feet, yards, or miles : ");
units = TextIO.getln().toLowerCase();
/* Read user’s measurement and convert to inches. */
System.out.print("Enter the number of " + units + ": ");
measurement = TextIO.getlnDouble();
switch ( units ) {
case "inches":
inches = measurement;
break;
case "feet":
inches = measurement * 12;
break;
case "yards":
inches = measurement * 36;
break;
case "miles":
inches = measurement * 12 * 5280;
break;
default:
System.out.println("Wait a minute! Illegal unit of measure! I quit!");
System.exit(1);
} // end switch
CHAPTER 3. CONTROL 112

3.6.3 Enums in switch Statements


The type of the expression in a switch can be an enumerated type. In that case, the constants
in the case labels must be values from the enumerated type. For example, suppose that the
type of the expression is the enumerated type Season defined by
enum Season { SPRING, SUMMER, FALL, WINTER }
and that the expression in a switch statement is an expression of type Season. The constants
in the case label must be chosen from among the values Season.SPRING, Season.SUMMER,
Season.FALL, or Season.WINTER. However, there is a quirk in the syntax: when an enum
constant is used in a case label, only the simple name, such as “SPRING” is used, not the full
name, such as “Season.SPRING”. Of course, the computer already knows that the value in the
case label must belong to the enumerated type, since it can tell that from the type of expression
used, so there is really no need to specify the type name in the constant. For example, assuming
that currentSeason is a variable of type Season, then we could have the switch statement:
switch ( currentSeason ) {
case WINTER: // ( NOT Season.WINTER ! )
System.out.println("December, January, February");
break;
case SPRING:
System.out.println("March, April, May");
break;
case SUMMER:
System.out.println("June, July, August");
break;
case FALL:
System.out.println("September, October, November");
break;
}

3.6.4 Definite Assignment and switch Statements


As a somewhat more realistic example, the following switch statement makes a ran-
dom choice among three possible alternatives. Recall that the value of the expression
(int)(3*Math.random()) is one of the integers 0, 1, or 2, selected at random with equal
probability, so the switch statement below will assign one of the values "Rock", "Paper",
"Scissors" to computerMove, with probability 1/3 for each case:
switch ( (int)(3*Math.random()) ) {
case 0:
computerMove = "Rock";
break;
case 1:
computerMove = "Paper";
break;
case 2:
computerMove = "Scissors";
break;
}
Now, this switch statement is perfectly OK, but suppose that we use it in the following code
segment:
CHAPTER 3. CONTROL 113

String computerMove;
switch ( (int)(3*Math.random()) ) {
case 0:
computerMove = "Rock";
break;
case 1:
computerMove = "Paper";
break;
case 2:
computerMove = "Scissors";
break;
}
System.out.println("The computer’s move is " + computerMove); // ERROR!
Now there is a subtle error on the last line! The problem here is due to definite assignment,
the idea that the Java compiler must be able to determine that a variable has definitely been
assigned a value before its value is used. Definite assignment was introduced in Subsection 3.1.4.
In this example, it’s true that the three cases in the switch cover all the possibilities, but the
compiler is not smart enough to figure that out; it just sees that there is an integer-valued
expression in the switch but not all possible integer values are covered by the given cases.
A simple solution is to replace the final case in the switch statement with default. With
a default case, all possible values of the expression in the switch are certainly covered, and
the compiler knows that computerMove is definitely assigned a value:
String computerMove;
switch ( (int)(3*Math.random()) ) {
case 0:
computerMove = "Rock";
break;
case 1:
computerMove = "Paper";
break;
default:
computerMove = "Scissors";
break;
}
System.out.println("The computer’s move is " + computerMove); // OK!

3.6.5 A New switch Statement Syntax


A new version of the switch statement has been added to the Java language in Java 14.
The new version uses -> in place of a colon after a case, and the code in a case is a single
statement, possibly a block statement consisting of several statements enclosed in braces. No
break statement is required, although one can be used to end a case early. This avoids the
common error of having control accidently fall through from one case to the next because of an
omitted break. Furthermore, instead of allowing just one value per case label, a case can take
several values separated by commas. Using the new syntax, the first example in this section
can be written as follows:
switch ( N ) { // (Assume N is an integer variable.)
case 1 -> System.out.println("The number is 1.");
case 2, 4, 8 -> {
CHAPTER 3. CONTROL 114

System.out.println("The number is 2, 4, or 8.");


System.out.println("(That’s a power of 2!)");
}
case 3, 6, 9 -> {
System.out.println("The number is 3, 6, or 9.");
System.out.println("(That’s a multiple of 3!)");
}
case 5 -> System.out.println("The number is 5.");
default ->
System.out.println("The number is 7 or is outside the range 1 to 9.");
}
This seems to me to be a big improvement. But the original switch syntax is still available.
Along with the improved switch statement, a new “switch expression” has been introduced.
Like any expression, a switch expression computes and returns a single value. The syntax is
similar to a switch statement, but instead of a statement in each case, there is an expression.
For example,
String computerMove = switch ( (int)(3*Math.random()) ) {
case 1 -> "Rock";
case 2 -> "Paper";
default -> "Scissors";
};
A switch expression must always compute a value and therefore will almost always have
a default case. The expression in a case can be replaced by a block containing several
statements; the value for that case should then be specified by a yield statement (such as
“yield 42;”) rather than a return or break statement.

3.7 Introduction to Exceptions and try..catch


In addition to the control structures that determine the normal flow of control in a
program, Java has a way to deal with “exceptional” cases that throw the flow of control off its
normal track. When an error occurs during the execution of a program, the default behavior
is to terminate the program and to print an error message. However, Java makes it possible to
“catch” such errors and program a response different from simply letting the program crash.
This is done with the try..catch statement. In this section, we will take a preliminary and
incomplete look the try..catch statement, leaving out a lot of the rather complex syntax of
this statement. Error handling is a complex topic, which we will return to in Section 8.3, and
we will cover the full syntax of try..catch at that time.

3.7.1 Exceptions
The term exception is used to refer to the type of event that one might want to handle with
a try..catch. An exception is an exception to the normal flow of control in the program.
The term is used in preference to “error” because in some cases, an exception might not be
considered to be an error at all. You can sometimes think of an exception as just another way
to organize a program.
Exceptions in Java are represented as objects of type Exception. Actual exceptions are
usually defined by subclasses of Exception. Different subclasses represent different types of
CHAPTER 3. CONTROL 115

exceptions. We will look at only two types of exception in this section: NumberFormatException
and IllegalArgumentException.
A NumberFormatException can occur when an attempt is made to convert a string
into a number. Such conversions are done by the functions Integer.parseInt
and Double.parseDouble. (See Subsection 2.5.7.) Consider the function call
Integer.parseInt(str) where str is a variable of type String. If the value of str is the
string "42", then the function call will correctly convert the string into the int 42. However,
if the value of str is, say, "fred", the function call will fail because "fred" is not a legal
string representation of an int value. In this case, an exception of type NumberFormatException
occurs. If nothing is done to handle the exception, the program will crash.
An IllegalArgumentException can occur when an illegal value is passed as a parameter to a
subroutine. For example, if a subroutine requires that a parameter be greater than or equal to
zero, an IllegalArgumentException might occur when a negative value is passed to the subroutine.
How to respond to the illegal value is up to the person who wrote the subroutine, so we
can’t simply say that every illegal parameter value will result in an IllegalArgumentException.
However, it is a common response.

3.7.2 try..catch
When an exception occurs, we say that the exception is “thrown.” For example, we say that
Integer.parseInt(str) throws an exception of type NumberFormatException when the value
of str is illegal. When an exception is thrown, it is possible to “catch” the exception and
prevent it from crashing the program. This is done with a try..catch statement. In simplified
form, the syntax for a try..catch statement can be:
try {
hstatements-1 i
}
catch ( hexception-class-name i hvariable-name i ) {
hstatements-2 i
}
The hexception-class-namei could be NumberFormatException, IllegalArgumentException, or
some other exception class. When the computer executes this try..catch statement, it ex-
ecutes hstatements-1 i, the statements inside the try part. If no exception occurs during the
execution of hstatements-1 i, then the computer just skips over the catch part and proceeds
with the rest of the program. However, if an exception of type hexception-class-namei occurs
during the execution of hstatements-1 i, the computer immediately jumps from the point where
the exception occurs to the catch part and executes hstatements-2 i, skipping any remaining
statements in hstatements-1 i. Note that only one type of exception is caught; if some other
type of exception occurs during the execution of hstatements-1 i, it will crash the program as
usual.
During the execution of hstatements-2 i, the hvariable-namei represents the exception object,
so that you can, for example, print it out. The exception object contains information about
the cause of the exception. This includes an error message, which will be displayed if you print
out the exception object.
After the end of the catch part, the computer proceeds with the rest of the program; the
exception has been caught and handled and does not crash the program.
By the way, note that the braces, { and }, are part of the syntax of the try..catch
statement. They are required even if there is only one statement between the braces. This is
CHAPTER 3. CONTROL 116

different from the other statements we have seen, where the braces around a single statement
are optional.
As an example, suppose that str is a variable of type String whose value might or might
not represent a legal real number. Then we could say:
double x;
try {
x = Double.parseDouble(str);
System.out.println( "The number is " + x );
}
catch ( NumberFormatException e ) {
System.out.println( "Not a legal number." );
x = Double.NaN;
}
If an error is thrown by the call to Double.parseDouble(str), then the output statement in
the try part is skipped, and the statement in the catch part is executed. (In this example,
I set x to be the value Double.NaN when an exception occurs. Double.NaN is the special
“not-a-number” value for type double.)
It’s not always a good idea to catch exceptions and continue with the program. Often that
can just lead to an even bigger mess later on, and it might be better just to let the exception
crash the program at the point where it occurs. However, sometimes it’s possible to recover
from an error.
Suppose, for example, we want a program that will find the average of a sequence of real
numbers entered by the user, and we want the user to signal the end of the sequence by entering
a blank line. (This is similar to the sample program ComputeAverage.java from Section 3.3, but
in that program the user entered a zero to signal end-of-input.) If we use TextIO.getlnInt()
to read the user’s input, we will have no way of detecting the blank line, since that function
simply skips over blank lines. A solution is to use TextIO.getln() to read the user’s input.
This allows us to detect a blank input line, and we can convert non-blank inputs to numbers
using Double.parseDouble. And we can use try..catch to avoid crashing the program when
the user’s input is not a legal number. Here’s the program:
import textio.TextIO;
public class ComputeAverage2 {
public static void main(String[] args) {
String str; // The user’s input.
double number; // The input converted into a number.
double total; // The total of all numbers entered.
double avg; // The average of the numbers.
int count; // The number of numbers entered.
total = 0;
count = 0;
System.out.println("Enter your numbers, press return to end.");
while (true) {
System.out.print("? ");
str = TextIO.getln();
if (str.equals("")) {
break; // Exit the loop, since the input line was blank.
}
try {
CHAPTER 3. CONTROL 117

number = Double.parseDouble(str);
// If an error occurs, the next 2 lines are skipped!
total = total + number;
count = count + 1;
}
catch (NumberFormatException e) {
System.out.println("Not a legal number! Try again.");
}
}
avg = total/count;
System.out.printf("The average of %d numbers is %1.6g%n", count, avg);
}
}

3.7.3 Exceptions in TextIO


When TextIO reads a numeric value from the user, it makes sure that the user’s response is
legal, using a technique similar to the while loop and try..catch in the previous example.
However, TextIO can read data from other sources besides the user. (See Subsection 2.4.4.)
When it is reading from a file, there is no reasonable way for TextIO to recover from an illegal
value in the input, so it responds by throwing an exception. To keep things simple, TextIO only
throws exceptions of type IllegalArgumentException, no matter what type of error it encounters.
For example, an exception will occur if an attempt is made to read from a file after all the data
in the file has already been read. In TextIO, the exception is of type IllegalArgumentException. If
you have a better response to file errors than to let the program crash, you can use a try..catch
to catch exceptions of type IllegalArgumentException.
As an example, we will look at yet another number-averaging program. In this case, we will
read the numbers from a file. Assume that the file contains nothing but real numbers, and we
want a program that will read the numbers and find their sum and their average. Since it is
unknown how many numbers are in the file, there is the question of when to stop reading. One
approach is simply to try to keep reading indefinitely. When the end of the file is reached, an
exception occurs. This exception is not really an error—it’s just a way of detecting the end of
the data, so we can catch the exception and finish up the program. We can read the data in a
while (true) loop and break out of the loop when an exception occurs. This is an example of
the somewhat unusual technique of using an exception as part of the expected flow of control
in a program.
To read from the file, we need to know the file’s name. To make the program more general,
we can let the user enter the file name, instead of hard-coding a fixed file name in the program.
However, it is possible that the user will enter the name of a file that does not exist. When
we use TextIO.readfile to open a file that does not exist, an exception of type IllegalArgu-
mentException occurs. We can catch this exception and ask the user to enter a different file
name. Here is a complete program that uses all these ideas:
import textio.TextIO;
/**
* This program reads numbers from a file. It computes the sum and
* the average of the numbers that it reads. The file should contain
* nothing but numbers of type double; if this is not the case, the
* output will be the sum and average of however many numbers were
CHAPTER 3. CONTROL 118

* successfully read from the file. The name of the file will be
* input by the user.
*/
public class AverageNumbersFromFile {
public static void main(String[] args) {
while (true) {
String fileName; // The name of the file, to be input by the user.
System.out.print("Enter the name of the file: ");
fileName = TextIO.getln();
try {
TextIO.readFile( fileName ); // Try to open the file for input.
break; // If that succeeds, break out of the loop.
}
catch ( IllegalArgumentException e ) {
System.out.println("Can’t read from the file \"" + fileName + "\".");
System.out.println("Please try again.\n");
}
}
/* At this point, TextIO is reading from the file. */
double number; // A number read from the data file.
double sum; // The sum of all the numbers read so far.
int count; // The number of numbers that were read.
sum = 0;
count = 0;
try {
while (true) { // Loop ends when an exception occurs.
number = TextIO.getDouble();
count++; // This is skipped when the exception occurs
sum += number;
}
}
catch ( IllegalArgumentException e ) {
// We expect this to occur when the end-of-file is encountered.
// We don’t consider this to be an error, so there is nothing to do
// in this catch clause. Just proceed with the rest of the program.
}
// At this point, we’ve read the entire file.
System.out.println();
System.out.println("Number of data values read: " + count);
System.out.println("The sum of the data values: " + sum);
if ( count == 0 )
System.out.println("Can’t compute an average of 0 values.");
else
System.out.println("The average of the values: " + (sum/count));
}
}
CHAPTER 3. CONTROL 119

3.8 Introduction to Arrays


In previous sections of this chapter, we have already covered all of Java’s control struc-
tures. But before moving on to the next chapter, we will take preliminary looks at two additional
topics that are at least somewhat related to control structures.
This section is an introduction to arrays. Arrays are a basic and very commonly used
data structure, and array processing is often an exercise in using control structures. The next
section will introduce computer graphics and will allow you to apply what you know about
control structures in another context.

3.8.1 Creating and Using Arrays


A data structure consists of a number of data items chunked together so that they can be
treated as a unit. An array is a data structure in which the items are arranged as a numbered
sequence, so that each individual item can be referred to by its position number. In Java—but
not in some other programming languages—all the items must be of the same type, and the
numbering always starts at zero. You will need to learn several new terms to talk about arrays:
The number of items in an array is called the length of the array. The type of the individual
items in an array is called the base type of the array. And the position number of an item in
an array is called the index of that item.
Suppose that you want to write a program that will process the names of, say, one thousand
people. You will need a way to deal with all that data. Before you knew about arrays, you might
have thought that the program would need a thousand variables to hold the thousand names,
and if you wanted to print out all the names, you would need a thousand print statements.
Clearly, that would be ridiculous! In reality, you can put all the names into an array. The array
is represented by a single variable, but it holds the entire list of names. The length of the array
would be 1000, since there are 1000 individual names. The base type of the array would be
String since the items in the array are strings. The first name would be at index 0 in the array,
the second name at index 1, and so on, up to the thousandth name at index 999.
The base type of an array can be any Java type, but for now, we will stick to arrays whose
base type is String or one of the eight primitive types. If the base type of an array is int, it
is referred to as an “array of ints.” An array with base type String is referred to as an “array
of Strings.” However, an array is not, properly speaking, a list of integers or strings or other
values. It is better thought of as a list of variables of type int, or a list of variables of type
String, or of some other type. As always, there is some potential for confusion between the two
uses of a variable: as a name for a memory location and as a name for the value stored in that
memory location. Each position in an array acts as a variable. Each position can hold a value
of a specified type (the base type of the array), just as a variable can hold a value. The value
can be changed at any time, just as the value of a variable can be changed. The items in an
array—really, the individual variables that make up the array—are more often referred to as
the elements of the array.
As I mentioned above, when you use an array in a program, you can use a variable to refer
to the array as a whole. But you often need to refer to the individual elements of the array. The
name for an element of an array is based on the name for the array and the index number of
the element. The syntax for referring to an element looks, for example, like this: namelist[7].
Here, namelist is the variable that names the array as a whole, and namelist[7] refers to
the element at index 7 in that array. That is, to refer to an element of an array, you use the
array name, followed by element index enclosed in square brackets. An element name of this
CHAPTER 3. CONTROL 120

form can be used like any other variable: You can assign a value to it, print it out, use it in an
expression, and so on.
An array also contains a kind of variable representing its length. For example, you can refer
to the length of the array namelist as namelist.length. However, you cannot assign a value
to namelist.length, since the length of an array cannot be changed.
Before you can use a variable to refer to an array, that variable must be declared, and it
must have a type. For an array of Strings, for example, the type for the array variable would
be String[ ], and for an array of ints, it would be int[ ]. In general, an array type consists of the
base type of the array followed by a pair of empty square brackets. Array types can be used to
declare variables; for example,
String[] namelist;
int[] A;
double[] prices;

and variables declared in this way can refer to arrays. However, declaring a variable does not
make the actual array. Like all variables, an array variable has to be assigned a value before
it can be used. In this case, the value is an array. Arrays have to be created using a special
syntax. (The syntax is related to the fact that arrays in Java are actually objects, but that
doesn’t need to concern us here.) Arrays are created with an operator named new . Here are
some examples:
namelist = new String[1000];
A = new int[5];
prices = new double[100];
The general syntax is
harray-variable i = new hbase-type i[harray-length i];
The length of the array can be given as either an integer or an integer-valued expression. For
example, after the assignment statement “A = new int[5];”, A is an array containing the five
integer elements A[0], A[1], A[2], A[3], and A[4]. Also, A.length would have the value 5.
It’s useful to have a picture in mind:

The statement The array contains ve


A = new int[5];
A: elements, which are
creates an array A.length: (5) referred to as
that holds ve A[0]: 0 A[0], A[1], A[2], A[3], A[4].
elements of type A[1]: 0 Each element is a variable
int. A is a name A[2]: 0 of type int. The array also
for the whole array. A[3]: 0 contains A.length, whose
A[4]: 0 value cannot be changed.

When you create an array of int, each element of the array is automatically initialized to
zero. Any array of numbers is filled with zeros when it is created. An array of boolean is filled
with the value false. And an array of char is filled with the character that has Unicode code
number zero. (For an array of String, the initial value is null, a special value used for objects
that we won’t encounter officially until Section 5.1.)
CHAPTER 3. CONTROL 121

3.8.2 Arrays and For Loops


A lot of the real power of arrays comes from the fact that the index of an element can be given
by an integer variable or even an integer-valued expression. For example, if list is an array
and i is a variable of type int, then you can use list[i] and even list[2*i+1] as variable
names. The meaning of list[i] depends on the value of i. This becomes especially useful
when we want to process all the elements of an array, since that can be done with a for loop.
For example, to print out all the items in an array, list, we can just write
int i; // the array index
for (i = 0; i < list.length; i++) {
System.out.println( list[i] );
}
The first time through the loop, i is 0, and list[i] refers to list[0]. So, it is the value
stored in the variable list[0] that is printed. The second time through the loop, i is 1,
and the value stored in list[1] is printed. If the length of the list is 5, then the loop ends
after printing the value of list[4], when i becomes equal to 5 and the continuation condition
“i < list.length” is no longer true. This is a typical example of using a loop to process an
array.
Let’s look at a few more examples. Suppose that A is an array of double, and we want to
find the average of all the elements of the array. We can use a for loop to add up the numbers,
and then divide by the length of the array to get the average:
double total; // The sum of the numbers in the array.
double average; // The average of the numbers.
int i; // The array index.
total = 0;
for ( i = 0; i < A.length; i++ ) {
total = total + A[i]; // Add element number i to the total.
}
average = total / A.length; // A.length is the number of items
Another typical problem is to find the largest number in the array A. The strategy is to
go through the array, keeping track of the largest number found so far. We’ll store the largest
number found so far in a variable called max. As we look through the array, whenever we find
a number larger than the current value of max, we change the value of max to that larger value.
After the whole array has been processed, max is the largest item in the array overall. The only
question is, what should the original value of max be? One possibility is to start with max equal
to A[0], and then to look through the rest of the array, starting from A[1], for larger items:
double max; // The largest number seen so far.
max = A[0]; // At first, the largest number seen is A[0].
int i;
for ( i = 1; i < A.length; i++ ) {
if (A[i] > max) {
max = A[i];
}
}
// at this point, max is the largest item in A
Sometimes, you only want to process some elements of the array. In that case, you can use
an if statement inside the for loop to decide whether or not to process a given element. Let’s
look at the problem of averaging the elements of an array, but this time, suppose that we only
CHAPTER 3. CONTROL 122

want to average the non-zero elements. In this case, the number of items that we add up can
be less than the length of the array, so we will need to keep a count of the number of items
added to the sum:
double total; // The sum of the non-zero numbers in the array.
int count; // The number of non-zero numbers.
double average; // The average of the non-zero numbers.
int i;
total = 0;
count = 0;
for ( i = 0; i < A.length; i++ ) {
if ( A[i] != 0 ) {
total = total + A[i]; // Add element to the total
count = count + 1; // and count it.
}
}
if (count == 0) {
System.out.println("There were no non-zero elements.");
}
else {
average = total / count; // Divide by number of items
System.out.printf("Average of %d elements is %1.5g%n",
count, average);
}

3.8.3 Random Access


So far, my examples of array processing have used sequential access. That is, the elements of
the array were processed one after the other in the sequence in which they occur in the array.
But one of the big advantages of arrays is that they allow random access. That is, every
element of the array is equally accessible at any given time.
As an example, let’s look at a well-known problem called the birthday problem: Suppose
that there are N people in a room. What’s the chance that there are two people in the room
who have the same birthday? (That is, they were born on the same day in the same month,
but not necessarily in the same year.) Most people severely underestimate the probability. We
will actually look at a different version of the question: Suppose you choose people at random
and check their birthdays. How many people will you check before you find one who has the
same birthday as someone you’ve already checked? Of course, the answer in a particular case
depends on random factors, but we can simulate the experiment with a computer program and
run the program several times to get an idea of how many people need to be checked on average.
To simulate the experiment, we need to keep track of each birthday that we find. There are
365 different possible birthdays. (We’ll ignore leap years.) For each possible birthday, we need
to keep track of whether or not we have already found a person who has that birthday. The
answer to this question is a boolean value, true or false. To hold the data for all 365 possible
birthdays, we can use an array of 365 boolean values:
boolean[] used;
used = new boolean[365];
For this problem, the days of the year are numbered from 0 to 364. The value of used[i] is
true if someone has been selected whose birthday is day number i. Initially, all the values in the
array are false. (Remember that this is done automatically when the array is created.) When
CHAPTER 3. CONTROL 123

we select someone whose birthday is day number i, we first check whether used[i] is true.
If it is true, then this is the second person with that birthday. We are done. On the other
hand, if used[i] is false, we set used[i] to be true to record the fact that we’ve encountered
someone with that birthday, and we go on to the next person. Here is a program that carries
out the simulated experiment (of course, in the program, there are no simulated people, only
simulated birthdays):
/**
* Simulate choosing people at random and checking the day of the year they
* were born on. If the birthday is the same as one that was seen previously,
* stop, and output the number of people who were checked.
*/
public class BirthdayProblem {
public static void main(String[] args) {
boolean[] used; // For recording the possible birthdays
// that have been seen so far. A value
// of true in used[i] means that a person
// whose birthday is the i-th day of the
// year has been found.
int count; // The number of people who have been checked.
used = new boolean[365]; // Initially, all entries are false.
count = 0;
while (true) {
// Select a birthday at random, from 0 to 364.
// If the birthday has already been used, quit.
// Otherwise, record the birthday as used.
int birthday; // The selected birthday.
birthday = (int)(Math.random()*365);
count++;
System.out.printf("Person %d has birthday number %d%n", count, birthday);
if ( used[birthday] ) {
// This day was found before; it’s a duplicate. We are done.
break;
}
used[birthday] = true;
} // end while
System.out.println();
System.out.println("A duplicate birthday was found after "
+ count + " tries.");
}
} // end class BirthdayProblem
You should study the program to understand how it works and how it uses the array. Also, try
it out! You will probably find that a duplicate birthday tends to occur sooner than you expect.
CHAPTER 3. CONTROL 124

3.8.4 Partially Full Arrays


Consider an application where the number of items that we want to store in an array changes
as the program runs. Since the size of the array can’t be changed, a separate counter variable
must be used to keep track of how many spaces in the array are in use. (Of course, every space
in the array has to contain something; the question is, how many spaces contain useful or valid
items?)
Consider, for example, a program that reads positive integers entered by the user and stores
them for later processing. The program stops reading when the user inputs a number that is
less than or equal to zero. The input numbers can be kept in an array, numbers, of type int[ ].
Let’s say that no more than 100 numbers will be input. Then the size of the array can be fixed
at 100. But the program must keep track of how many numbers have actually been read and
stored in the array. For this, it can use an integer variable. Each time a number is stored in
the array, we have to count it; that is, value of the counter variable must be incremented by
one. One question is, when we add a new item to the array, where do we put it? Well, if the
number of items is count, then they would be stored in the array in positions number 0, 1, . . . ,
(count-1). The next open spot would be position number count, so that’s where we should put
the new item.
As a rather silly example, let’s write a program that will read the numbers input by the
user and then print them in the reverse of the order in which they were entered. Assume that
an input value equal to zero marks the end of the data. (This is, at least, a processing task
that requires that the numbers be saved in an array. Note that many types of processing, such
as finding the sum or average or maximum of the numbers, can be done without saving the
individual numbers.)
import textio.TextIO;
public class ReverseInputNumbers {
public static void main(String[] args) {
int[] numbers; // An array for storing the input values.
int count; // The number of numbers saved in the array.
int num; // One of the numbers input by the user.
int i; // for-loop variable.
numbers = new int[100]; // Space for 100 ints.
count = 0; // No numbers have been saved yet.
System.out.println("Enter up to 100 positive integers; enter 0 to end.");
while (true) { // Get the numbers and put them in the array.
System.out.print("? ");
num = TextIO.getlnInt();
if (num <= 0) {
// Zero marks the end of input; we have all the numbers.
break;
}
numbers[count] = num; // Put num in position count.
count++; // Count the number
}
System.out.println("\nYour numbers in reverse order are:\n");
for ( i = count - 1; i >= 0; i-- ) {
CHAPTER 3. CONTROL 125

System.out.println( numbers[i] );
}
} // end main();
} // end class ReverseInputNumbers
It is especially important to note how the variable count plays a dual role. It is the number
of items that have been entered into the array. But it is also the index of the next available
spot in the array.
When the time comes to print out the numbers in the array, the last occupied spot in the
array is location count - 1, so the for loop prints out values starting from location count - 1
and going down to 0. This is also a nice example of processing the elements of an array in reverse
order.
∗ ∗ ∗
You might wonder what would happen in this program if the user tries to input more than
100 numbers. The result would be an error that would crash the program. When the user enters
the 101-st number, the program tries to store that number in an array element number[100].
However, there is no such array element. There are only 100 items in the array, and the
index of the last item is 99. The attempt to use number[100] generates an exception of type
ArrayIndexOutOfBoundsException. Exceptions of this type are a common source of run-time
errors in programs that use arrays.

3.8.5 Two-dimensional Arrays


The arrays that we have considered so far are “one-dimensional.” This means that the array
consists of a sequence of elements that can be thought of as being laid out along a line. It
is also possible to have two-dimensional arrays, where the elements can be laid out in a
rectangular grid. We consider them only briefly here, but will return to the topic in Section 7.5.
In a two-dimensional, or “2D,” array, the elements can be arranged in rows and columns.
Here, for example, is a 2D array of int that has five rows and seven columns:

0 1 2 3 4 5 6
0 13 7 33 54 -5 -1 92
1 -3 0 8 42 18 0 67
2 44 78 90 79 -5 72 22
3 43 -6 17 100 1 -12 12
4 2 0 58 58 36 21 87

This 5-by-7 grid contains a total of 35 elements. The rows in a 2D array are numbered 0, 1, 2,
. . . , up to the number of rows minus one. Similarly, the columns are numbered from zero up
to the number of columns minus one. Each individual element in the array can be picked out
by specifying its row number and its column number. (The illustration shown here is not what
the array actually looks like in the computer’s memory, but it does show the logical structure
of the array.)
In Java, the syntax for two-dimensional arrays is similar to the syntax for one-dimensional
arrays, except that an extra index is involved, since picking out an element requires both a row
number and a column number. For example, if A is a 2D array of int, then A[3][2] would be
the element in row 3, column 2. That would pick out the number 17 in the array shown above.
CHAPTER 3. CONTROL 126

The type for A would be given as int[ ][ ], with two pairs of empty brackets. To declare the array
variable and create the array, you could say,
int[][] A;
A = new int[5][7];
The second line creates a 2D array with 5 rows and 7 columns. Two-dimensional arrays are
often processed using nested for loops. For example, the following code segment will print out
the elements of A in neat columns:
int row, col; // loop-control-variables for accessing rows and columns in A
for ( row = 0; row < 5; row++ ) {
for ( col = 0; col < 7; col++ ) {
System.out.printf( "%7d", A[row][col] );
}
System.out.println();
}
The base type of a 2D array can be anything, so you can have arrays of type double[ ][ ],
String[ ][ ], and so on.
There are some natural uses for 2D arrays. For example, a 2D array can be used to store the
contents of the board in a game such as chess or checkers. And an example in Subsection 4.7.3
uses a 2D array to hold the colors of a grid of colored squares. But sometimes two-dimensional
arrays are used in problems in which the grid is not so visually obvious. Consider a company
that owns 25 stores. Suppose that the company has data about the profit earned at each store
for each month in the year 2018. If the stores are numbered from 0 to 24, and if the twelve
months from January 2018 through December 2018 are numbered from 0 to 11, then the profit
data could be stored in an array, profit, created as follows:
double[][] profit;
profit = new double[25][12];
profit[3][2] would be the amount of profit earned at store number 3 in March, and more
generally, profit[storeNum][monthNum] would be the amount of profit earned in store number
storeNum in month number monthNum (where the numbering, remember, starts from zero).
Let’s assume that the profit array has already been filled with data. This data can be
processed in a lot of interesting ways. For example, the total profit for the company—for the
whole year from all its stores—can be calculated by adding up all the entries in the array:
double totalProfit; // Company’s total profit in 2018.
int store, month; // variables for looping through the stores and the months
totalProfit = 0;
for ( store = 0; store < 25; store++ ) {
for ( month = 0; month < 12; month++ )
totalProfit += profit[store][month];
}
Sometimes it is necessary to process a single row or a single column of an array, not the
entire array. For example, to compute the total profit earned by the company in December,
that is, in month number 11, you could use the loop:
double decemberProfit;
int storeNum;
decemberProfit = 0.0;
for ( storeNum = 0; storeNum < 25; storeNum++ ) {
CHAPTER 3. CONTROL 127

decemberProfit += profit[storeNum][11];
}
Two-dimensional arrays are sometimes useful, but they are much less common than one-
dimensional arrays. Java actually allows arrays of even higher dimension, but they are only
rarely encountered in practice.

3.9 Introduction to GUI Programming


For the past two chapters, you’ve been learning the sort of programming that is done
inside a single subroutine, “programming in the small.” In the rest of this book, we’ll be more
concerned with the larger scale structure of programs, but the material that you’ve already
learned will be an important foundation for everything to come. In this section, we see how
techniques that you have learned so far can be applied in the context of graphical user interface
programming. GUI programs here, and in the rest of this book, are written using JavaFX, a
collection of classes that form a “toolkit” for writing GUI programs. All of the classes mentioned
in this section are part of JavaFX, and they must be imported into any program that uses them.
See Section 2.6 for information about compiling and running programs that use JavaFX.
When you run a GUI program, it opens one or more windows on your computer screen. As
a programmer, you can have complete control over what appears in the window and how the
user can interact with it. For our first encounter, we look at one simple example: the ability
of a program to display simple shapes like rectangles and lines in the window, with no user
interaction. For now, the main point is to take a look at how programming-in-the-small can be
used in other contexts besides text-based, command-line-style programs. You will see that a
knowledge of programming-in-the-small applies to writing the guts of any subroutine, not just
main().

3.9.1 Drawing Shapes


To understand computer graphics, you need to know a little about pixels and coordinate sys-
tems. The computer screen is made up of small squares called pixels, arranged in rows and
columns, usually about 100 pixels per inch. (Many screen now have many more physical pixels
per inch. On these “high-resolution” screens, a JavaFX “pixel” might refer to a physical pixel,
but it is more likely to refer to a “logical pixel,” which is a unit of measure somewhere close to
1/100 inch.)
The computer controls the color of the pixels, and drawing is done by changing the colors
of individual pixels. Each pixel has a pair of integer coordinates, often called x and y, that
specify the pixel’s horizontal and vertical position. When drawing to a rectangular area on
the screen, the coordinates of the pixel in the upper left corner of the rectangle are (0,0). The
x coordinate increases from left to right, and the y coordinate increases from top to bottom.
Shapes are specified using pixels. For example, a rectangle is specified by the x and y coordinates
of its upper left corner and by its width and height measured in pixels. Here’s a picture of a
rectangular drawing area, showing the ranges of x and y coordinates. The “width” and “height”
in this picture give the size of the drawing area, in pixels:
CHAPTER 3. CONTROL 128

0 width
x
0

Hello World
height

Assuming that the drawing area is 800-by-500 pixels, the rectangle in the upper left of the
picture would have, approximately, width 200, height 150, and upper left corner at coordinates
(50,50).
∗ ∗ ∗
Drawing in Java is done using a graphics context. A graphics context is an object. As an
object, it can include subroutines and data. Among the subroutines in a graphics context are
routines for drawing basic shapes such as lines, rectangles, ovals, and text. (When text appears
on the screen, the characters have to be drawn there by the computer, just like the computer
draws any other shapes.) Among the data in a graphics context are the color and font that
are currently selected for drawing. (A font determines the style and size of characters.) One
other piece of data in a graphics context is the “drawing surface” on which the drawing is done.
Different graphics context objects can draw to different drawing surfaces. For us, the drawing
surface will be the content area of a window, not including its border or title bar.
There are two ways to draw a shape in JavaFX: You can fill the shape, meaning you can
set the color of each of the pixels inside the shape. Or you can stroke the shape, meaning that
you set the color of the pixels that lie along the border of the shape. Some shapes, such as a
line, can only be stroked. A graphics context actually keeps track of two separate colors, one
used for filling shapes and one used for stroking shapes. Stroking a shape is like dragging a
pen along the border of the shape. The properties of that pen (such as its size and whether it
produces a solid line or a dashed line) are properties of the graphics context.
A graphics context is represented by a variable. The type for the variable is GraphicsContext
(just like the type for a string variable is String ). The variable is often named g, but the name
of the variable is of course up to the programmer. Here are a few of the subroutines that are
available in a graphics context g. Note that all numerical parameter values can be of type
double.
• g.setFill(c) is called to set the color to be used for filling shapes. The parameter,
c is an object belonging to a class named Color. There are many constants represent-
ing standard colors that can be used as the parameter in this subroutine. The stan-
dard colors range from common colors such as Color.BLACK, Color.WHITE, Color.RED,
Color.GREEN, Color.BLUE, and Color.YELLOW, to more exotic color names such as
Color.CORNFLOWERBLUE. (Later, we will see that it is also possible to create new colors.)
For example, if you want to fill shapes with red, you would say “g.setFill(Color.RED);”.
The specified color is used for all subsequent fill operations up until the next time
g.setFill() is called. Note that previously drawn shapes are not affected!
• g.setStroke(c) is called to set the color to be used for stroking shapes. It works similarly
to g.setFill.
CHAPTER 3. CONTROL 129

• g.setLineWidth(w) sets the size of the pen that will be used for subsequent stroke oper-
ations, where w is measured in pixels.
• g.strokeLine(x1,y1,x2,y2) draws a line from the point with coordinates (x1,y1) to
the point with coordinates (x2,y2). The width of the line is 1, unless a different line
width has been set by calling g.setLineWidth(), and the color is black unless a different
color has been set by calling g.setStroke().
• g.strokeRect(x,y,w,h) draws the outline of a rectangle with vertical and horizontal
sides. This subroutine draws the outline of the rectangle whose top-left corner is x pixels
from the left edge of the drawing area and y pixels down from the top. The horizontal
width of the rectangle is w pixels, and the vertical height is h pixels. Color and line width
are set by calling g.setStroke() and g.setLineWidth().
• g.fillRect(x,y,w,h) is similar to g.strokeRect() except that it fills in the inside of
the rectangle instead of drawing an outline, and it uses the color set by g.setFill().
• g.strokeOval(x,y,w,h) draws the outline of an oval. The oval just fits inside the rect-
angle that would be drawn by g.strokeRect(x,y,w,h). To get a circle, use the same
values for w and for h.
• g.fillOval(x,y,w,h) is similar to g.strokeOval() except that it fills in the inside of
the oval instead of drawing an outline.
This is enough information to draw some pictures using Java graphics. To start with
something simple, let’s say that we want to draw a set of ten parallel lines, something like this:

Let’s say that the lines are 200 pixels long and that the distance from each line to the next
is 10 pixels, and let’s put the start of the first line at the pixel with coordinates (100,50). To
draw one line, we just have to call g.strokeLine(x1,y1,x2,y2) with appropriate values for
the parameters. Now, all the lines start at x -coordinate 100, so we can use the constant 100 as
the value for x1. Since the lines are 200 pixels long, we can use the constant 300 as the value
for x2. The y-coordinates of the lines are different, but we can see that both endpoints of a
line have the same y-coordinates, so we can use a single variable as the value for y1 and for
y2. Using y as the name of that variable, the command for drawing one of the lines becomes
g.strokeLine(100,y,300,y). The value of y is 50 for the top line and increases by 10 each
time we move down from one line to the next. We just need to make sure that y takes on the
correct sequence of values. We can use a for loop that counts from 1 to 10:
int y; // y-coordinate for the line
int i; // loop control variable
y = 50; // y starts at 50 for the first line
for ( i = 1; i <= 10; i++ ) {
g.strokeLine( 100, y, 300, y );
y = y + 10; // increase y by 10 before drawing the next line.
}
CHAPTER 3. CONTROL 130

Alternatively, we could use y itself as the loop control variable, noting that the value of y for
the last line is 140:
int y;
for ( y = 50; y <= 140; y = y + 10 )
g.strokeLine( 100, y, 300, y );
If we wanted the lines to be blue, we could do that by calling g.setStroke(Color.BLUE)
before drawing them. If we just draw the lines without setting the color, they will be black.
If we wanted the lines to be 3 pixels wide, we could call g.setLineWidth(3) before drawing
the lines.
For something a little more complicated, let’s draw a large number of randomly colored,
randomly positioned, filled circles. Since we only know a few colors, I will randomly select the
color to be red, green, blue, or yellow. That can be done with a simple switch statement, similar
to the ones in Subsection 3.6.4:
switch ( (int)(4*Math.random()) ) {
case 0:
g.setFill( Color.RED );
break;
case 1:
g.setFill( Color.GREEN );
break;
case 2:
g.setFill( Color.BLUE );
break;
case 3:
g.setFill( Color.YELLOW );
break;
}
I will choose the center points of the circles at random. Let’s say that the width of the
drawing area is given by a variable, width. Then we want a random value in the range 0 to
width-1 for the horizontal position of the center. Similarly, the vertical position of the center
will be a random value in the range 0 to height-1. That leaves the size of the circle to be
determined; I will make the radius of each circle equal to 50 pixels. We can draw the circle with
a statement of the form g.fillOval(x,y,w,h). However, in this command, x and y are not
the coordinates of the center of the circle; they are the upper left corner of a rectangle drawn
around the circle. To get values for x and y, we have to move back from the center of the circle
by 50 pixels, an amount equal to the radius of the circle. The parameters w and h give the
width and height of the rectangle, which have to be twice the radius, or 100 pixels in this case.
Taking all this into account, here is a code segment for drawing a random circle:
centerX = (int)(width*Math.random());
centerY = (int)(height*Math.random());
g.fillOval( centerX - 50, centerY - 50, 100, 100 );
This code comes after the color-setting code given above. In the end, I found that the picture
looks better if I also draw a black outline around each filled circle, so I added this code at the
end:
g.setStroke( Color.BLACK );
g.strokeOval( centerX - 50, centerY - 50, 100, 100 );
CHAPTER 3. CONTROL 131

Finally, to get a large number of circles, I put all of the above code into a for loop that runs
for 500 executions. Here’s a typical drawing from the program, shown at reduced size:

3.9.2 Drawing in a Program


Now, as you know, you can’t just have a bunch of Java code standing by itself. The code
has to be inside a subroutine definition that is itself inside a class definition. In fact, for my
circle-drawing program, the complete subroutine for drawing the picture looks like this:
public void drawPicture(GraphicsContext g, int width, int height) {
g.setFill(Color.WHITE);
g.fillRect(0, 0, width, height); // First, fill with a background color.
// As an example, draw a large number of colored disks.
// To get a different picture, erase this code, and substitute your own.
int centerX; // The x-coord of the center of a disk.
int centerY; // The y-coord of the center of a disk.
int colorChoice; // Used to select a random color.
int count; // Loop control variable for counting disks
for (count = 0; count < 500; count++) {
centerX = (int)(width*Math.random());
centerY = (int)(height*Math.random());
colorChoice = (int)(4*Math.random());
switch (colorChoice) {
case 0:
g.setFill(Color.RED);
break;
case 1:
g.setFill(Color.GREEN);
break;
case 2:
g.setFill(Color.BLUE);
break;
case 3:
g.setFill(Color.YELLOW);
break;
}
g.fillOval( centerX - 50, centerY - 50, 100, 100 );
CHAPTER 3. CONTROL 132

g.setStroke(Color.BLACK);
g.strokeOval( centerX - 50, centerY - 50, 100, 100 );
}
} // end drawPicture()

This is the first subroutine definition that you have seen, other than main(), but you will
learn all about defining subroutines in the next chapter. The first line of the definition makes
available certain values that are used in the subroutine: the graphics context g and the width
and height of the drawing area. These values come from outside the subroutine, but the
subroutine can use them. The point here is that to draw something, you just have to fill in the
inside of the subroutine, just as you write a program by filling in the inside of main().
The subroutine definition still has to go inside a class that defines the program. In this case,
the class is named SimpleGraphicsStarter, and the complete program is available in the sample
source code file SimpleGraphicsStarter.java. You can run that program to see the drawing. You
can use this sample program as a starting point for drawing your own pictures.
There’s a lot in the program that you won’t understand. To make your own drawing, all you
have to do is erase the inside of the drawPicture() routine in the source code and substitute
your own drawing code. You don’t need to understand the rest.
(By the way, you might notice that the main() subroutine uses the word static in its
definition, but drawPicture() does not. This has to do with the fact that drawPicture is a
subroutine in an object rather than in a class. The difference between static and non-static
subroutines is important but not something that we need to worry about for the time being. It
will become important for us in Chapter 5.)

3.9.3 Animation
We can extend the idea of drawing pictures to making animations. A computer animation is
simply a sequence of individual pictures, displayed quickly one after the other. If the change
from each picture to the next is small, the user will perceive the sequence of images as a
continuous animation. Each picture in the animation is called a frame. The sample program
SimpleAnimationStarter.java can be used as a starting point for writing animations. It contains
a subroutine named drawFrame() that draws one frame in an animation. You can create an
animation by filling in the definition of this subroutine. In addition to the graphics context
and the width and height of the drawing area, you can use the value of two other variables in
your code: frameNumber and elapsedSeconds. The drawFrame subroutine will automatically
be called about 60 times per second. The variable frameNumber takes on the values 0, 1, 2,
3, . . . in successive calls to the subroutine, and the value of elapsedSeconds is the number of
seconds that the animation has been running. By using either of these variables in your code,
you can draw a different picture each time drawFrame() is called, and the user will see the
series of pictures as an animation.
As an example of animation, we look at drawing a set of nested rectangles. The rectangles
will shrink towards the center of the drawing, giving an illusion of infinite motion. One frame
from the animation looks like this:
CHAPTER 3. CONTROL 133

Consider how to draw a picture like this one. The rectangles can be drawn with a while
loop, which draws the rectangles starting from the one on the outside and moving in. Think
about what variables will be needed and how they change from one iteration of the while loop to
the next. Each time through the loop, the rectangle that is drawn is smaller than the previous
one and is moved down and over a bit. The difference between two rectangles is in their sizes
and in the coordinates of their upper left corners. We need variables to represent the width
and height of the rectangle, which I call rectWidth and rectHeight. The x and y-coordinates
of the upper left corner are the same, and they can be represented by the same variable. I call
that variable inset, since it is the amount by which the edges of the rectangle are inset from
the edges of the drawing area. The width and height decrease from one rectangle to the next,
while the inset increases. The while loop ends when either the width or the height becomes
less than or equal to zero. In general outline, the algorithm for drawing one frame is
Fill the drawing area with white
Set the amount of inset for the first rectangle
Set the width and height for the first rectangle
Set the stroke color to black
while the width and height are both greater than zero:
draw a rectangle (using the g.strokeRect subroutine)
increase the inset (to move the next rectangle over and down)
decrease the width and height (to make the next rectangle smaller)
In my program, each rectangle is 15 pixels away from the rectangle that surrounds it, so the
inset is increased by 15 each time through the while loop. The rectangle shrinks by 15 pixels
on the left and by 15 pixels on the right, so the width of the rectangle shrinks by 30 before
drawing the next rectangle. The height also shrinks by 30 pixels each time through the loop.
The pseudocode is then easy to translate into Java, except that we need to know what
initial values to use for the inset, width, and height of the first rectangle. To figure that out,
we have to think about the fact that the picture is animated, so that what we draw will depend
in some way on the frame number. From one frame to the next frame of the animation, the
top-left corner of the outer rectangle moves over and down; that is, the inset for the outer
rectangle increases from one frame to the next. We can make this happen by setting the inset
for frame number 0 to 0, the inset for frame number 1 to 1, and so on. But that can’t go on
forever, or eventually all the rectangles would disappear. In fact, when the animation gets to
frame 15, a new rectangle should appear at the outside of the drawing area—but it’s not really
a “new rectangle,” it’s just that the inset for the outer rectangle goes back to zero. So, as the
CHAPTER 3. CONTROL 134

animation proceeds, the inset should go through the sequence of values 0, 1, 2, . . . , 14 over and
over. We can accomplish that very easily by setting
inset = frameNumber % 15;
Finally, note that the first rectangle fills the drawing area except for a border of size inset
around the outside of the rectangle. This means that the width of the rectangle is the width
of the drawing area minus two times the inset, and similarly for the height. Here, then is the
drawFrame() subroutine for the moving rectangle example:
public void drawFrame(GraphicsContext g, int frameNumber,
double elapsedSeconds, int width, int height) {
g.setFill(Color.WHITE);
g.fillRect(0,0,width,height); // Fill drawing area with white.
double inset; // Gap between edges of drawing area and outer rectangle.
double rectWidth, rectHeight; // The size of one of the rectangles.
g.setStroke(Color.BLACK); // Draw the rectangle outlines in black.
inset = frameNumber % 15 + 0.5; // (The 0.5 is a technicality that gives
// a sharper picture.)
rectWidth = width - 2*inset;
rectHeight = height - 2*inset;
while (rectWidth >= 0 && rectHeight >= 0) {
g.strokeRect(inset, inset, rectWidth, rectHeight);
inset += 15; // rectangles are 15 pixels apart
rectWidth -= 30;
rectHeight -= 30;
}
}
You can find the full source code for the program is in the sample program MovingRects.java.
Take a look! It’s a neat effect. For another example of animation, see the sample program
RandomCircles.java. That program adds one random colored disk to the picture in each frame;
it illustrates the fact that the image from one frame is not automatically erased before the next
frame is drawn.
Exercises 135

Exercises for Chapter 3

1. How many times do you have to roll a pair of dice before they come up snake eyes? You (solution)
could do the experiment by rolling the dice by hand. Write a computer program that
simulates the experiment. The program should report the number of rolls that it makes
before the dice come up snake eyes. (Note: “Snake eyes” means that both dice show a
value of 1.) Exercise 2.2 explained how to simulate rolling a pair of dice.

2. Which integer between 1 and 10000 has the largest number of divisors, and how many (solution)
divisors does it have? Write a program to find the answers and print out the results. It is
possible that several integers in this range have the same, maximum number of divisors.
Your program only has to print out one of them. An example in Subsection 3.4.2 discussed
divisors. The source code for that example is CountDivisors.java.
You might need some hints about how to find a maximum value. The basic idea is
to go through all the integers, keeping track of the largest number of divisors that you’ve
seen so far. Also, keep track of the integer that had that number of divisors.

3. Write a program that will evaluate simple expressions such as 17 + 3 and 3.14159 * 4.7. (solution)
The expressions are to be typed in by the user. The input always consists of a number,
followed by an operator, followed by another number. The operators that are allowed are
+, -, *, and /. You can read the numbers with TextIO.getDouble() and the operator
with TextIO.getChar(). Your program should read an expression, print its value, read
another expression, print its value, and so on. The program should end when the user
enters 0 as the first number on the line.

4. Write a program that reads one line of input text and breaks it up into words. The (solution)
words should be output one per line. A word is defined to be a sequence of letters. Any
characters in the input that are not letters should be discarded. For example, if the user
inputs the line
He said, "That’s not a good idea."
then the output of the program should be
He
said
That
s
not
a
good
idea
An improved version of the program would list “that’s” as a single word. An apostrophe
can be considered to be part of a word if there is a letter on each side of the apostrophe.
To test whether a character is a letter, you might use (ch >= ’a’ && ch <= ’z’) ||
(ch >= ’A’ && ch <= ’Z’). However, this only works in English and similar languages.
A better choice is to call the standard function Character.isLetter(ch), which returns
a boolean value of true if ch is a letter and false if it is not. This works for any Unicode
character.
Exercises 136

5. Suppose that a file contains information about sales figures for a company in various cities. (solution)
Each line of the file contains a city name, followed by a colon (:) followed by the data for
that city. The data is a number of type double. However, for some cities, no data was
available. In these lines, the data is replaced by a comment explaining why the data is
missing. For example, several lines from the file might look like:
San Francisco: 19887.32
Chicago: no report received
New York: 298734.12
Write a program that will compute and print the total sales from all the cities together.
The program should also report the number of cities for which data was not available.
The name of the file is “sales.dat”.
To complete this program, you’ll need one fact about file input with TextIO that was
not covered in Subsection 2.4.4. Since you don’t know in advance how many lines there
are in the file, you need a way to tell when you have gotten to the end of the file. When
TextIO is reading from a file, the function TextIO.eof() can be used to test for end of
file. This boolean-valued function returns true if the file has been entirely read and
returns false if there is more data to read in the file. This means that you can read the
lines of the file in a loop while (TextIO.eof() == false).... The loop will end when
all the lines of the file have been read.
Suggestion: For each line, read and ignore characters up to the colon. Then read the
rest of the line into a variable of type String. Try to convert the string into a number, and
use try..catch to test whether the conversion succeeds.

6. Exercise 3.2 asked you to find the number in the range 1 to 10000 that has the largest (solution)
number of divisors. You only had to print out one such number. Revise the program so
that it will print out all numbers that have the maximum number of divisors. Use an array
as follows: As you count the divisors for each number, store each count in an array. Then
at the end of the program, you can go through the array and print out all the numbers
that have the maximum count. The output from the program should look something like
this:
Among integers between 1 and 10000,
The maximum number of divisors was 64
Numbers with that many divisors include:
7560
9240

7. An example in Subsection 3.8.3 tried to answer the question, How many random people do (solution)
you have to select before you find a duplicate birthday? The source code for that program
can be found in the file BirthdayProblem.java. Here are some related questions:
• How many random people do you have to select before you find three people who
share the same birthday? (That is, all three people were born on the same day in
the same month, but not necessarily in the same year.)
• Suppose you choose 365 people at random. How many different birthdays will they
have? (The number could theoretically be anywhere from 1 to 365).
• How many different people do you have to check before you’ve found at least one
person with a birthday on each of the 365 days of the year?
Exercises 137

Write three programs to answer these questions. Each of your programs should sim-
ulate choosing people at random and checking their birthdays. (In each case, ignore the
possibility of leap years.)

8. Write a GUI program that draws a checkerboard. Base your solution on the sample pro- (solution)
gram SimpleGraphicsStarter.java You will draw the checkerboard in the drawPicture()
subroutine, after erasing the code that it already contains.
The checkerboard should be 400-by-400 pixels. You can change the size of the drawing
area in SimpleGraphicsStarter.java by modifying the first two lines of the start()
subroutine to set width and height to 400 instead of 800 and 600. A checkerboard
contains 8 rows and 8 columns of squares. If the size of the drawing area is 400, that
means that each square should be 50-by-50 pixels. The squares are red and black (or
whatever other colors you choose). Here is a tricky way to determine whether a given
square should be red or black: The rows and columns can be thought of as numbered from
0 to 7. If the row number of the square and the column number of the square are either
both even or both odd, then the square is red. Otherwise, it is black. Note that a square
is just a rectangle in which the height is equal to the width, so you can use the subroutine
g.fillRect() to draw the squares. Here is a reduced-size image of the checkerboard that
you want to draw:

9. Often, some element of an animation repeats over and over, every so many frames. Some- (solution)
times, the repetition is “cyclic,” meaning that at the end it jumps back to the start.
Sometimes the repetition is “oscillating,” like a back-and-forth motion where the second
half is the same as the first half played in reverse.
Write an animation that demonstrates both cyclic and oscillating motions at various
speeds. For cyclic motion, you can use a square that moves across the drawing area, then
jumps back to the start, and then repeats the same motion over and over. For oscillating
motion, you can do something similar, but the square should move back and forth between
the two edges of the drawing area; that is, it moves left-to-right during the first half of
the animation and then backwards from right-to-left during the second half. To write the
program, you can start with a copy of the sample program SimpleAnimationStarter.java.
A cyclic motion has to repeat every N frames for some value of N. What you draw in
some frame of the animation depends on the frameNumber. The frameNumber just keeps
increasing forever. To implement cyclic motion, what you really want is a “cyclic frame
Exercises 138

number” that takes on the values 0, 1, 2, . . . , (N-1), 0, 1, 2, . . . , (N-1), 0, 1, 2, . . . . You


can derive the value that you need from frameNumber simply by saying
cyclicFrameNumber = frameNumber % N;
Then, you just have to base what you draw on cyclicFrameNumber instead of on
frameNumber. Similarly, for an oscillating animation, you need an “oscillation frame
number” that takes on the values 0, 1, 2, . . . (N-1), N, (N-1), (N-2), . . . 2, 1, 0, 1, 2, and
so on, repeating the back and forth motion forever. You can compute the value that you
need with
oscilationFrameNumber = frameNumber % (2*N);
if (oscillationFrameNumber > N)
oscillationFrameNumber = (2*N) - oscillationFrameNumber;
Here is a screen shot from my version of the program. I use six squares. The top three
do cyclic motion at various speeds, while the bottom three do oscillating motion. I drew
black lines across the drawing area to separate the squares and to give them “channels”
to move in.
Quiz 139

Quiz on Chapter 3
(answers)

1. What is an algorithm?

2. Explain briefly what is meant by “pseudocode” and how is it useful in the development
of algorithms.

3. What is a block statement? How are block statements used in Java programs?

4. What is the main difference between a while loop and a do..while loop?

5. What does it mean to prime a loop?

6. Explain what is meant by an animation and how a computer displays an animation.

7. Write a for loop that will print out all the multiples of 3 from 3 to 36, that is: 3 6 9 12
15 18 21 24 27 30 33 36.

8. Fill in the following main() routine so that it will ask the user to enter an integer, read
the user’s response, and tell the user whether the number entered is even or odd. (You can
use TextIO.getInt() to read the integer. Recall that an integer n is even if n % 2 == 0.)
public static void main(String[] args) {
// Fill in the body of this subroutine!
}

9. Write a code segment that will print out two different random integers selected from the
range 1 to 10. All possible outputs should have the same probability. Hint: You can easily
select two random numbers, but you have to account for the fact that the two numbers
that you pick might be the same.

10. Suppose that s1 and s2 are variables of type String, whose values are expected to be
string representations of values of type int. Write a code segment that will compute and
print the integer sum of those values, or will print an error message if the values cannot
successfully be converted into integers. (Use a try..catch statement.)

11. Show the exact output that would be produced by the following main() routine:
public static void main(String[] args) {
int N;
N = 1;
while (N <= 32) {
N = 2 * N;
System.out.println(N);
}
}

12. Show the exact output produced by the following main() routine:
Quiz 140

public static void main(String[] args) {


int x,y;
x = 5;
y = 1;
while (x > 0) {
x = x - 1;
y = y * x;
System.out.println(y);
}
}

13. What output is produced by the following program segment? Why? (Recall that
name.charAt(i) is the i-th character in the string, name.)
String name;
int i;
boolean startWord;
name = "Richard M. Nixon";
startWord = true;
for (i = 0; i < name.length(); i++) {
if (startWord)
System.out.println(name.charAt(i));
if (name.charAt(i) == ’ ’)
startWord = true;
else
startWord = false;
}

14. Suppose that numbers is an array of type int[ ]. Write a code segment that will count and
output the number of times that the number 42 occurs in the array.

15. Define the range of an array of numbers to be the maximum value in the array minus
the minimum value. Suppose that raceTimes is an array of type double[ ]. Write a code
segment that will find and print the range of raceTimes.
Chapter 4

Programming in the Large I:


Subroutines

One way to break up a complex program into manageable pieces is to use subroutines.
A subroutine consists of the instructions for carrying out a certain task, grouped together and
given a name. Elsewhere in the program, that name can be used as a stand-in for the whole set
of instructions. As a computer executes a program, whenever it encounters a subroutine name,
it executes all the instructions necessary to carry out the task associated with that subroutine.
Subroutines can be used over and over, at different places in the program. A subroutine
can even be used inside another subroutine. This allows you to write simple subroutines and
then use them to help write more complex subroutines, which can then be used in turn in other
subroutines. In this way, very complex programs can be built up step-by-step, where each step
in the construction is reasonably simple.
Subroutines in Java can be either static or non-static. This chapter covers static subroutines.
Non-static subroutines, which are used in true object-oriented programming, will be covered in
the next chapter.

4.1 Black Boxes


A subroutine consists of instructions for performing some task, chunked together and
given a name. “Chunking” allows you to deal with a potentially very complicated task as
a single concept. Instead of worrying about the many, many steps that the computer might
have to go though to perform that task, you just need to remember the name of the subroutine.
Whenever you want your program to perform the task, you just call the subroutine. Subroutines
are a major tool for dealing with complexity.
A subroutine is sometimes said to be a “black box” because you can’t see what’s “inside”
it (or, to be more precise, you usually don’t want to see inside it, because then you would
have to deal with all the complexity that the subroutine is meant to hide). Of course, a black
box that has no way of interacting with the rest of the world would be pretty useless. A black
box needs some kind of interface with the rest of the world, which allows some interaction
between what’s inside the box and what’s outside. A physical black box