100% found this document useful (1 vote)
16 views189 pages

Ryan

This document is a textbook on programming with Java, designed for an introductory course in programming. It covers fundamental concepts, programming structures, and practical applications, while also incorporating engineering lore to enhance the learning experience. The author aims to provide a solid foundation in Java while addressing the needs and impatience of new learners in the field of programming.

Uploaded by

peachypaimon
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)
16 views189 pages

Ryan

This document is a textbook on programming with Java, designed for an introductory course in programming. It covers fundamental concepts, programming structures, and practical applications, while also incorporating engineering lore to enhance the learning experience. The author aims to provide a solid foundation in Java while addressing the needs and impatience of new learners in the field of programming.

Uploaded by

peachypaimon
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/ 189

YA Ş A R S A F K A N

PROGRAMMING WITH
J AVA
Todo list

Write sections about ArrayList and LinkedList. . . . . . . . . . . 119


Write section about Javadoc. . . . . . . . . . . . . . . . . . . . . . 143
Contents

Preface 1
Why Write This Book? 1
To the Student 1

1 Introduction 3
What is Java? 3
Is Java a Good Language to Learn Programming? 3
Popularity 3
What is Java Good For? 5
What is Java not Good For? 5
Recap 5
Exercises 6
Engineering Lore: What is Engineering? 6

2 Starting with Java 9


Programming Concepts 9
Source Code 9
Machine Code 10
Interpreter 10
Compiler 10
Virtual Machine 10
Library 11
JRE and JDK 11
Integrated Development Environment 11
Recap 12
Exercises 12
Engineering Lore: Incremental Improvement 13
6 ya şar safkan

3 Hello, World! 15
A Short History of "Hello, World!" 15
The Java "Hello, World!" Program 15
Running the Program 17
Whitespace and Style 17
Comments 18
When to Comment, What to Comment? 18
Recap 19
Exercises 19
Engineering Lore: Quality, Value, and Effort 20

4 Data Types 23
Bits, Bytes and Memory 23
Primitive Data Types 24
Integer Types 24
Floating-Point Types 24
Boolean Type 25
Character Type 25
Recap 26
Engineering Lore: Essential and Accidental Knowledge 26

5 Codes 31
Prelude: Number Systems 31
The Decimal Number System 31
The Binary Number System 32
The Hexadecimal Number System 34
The Octal Number System 35
Integer Codes 36
Unsigned Integers 36
Signed Integers 37
Another Prelude: Binary Fractions 39
Floating-Point Codes 40
32-bit IEEE754 Single Precision Floating Point Numbers 40
64-bit IEEE754 Double Precision Floating Point Numbers 42
Important Things to Remember About Floating Point Numbers 43
programming with java 7

Character Codes 43
ASCII 43
8-bit Codes 46
Unicode 47
Recap 49
Exercises 50
Engineering Lore: Problem Solving 51
Write, Draw, Visualize 51
Take Parameters to Extremes 51
Consider Small Cases 51
Create Your Own Failing Cases 51
If You Can Not Fix, Try Breaking 52
Explain the Probem to a Rubber Duck 52
Turn the Chess Board Around 52
Once it is Down, Attack all its Friends, too! 52
Example Problem: The King’s Rings 53
Example Problem: Cutting Pizza 57
Psychology of Problem Solving 60

6 Variables, Literals and Mathematical Operators 61


Declaring Variables 61
A Second Program 62
Arithmetic Operators 63
General Rules of Operands and Promotion 63
Division 64
Modulus (Remainder) 65
Operator Shorthand 66
Increment and Decrement Operators 66
Literals 68
Integer Literals 68
Integers in Other Bases 69
Floating-Point Literals 69
Use of Underscores in Literals 70
Character Literals 70
Boolean Literals 70
8 ya şar safkan

Casting 71
Exercises 72

7 More Operators and Branching 75


Comparison Operators 75
Branching 76
The if Statement 76
The switch Statement 78
Exercises 82

8 Looping 85
The for Loop 85
The while Loop 86
The do while Loop 88
The break and continue Statements 89
The break Statement 89
The continue Statement 90
Exercises 90

9 Logical Operators 93
The Logical Complement Operator ! (NOT) 93
The AND Operators, & and && 93
The OR Operators, | and || 94
The XOR Operator, ˆ 95

10 Strings 97
A Simplified Model of Computer Memory 97
Creating Strings 101
Methods of String 102
length 102
charAt 102
String Comparison 103
Exercises 105
programming with java 9

11 Mixed Exercises 107

12 Static Methods 109


The Math Class 109
Writing Your Own Static Methods 110
Calling Conventions 112
Exercises 113

13 Arrays 115
Declaring and Creating Arrays 115
Accessing Individual Elements of the Array 116
Literal Array Initializers 116
Automatic Initialization 117
The length Member Variable 117
Enhanced for Loop 117
The Arrays class 118
Better Data Structures 119
Recap 119
Exercises 120

14 Classes 123
Defining the Class 123
Member Variables 123
Methods 124
Constructors 130
The this Reference Variable 131
Access Modifiers and Encapsulation 133
Immutable Classes 134
Recap 135
Exercises 136

15 Miscellanea 141
Packages 141
Javadoc 143
10 ya şar safkan

16 Advanced Exercises 145

17 Exam Questions 149


Fall 2022, First Midterm Exam 149
Spring 2023, First Midterm Exam 153

18 Solutions to Exam Questions 161


Fall 2022, First Midterm Exam 161

19 Projects 173
The Rules of Project Work 173
Project Level and Content 174
Project: Mastermind with Six Digits 174
Description of the Game Rules 174
The Goal of the Project 175
Required Work 175
Project: Scrabble Assistant 177
Description of the Project 177
Required Work 178
Preface

Why Write This Book?

My efforts to write this book started in June 2022, with the intention
of using this as the textbook for the course I will be teaching in the
fall semester, CS101 - "Introduction to Programming".
These are interesting times - there is much interest in the area
of programming; significantly more so than the last two decades.
Young people, both students and new graduates, are as impatient
as young people have always been. They wish to forge wonders
and embark on exciting careers as soon as possible. There is also
an unprecedented amount of apparent learning methods. Online
courses, live courses, videos, tutorials, and textbooks. Most of them
are of the lowest quality possible; owing to the great demand -
anything sells.
Counting from the date of my Ph.D., my career is roughly
twenty years now. No simple description fits my career. I have
been teaching for twenty years, with very few breaks. I have been
a programmer, a software architect, a manager, a founder... I have
had many seats often with multiple hats.
I feel this puts me in a unique position to teach freshmen their
very first programming course. This is the course most of them
meet programming for the first time, and decide whether they like
it, and whether it is a viable career option for them.
I intend to design a course that will teach them the fundamentals
and provide them a solid foundation while at the same time giving
them sufficient practical information to feed their curiosity and
appease their impatience.
This textbook is one of the pillars of this course design. As such,
it is not intended to be a complete Java reference. Although modern
language features will be covered, the main focus is on the learning
experience.

To the Student

As a student, you are likely to come from a high school background


with emphasis on mathematics and the sciences. In your freshman
year, most of your courses are similar to those you have encoun- Freshman just means a first year
tered in high school and the various examinations you took on your undergraduate student. Second-year
students are called sophomores, third-
way here. Certainly, they are deeper and more solid, but they build year students are called juniors, and
fourth-year students are called seniors.
2

on familiar foundations.
Unless you have had deep personal interest and access to high
quality resources, your familiarity with programming is unlikely to
be level with your familiarity with the rest of the subjects. Program-
ming is different from the sciences; the sciences contain analysis,
understanding, and problem solving. Programming is largely a
design and synthesis activity.
The advice is clear: Pay special attention to the topic of program-
ming unless you have previous strong exposure. I am pretty sure
that as a student you are used to every high school teacher believ-
ing their course is the most important one and trying to convince
you, too. However, I am in the uncommon position that I could be
teaching your introductory physics course just as easily. Skipping
one lecture of programming is likely to hurt you much worse than
skipping one lecture of physics, especially in the first four weeks of
the term.
This course also has the distinction of being your first engineering
course. Whenever I have a class of engineers, I tend to spend some
time talking about what engineering is, and sharing some engi-
neering knowledge and lore with the students. It dawned on me to
incorporate that knowledge and lore into this book because why The toughest chains to break are those
not? So, each chapter contains a section called "Enginnering Lore", in your mind.. I had to break the
chains about what can and cannot go
which should be informative and fun to read. into a textbook.
This book represents my best effort to solidify my teachings to
students and present them to the student in a fun and digestible May the source be with you.
way. May it be useful to you.
1 Introduction

What is Java?

Java is intended to be a platform-independent, object-oriented pro-


gramming language. The idea behind Java is to have a “standard” Java is an island of Indonesia. The type
platform, where you write code just once, and it can be run on any of coffee grown on that island is called
Java, too. Java is also an American
operating system with no modification. This is no simple task, since nickname for coffee.
there are different operating systems running on different hardware
architectures.
Java has been around for quite a while now. It was designed
by James Gosling, and first appeared in 1995. It was originally Oracle was not really the best thing
developed and owned by Sun Microsystems, which got acquired by that happened to Java...

Oracle in 2010. As of 2022, Oracle is owner of the Java platform.

Is Java a Good Language to Learn Programming?

This is a long-running debate: What programming language is a


good language to learn programming? About twenty years ago, "Real programmers write in C!" "No,
we used to teach C in introductory courses. The question then was real programmers write in assembly
language!" "No, real programmers
whether to transition to Java or not. These days, we are teaching write machine code!" "No, no, no, real
Java, and the question seems to be whether to transition to Python programmers only tap zeroes and ones
into the CPU!"
or not.

Popularity

One approach is to look at "popularity" of languages.


The Stack Overflow annual developer survey is one such mea-
sure. You can see the results of the survey in Figure 1.1. JavaScript
is the undisputed champion, as it has been for ten years in a row
now. Some care is needed in interpretation of these results. First, it
is not a "single choice" answer; most people report using more than
one programming language. Second, JavaScript is the "de facto"
language for web page programming.
HTML/CSS (Hypertext Markup Language/Cascading Style
Sheets) are not programming languages. SQL (Structured Query
Language) is not a general-purpose programming language. Even the TIOBE index (see below) lists
Python is a programming language, and has gained popularity SQL among programming languages.
While writing SQL queries is not easy,
slowly and surely for years and got a big boost with data science this does not make it a programming
and machine learning libraries. language.
4 programming with java

Figure 1.1: Stack Overflow 2022 Devel-


oper Survey

TypeScript is built on JavaScript, is a strict superset of JavaScript,


and is converted to JavaScript before it runs.
Java is still quite popular, followed by Bash/Shell programming
(which is used for scripting on Unix/Linux systems) and then by
C#, which is Microsoft’s alternative to Java.
The TIOBE index is another attempt at measuring programming
language popularity.

Figure 1.2: TIOBE Index June 2022

The TIOBE web page describes the index as follows: The TIOBE index is about being able
to find resources on the web about a
The TIOBE Programming Community index is an indicator of the given programming language. What
they do in essence is comparing search
popularity of programming languages. The index is updated once
result counts for "Java Programming"
a month. The ratings are based on the number of skilled engineers and "Python Programming" across
world-wide, courses and third party vendors. Popular search engines multiple search facilities on the web.
such as Google, Bing, Yahoo!, Wikipedia, Amazon, YouTube and
Baidu are used to calculate the ratings. It is important to note that
the TIOBE index is not about the best programming language or the
language in which most lines of code have been written.

You can see the TIOBE index in Figure 1.2. Here, the results are
introduction 5

more clear. Python has finally overtaken C to reach first place. Java
is up there in third place.
You can find other measures of popularity. You will find that
Java is pretty much alive and kicking.

What is Java Good For?


In the initial years of Java, one of the main uses was planned to be
"Java applets". Applets were small applications that would run in- If a lot of the terms here are foreign
side a web page within the browser, enabling functionality that was to you, do not worry about it. All I
am trying to get through is that Java
not possible using HTML, and difficult with other technologies. has quite wide applicability in the
To make a long story short, applets were never too popular, and programming world.
they are now dead. Bringing functionality and programmability to
web pages has been fully taken over by JavaScript, and that seems
extremely unlikely to change in the foreseeable future.
Instead, Java took off on the so-called "server-side", or "back-
end". It is also one of the official programming languages of the
Android mobile platform. (The other one is Kotlin.)
It is a very good statically typed, object-oriented, garbage-
collected programming language. While modern-day Java performs
quite well, lower-level languages such as C/C++ perform better.
However, very few applications require that level of performance
optimization.
Standard Java, being run on a virtual machine tends to consume
quite a bit of memory for even the smallest program. However,
Java virtual machines do exist for restricted memory situations and
embedded systems.

What is Java not Good For?


Java is not a scripting language. Its syntax also tends to be lengthy
compared to other languages. So, it is not the ideal tool for creating
quick prototypes.

Recap

• Java is as good as any other language to learn programming.

• It is still popular, in use, alive and kicking.

• Java is mainly popular for back-end programming, and one of


the official languages for Android.

• It is also fast, only specialized programs need better optimization


than Java.

• It can be a bit RAM-hungry.

• It is not a scripting language, and may not be suitable for quick


prototyping.
6 programming with java

Exercises

Exercise 1 Check the latest Stack Overflow Developer Survey and


the TIOBE index, if they still exist. Is Java still alive and kicking? If
not, consider dropping this book, and picking up a better option to I fully expect this book to be around
start learning programming. after I am gone. I also expect parts of
it to be obsolete, while parts of it to be
as relevant as ever. At the very least, it
Exercise 2 Find out which programming languages are considered should help you get rid of itself!
good languages for scripting purposes.

Exercise 3 Are HTML and CSS programming languages? Are they


important?

Exercise 4 Is SQL a programming language? Learn the differ-


ence between a "programming language", and a "general-purpose
programming language".

Exercise 5 Find out which general-purpose programming lan-


guage is the "latest cool language".

Exercise 6 Go on YouTube. Search for videos with titles beginning


with "Most Popular Programming Languages". Most have a date
range, like 1965-2023. Watch one of them from beginning to end.

Exercise 7 Is COBOL (the name stands for COmmon Business


Oriented Language) dead? Is it popular? When do you think a The hope with COBOL was that
programming language should be considered dead? business people would be able to write
their own code, without the need for
programmers. That failed miserably.

Engineering Lore: What is Engineering?

What is an engineer? I like to look up the etymology of a word Well, "etymology" itself also needs a
when trying to answer such questions. Looking up "engineer" in definition - the word etymology stems
from the Greek word etumos which
the Online Etymology Dictionary yields the following: means true. As such, etymology is the
study of true meanings of words.
engineer (n.) mid-14c., enginour, "constructor of military engines,"
from Old French engigneor "engineer, architect, maker of war-
engines; schemer" (12c.), from Late Latin ingeniare (see engine);
general sense of "inventor, designer" is recorded from early 15c.; civil
sense, in reference to public works, is recorded from c. 1600 but not
the common meaning of the word until 19c (hence lingering distinc-
tion as civil engineer). Meaning "locomotive driver" is first attested
1832, American English. A "maker of engines" in ancient Greece was
a mekhanopoios.

In modern use, we use the term engine to mean a machine that


converts other forms of energy into mechanical energy. Once again,
the Online Etymology Dictionary to the rescue:

engine (n.) c. 1300, "mechanical device," especially one used in war;


"manner of construction," also "skill, craft, innate ability; deceitful-
ness, trickery," from Old French engin "skill, wit, cleverness," also
introduction 7

"trick, deceit, stratagem; war machine" (12c.), from Latin ingenium


"innate qualities, ability; inborn character," in Late Latin "a war en-
gine, battering ram" (Tertullian, Isidore of Seville); literally "that
which is inborn," from in- "in" (from PIE root *en "in") + gignere,
from PIE *gen(e)-yo-, suffixed form of root *gene- "give birth, beget."
Sense of "device that converts energy to mechanical power" is 18c.; in
19c. especially of steam engines. Middle English also had ingeny (n.)
"gadget, apparatus, device," directly from Latin ingenium.

So, the roots of most engineering lie in the military and war.
Why our structure-building, bridge-designing colleagues are called Figure 1.3: Trebuchets. Castle of
"Civil Engineers" rather than "Construction Engineers" or "Structure Castelnaud, France. These are consid-
ered to be siege engines, and the people
Engineers" is pretty much because of this. building them would have been called
If we attempt to define modern engineering, we can say skill, engineers.
craft, and abilty are still part of the job. We still build things, how- The joke is, Civil Engineers build
targets.
ever the definition of what we build has extended beyond mechan-
ical devices and structures to electrial, electronic, chemical, and
computational gadgets, devices, and contraptions.
In addition to wit, cleverness, and skill, the modern engineer has
to learn and make extensive use of the sciences and mathematics.
The main difference between a scientist and an engineer is that
the engineer intends to use available science to build her devices
- although it sometimes happens, it is not her intention to further
human knowledge by expanding the boundaries of science.
In the modern definition, we reject the meaning of the word
in the sense of deceitfulness and trickery. In fact, part of modern
engineering is the attempt to remove uncertainty and suprise, to
create order out of chaos instead.
So, here is my crystallized one-sentence definition of what an
engineer is:

An engineer is a skilled craftsperson who solves problems and


builds things utilizing the knowledge provided by science and
mathematics.

This should answer at least some of your questions about why


the engineering curriculum contains so many science and math-
ematics courses. Those are a large part of what differentiates an
engineer from other craftspeople.
2 Starting with Java

Despite the name of the chapter, first we need to learn a few things
about programming concepts.

Programming Concepts

Source Code
Programming is a human activity. What a programmer writes in a
programming language is called source code. Source code usually
resides on files on a computer, and is human readable. Well, at least it is supposed to be human
Here is an example of Java source code. This content would readable.

normally go into a file called LatticePoint.java: All you are expected to understand
from the source code is that you can
package tr.edu.ozyegin.exam;
read the words in there! If you have
import java.util.Objects;
no idea what is going on, you are just
fine.
public class LatticePoint {
private int x;
private int y;
private int z;

public LatticePoint(int x, int y, int z) {


this.x = x;
this.y = y;
this.z = z;
}

public int getX() {


return x;
}

public int getY() {


return y;
}

public int getZ() {


return z;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof LatticePoint) {
LatticePoint other = (LatticePoint)obj;

return this.x == other.x


&& this.y == other.y
&& this.z == other.z;
} else {
return false;
}
}
10 programming with java

@Override
public int hashCode() {
return Objects.hash(x, y, z);
}
}

Machine Code
Machine code, also called "machine language" is the code that the
CPU cores in your computer can read and execute. This is the CPU stands for Central Processing
only thing that the CPU can "run". However, it is very difficult for Unit.

human beings to understand, and next to impossible to modify for


any meaningful purpose.
As a human, you can only write source code. The computer
can only run machine code. Clearly, you need to somehow convert
source code into machine code.

Interpreter
Some languages are interpreted. This means that, there is a special Of course, things are much more
program called an interpreter that converts your source code line-by- complicated in reality. For instance,
JavaScript is said to be interpreted,
line to into machine code, and runs it. however it does get compiled too.
Many languages employ a mix of
things. This "technology" thing is
Compiler bound to give you a headache.

Some languages are compiled. In this case, there is a program called


a compiler that takes source code, and converts it to machine lan-
guage, which can be directly executed by the CPU.

Virtual Machine
A virtual machine is yet another special kind of program which
emulates a CPU. However, it emulates a CPU which does not really
exist.
The kind of code the virtual machine understands and can exe-
cute is known as bytecode.
The virtual machine employed by Java is aptly called the Java
Virtual Machine (JVM).
In essence, you can think of the JVM as an interpreter for Java
bytecode.
How do you produce the bytecode? You compile Java source
code into bytecode using the Java compiler (normally a program
named javac).
Why this complication? The idea is to make Java bytecode uni-
versal (you compile it only once) and be able to run it on any plat-
form for which you have a virtual machine for.
There are different versions of the Java Virtual Machine for dif-
ferent platforms; there is a JVM for Windows, and there is a JVM
for MacOS.
starting with java 11

Library

A library in the "real world" is a building that holds books con-


taining the collective wisdom of authors. In the context of pro-
gramming, when we say library we mean a code library which is a
collection of partial programs to be utilized by other programs.
There is quite a large collection of libraries for Java; some are
part of the platform, and some are even part of the language defini-
tion.

JRE and JDK


We are getting Java specific now.
JRE stands for Java Runtime Environment. This contains the JVM
and the standard Java libraries to execute programs. If all you want
to do is to be able to run existing Java programs on your machine,
you only need to download and install the JRE for your particular
computer and architecture.

Figure 2.1: Conceptual figure of the


Java Runtime Environment (JRE) and
Java Development Kit (JDK).

JDK, which stands for Java Development Kit, is a superset of the


JRE and contains programming tools in addition to the JVM and
libraries. The tools include the compiler and the debugger, among
others. Since what we want to do is programming, this is what
needs to be present on your computer.
You can see their conceptual realtion in Figure 2.1.

Integrated Development Environment

You need some sort of text editor to write your source code. Then,
you need to employ the Java compiler to compile it into bytecode.
Then, you need to employ the Java Virtual Machine (this is the java
command) to execute it.
There are programs (of course) that perform all these functions
together in a seamless way. These are called Integrated Development
Environments, or IDEs for short.
There are many IDEs available for Java, as a quick web search for
"Java IDE" will reveal.
Three of the long-time contenders are:
12 programming with java

• Eclipse (eclipse.org) Eclipse is free and open source software,


distributed under the EPL (Eclipse Public License).

• IntelliJ IDEA (jetbrains.com IntelliJ IDEA is proprietary soft-


ware, written by Jetbrains. There is a free community edition
distributed under the Apache 2.0 license, which means it is free
for both personal and commercial use.

• Netbeans (apache.org) Netbeans is free and open source soft-


ware, distributed under the Apache 2.0 license.

Recap

• Source code is what humans write to tell the computer what to


do. It consists of text files.

• Machine code is what a CPU core can understand and execute.


Not really for humans.

• An interpreter is a program that executes source code line-by-


line, converting it to machine code at every step.

• A compiler is a program that converts source code into machine


code.

• Some compilers convert source code into an intermediate code,


called byte code.

• A virtual machine is a program that runs bytecode. This makes


the same bytecode able to run on different hardware and archi-
tectures.

• A library is a collection of program pieces usable by other pro-


grams packaged together.

• The JRE (Java Runtime Environment) is what you need to run


Java programs. It includes the JVM (Java Virtual Machine) and
standard Java libraries.

• The JDK (Java Development Kit) is a superset of the JRE, con-


taining programming tools. The tools include a compiler and a
debugger, among other things.

• An IDE (Integrated Development Environment) is a program


that lets you write, compile, run, debug programs in a single
environment.

Exercises

Exercise 1 The following executables (programs) are present in


JDK version 20:
starting with java 13

jar jcmd jhsdb jpackage jstatd


jarsigner jconsole jimage jps jwebserver
java jdb jinfo jrunscript keytool
javac jdeprscan jlink jshell rmiregistry
javadoc jdeps jmap jstack serialver
javap jfr jmod jstat
Find out what these do, for as many of these entries as you can.
Some of them can be hard, if not impossible to grasp with your
current knowledge, so do not be discouraged.

Exercise 2 Find out what other virtual machines exist apart from
the Java Virtual Machine, JVM.

Engineering Lore: Incremental Improvement

One of the most important mechanics of engineering as a discipline


is incremental improvement. The idea is that with each new design
of the same thing you incorporate what you learned from the previ-
ous designs into the process, making the new product better than
the previous one. For example, the basic elements of car design
were pretty much established by the time the Ford Model T hit the
market in 1908.
The modern car is much improved in almost all aspects. Fuel
Figure 2.2: The Ford Model T. Four
efficiency, comfort, passenger safety, engine power, steering, aerody- wheels, engine, headlights, steering
namics. Even game-changing technolgies, such as the introduction wheel, windshield - all the basic ele-
ments of a car. This can be considered
of electronic components into cars happened incrementally.
to be the beginning of the incremental
In most fields of engineering, once you have designed some- improvement process for cars.
thing, that something has an existence in the physical world. It has
to be built, assembled, or manufactured. For a car, a new model takes
about 72 months to hit the showroom starting with a new idea.
And, this is given all the previous knowledge of existing models,
and no fundamental changes to the basic design.
The field of software engineering is quite different. One funda-
mental difference is that the cost of replication (the production of
additional copies when the prototype is built) is essentially zero.
As a side effect, this tends to mask the fact that the production of There is a whole world of copy protec-
software is a design activity, and not a production activity. Since peo- tion techniques just because of the ease
of replicating software.
ple involved in the production of software are designers (and more
often than not engineers) it is perceived to be very expensive and
slow.
The second fundamental difference (which is also the cause of the
essentially zero cost of replication) is that software has no physical
component. It is all information. Scientists, engineers, architects are
known as knowledge workers, since most of their work is centered
around processing, combining, and producing new knowledge. According to Wikipedia: Knowledge
According to this definition, software engineering, having no other workers are workers whose main capital
is knowledge. Examples include program-
component is the pinnacle of knowledge work. mers, physicians, pharmacists, architects,
These facts coalesce to bring about the main difficulty of soft- engineers, scientists, design thinkers,
public accountants, lawyers, editors, and
ware engineering. Because replication costs are zero, and because academics, whose job is to "think for a
living". So there, nobody should feel
offended.
14 programming with java

it is all information, almost all new software projects require fun-


damentally new and different designs. As engineers working on those
projects, you only have bits and pieces of previous designs to help
you tackle these problems. These "bits and pieces" have improved
by a lot over the decades, but expectations have also scaled up.
The only known solution to this is to build the incremental im-
provement process into the project itself. This is known as agile
software development; although it is much older, its modern for-
mulation is Manifesto for Agile Software Development.
Looking from the negative end, the main idea here can also be
expressed as follows:

Nothing new ever works. The credit for this law goes to Gerald
M. Weinberg, in his book "The Secrets
There are always new technologies on the market. New libraries. of Consulting". In its expanded form,
the law is Nothing new ever works,
New programming languages. New communication protocols. but there is always hope that this
New platforms. time will be different. and he names
the law "Pandora’s Pox".
This, of course, is not a bad thing. In fact, it is a good thing - this
is how progress happens. The problem is with the human disease
(Pandora’s Pox - see the margin) of hoping to circumvent the law
that nothing new ever works. An engineer should be immune to
this disease. There are two ways: Get the disease, suffer its effects,
and then build an immunity. Or, get vaccinated here and now: New
technologies are fine, but always use technologies that have gone
through the process of incremental improvement in your designs.
Let us close by saying that Java has gone through quite a bit of
incremental improvement. So, it makes better engineering sense to
use a language that has had over 25 years of incremental improve-
ment by a large community of contributors to a language that was
invented two years ago but looks very cool and shiny.
3 Hello, World!

A Short History of "Hello, World!"

"The C Programming Language", by Brian W. Kernighan and Den-


nis M. Ritchie was the seminal book of C. The first program found This book is also known as K&R.
in it is in Figure 3.1. Kernighan and Ritchie are also known
as "the prophets".

Figure 3.1: The C "Hello, world!"


program. Capitalization and punctu-
ation are apparently not something
Kernighan and Ritchie cared about
much. Oh well.

Since then, it has been customary to use a program that prints


"Hello world!" (in some form).
We will follow this tradition.

The Java "Hello, World!" Program

So, here is the "Hello, world!" program in Java:


public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

A Java program even this small contains a lot of keywords and


concepts. While it is not possible to fully grasp the meaning of This is what people mean when they
every single thing in this program at this point, we will try to make say "Java contains a lot of boilerplate
code". Boilerplate code is the code you
sense of every line in this three-line program. just have to write even though it does
not serve your actual purpose.
public class HelloWorld {

Everything in Java must be enclosed in what is called a "class".


Java is an object-oriented language, and a class is the way to define
types of objects. Java is forcefully and zealously object-oriented, and
16 programming with java

makes you put everything into a class even when you have no
intention of working with objects.
So, this first line declares a class. "HelloWorld" is the name of the
class, for lack of a better choice - it hopefully conveys our intention.
The word "class" is a keyword in Java. Keywords are special
words defined by a language that can not be used for other pur-
poses (naming things).
The word before that, "public" is another keyword. It signifies The keyword public is one of the
that whatever follows, can be accessed from anywhere else in the access modifiers in Java. More on this
later.
program. It can be accessed by the "general public", so to speak.
The opening brace is also significant. It marks the beginning of These things {} are called braces, curly
the "body" of the class we just defined. Braces are used to mark the brackets, or even curly braces. For
brevity, I prefer braces, and I will use it
start and end of many things, and classes are just one of them. consistently.
public static void main(String[] args) {

The things that contain instructions to be executed by the CPU


cores are called "methods" in Java. Pretty much like everything In many other programming lan-
else, methods live inside classes. guages, especially where they are not
inside classes, these are called "func-
Methods can "call" (call means to execute) other methods. But tions". In Java such functions do not
of course, a program must start somewhere. There must be an entry exist, and therefore the terminology
"function" is never used.
point. That entry point in Java is the main method. And, it must
be declared exactly as it is shown in the code, you can not even
change a single letter of it. There is no point to going into the detail
of every keyword here; consider this a "magic incantation" for now,
where you have to get every letter right, otherwise it will not work.
There is some more information in Figure 3.2.

Figure 3.2: Some more information


about the parts of the "main incanta-
tion" in Java. The explanations may
raise more questions, they will all be
answered eventually.

The opening brace is again, significant. It marks the beginning of


the main method.
System.out.println("Hello, world!");

Finally, something that does something. This line calls another


method, whose job is to print a line of text on the screen. You
should intuitively understand that System.out means something like
"the output of the system", and you would be conceptually correct,
hello, world! 17

and we will leave it at that. We are calling a method, whose name is


println and that stands for "print line" (and it is pronounced like If Java was designed today, the
println method would probably
that). When you are calling a method, the method name is always
have been named printLine. Abbre-
followed by a pair of parentheses, and inside those parentheses are viation rather than ease of reading
what are called arguments to the method. Arguments are the things and understanding was still the "social
norm" of programming in the late
you give the method to work on and do its job. Here, our argument nineties when Java was designed.
is a piece of text in double quotes.
In Java, such pieces of text are called Strings, and they are al-
ways enclosed in double quotes. Here, we are giving the String
"Hello, world!" to the method to get it printed on the screen.
So, what we are saying is this: Dear println method, who lives
inside out (whatever that is) who lives inside System (whatever that
is) , take this String "Hello, world!" and do your job please.
The semicolon at the end is significant. All statements (you can
think of a statement as a complete line of code) must end with a
semicolon in Java.
The next two lines are just closing braces for the main method
and HelloWorld class respectively.

Running the Program

Java is also picky about filenames. Since we have named our class When you use an IDE, as you are
HelloWorld, our program must go in a file called HelloWorld.java. expected to do matching class names
with file names comes (almost) natu-
If the filename and the name of the class inside do not match, the rally. However, you may get surprised
Java compiler will generate an error. when you change the class name
manually and start getting an error.
Running the program without an IDE is possible, however is
not instructive at this point. Getting your IDE of choice to run the
program is IDE specific, so it will not be covered here.

Whitespace and Style

Naturally, all words in Java are separated by spaces. Space is not


the only character you can use to separate things. Tabs, newline
characters and some other (rarely used) characters are also valid
separators. All these characters are collectively known as whitespace.
In Java, apart from their role of separating tokens , whitespace A token is a term from the world of
is not part of the syntax. As such, the format of the code is not compilers used as a general name
for every meaningful combination of
specified by Java itself, it is left to "style guide"s and personal pref- characters in source code – names of
erences. things, keywords, constants. . .
To demonstrate, the following code is the same exact Hello
World program as far as Java is concerned:
public

class

HelloWorld {
public static
void main(String[]
args) {
System
.out
18 programming with java

.println("Hello, world!"); }}

However, it is not the same to us humans. This is why coding


styles exist. We are not going to go into any detail about coding
style at this point; but all code you see in this book will be consis-
tent in style. Except the horrible example here.

Comments

Comments are things you write into your source code that is only
intended for other human beings to read. The computer will ignore
them. Java, similar to most C-descendant programming languages There are also specifically formatted
has two (main) forms of comments. "Javadoc" comments, which can be
extracted by tools to create documenta-
The first form of comment is known as the "block comment" or tion. But we will not bother ourselves
"multi-line comment". The comment starts with a "/*" and ends with that.
with a "*/". These need not be on the same line. A block comment
can contain anything, except the combination of characters "*/"
which would mark the end of the comment. The block comment is the original
The second form of comment is called a "single line comment". form of commenting in the C lan-
guage.
The comment starts with a "//" and continues to the end of the
line. This kind of comment can contain anything, except the end-of-
line character.
The following modified "Hello, world!" program demonstrates
both types of comments:
/* The customary "Hello, world!" program in Java.
Unfortunately, this is not as simple as we would like.*/
public class HelloWorld {
public static void main(String[] args) {
// Do the actual printing here:
System.out.println("Hello, world!");
}
}

When to Comment, What to Comment?


If you look around, and read about comments and commenting,
you will find a lot of people defending the following idea:

Comment everything, the more comments the better! WRONG!

You can also find, or meet later, professors that still defend this
idea. These people are all seriously misguided. The correct under-
standing of the proper use of comments is as follows:
A comment is an apology to the person reading your code. By
commenting your code, you are saying "I am sorry, I could not
write the code simply and clearly enough, so now I have to explain
it to you in human language."
You should first spend enough time on your code to make it as
simple and as easy to understand as you can. To do this, you need Note that the rules for Javadoc com-
to learn about proper ways for naming things (classes, methods, ments are a bit different.

variables . . . ) and writing "clean code". And only then, if you can
hello, world! 19

not express your intent by properly naming everything and match-


ing the form of the code to its purpose, you should consider writing
a comment to apologize for not being able to do this, and explain
things in human language.
There is more depth to writing comments, but this is as far as we
are going at this level.

Recap

• A "Hello World!" program is a program that prints "Hello,


World!" on the screen.

• It is a tradition started in the book "The C Programming Lan-


guage".

• Everything in Java goes into a class. We do not fully understand


yet.

• A method is something that a computer can execute. It also goes


in a class.

• The execution of a Java program starts at the main method.

• It must look like public static void main(String[] args). We


do not fully understand why yet, either.

• System.out.println(<something>); prints that something on


the screen.

• "Hello World!", or anything that is inside double quotes is a


String. We only understand it is how the computer stores text.

• Braces {} mark the beginning and end of classes and methods.


They seem to be important.

• Java does not care about the exact use of whitespace, except
proper separation of tokens.

• Yet, using whitespace well to make code readable is good style.


Style is important.

• Java has /* Block comments */ and // Single line comments


containing text for other humans to read.

• Do not comment everything! Comment only when you can not


write clear, clean code.

Exercises

Exercise 1 Find out what the "Hello, world!" program is in the


Python programming language. Compare with the Java version.
Can this be part of the reason why Python has become so popular?

Exercise 2
20 programming with java

Engineering Lore: Quality, Value, and Effort

A good understanding of quality is essential for all kinds of work,


not just engineering. However, the work of engineers tends to have
greater effect all around making the discussion more important.
You may think of the quality of something as an intrinsic prop-
erty. In other words, that it is possible to assign an objective mea-
sure of quality to something. The fact is that this is only an illusion.
The fact is that all measures of quality are assigned by human be-
ings. The illusion that some things are intrinsically of higher quality
than others is due to shared values in certain cultures - it is just
collective assignment by a group of humans.
The fact is quite simple:

Quality is value for someone. The credit for this definition also goes
to Gerald M. Weinberg, in his book
"How Software is Built".
So, quality is value assigned to something by someone. As such,
quality is subjective. As already stated, this understanding is central
to all work - you need to create and deliver value - to someone. So,
what is valuable is defined by who your customer is. This is exactly
where the saying "The customer is always right" comes from.
Of course, your customer is not always easy to identify. And
your customers may not agree on what is valuable. You may have
to choose whom to keep happy.
In a university course, your customer is the professor. This is ex-
actly because the professor decides what is valuable for the course.
The quality of your work is graded according to the value judge-
ments of the professor. You may think that at least in engineering Oh dear, are you in trouble or what?
a question has a definite answer but is usually does not. Even if it
did, the value judgement of the professor asserts itself in the choice
of what is asked.
In a rational world, the customer should only care about the end
result they receive. However, the world is not really rational. The
case where the customer ascribes value to the effort has been named
"The Locksmith’s Paradox" by Dan Ariely. Dan Ariely is a professor of psychol-
The story, as told to Dan Ariely by a locksmith is as follows: ogy and behavioral economics at Duke
University. His most groundbreak-
When the locksmith was just an apprentice, he usually took a long ing (in my opinion) book is titled
time to open locks. Occasionally, he would break the lock, and had "Predictably Irrational".
to replace it. The customers would pay for his labor, the replace-
ment lock, and give him a tip, and even thanked him. After so
many years, he became a master locksmith, now opening locks in
under a minute and without any damage. While the end result the
customer is getting is better and quicker, they do not tip him, and
they complain about the high fees he charges rather than thanking
him.
I had a similar experience back in 1998, when I was working at
the Los Alamos National Laboratory in New Mexico as a graduate
student. I managed to lock the key in my car with the engine still
running. I called a locksmith. He said right on the phone that his
fee will be $40 if he shows up. I agreed (well, I felt so stupid, I
hello, world! 21

would have agreed to more.) He showed up, and it took him about
eight seconds to open the door. I paid him his $40, and he left. He forced the top of the car door with
I could see the reason he told about the fee up front. People feel a plastic covered crowbar, reached in
with a long metal arm, and unlocked
ripped off because it was so quick. Personally, I did not feel ripped it from the latch on the handle. He did
off at all. If anything, I admired the guy for his knowledge and skill, not even touch the keyhole. In case
you were wondering.
and was very satisfied that the problem got solved so quickly.
Ascribing value to effort and/or labor is yet another human
disease. It tends to penalize high skill and efficient work, instead
idolizing wasted effort. As you can tell by the anecdote above, I was
unaffected by this disease so many years ago, and I am quite im-
mune to it today. I ascribe no value to effort that does not produce
valuable results.
Seemingly paradoxically, I value and respect human effort and
labor greatly. True respect for human effort is shown by preventing
its waste, and making sure it is used effectively and efficiently.
Pronouncing some useless, worthless result valuable just because a
lot of effort was expended on it is the worst kind of disrespect for So, arguments like "I worked really
human effort and labor. It encourages further waste. When faced hard on that homework, so I should
get a better grade" will utterly fail. You
with wasted effort, the best you can do is explain why the end can not ask for a better grade, but you
result is worthless, and give honest yet respectful feedback. can ask for feedback on how not to
waste your efforts next time.
Ultimately, the work of an engineer to build things valuable for
a customer effectively and efficently. Effective means that it does
what it is supposed to do, and efficient means that it uses the least
amount of the triple trade-off tiangle: Money, time, people.
4 Data Types

Bits, Bytes and Memory

No doubt being a user of computers, you are used to the computer


transmitting, displaying and manipulating all sorts of data for
you. The computer can work with text, sounds, images, and even
movies.
It is also often said that "The computer only knows about zeros
and ones". How do these zeros and ones get combined into all that
stuff?
The first secret is that there are seriously a lot of zeros and ones in a
typical computer. You can see the amount of memory reported by
the computer I am typing on in Figure 4.1.

Figure 4.1: The "About This Mac" win-


dow of the computer I am currently
typing on. It reports 64GB of RAM.

So it reports 64GB of RAM. What does this mean? (Well actually


it says "memory".) Let us decipher what that means. First, RAM
stands for Random Access Memory. The word "random" is confus-
ing. This just means the following: This is the memory which the
CPU cores can reach any piece of it directly. A better term for RAM
could have been Arbitrary Access Memory, but that probably does
not sound as good.
The GB stands for gigabyte. Giga is just a number prefix (more
on that a bit later). What is a byte then? The answer is simple: A
byte is eight bits packed together. What is a xont? Well, it is a group of
So, what is a bit then? Remember the zeros and ones? A bit vrants.

is the thing that can be either a zero or a one. The name "bit" is a
contraction - it stands for binary digit.
As a mental image, you can think that a byte looks something
like Figure 4.2.
24 programming with java

Figure 4.2: A way of visualizing a byte


0 1 1 0 0 1 0 1
consisting of eight bits.

Each of the cells is a bit, and the number inside it is its value.
The whole rectangle is a byte. Note that any bit pattern (or this
particular bit pattern) does not automatically mean anything at all. A code is a table which lists corre-
We assign meaning to bit patterns according to our wishes - what spondence between bit patterns and
symbols. There is no way to know
we want to represent using them. what a particular bit pattern means
Back to the question of how many bits of RAM my computer without knowing what the code is.
contains: Believe it or not, my computer contains: If we answered this question back
in 1984 when I got my Commodore
64, the answer was 524 288 bits. The
549 755 813 888 bits of RAM amount of RAM in "my computer" has
increased by a factor of 1 048 576 in 38
That number is calculated as follows: Due to historical reasons, years. That is roughly a 44% annual
when talking about RAM capacity, a gigabyte is not 1 000 000 000 increase!

bytes, but rather 230 = 1 073 741 824 bytes. Multiply that by 64 to get
the total number of bytes, and then multiply the result by eight to
find the number of bits.

Primitive Data Types

Java provides us with a few primitive data types to represent the


most basic data types used in computing. The primitive data types
are:

• Integer types (byte, short, int, long)

• Floating point types (float, double)

• Boolean type (boolean)

• Character type (char)

Integer Types
These are used to represent integers, positive and negative. The
only difference of the four types is their sizes, and correspondingly,
limits of the values they can take. Figure 4.3 summarizes this infor-
mation.

Type Minimum Value Maximum Value Size in Bytes Figure 4.3: Integer types in Java, their
minimum and maximum values, and
byte −128 127 1 sizes in bytes. The "default" type to
short −32 768 32 767 2 use, unless you have reason to do
otherwise is int.
int −2 147 483 648 2 147 483 647 4
long −9 223 372 036 854 775 808 9 223 372 036 854 775 807 8

Floating-Point Types
The name is a bit intimidating - saying "integers" was fine. Ev-
eryone has heard of integers. What is a floating-point number? A
floating-point number is an approximate, limited precision way a
computer can use to represent real numbers. A few mathematicians probably want
to strangle me now. I do not care!
data types 25

You may be familiar with scientific notation, where we represent


numbers like:

1.60217663 × 10−19

In this form, the part to the left (1.60217663) is called the signif-
icand, and the number in the exponent (−19) is called the exponent
(duh!). The significand holds the significant digits of the number, You can also think of the exponent
while the exponent represents the magnitude (size) of the num- as telling where the decimal point is.
It can make the decimal point "float
ber. A floating-point number packs these two things together to around" when its value changes. This
represent numbers. is the origin of the name "floating
point".
The two different floating-point types have different number of
Of course, everything is in base two
significant digits and range for the exponents. These are shown in rather than base ten. But the essence is
Figure 4.4. Both types conform to the "IEEE Standard for Floating- exactly the same.
Point Arithmetic", also known as IEEE 754.

Type Smallest Value Largest Value Size in Bytes Figure 4.4: Floating-point types in Java.
float ±1.4 × 10−45 ±3.4 × 1038 4
double ±4.9 × 10−324 ±1.7 × 10308 8
With a float, you get a bit over 7 significant decimal digits (this
includes the digit to the left of the decimal point). A double will What really happens is, float reserves
give you almost 16 significant decimal digits. Although the name 23 bits for the significand. However,
since for any binary number, the
double is meant to mean double precision, it actually gives you more leftmost bit is always 1 (think about
than double precision. it - with the exception of zero) it is
not stored but implied. This gives
Note that a float has the same size as an int (both 4 bytes or 24 bits precision. In decimal, that
32 bits) and a double has the same size as a long (both 8 bytes or corresponds to 24 · log10 2 = 7.225. A
double reserves 52 bits giving 53 bits
64 bits). This means the same bit patterns mean different things
of precision with the implied bit. That
- their coding is different. As such, the computer will do different results in 53 · log10 2 = 15.955 decimal
things when you try to add two ints and two floats. The data type digits of precision.
defines the coding, and the rules of operating on the bit patterns to
get the expected results. This is the heart and soul of data types.

Boolean Type
Yet another strange name. This time, it is the name of a person,
George Boole (1815-1864) an English mathematician, philospher,
and logician. His first book, "The Mathematical Analysis of Logic"
introduced what is now known as Boolean algebra. Boolean algebra
is the algebra of truth values - true and false.
The Boolean type is a type which can take on only two values -
true and false. It should be obvious that such a thing requires just
one bit to store. The boolean type is the simplest type there is, but
it is also the only primitive type the Java standard does not define a
size for.

Character Type
Remember the String "Hello, world!"? Each letter, comma, and
exclamation mark in there is a character. The primitve type char
can hold one character. The size of a char is two bytes. Once again,
26 programming with java

although it has the same size as a short, the meaning it assigns to


each bit pattern is completely different.

Recap

• It is true that computers use only zeroes and ones to store infor-
mation. But computers have a lot of them.

• The "thing" that can be 0 or 1 is called a bit. The name is a con-


traction of "binary digit".

• Eight bits make a byte.

• Java provides primitive types to store integers, floating point


numbers, booleans and characters.

• There are four integer types

– byte, one byte, stores numbers −128 to +127


– short, two bytes, stores numbers −32768 to +32767
– int, four bytes, stores numbers from negative two billion
something to positive two billion something.
– long, eight bytes, stores seriously large integers, you can not
remember, look it up when you need to.

• There are two floating point types. These conform to a standard


called IEEE754.

– float, four bytes, can go to 1038 , gives about 7 significant


decimal digits.
– double, eight bytes, can go to 10308 , gives about 16 significant
decimal digits.

• There is a boolean type, which can take the values true or false.
Its size is unspecified.

• There is a char type, which can hold a single character. Its size
is two bytes. The heart and soul of data types is that each data
type assigns a different meaning to the same bit pattern. The bit
patterns do not mean much by themselves, but we are free to
assign them any meaning we need.

Engineering Lore: Essential and Accidental Knowledge

Aristotle is a Greek philosopher who lived between 384 and 322 BC.
He made the distinction between essential and accidental properties Note that accidental here is not used in
of an object. By essential he means those properties that the object it common meaning of "an unwanted
event, possibly causing harm". You
in question must have - they are part of its identity, or essence, so to can think of it as "something that just
speak. On the other hand, accidental properties are properties that it happened to exist".
just happens to have - it may have these properties independently
data types 27

or in relation to its essential properties. The loss or addition of acci-


dental properties does not cause a change is the essence of the object
in question.
In the same way, we can classify our knowledge about things as
"essential" and "accidental". For example, when you get a driver’s
license, you get it for the essential knowledge of how to drive a car.
It is thought that any car you are going to drive will have the same
essential properties, or those things are not cars. A steering wheel,
gas pedal, brakes, signaling, headlights, mirrors are all considered
essential properties, and you need to know how to operate and use
each of these to get a driver’s license. At least in theory. Many drivers on
On the other hand, every car also has other, accidental proper- the road make me feel they got their
licenses via mail-order.
ties. It can have manual crank windows or power windows; it can
have air conditioning or not, it can have seat warmers; it can have
manual or automatic transmission; it can have a radio, navigation In the case of cars, you need to pay
system... Each brand, model and make of car tends to be slightly money to make most of these "acci-
dents" happen.
different in these accidental properties.
This does not mean things can not change essentially. The en-
gine starter crank disappeared totally, once electric starter motors
became a part of every car produced. One can also argue that the
essential property was that the engine needs a starter, the fact that
it was provided manually was the accident. However, we seem
to be moving to electric cars, with electric motors, which have no
need for a starter engine - only internal combustion engines do. So,
Figure 4.5: For the first few decades of
are electric cars not cars? Was the need for a starter essential, or cars, the engine starting crank was a
accidental? My answer is that a starter is essential for an internal fundamental part of a car. The figure
shows the crank of a Rolls Royce, no
combustion engine, while the use of an internal combustion engine less.
in a car was accidental.
How will these notions change when self-driving cars become
the only type of cars in existence?
Philosophy is hard.
There seems to be (at least to me) an inevitable connection with
time in the consideration of essential and accidental. Whether we
consider something essential or accidental has something to do
with its rate of change relative to other things. What tends to re-
main unchanged for longer periods of time is more on the essential
side, while what tends to change quickly is more on the accidental
side.
The field of software engineering seems to be changing very
rapidly - apparently more rapidly than the university system can
keep up with. Everything seems to change from the time you start
as a freshman until the time you graduate. But is this really so?
The knowledge of software engineering and programming can
also be partitioned into essential knowledge and accidental knowledge.

Essential knowledge takes a long time to learn, because it


needs to be distilled from multiple sources, and experiences. How-
ever, it is more useful, and valid for longer periods of time.
28 programming with java

Accidental knowledge is relatively quick to learn. It is also


required to both do something and build essential knowledge.
For a beginner, essential knowledge often seems like unneces-
sary and useless. It is useless in the sense that it does not help with
doing things, or answering exam questions. It is not unnecessary Well, essential knowledge can be made
because it will shape your essential understanding of programming into exam questions, but they tend to
be either very easy or very hard.
and software engineering.
Let us give some examples of essential and accidental knowl-
edge.

• The definition of essential and accidental knowledge is essential


knowledge. The examples given are accidental knowledge. How is this for a meta example?

• The particulars of any programming language is accidental


knowledge. Most of the things you learn specific to Java in this
book are examples of accidental knowledge.

• Java data types are accidental knowledge. Understanding of


codes, including integer and floating point codes is essential
knowledge.

• Writing clean and simple code is essential knowledge.

• Structure and organization of code is essential knowledge.

In undergraduate education, the goal should be to transfer as It is not about the exams. It is about
much essential knowledge as possible. If you graduate equipped what you learn.

with a lot of accidental knowledge, yet little essential knowledge,


the effect of your education will be very short-lived. The culture
that exists in many companies will all but prevent you from gaining
any essential knowledge - exactly because it has no direct, short-
term application.
So, especially in an age where the stream of knowledge has
reached great volume, it is much more important than ever to focus
on essentials rather than accidentals.
This does not mean that everything being taught to you is in fact
essential. There are truly essential changes to the field, and some
effort is required on part of the professor to pick and choose what
to keep, and what to discard. The ugly truth is that not all professors
find efforts to update courses worth-
while, making their courses static and
Memory management was thought to be essential to program- stale.
ming. Lately, since most modern programming languages have Memory management is the art of
garbage collection by default, we are not teaching that any more. asking for memory when needed, and
returning it when done. If you forget
to return it, you create garbage, and
The C language was considered essential, and was taught as the situation is called a memory leak
because that memory is lost. Garbage
the first language for about fifteen years. We have dropped it, and collection is like my mother, cleaning
adopted other, more practical programming languages. The C lan- up my mess after me.
guage is still important for systems programming and embedded I still have much respect for those
who have good command of the C
applications, but is not considered essential knowledge for a program- language. The main reason is they
mer. have a more intimate understanding of
how things work at a lower level, and
do not tend to look for code libraries
for simplest of tasks.
data types 29

Database management systems was considered accidental


knowledge. It existed as an elective course in most computer en-
gineering programs. Now, it is in the main curriculum in most
schools. It is now considered essential knowledge.

Source control systems were considered accidental knowl-


edge. Apparently, in most places they are still considered accidental
knowledge. I do believe that the principles of source control sys-
tems and the concepts involved have become widespread enough to
be considered essential knowledge.
Choose your elective courses wisely. What is considered acciden-
tal knowledge today may the essential knowledge tomorrow.
5 Codes

Strictly speaking, this chapter is not really related to Java program-


ming, and most, perhaps all of it can safely be skipped to come
back later. While this knowledge is fundamental, it is not relevant
for the practice of programming, which is usually of more interest
for a beginner.
While the subject of codes in general is quite wide, we will re-
strict ourselves to codes relevant to the data types in Java.

Prelude: Number Systems

The Decimal Number System

The number system we use is a positional number system. So, when


we talk about the number 6742, each decimal digit represents a Why a decimal (base ten) number
value dependent on its position. The 2 represents just two, because system? Because we have ten fingers.
How different would life have been if
it is the rightmost digit. The 4 represents forty, because it is one we had eight? Or twelve?
place to the left of 2. The 6 represents six thousand, and the 7 repre-
sents seven hundred.

Value 103 102 101 100 Figure 5.1: The interpretation of a


decimal number. The value of each
Digits 6 7 4 2 digit is dependent on its position. This
positional system is what required the
You can see the specific interpretation of 6742 in Figure 5.1, and invention of "zero".
the general case of n + 1 digit decimal numbers in Figure 5.2.

Figure 5.2: The general formula for the


interpretation of a decimal number.
n Note that, when the base is 10, you

d n d n −1 d n −2 . . . d 2 d 1 d 0 = ∑d k · 10k
need every digit to be able to take
values up to 9, one less than the base.
k =0 Otherwise, things will not work.

dk ∈ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

All this comes very naturally, and in our minds their decimal
representation is the identity of those numbers. The point here is
to remember and realize that the decimal system is in fact just one
possible representation method for numbers. Then, we can think
about other number systems.
32 programming with java

The Binary Number System


If you have designed your computer system to be able to work with
only zeros and ones, and you want a number system to go with it,
your natural option is base two, or binary.
In a binary number, each binary digit (well, bit, but now we
arrived here from the other direction) takes values according to its
position too. But now, the values of each position are not powers of
ten, but are powers of two.
If we want to express the decimal number 6742 as a binary num-
ber, it turns out to be:

1101001010110

It took 13 digits in binary to represent a number of four decimal


digits. Here is the table, including values of each digit:

Value 4096 2048 1024 512 256 128 64 32 16 8 4 2 1 Figure 5.3: The interpretation of a
212 211 210 29 28 27 26 25 24 23 22 21 20
Digits 1 1 0 1 0 0 1 0 1 0 1 1 0
binary number. Again, the value of
each digit is dependent on its position.
The values are given in decimals.
You can verify that the bit pattern really corresponds to 6742 by
adding the numbers in the boxes corresponding to ones. There I recall being taught how to do
are methods for conversion between binary and decimal, but the decimal-binary conversions in fifth
grade. We were also taught base five
correspondence is not obvious. Except very small numbers, using which turned out to be useless.
binary is impractical for humans.
The notation to denote binary numbers as used in Java is to
prepend the number with 0b. So, 0b1101001010110 is the way do
properly denote a binary number in Java, and we will use this in
this book to avoid confusion when necessary.

An Aside: How we do arithmetic


We were all taught how to do arithmetic with decimal numbers
in primary school, pretty much right after we were taught how to
read and write. At the time we never thought about other bases; we
all took base 10 as granted.

Figure 5.4: Doing addition in base 10.

So, let us remember how we do addition in base 10. Figure 5.4


contains two examples. First we add the rightmost digits (the ones
digit) of the two numbers. The ones digit of the sum is the ones
digit of the result. If the sum is greater than 10 then the tens digit
of the sum becomes what we call the carry. Note that the carry
codes 33

can be at most 1 (so it is either 1 or there is no carry). We go to the


next digit. We add the previous carry, and the two corresponding
numbers there. This just continues until we run out of digits.

Figure 5.5: Addition in base 2. The


operation is almost the same, except
now 2 = 0b10 takes the role of 10.

What changes when we do addition in base 2? Well, one op-


tion is to convert things back to base 10, to our "comfort zone", do
the addition there, and the convert back to base 2. Then again we
neither learn anything new, nor do things in their "natural" course.
So how do we add natively in base 2? First, we need both num-
bers written in base 2 (obviously). Then we operate pretty much as
in base 10, but we think of everything is base 2. If the sum of the
two digits is greater than 1, it makes two binary digits. For example,
one plus one is two, which is 0b10. So you put down the zero, and
carry the 1, just as in base 10. Figure 5.5 shows the same operation
as in Figure 5.4 done in base 2.

Figure 5.6: Another example of addi-


tion in base 2. Here, we have the case
where the sum of a column including
the carry is in fact 3 = 0b11. Base 2
addition contains simpler steps (the
addition table is really short) but more
steps than base 10.

Each operation in binary addition is in fact simpler - the number


of possibilities is really limited. The result of an addition, even
including the carry is one of four possibilities: Zero, one, two, or
three. Just like base 10, if there is a carry, it is always 1. A slightly
more interesting case of binary addition is shown in Figure 5.6.
34 programming with java

The Hexadecimal Number System


I remember first seeing a hexadecimal number in 1985, when I first
got my hands on a Commodore 64 Programmer’s Reference Guide. Probably 1985. Maybe 1984.
My first reaction was: "Is that supposed to be a number? There are
letters in there!" You can see what I saw in Figure 5.7.

Figure 5.7: From the Commodore


64 Programmer’s Reference Guide,
Chapter 3: Programming Graphics,
page 102. This is one of the very first
places hexadecimal numbers occur in
that book - previous chapters deal with
BASIC.

It turns out these are numbers, they do contain letters, and for
good reason. Hexadecimal means base sixteen. With base sixteen,
you need digits up to fifteen - but our everyday decimals end at
nine. What to do? Instead of inventing new symbols and giving a
headache to everyone, we use existing familiar letters for the rest of
the numbers.
In hexadecimal, A stands for 10, B stands for 11, C stands for 12,
D stands for 13, E stands for 14, and F stands for 15. And of course,
our positional values are now powers of sixteen rather than ten.
You can see the representation of good old 6742 in hexadecimal
in Figure 5.8.

Value 4096 256 16 1 Figure 5.8: The interpretation of a


hexadecimal number. The value of
163 162 161 160 each digit is dependent on its position.
Digits 1 A 5 6
Inevitably, hexadecimal and decimal numbers end up being
used in the same context. There are many different ways to signify
that a number is hexadecimal rather than decimal. Some common
notations are:
#1A56 - Used with color codes.
$1A56 - Some assembly languages. Commodore used to do this!
0x1A56 - C and descendant languages, including Java.
Going to hexadecimal from decimal has the effect of shortening
the numbers, but not an awful lot - and certainly not worth getting
used to a new number system. The real advantage becomes appar-
ent when you realize one hexadecimal digits corresponds exactly to
four bits, as you can see in Figure 5.9.
This correspondence makes hex-binary and binary-hex con-
versions very easy. Given the binary number 1101001010110, to Hexadecimal is shortened to hex in
convert it to hexadecimal, I break it down into groups of four bits most places and in conversation.

starting from the right to get 0001 − 1010 − 0101 − 0110 (with a lit-
tle padding on the left with zeroes to complete the group of four). Eight bits are a byte. A nybble is a
Now, convert each group into hexadecimal: 1 − A − 5 − 6, which is group of four bits.

just 0x1A56.
When working with an underlying binary systems, hexadecimal
is practical enough for humans to use. The notation is suitably
shorter, while simultaneously enabling easy conversion to binary.
codes 35

Decimal Hexadecimal Binary Figure 5.9: Correspondence of decimal,


hexadecimal and binary numbers.
0 0 0000
1 1 0001
2 2 0010
3 3 0011
4 4 0100
5 5 0101
6 6 0110
7 7 0111
8 8 1000
9 9 1001
10 A 1010
11 B 1011
12 C 1100
13 D 1101
14 E 1110
15 F 1111

The Octal Number System

Seriously, we are not done yet with number systems? My sincere


apologies, but it turns out we are not. I was perfectly willing to
skip octal numbers on account that "well, they are not being used as
much", but it turns out you can use these, and there is a particularly
nasty error you can make if you do not know about them.
Assuming you understand hexadecimals, octals are not that hard
to grasp.
The base is eight, so you need not introduce any letters. In hex-
adecimal numbers, each hexadecimal digits corresponds to four
bits. For octal numbers, each octal digit corresponds to three bits.
You can see the obviuous correspondence table in Figure 5.10.

Decimal Octal Binary Figure 5.10: Correspondence of deci-


mal, octal and binary numbers.
0 0 000
1 1 001
2 2 010
3 3 011
4 4 100
5 5 101
6 6 110
7 7 111

What is the nasty error? The common notation for octal numbers
is to prepend the number with an (unnecessary in decimal) zero.
So, when you type 010, you may think it is a decimal 10. However,
the leading zero denotes this as an octal number, and that is in fact
a decimal 8. Argh.
36 programming with java

Integer Codes

Number systems, as described above, are theoretical constructs.


They are mental models more than anything else. This is not a bad Real knowledge is theory. A theory
thing - in fact that is the only point things can start. Theoretical is a hypothesis formed through ob-
servation and experince, which has
constructs can be very clean, symmetric, beautiful and wonderful. passed subsequent tests (experiments).
At least some of the beauty and wonder stems from the fact that it Only when theory and application go
hand-in-hand reinforcing and building
is free of any constraints and limitations of an implementation - an each other can the full power of the
existence in the physical world. human mind be realized.
Bringing the implementation into the game introduces lim-
its, constraints, and some unexpected behaviors as well. Now,
equipped with the knowledge of number systems, we will explore
particular implementations in binary computers.

Unsigned Integers
You probably have an educated guess about "unsigned" means.
It means that the code does not worry about a sign - there is no
place in the code itself to store the sign. So, only zero and positive
numbers can be expressed with an unsigned integer code.
The most common code used for unsigned integers is binary code.
This is using a fixed number of bits to represent a number. For all
integer codes, we will be considering 8-bit versions which is easy
to follow. Making the numbers of more bits does not change the
behavior, only makes the numbers longer.
The representation of zero is pretty straightforward, as in Figure
5.11.

Bit 128 64 32 16 8 4 2 1 Number Figure 5.11: Zero in 8-bit binary code.


Value 27 26 25 24 23 22 21 20 Represented
Bits 0 0 0 0 0 0 0 0 0

The largest number you can represent with eight bits is 255, as in
Figure 5.12.

Bit 128 64 32 16 8 4 2 1 Number Figure 5.12: Largest 8-bit unsigned


Value 27 26 25 24 23 22 21 20 Represented
Bits 1 1 1 1 1 1 1 1 255
integer 255.

The CPU cores can perform operations on these numbers as they


are stored in memory, like addition and subtraction (and many This is where we normally want to
more). The results of some calculations can be represented properly, operate - this is where the theoretical
number system and the implementa-
and in that case there is no problem. For example, consider adding tion agree perfectly.
15 and 25, as in Figure 5.13. The result is 40, which fits nicely into
eight bits, so everything is fine.

Bit 128 64 32 16 8 4 2 1 Number Figure 5.13: Adding 15 and 25 together


Value 27 26 25 24 23 22 21 20 Represented
A 0 0 0 0 1 1 1 1 15
to get 40. All is well.
B 0 0 0 1 1 0 0 1 25
A+B 0 0 1 0 1 0 0 0 40

Things can go wrong, though. Consider adding 100 and 200


together as shown in Figure 5.14. The result comes out to be 44,
and not 300. It could not come out to be 300, since that does not fit
codes 37

into 8 bits - 300 takes 9 bits to represent, it is in fact 0b100101100.


What happens is, whatever part that does not fit (the leftmost 1 in
this case) is dropped out of existence. This is one example of an
overflow. Whenever the result of an operation is larger than what
the target can hold and some bits are lost, we say that an overflow
happened. Most CPU cores can detect and report that this has
happened.

Bit 128 64 32 16 8 4 2 1 Number Figure 5.14: Adding 100 and 200


Value 27 26 25 24 23 22 21 20 Represented
A 0 1 1 0 0 1 0 0 100
together to get 44. An overflow hap-
B 1 1 0 0 1 0 0 0 200 pened here, which makes the result
A+B 0 0 1 0 1 1 0 0 44(?!) unexpected.

It is also possible to have an underflow. Let us to the most crazy


example, and subtract 1 from 0. You can not expect to get −1, since
it is not possible to represent that in an unsigned integer code. You
can see what happens instead in Figure 5.15.
How the subtraction actually works like this can be a little mys-
tifying. There are two ways to wrap your head around this: First,
you can consider that A, the top number (which is zero) has a ninth
bit on the left, which is a 1. That would make it 256 rather than 0.
Since bits that are out of range do not matter, it all works out. Sec-
ond, you can imagine adding B and A-B rows together, which will
result in an overflow and a result of 0, which also works out.

Bit 128 64 32 16 8 4 2 1 Number Figure 5.15: Subtracting 1 from 0 to


Value 27 26 25 24 23 22 21 20 Represented
A 0 0 0 0 0 0 0 0 0
get 255. An underflow happened here,
B 0 0 0 0 0 0 0 1 1 where the number ends up "wrapping
A-B 1 1 1 1 1 1 1 1 255(?!) around" to the largest possible value.

The results you get when overflows and underflows happen


depend on the size of the integer variable. For example, if you did
the operation of subtracting 1 from 0 with 16-bit unsigned integers,
the result would be 65535 rather than 255. While this should be
rather obvious, it can be surprising in the case of an underflow.
Here is a bit of bad news: As opposed to many other languages,
Java does not have any unsigned integer types. Well, you can consider this good news
- less things to worry about!

Signed Integers
More often than not, you want to be able to use negative values as
well as positive values in your integer types. The obvious idea is to
use half of the available range for positive values, and the other half
for negative values.
The most common way of doing this is called "2’s complement
signed integers".
First let us define and understand the operation "1’s comple-
ment" performed on a finite-size binary number. The 1’s comple-
ment of a binary number is found by changing every 0 bit to a 1
and every 1 bit to a 0. The operation is really simple, and it is its
own inverse operation, as you can see in Figure 5.16.
Note that adding a number and its 1’s complement together will
result in a number composed of all ones.
38 programming with java

A 0 0 0 0 0 1 1 0 Figure 5.16: 1’s complement of an 8-bit


1’s complement of A 1 1 1 1 1 0 0 1
binary number. Taking 1’s complement
twice gives you back the original
number.
Next, we define 2’s complement as follows: The 2’s complement
of a binary number is found by finding its 1’s complement, and
adding 1 to the result. Perhaps a bit surprisingly, this operation is
also its own inverse.

A 0 0 0 0 0 1 1 0 Figure 5.17: 2’s complement of an 8-bit


B = 2’s complement of A 1 1 1 1 1 0 1 0
binary number.

When we use 2’s complement signed integers, we define things


as follows:

• All numbers with the leftmost bit zero are positive (or zero).

• All positive numbers have the same values as the unsigned case.

• All numbers with the leftmost bit one are negative.

• The negative of a number is its 2’s complement.

Considering the rules, all bits zero still corresponds to a zero.


The largest positive number we can make is 127.

Bit # 7 6 5 4 3 2 1 0 Value Figure 5.18: The largest positive num-


A 0 1 1 1 1 1 1 1 127
ber for 8-bit signed 2’s complement
integers is 127.
Note that I have switched the top row to bit numbers. Also note
that the numbering of bits starts on the right hand side with a zero.
Zero-base counting is very common in the field of computers. Here
it makes sense because we use the the exponent of two of its value
as the bit number. What is the value of bit #6? It is 26 .
We said any number with its leftmost bit (bit #7 for our 8-bit
numbers) 1 is a negative number. So, the a number with all its
bits ones is a negative number. Let us call that number X, see it in
Figure 5.19.
Referring to the rules, to negate a number, we take its 2’s com-
plement. If this is a negative number, by negating it, we will find
its absolute value. It is easy to see that the 1’s complement of X is
just 0. Adding 1 to that gives the 2’s complement, which is 1. This
shows that this "mystery negative number X" is −1. You can see the
details in Figure 5.20.
Why 2’s complement signed integers? It seems a little compli-
cated; surely we could have come up with a system where negative
numbers are more easily recognizable to the human eye, right? The
answer is yes, but none of the others have the distinct advantage
that all operations are done exactly the same way they are done for un-
signed integers. There are no special rules about how to perform
addition or subtraction for signed integers. What changes is the
interpretation of the resulting numbers. This is a great advantage
because it means that the CPU doing the operations needs no alter-
native circuitry or instructions.
codes 39

Bit # 7 6 5 4 3 2 1 0 Value Figure 5.19: The number X, with all its


X 1 1 1 1 1 1 1 1 ?
bits ones.
Bit # 7 6 5 4 3 2 1 0 Value Figure 5.20: By calculating the 2’s
X 1 1 1 1 1 1 1 1 −1
1’s complement of X 0 0 0 0 0 0 0 0 0
complement, we negate X. Since -X is
2’s complement of X 0 0 0 0 0 0 0 1 1 1, X itself is −1.

The 2’s complement signed integers have a few peculiarities.


Continuing with 8-bit numbers, we have 256 distinct bit patterns.
One of them represents zero. There are 127 positive integers. The
rest, 128 numbers, are negative. There is a built-in asymmetry.
The most negative number you can have is −128, whose bits are
10000000. If you try to negate that by taking its 2’s complement,
you will get −128 back. This happens for signed integers of any
size - the most negative number you can represent can not be
negated properly - its negative will give itself back.

Another Prelude: Binary Fractions

We are all familiar with decimal fractions - the fractional part is a


natural extension of the concept to quantities less than 1. Values
are positional, but now the powers of ten go towards the negative.
When the write 2.75, the integral part is 2, the 7 stands for the num-
ber of tenths (10−1 ) and the 5 stands for the number of hundredths
(10−2 ). The period in between the integral and fractional parts of
the number is called the "decimal point".
Carrying the idea over to binary numbers, we get binary frac-
tions. You can see the interpretation of the binary 10.11 in Figure
5.21. The point here is a binary point, and obviously not a decimal
point.

Value 2 1 1/2 1/4 Figure 5.21: A binary number with a


21 20 2−1 2−2
Digits 1 0 . 1 1
fractional part. This binary fraction
corresponds to the decimal binary
number 2.75.
We are used to the idea of some numbers not having exact deci-
mal representations. The innocent number 1/3 as a decimal fraction
ends up with a repeating representation, 0.3333333 . . . which an-
noys us as humans.
Mathematically, it can be shown that any irreducible fraction
of the form a/b (where a, b ∈ Z) will have a terminating decimal
fraction if and only if b has only 2 and 5 as its prime factors. So,
7/25, 13/250, 21/50 will terminate while 4/7, 7/9, 11/120 will not
terminate.
With binary numbers, termination is even less common. Only
fractions where the denominator is a power of 2 will terminate.
This brings about the annoying fact that some decimal fractions will
not have exact binary fraction representations. A simple example is:

(0.2)10 = (0.0011001100110011 . . .)2


So, while every integer has an exact binary representation, not
every decimal fraction we consider "simple" does. This makes binary fractions patently
bad for handling monetary values,
where being able to represent 65 cents
is crucial.
40 programming with java

Floating-Point Codes

While examining integer codes, we dealt with 8-bit codes to keep


things as simple as possible. The extension to longer numbers
should be almost obvious. Here, floating-point numbers of inter-
est are 32-bit and 64-bit numbers. We will follow a similar path,
and deal in detail with the 32-bit case only, and be content with
stating the parameters of the 64-bit case.

32-bit IEEE754 Single Precision Floating Point Numbers


Primarily, 32-bit floating-point numbers represent numbers of the
following form: We will soon find out numbers of this
form are called normal numbers.

(−1) g 1.sssssssssssssssssssssss × 2eeeeeeee−127


In this form, g is the sign bit, the s group symbolizes the 23
bits of the significand, and the e group is an unsigned number
representing the exponent. The layout of these parts of the number
is shown in Figure 5.22.

Figure 5.22: The bit layout of the 32-bit


single precision IEEE754 floating-point
number.

An example should clarify things. Consider the decimal number


2.75. Converting it to a binary fraction we find 10.11. We can write
this as 1.011 × 21 . When we put it into the norm specified by the
standard, it becomes:

(−1)0 1.01100000000000000000000 × 20b10000000−127


Inserting all bits into their places, the bits actually stored can be
seen in Figure 5.23.

Figure 5.23: The bits of the represen-


tation of the decimal number 2.75.
number.

Important things to note is that since the exponent should be 1,


we encoded 128 into the exponent field. The significand is 1.011,
but since any number in this format begins with a 1, that is not
stored, the rest of the number is stored, so what goes into the sig-
nificand field is 01100000000000000000000.
What is described above are normal numbers. It should be obvi-
ous that zero can not be represented as a normal number. If the
exponent bits as encoded is between 1 (0x01) and 254 (0xFE) the
number is interpreted as a normal number. This makes the smallest
codes 41

normal exponent 2−126 (when exponent is 1) and the largest normal


exponent 2127 . This leaves two special cases.

Special case: Zero encoded exponent.


When the encoded exponent is zero, the result is known as sub-
normal number. In this case, the interpretation of the number be-
comes:

(−1) g 0.sssssssssssssssssssssss × 2−126


A special case is zero. When the significand bits are also all zero,
you get a zero. Congratulations if you noticed that there is also a
negative zero. This is a result of using a separate sign bit. The fact
that all bits being zero results in a number that is equal to zero is a
result of good design - a zero is a zero whether you interpret it as
an unsigned integer, signed integer, of floating-point number.

Special case: 255 (0x F F) encoded exponent.


When the encoded exponent is all ones, special values come into
play.
If the significand bits are all zero, we get positive or negative
infinity depending on the value of the sign bit. Operations can be
carried out with infinities when the results are defined; for example
dividing a finite number by infinity results in zero.
If significand bits are nonzero, the value represented becomes
what is known as "not a number", often abbreviated to "NaN". This
normally occurs as a result of an operation with undefined results.
Subtracting infinity from infinity is one case that will result in a
NaN. Any operations involving a NaN as one of the operands will
also result in a NaN value. NaN values are contagious!
An important thing to remember is that a 32-bit floating point
number can not store every integer that a 32-bit integer can. It can
store any integer approximately but not exactly.
A signed integer has 31 bits to store the integer value. However,
the 32-bit floating point number has 24 bits of precision. So, there
will be some loss.
The smallest positive integer a 32-bit floating point number can
not store exactly is 16777217. Converting to binary:

( 16777217 ) 10 = ( 1000000000000000000000001 ) 2
The number we started with is 2 24 + 1, which makes it a 25-bit
number. If we normalize the number, it becomes:

1.000000000000000000000001 × 2 24
What am I going to store in the number? The sign bit is zero,
because this is positive. The exponent I will store is 127 + 24 =
151 = ( 10010111 ) 2 . That also works.
The significand I am going to store is what is to the right of the
binary point. And I can only store 23 bits. There are obviously 24
42 programming with java

bits in there. So, the rightmost bit will be dropped, and I will store
00000000000000000000000 as the significand!
What just happened? I lost the 1 at the end. I tried to store
16777217, but since I dropped the 1 at the end, I ended up storing
exactly the same stuff as 16777216. In fact, if I read this number
back, I will read 16777216. This is what is called loss of precision.
The larger the integer you try to store, the more of it will be cut off
from the end.
It turns out that the above example is not sufficient for a good
understanding of how loss of precision happens. As such, here is I have been told this by two students
an example that loses way more precision. in Fall 2023. I find they are right,
instead of pointing to the boundary, it
The world population clock, on November 4th, 2023 at 21:26 is a better idea to point at an extreme
UTC+03 gives the following figure for population of the world: case.

8 070 845 511


Now, how would this go into a 32-bit IEEE-754 floating point
number? First, we convert it into binary. This as a binary number is
given below:

11110000 10000111 10101010 00100011 1


This is a 33-bit number. Now we will need to put it into normal
form. In normal form, it becomes the following:

1.1110000 10000111 10101010 00100011 1 × 2 32


The leftmost 1 is stored implicitly. After that, we can store 23
more bits. So, the rest of it is (rounded and then) broken off. So,
what ends up being stored is the following number:

1.1110000 10000111 10101010 × 2 32


The last nine bits were broken off, torn off, whatever you want to
call it, and they are lost. So, what is this number we can store in
decimal? Well, it turns out to be:

8 070 845 440


It is close, but not quite the same. This is what loss of precision It is off by 71. This is the value of the
means. The last digits are lost, but they are lost in binary form, so bits that were lost from the end. The
lost bits are 001000111, which is 71.
what ends up stored is not immediately intuitively obvious.

64-bit IEEE754 Double Precision Floating Point Numbers


These are called "double precision", but the precision you get is a
little more than double of "single precision". The bits reserved for
the three parts of the number are as follows:

• 1 bit for the sign.

• 52 bits for the significand (giving 53 bits of precision)

• 11 bits for the exponent


codes 43

With single precision numbers, you got 24 bits of precision, but


with double precision numbers, you get 53 bits - more than 2 ×
24 = 48. I always thought the decision was made because more
bits in the exponent were thought to be really unnecessary. With 11
bits of exponent, you can go up to 21023 = 10308 . Considering there
are roughly 1080 protons (or Hydrogen atoms) in the observable Immense numbers are numbers so large,
universe, you are already far into immense numbers territory. that many of anything can not exist,
can not be stored, therefore can not
be computed. Being confined to earth,
that limit is around 1052 .
Important Things to Remember About Floating Point Numbers
Many programmers treat floating-point numbers as magic. "If a
float does not work, just throw in a double, and all will be fine" they
think. But, it will not be fine. The solution is to know what you are
doing.
Here is a short list of important takeaways:

• If your data is explicitly an integer, avoid floating point numbers.

• Some exact decimal fractions like 0.2 and 0.3 are not exact in
floating point.

• Floating point numbers can store integers exactly only up to a


point.

Character Codes

In the discussion of Java data types, we mentioned the char data


type, which can hold one character. But, we did not go into how
that data type holds a character, which is obviously different than
the numeric data types.
The need for computers to represent text readable by humans
has been a present problem since the earliest days of computers,
and even before that for telegraphic transmission of information. We
will not go that far back, but we will start our journey with ASCII,
as it is still relevant today.

ASCII
ASCII stands for "American Standard Code for Information In-
terchange" and is pronounced as "ask-key". ASCII was originally
developed in the 1960s, and was updated well into the 1980s.
ASCII is a character encoding system, which is essentially a way
to match characters with numeric codes, or bit patterns. In general,
when encoding characters, a number (or bit pattern) corresponding
to a character is called a code point.
ASCII was designed as a 7-bit code. The (inexact yet simplified)
explanation is that they needed codes to reprsent upper and lower
case letters of the English alphabet (a total of 2 × 26 = 52 codes)
decimal digits (10 codes) space and punctuation, which easily goes
over 64 code points. So, 6 bits was not sufficient. An 8-bit code was
44 programming with java

also considered, but 7-bits was eventually settled on to conserve


data transmission bandwidth.

Figure 5.24: The ASCII character set.


The characters with red background
are the so-called control characters,
and characters with the white back-
grounds are the printable characters.
A "stick" is the name given to a group
of 16 characters, now pretty much
archaic.

You can see the actual ASCII code table in Figure 5.24. The first
two sticks (group of 16 characters, a term hardly ever used these
days) contain control characters. The main reason is that at the
time, computer output or digital communication was carried out
with what we call teleprinters, teletypes, or TTY for short. These
devices were essentially printers that would print incoming digital
information on paper - remotely controlled printers, so to speak.
As such, the whole protocol of remotely communicating with
these devices had to be present in the character set itself (well, other
designs were possible, but this made perfect sense at the time). A
full explanation of every control code is unnecessary and futile, we
will go through a few that are still relevant today, and a couple that
are fun to know.
Character code 7 is BEL in the table, which stands for "bell".
What this normally did is literally ring the bell (which TTY devices
had) to alert the operator. This still works as of today; if you try
to print the character with code 7, it will make the computer play Unfortunately, the IntelliJ IDEA
some sort of beep sound. environment will not honor this code
and will play no sound. However, if
Just to satisfy your curiosity, the following statement will attempt you run it directly it still does work,
to output the character code 7: which I confirmed on my Macbook
Pro.
System.out.println("\u0007");

Codes 10 and 13 (0x0A and 0x0D) are LF and CR, respectively.


These are the two commands that control the printing head of the
TTY. LF stands for "line feed", which advances the paper one line
forward. CR stands for "carriage return" ("carriage" is the name
given to the whole printing head contraption) which moves the
printing head back all the way to the left. At the end of a normal
line, sending both characters in the order CR, NL was the usual
way of beginning at the start of the next line. This allowed for tricks
like underlining a line by sending just a CR, and printing a whole
codes 45

line of underscore characters.


Soon enough, the ASCII character set started being used for text
files. This produced much confusion, because for a text file, carriage
return and newline characters for a text file meant pretty much the
same thing - on a computer screen, "printing over" was not really
an option.
DOS and later by inheritance, Windows systems used both char-
acters together (CR, NL) to denote the end of a line. They still do.
Unix, and later Linux systems opted to just use a NL character
without the CR. Macintosh systems went another way, using just
CR without the NL character. Later, when MacOS X was intro-
duced, they went the Unix way, and switched to just NL just like
Linux systems. To this day, there is the problem of end-of-line char-
acter conversion when transmitting files although with the univer-
sal availability of the Internet, most programs on all platforms can
handle different kinds of end-of-line characters.
There is one additional control character at the very end of the
table - DEL. All other control characters have lower codes, but this
one sits at the very end by itself.

Figure 5.25: Punched card, with and


without holes.

The meaning of the DEL character (as you might have already
guessed it) is "delete". In the good old days, programs lines were
fed into the computers with what is known as a "punched card".
You can see what a punched card looks like in Figure 5.25. Each
column corresponds to a character code, and the whole card cor-
responds to a line of text. A computer program would consist of
a deck of these cards. Of course, there were special typewriter-like
46 programming with java

devices, which you would use to punch the proper holes on these
cards.
Now, what happens if you make a typing mistake? How do you
delete the character? Closing the holes back up is really impractical.
Throwing away a whole card for one mistake is a waste. A good
solution is, if and when you make a mistake, go back one place and
punch through all holes so that the whole column consists of holes.
In turn, when the computer (which senses the holes physically or
optically) ignores any such column, just moving to read the next
character. This automatically makes "all holes" which is "all ones" a
delete character. Therefore, the code for DEL is 0b1111111 which is
127.

8-bit Codes
What followed ASCII was a multitude of 8-bit codes. Increasing the
number of bits from 7 bits to 8 bits increases the number of avail-
able character codes from 128 to 256. There were a multitude of
8-bit character sets; these codes essentially extended ASCII, meaning
that the lower 128 codes were common to all of them.

Figure 5.26: Code Page 437, as would


be printed by the IBM PC on a VGA
screen. This is known both as IBM437,
CP437. You can see the graphics
characters, as well as more letters from
other languages, the Pound sign (£),
the Yen sign (¥) among other symbols.

One well-known code is Code Page 437, which is what IBM


used in the original IBM PC. IBM wanted to be able to show some
amount of windowed graphics (boxes, etc...) so they used a lot of
the new available characters for things like lines and corners. You
can see the character set as they would be printed on the screen of
an IBM PC computer back in the day in Figure 5.26.
Another legacy 8-bit character set is the Code Page 1252, also
known as Windows-1252 character set. This was the character set
used by default for Windows computers, and rather than graphics
characters (which were unnecessary in Windows) contains letters
used in Western European languages.
Windows-1252 character set was so common that, sometimes
people thought of it as the "default" extension to the ASCII char-
acter set, and while it never was part of the standard, it has been
called the "ANSI character set" even though it was never an ANSI
standard. ANSI is the acronym for the American
National Standards Institute, the
originator of the ASCII character set
back in the days when it was called
ASA, American Standards Association.
codes 47

Figure 5.27: Windows-1252, the default


character set of legacy Windows
systems. Rather than box-graphics
characters, it contains more letters
from other Latin languages.

Over time, more 8-bit character sets were created to support vari-
ous languages of the world. Before the Internet became widespread,
most computer usage was limited to a single system, or mostly a
single type of system, so using a character set that fit your language
covered most, if not all of your needs.
When the proliferation of the Internet brought systems together,
and people started to share documents written in all sorts of char-
acter sets, the need for a better solution was felt more and more.
In theory, you could and should serve every document while also
specifying its character set, and the person viewing your document
should have no trouble at all. However, in practice any documents
not in the web’s default character set (Latin-1 or ISO-8859-1) had a
very good chance of displaying incorrectly on someone’s screen.

Unicode
The idea of Unicode can be traced back to 1987. In 1988, Joe Becker
published a draft proposal for Unicode, in which he described a
16-bit encoding:
Unicode is intended to address the need for a workable, reliable
world text encoding. Unicode could be roughly described as "wide-
body ASCII" that has been stretched to 16 bits to encompass the
characters of all the world’s living languages. In a properly engi-
neered design, 16 bits per character are more than sufficient for this
purpose.

You can read about the exact evolution of the Unicode from
other sources, so trying to replicate that here is of little practical
use. However, let us just state that a 16-bit encoding turned out to
be insufficient, and Unicode ended up separating the definition of
the character set from the encoding used in practice. The follwing is
extracted from the Unicode Standard, version 15.0:
The range of integers used to code the abstract characters is called
the codespace. A particular integer in this set is called a code point.
When an abstract character is mapped or assigned to a particular
code point in the codespace, it is then referred to as an encoded
character.
48 programming with java

In the Unicode Standard, the codespace consists of the integers


from 0 to 10FFFF16 , comprising 1, 114, 112 code points available for
assigning the repertoire of abstract characters.
...
When referring to code points in the Unicode Standard, the usual
practice is to refer to them by their numeric value expressed in hex-
adecimal, with a "U+" prefix. . . . Encoded characters can also be re-
ferred to by their code points only. To prevent ambiguity, the official
Unicode name of the character is often added; this clearly identifies
the abstract character that is encoded. For example:
U+0061 latin small letter a
U+10330 gothic letter ahsa
U+201DF cjk unified ideograph-201df
...
In running text, an individual Unicode code point is expressed as
U+n, where n is four to six hexadecimal digits, using the digits 0–9
and uppercase letters A–F (for 10 through 15, respectively). Leading
zeros are omitted, unless the code point would have fewer than four
hexadecimal digits—for example, U+0001, U+0012, U+0123, U+1234,
U+12345, U+102345.

So, the Unicode Standard defines a code space, and code points
which correspond to abstract characters. It does not specify how The Unicode Standard defines abstract
these code points will be represented on the computer; that is left to characters as opposed to glyphs, which
are the specific way a character is
the specific encoding. graphically represented; a collection of
The three Unicode encodings of interest are: glyphs is a font, which is a term you
are much more likely to be familiar
• UTF-8 with.
Here, UTF stands for Unicode Trans-
• UTF-16 formation Format.

• UTF-32

UTF-8 represents each code point using one to four bytes. Specifi-
cally, it uses a single byte to represent ASCII characters, making it
fully backwards compatible with existing ASCII text.
As such, UTF-8 has become the de facto standard for the web
and most documents because of its compactness and backward
compatibility.

UTF-16 represents each code point using one or two 16-bit units.
This is the encoding used internally by Java. While a char in Java
is always 16 bits, some Unicode characters will be encoded as two
chars. Those characters are called supplementary characters, and
have code points larger than U+FFFF. And we used the correct notation to
refer to a Unicode code point.

UTF-32 represent each code point with a single 32-bit unit. This
means that this is a fixed size encoding as opposed to the variabil-
ity in UTF-8 and UTF-16. While this has the advantage that the
values of each Unicode code point is stored directly, the disadvan-
tage is that it expands everything to 32 bits in memory even though
the maximum number of bits required by the Unicode standard is
21bits.
codes 49

Recap

• The number system we use every day is called the decimal num-
ber system. Its base is ten.

• The binary number system is base two.

• The hexadecimal number system is base sixteen. Digits up to 15


are needed, so letters A, B, C, D, E, F are used. 0xFF is 255.

• Hexadecimal to binary conversion is easy since 4 bits make one


hexadecimal digit.

• There are also octal numbers, base eight. Octal numbers are
shown with a prepended zero, so 010 is actually 8.

• Unsigned integers just code zero and positive integers. A fixed


length means overflows and underflows are possible, generating
unexpected results to the unwary.

• Java does not have unsigned integers.

• Signed integers are most often implemented with 2’s comple-


ment coding. With 8 bits, 0b11111111 codes -1.

• Floating point numbers represent fractional numbers in a code


very similar to scientific notation, except the base is two.

• 32-bit floating point numbers can store integers up to about 16


million exactly. The Java type is float.

• 64-bit floating point numbers are doubles in Java.

• Floating-point numbers can represent positive and negative


infinities, as well as NaN (Not a Number) values.

• NaN values have peculiar behavior. NaN values are contagious,


and NaN is not equal to NaN.

• ASCII is the fundamental character set, encoding English charac-


ters, numbers, punctuation and some control characters. Only a
few of the control characters are still useful. Oh, and ASCII is 7
bits.

• 8-bit character sets exist for specific languages, extending ASCII


to include the "needed" characters.

• Unicode is a standard to encode every character in every lan-


guage in a single character set. It defines code points, which go
up to 21 bits in size. It does not say how to store the so-called
"code points" in practice.

• The three well-known Unicode encodings are UTF-8, UTF-16,


and UTF-32.

• UTF-8 is ASCII-compatible, and the de facto interchange stan-


dard.
50 programming with java

• UTF-16 is what Java uses internally. It uses either one or two 16-
bit units to encode all Unicode code points. Non-supplementary
characters with code points less than U+FFFF fit into one char in
Java.

Exercises

Exercise 1 Convert the decimal number 15 to binary.

Exercise 2 Convert the binary number 11110000 to decimal.

Exercise 3 Convert the hexadecimal number 0x64 to decimal.

Exercise 4 Convert the hexadecimal number 0x64 to binary.

Exercise 5 Note that two hexadecimal digits represent eight bits.


Interpreting 0x78 as a signed integer (2’s complement) is it negative
or positive?

Exercise 6 Consider the binary number 11111000. What value


does it represent as an unsigned integer? What value does it repre-
sent as a signed integer?

Exercise 7 Consider the addition of 11111000 and 00000011. Add


them together as unsigned numbers and interpret the result. Now,
add them together as signed numbers and interpret the result.
(The bit patterns and the operations are identical, verify that the
interpretations actually make sense.)

Exercise 8 Is it possible for terminating binary fraction to not have


a corresponding terminating decimal fraction? If yes, provide an
example. If no, explain why not.

Exercise 9 Encode the decimal number 7500 as an IEEE754 single


precision floating point number. Now do the same for 30000. (This
should be easy after having encoded 7500.)

Exercise 10 Can a 32-bit signed integer value always fit into an


IEEE754 double precision floating point number? Can a 64-bit
signed integer value always fit into an IEEE754 double precision
floating point number?

Excercise 11 What is the smallest positive even integer that can


not be exactly encoded as a 32-bit IEEE754 single precision floating
point number?
codes 51

Engineering Lore: Problem Solving

Problem solving is an important skill, especially for engineers. Af- I did not have a course on problem
ter all, problem solving is part of the job definition for an engineer. solving, either. The fact that most
problem solving in undergraduate
Despite this fact, there is rarely, if at all, any coursework geared education is of the type "here is the
toward teaching the well-known practices of problem solving. In- problem, here is the formula, now go
ahead and apply it" does not help.
stead, students are expected to learn problem solving by "diffu- Real life problems rarely come with
sion", that is, by seeing how problem solving is done in different their solution methods attached.
disciplines.
There are two problems with this: First, problem solving work is
given to teaching assistants, who have little experience themselves
in problem solving. Second, efficient learning happens when the- There will be examples. I will practice
ory and practice complement each other - otherwise this learning what I preach.

either takes a very long time, or is never properly complete.


So, without further ado, let us go straight into the principles of
problem solving.

Write, Draw, Visualize

The "working memory" of humans is surprisingly small. This is


often called "short term memory", and is considered to be between This is what my "rule of 3-5-7" is
five and nine "items". To break this limitation, we invented writing, based on. If you expect anything
to be grasped by humans, it should
drawing, and visualization. This has two effects: We call our visual have 3-5-7 parts to it, and no more. I
cortex in to help with memory, and it also helps organizing our am pretty sure someone must have
thought of this before me, but I claim
thoughts. credit for calling this the "rule of
Do not hesitate to write the problem, or the cases over and over 3-5-7".
on paper (or blackboard, tablet, sand, . . . whatever you prefer).

Take Parameters to Extremes

If the problem has parameters in it (mass, time, length, data size,


size of resources, . . . ) take them to their logical extremes. The log-
ical extreme could be zero or one at one end, and infinity on the
other end. Consider what would happen in those cases. Does the
problem or your candidate solution make sense in those cases?

Consider Small Cases

Instead of attacking the general problem (and failing misearbly)


try attacking small sizes of the same problem. Usually, small cases
can be solved either intuitively or by working out every possibility.
More often than not, this will foster better understanding of the
problem.

Create Your Own Failing Cases

If you have a solution that works for some cases, but fails for oth-
ers, try to fabricate your own failure cases, and make them minimal
while you are at it.
52 programming with java

If You Can Not Fix, Try Breaking


Sometimes, you keep trying many different solutions, but nothing
seems to work; especially when you are trying to get something to Sometimes, when you actually try
to break the code, you realize to
work in code. If this does not work, try to make it fail in different
your horror that you can not. In fact,
ways - usually by removing functionality rather than trying to fix it. everything you were doing was of no
This usually ends up exposing some behavior of the system you did effect whatsoever. You were messing
with the "wrong controls", so to speak.
not know or anticipate, helping with the solution.

Explain the Probem to a Rubber Duck


Many times, when people come to me for their problems the fol-
lowing happens: They start explaining the problem to me (because
I am totally unfamiliar with the problem) and before they get to the
point where they can ask for help, they stop suddenly, say some-
thing to the effect of "Oh, I see" and leave quickly.
What happens in these cases is that the effort of putting the prob-
lem into a form where it can be coherently explained to someone
else is exactly what is needed to find the solution. Trying to explain
it to someone else, makes them see the solution.
Figure 5.28: Here is a rubber duck for
In this process, my only function is as a person who they believe
you to explain your problems to.
can help with their problem. I do not actually do anything. There-
fore, it has been postulated that even if you try explaining your
problem to a rubber duck, you might find a solution - as long as you
nominally believe that the rubber duck can help with your problem.

Turn the Chess Board Around


When you have a solution which is imperfect, that is, either fails in
some cases or just does not perform as well as you wish, take the
other point of view - play against yourself. This is useful when you worry about
With insider information of how the solution works, try to make things in life. Try to genuinely take the
reverse point of view. In most cases
it fail, and fail horribly. Think of creating situations or inputs which you find that things are much more
will either make it fail, or make it perform really badly. balanced than they looked at first.
Then, you can "turn the chess board around" again, and fix your
solution to handle those cases again.

Once it is Down, Attack all its Friends, too!


You do not just want to get the right answer. You want to solve
the problem once and for all time, and if possible, every possible
similar problem. You want to generalize the problem as much as
possible, and find out its limits. Next time you see a problem from
the same family, you want to have very specialized weapons about
them.
So, when you are done, you are not really done. I was about
fifteen years old when I first encountered the problem below called
"The King’s Rings". Since then, I have asked it to people for fun,
used it as an interview question, thought about it periodically, and
I am now including it an introductory programming textbook. I
codes 53

think this is as much as anyone can kick this problem. Or so I thought. By the time I finished
writing up the solution, I discovered
there was more to kick.
Example Problem: The King’s Rings

The king had 1000 grams of gold, and wanted to have 100
pure gold rings of 10 grams each be made for himself. For this, he
appointed a jeweler, and handed him the gold.
The jeweler took his time, and was finally summoned to present
the rings. He brought 10 pouches with him, each containing 10
rings.
You, as the chancellor, have received an intelligence report that
the jeweler has been unable to contain himself, and stole some of
the gold. The intelligence report also says that to minimize his
chances of being caught, he stole 1 gram of gold from 10 rings only,
and put all those rings in the same pouch. So, nine of the pouches
contain 10 rings of 10 grams, while one pouch contains 10 rings of 9
grams each.
Dutifully, you inform the king about this report. The king is
naturally furious, but wants to be fair as well. He orders the Royal
Weight Scale to be brought. Surprisingly, this weight scale is an
electronic one, and displays on its screen the weight placed on it
very accurately and precisely.
Then, the king turns to you, and says: "Here are the ten pouches,
and the rings. You can take as many rings as you wish from any of
the pouches and place them on the Royal Weight Scale. Then, you
press the button, and see the weight. Then, you must be able to tell
me exactly which pouch contains the rings which the jeweler stole
the gold from. But, -this is important- you can use the Royal Weight
Scale only once. If you fail, I will have you thrown in jail instead of
the jeweler, because you are either commiting slander, or are not
smart enough to be the chancellor."

Perhaps you are intelligent enough to come up with the an-


swer in a few seconds on your own. If so, congratulations, but most
people are not able to do so. First, the problem is not short, and
its statement is not very simple. There is gold, rings, and pouches.
Plus, as opposed to most problems of its type, the scale is not me-
chanical, but electronic.

"Write. Draw. Visualize." is our first weapon of attack. It usu-


ally is the first weapon of attack; since it helps with any sort of
problem.
You can see the visualization of this problem in Figure 5.29. Note
that I have numbered the pouches here, even though the problem
does not state them as numbered - but they have to be identifi-
able somehow if we are to find the pouch containing the defective
rings. In fact, the numbering came naturally as I started drawing -
I needed to keep track of how many pouches I was drawing. The
54 programming with java

Figure 5.29: The King’s Rings problem


turned into a drawing. Note that I
took the time to draw all 10 pouches
and all 100 rings. Making the visual-
ization proper does help.

numbering then naturally turned into an identifier for the pouches.

Let us examine the thought process of most people who


meet this problem for the first time:
"Weigh one ring from one pouch." This obviously fails. If the
ring is 9 grams you got lucky; but if it is 10 grams you do not get to
do a second weighing - you get thrown in jail.
"Take one ring from every pouch." Well, this also fails. Believe
it or not, in this case the situation is even worse - you do not even
have a slim chance. Taking one ring from each pouch makes sure
you have 9 "good" rings and one "defective" ring. You will see the
number 99 on the scale, for certain. There is no point to even doing
this.
This is where most people get stuck. Let us pull out more
weapons.

Consider Small Cases is my next weapon of choice. Ten pouches


and ten rings in each pouch is kind of too large. It turns out there
are two parameters here - number of pouches, and number of rings
per pouch. Interestingly enough, these paremeters are equal. This is
quite suspicious in itself. As such, I will keep the parameters equal,
unless I am forced to change it.
One pouch and one ring is not a case, really, because then the
solution is trivial - that ring must be 9 grams if you interpret the
problem literally. But thinking of this explicitly does help.
Next larger case is two pouches, with two rings each. Now, there
are just four rings. This might, of itself, cause a flash of light, re-
vealing the solution. This is still a trivial case, because we can just
weigh one ring; if 9 grams we got the pouch, if 10 grams, it is the
other pouch.
codes 55

Figure 5.30: Studying the "two pouches


and two rings per pouch" case to
death.

However, it is a case, and worth studying to death. Of course,


our weapon of "Write. Draw. Visualize." is still in effect. You can see
this case studied to death in Figure 5.30. I have called the weight
of each ring in Pouch 1 a, and the weight of each ring in Pouch 2
b. By the setup of the problem, it must be that either a = 10 and
b = 9; or a = 9 and b = 10. Since the rings in a pouch are virtually
identical, I need only consider how many rings from each pouch
I take. I can take either 0, 1, or 2 rings from each pouch. And, for
each combination, I have listed what the weight should be.
Now, in three cases, I know exactly what the weight will be.
These are the cases where the number of rings I take from each
pouch is the same. These cases are where I fail to solve my prob-
lem. All other combinations give me the value of b (and therefore
the value of a) depending upon the result of my measurement.
So, one important conclusion is the following:
The number of rings I pick from each pouch must be different
for the measurement to work.
With this information, I am ready to attack the one-larger prob-
lem: Three pouches with three rings each. Now, with my built-up
knowledge from the smaller case, I know taking the same number
of rings is out of the question. So, I conjecture the following:
Taking one ring out of Pouch 1, two rings out of Pouch 2, and
three rings out of Pouch 3 should do the trick.
I could also take 0, 1, 2 rings out of the pouches perfectly equiva-
lently, but this sounds much better.
At this point, the general solution must be obvious. But we will
pretend it is not-so-clear yet.

Let us invoke "Create Your Own Failure Cases". Under


what conditions would our idea fail? Can we think of a minimal
56 programming with java

case? Less rings per pouch looks like it could cause problems.
Two pouches with one ring each is still solvable - any case of
two pouches is. So, we need at least three pouches to fail. Three
pouches with one ring each. Then, whatever we do, we can not take
a different number of rings from each pouch. We can take 0 or 1
ring from each pouch which gives two cases, but there are three
pouches. So that fails.
If you think a little about it, in the general case, for n pouches,
you need at least n − 1 rings in each pouch. Otherwise, the problem
can not be solved as stated. This last observation is actually "Turn-
ing the Chess Board Around"; except that there is no way of fixing
the solution for the impossible cases.

In this case, the "Take Parameters to Extremes" was not


useful. That one usually works in cases where there is a continuous
parameter, or some physical measure which can be adjusted. Con-
sidering an infitinte number of pouches with an infinite number
of rings does not simplify anything, not to me anyway. In dis-
crete cases like this problem, the "Consider Small Cases" weapon
is usually more effective. (It can be considered a subset of "Take
Parameters to Extremes" - but it is so important it deserves its own
name.)

Let us complete the problem by stating the canonical right an-


swer. Take 1 ring from Pouch 1, 2 from Pouch 2, . . . and 10 rings
from Pouch 10. Weigh them. If all rings were fine, the weight
should be 550 grams. If 1 gram is missing, Pouch 1 contains the
defective rings. If 2 grams are missing, Pouch 2 contains the defec-
tive rings. If n grams are missing, Pouch n contains the defective
rings.
Are we done? Yes, and no. We said, "Once it is Down, Keep
Kicking". To be fair, we kicked it around a little, but we can do
better.
Abstracting the problem usually means reformulating in a math-
ematical way, and removing unnecessary detail. We can abstract
our problem as follows: Suppose we have n variables, x1 , x2 , . . . xn .
All of these have the same value a, except xk which has the value b.
We can obtain the value of the function f ( x1 , x2 , . . . xn ):

f ( x1 , x2 , . . . x n ) = c1 · x1 + c2 · x2 + . . . c n · x n

where ci ∈ N and ci ≤ m. But we can do this once only (only for


one set of ci , and we want to be able to determine the value of k.
We just found out that this is possible if and only if m ≥ n − 1.
What more to investigate? The case where m < n − 1, our orig-
inal problem has no solution (as we stated before). But what hap-
pens if we can use more than one set of ci ? In words of the original
problem, how about the case when you have ten pouches with five
rings each, but you can use the scale twice? Is it possible to figure
things out in that case?
codes 57

I must confess that I have not gone to this degree of investiga- Then again, it turns out once you have
tion on this problem myself. However, this should give you an idea the solution to the first problem, this
is not so hard. It took me about a
about how to keep working on a problem once you are done with minute of thinking to come up with
it. the general solution.
This concludes our discussion of this problem.

Example Problem: Cutting Pizza

A regular pizza has eight slices. These are formed by four


cuts, all intersecting at the center, forming 45◦ with the closest lines.
Following the same pattern, with eight cuts you could get 16 rather
narrow slices.
If all the cuts did not intersect at the center, you could get more
pieces of pizza with less cuts - although you could hardly call them
"slices" any more. Assuming you want more pieces with less cuts,
the question is this: Using straight cuts, how many pieces of pizza
can you produce with eight cuts?

"Write. Draw. Visualize." is once again, our first weapon of


attack. Since eight cuts have to start with one, "Consider Small
Cases" comes quite naturally. The result with a few small cases can
be seen in Figure 5.31.

Figure 5.31: Cutting a pizza to pieces


with a few cuts. Even four cuts makes
the counting hard, five lines are really
hard to do. Note that doing too few
cases is a bit misleading.

One cut is easy. Two pieces, obviously. Two cuts is also easy,
resulting in four pieces. As an afterthought, I have also considered
zero cuts, which is a very valid number of cuts (negative numbers
do not work) which results in a single piece.
So, if you consider zero, one, or two cuts, you get one, two, four
pieces. If you stop here, things are a bit misleading. It looks like
every new cut doubles the number of pieces, result in powers of
58 programming with java

two. However, three cuts breaks the pattern. Whatever you do, the
best you can do is get seven pieces.
With four cuts, the drawing already gets difficult. But, it is still
possible to do within human limits, and it is worth it to get one
more data point. Some care in drawing shows that with four cuts
we get 11 pieces.

If You Can Not Fix, Try Breaking is something worth trying


here. Our goal is to make as many pieces as possible with a given
number of cuts (well, we are asked for eight, but we are too smart
to stick with the given number). How about we try to get fewer
pieces with the same number of cuts, and try to understand how
things end up breaking? (You can see the drawing of the failures in
Figure 5.32.)

Figure 5.32: Failing to get the maxi-


mum number of pieces with a given
number of cuts. The two failure
modes.

The first cut divides the pizza into two. With the second cut,
we can break things - we can avoid intersecting the first cut. If we
do that, we will get three pieces rather than four. This means the
following:
To get the maximum number of pieces, each cut must intersect
the other cuts.
Now, consider already having two cuts intersecting at a point,
which gives four pieces. With the third cut, we can do a cut which
intersects none of the previous cuts, which will cut out just one
additional piece, but we already know that method of breaking
stuff. There is a new way now, though. If we intersect with the
other two cuts at their existing point of intersection, we get six
pieces rather than seven. This failure mode means the following:
To get the maximum number of pieces, each pair of cuts must
intersect at a unique point.
codes 59

Take Parameters to Extremes can also be applied here. The


paremeter we are talking about is one which has not been named
in the problem. It is the radius of the pizza. How large is it? It is
as large as we want it to be. Taking it to zero does not help, but
taking it to infinity is a good idea - makes thinking a little easier (if
you are a bit inclined towards mathematical thinking). This moves
things into the realm of mathematics - how many regions can n
lines partition a plane into?
Then, our rules for getting the maximum number of regions (we
are in mathematics land now) becomes:
None of the lines are parallel.
Every pair of lines intersect at a unique point.
Now, some clear-headed thinking is order. Assume you are
drawing the nth line, which means there are already n − 1 lines
on the plane, none of them parallel, each pair intersecting at a
unique point. You start drawing the line from infinity (very far
away). Eventually, you intersect the first line. The region you
started from just got divided into two pieces. Which means you
added one piece. For each line you intersect, you change regions,
and the region you just left gets divided into two, adding exactly
one piece. Once you have intersected the last existing line, which is
the (n − 1)st line, you have added n − 1 regions. The last region you
are in, once you set off to infinity again, is divided into two regions,
adding another region. Once your journey is complete, you have
added n more regions.
This means:
The nth line adds n more regions.
Or, in pizza terms:
The nth cut creates n more pieces.
Looking back at Figure 5.31 we can see and check our new idea:
With zero cuts you have 1 piece. The first cut adds one piece, giv-
ing 2 pieces total. The second cut adds two pieces, giving four
pieces. The third cut adds three pieces, giving seven total pieces.
The fourth cut adds four more, making the total 11 pieces! So, we
apparently have our general solution for P(n), number of pieces
with n cuts:

P(n) = 1 + 1 + 2 + . . . + n
This can be simplified:

n ( n + 1)
P(n) = 1 +
2
The answer to the original problem now seems to be but a trivi-
ality. We just need to compute P(8):

8(8 + 1)
P (8) = 1 + = 1 + 4 · 9 = 37
2
So, with 8 cuts you can create a maximum of 37 pizza pieces.
60 programming with java

Psychology of Problem Solving


Last, but not least, your psychology is very important for problem
solving.

If you think you will fail then you will definitely fail in solv-
ing the problem. Rather than thinking "I can not solve this", you
should be thinking "I have not solved this yet".

You should respect every problem. There are no small prob-


lems - even the easy ones can teach you something, and the sim-
plest problems can humiliate you if you lack respect.

Assume the problem is an unsolved problem. Most prob-


lems you will attempt have been solved by other people before. But,
do not let this entice you to "looking up the solution". Treat prob-
lems like movies or books; the solution is the worst kind of spoiler
there is.

Work your problem solving muscles. Before you can decide


whether or not you like solving problems, you need to work your
problem solving muscles. Getting stronger will make things easier,
this in turn will make working your muscles easier.
6 Variables, Literals and Mathematical Operators

Declaring Variables

Variables are about creating a piece of data (of a certain type) in


memory, and giving them a name so we can operate on them later. Oscar Wilde, at the New York Customs
Here is the simplest way to declare a variable in Java: House in 1882: "I have nothing to
declare except my genius."
<variable type> <variable name>; I am waiting for the chance to say
"Nothing to declare, but my variables!"
An quick example is in order: I will probably get my luggage turned
inside-out, though.
int x;

What I am telling the computer here is as follows: Dear com-


puter, please reserve sufficient space in memory to store a 32-bit
integer. And, we are going to call that reserved space x.
In Java, all variables must be declared before you can refer to
them to do anything. Otherwise, the compiler will tell you a mes-
sage which in essence means "I do not know what you are talking
about".
Why an int and not a short or long? The rule of thumb is
this: If you are thinking just "integer", you use int. This is semi- What reasons can you have? One good
traditional; the name pretty much hints to the fact that int is the reason is that you know the values you
will be using will not fit into an int.
default integer type. (Obviously, shorts are short, longs are long, Another good reason is when you
ints are just right. Right?) Also, at least in C, the size of an int was are going to be creating a lot of these
variables somehow, and you know the
not fixed, but left to the compiler. The idea was for the compiler to values can fit into a smaller type. Yet
use the size of a CPU register (CPU registers are where CPU cores another reason could be that the values
load data into to operate) so that operations would be faster for are being supplied externally, and you
want to use the exact same type to
ints. In Java, type sizes are fixed, but the tradition continues to use prevent any divergent behavior.
int as the default integer type unless you have reasons not to.
I could also give the variable an initial value:
<variable type> <variable name> = <initializer>;

Continuing with our example:


int x = 5;

Now, what we are saying has change a bit into: Dear computer,
please reserve sufficient space in memory to store a 32-bit integer.
And, we are going to call that reserved space x. Oh, before I for-
get, put the value 5 into that memory space we just reserved. Ok,
thanks, bye.
The 5 you see there is called a literal. Any value you type directly
into the source code is called a literal. Literals also have types, and
62 programming with java

there are few formats you can use to type literal values into the
code; but now we will just push forward and leave the details for
later.
While I hate the practice to the point of not wanting to teach it, it
is possible to declare more than one variable of the same type in one There are more practices allowed by
line. For example: programming languages which no-
body should be doing, ever. However,
int x = 5, y, z = 3; people do these, and you are likely
to see them in code. So, I still have to
teach them, even just for making you
In this case, x, y, and z are all of type int. Note that y has no "know your enemy".
initial value. If you were to do:
int x, y = 7;

Only y gets an initial value. The only thing variables share in a


multi-variable declaration is the data type.
Now you know it. Please do not use it. If a variable is worth
declaring, it is worth declaring on its own line. Like any other rule, this may have
exceptions. But using multi-variable
declarations "because you can" is never
a good idea.
A Second Program

Here is a short program to try out:


public class Main {
public static void main(String[] args) {
int x = 7;

x = x + 2;

System.out.println(x);
}
}

If you run this program, you can verify that it prints out a 9 and
terminates.
Although it is a very short program, there are quite a few things
to understand in here. Let us go through this line-by-line.
int x = 7;

This is variable declaration, declaring the variable x as an int


and giving it a value of 7. If the equals sign here feels natural to
you, very well. Only mathematics majors ever gave
But it really should not - in mathematics, the equals sign is sym- me trouble about this. I think in our
"natural" non-mathematician state, this
metric. Here, the variable must be on the left, and the value must be notation makes more sense.
on the right. Mathematics majors always give me
What is also important here is where the declaration is placed. It some amount of trouble. Mathemat-
ics contains more abstraction and
is placed inside a method - specifically the main method. This makes more rigor than all other disciplines,
the variable a local variable. In the place that it has been declared, it precisely because it is an axiomatic
system.
gets created when execution reaches the declaration, and lives until
the execution of the method ends. The limits within which a variable
remains accessible is called that variable’s scope. In this particular
case, the scope of the variable x is the main method.
x = x + 2;
variables, literals and mathematical operators 63

This line has two things going on. One is an assignment. This is
very similar to the case of the variable declaration with an initial-
izer. In fact, the variable declaration with an initializer is a short-
hand for doing two things: Declare the variable, and assign a value
to it. Here, we see an assignment operation on its own. The equals
sign = is the assignment operator. Assignment means "take the
value on the right, compute it if necessary, and put in into the vari-
able on the left".
The right hand side contains an operation. The operation is ad-
dition, which is signified by the addition operator, which is +. The
addition operator takes two operands, and adds their values to-
gether, creating a new value which is the sum of the two operands.
The computer does the following: To do the assignment, it needs
to calculate what is to the right of the equals sign. There, it sees the
variable x and the literal value 2. It accesses the variable, and gets
the value 7. Then, it is prepared to do the addition. It adds 7 and 2,
obtaining the value 9. Now, the assignment happens, and the value
of 9 gets written into the memory space reserved for the variable x.
System.out.println(x);

This last line, as you should know from the "Hello, world!" pro-
gram, prints the value of x on the screen, resulting in a 9.

Arithmetic Operators

Java has five binary arithmetic operators. With binary here, we


mean operators that take two operands, one on either side of the
operator. These are:

• + Addition

• - Subtraction

• * Multiplication

• / Division

• % Modulus (remainder)

Out of these, addition, subtraction and multiplication present


little complication. They act pretty much as you would expect them
to. They will not produce any astonishing results.
Division and the modulus operators have some more depth to
them, so we will treat these specifially.

General Rules of Operands and Promotion


The first rule of binary operations is that binary operations always
happen between two operands of the same type. The first rule of Fight Club is: You do
For easy mental modeling, we can think that the "promotion not talk about Fight Club. (This is a
movie reference to "Fight Club (1999)".
order" among the numeric types in Java is as follows: Suggested viewing.)
64 programming with java

byte → short → int → long → float → double

The types to the right are generally considered to be "larger"


than the types to the left.
The arrow from long to float is especially troublesome, since
long is 64 bits while float is 32 bits - however this is just how
promotions work.
The rules for promotions are as follows:

• The smallest type that can be operated on is int. So, if both


operands are ints or smaller, they will be promoted (as neces- This is another reason why we prefer
int as our default integer type.
sary) to ints, and the result of the operation will also be an int.
So, the result of any operation can not be a byte or a short.

• If either operand is larger than an int, the smaller type will be


promoted to match the larger type. In this case, the type of the
result will be the same as the type of the larger operand.

• As a consequence, if either operand is a floating point number,


the operation will be carried out as a floating point operation.

Division
Division requires special attention, specifically because the set of
integers is not closed under the division operation. This just means How mathematical of me.
when you divide two integers, you may get something that is not
an integer.
If you read and understood the rules of type promotion in the
previous section, then you should realize there is a problem: If you
divide two integer types, they will both be automatically promoted
to ints if necessary, and the result of the division will be an int!
What?
Well, there is such a thing as integer division. You must have done
this in second grade - you divide 14 by 3, the quotient is 4 and the
remainder is 2. This is what Java will give you when you perform
division on two integers.
So, computing 1/2 will result in a 0.
The results of integer division can be mystifying when negative
numbers enter the picture. The easiest way to understand how
it works is to think of doing the exact division in real numbers,
and drop the fractional part. This is equivalent to doing the exact
division in real numbers, and always round towards zero. So, 1/2,
−1/2, 1/ − 2, −1/ − 2 all result in 0.
Also, if you are performing integer division, and the divisor is
zero, your program will throw an exception. This means that you will
not get a result, and your program will stop printing an annoying
error message. Remember, division by zero is strictly forbidden in
integers.
If at least one of the operands is floating point, what you more-
or-less expect happens. Also, in the case of floating point operands,
variables, literals and mathematical operators 65

division by zero is not an error - it will produce an appropriate


result. 1.0/0.0 will yield Infinity, while 0.0/0.0 will yield NaN. But,
your program will run, and there will be no error messages.

Modulus (Remainder)
In mathematics, the modulus operation is commonly defined for
integers. When you calculate a % b in Java where both operands
are integral types, you are in well-defined territory. The modulus
operator can actually be expressed as:

a % b = a − ( a/b) ∗ b
The division is (obviously) to be interpreted as integer division.
Recall that integer division always rounds towards zero.
When the right operand is positive, the sign of the the remain-
der is the same as the sign of the left operand. This means things
change around zero a little bit:

−3 % 3 = 0
−2 % 3 = −2
−1 % 3 = −1
0%3 = 0
1%3 = 1
2%3 = 2
3%3 = 0
4%3 = 1

When the right operand is negative, all results are the same as
the case where it was positive:

−3 % − 3 = 0 while you’re doing the


remainder thing, it doesnt
−2 % − 3 = −2 matter if its - or +, youre just
caring baout the remainder
−1 % − 3 = −1 dont forget it

0% −3 = 0
1% −3 = 1
2% −3 = 2
3% −3 = 0
4% −3 = 1

This looks like it is wrong, but it works perfectly well to preserve


the relation a % b = a − ( a/b) ∗ b.
This definition can be generalized to include floating point num-
bers. In Java, you can use the modulus operator on floating point
66 programming with java

numbers. For example, 7.75, %, 0.5 will result in 0.25. In particular,


for any floating point number x, x % 1 will give you the fractional
part of x, with the same sign as x.
Floating point modulus operator maintains the same relation and
behaves the same as integers for negative numbers. This is a minor
point, but this behavior in Java breaks the IEEE754 specification.

Operator Shorthand

Very often, you need to do things like multiplying the value of a


variable by two. Or divide the value of a variable by something. Or
add a value to a variable. You end up doing things like:
x = x * 2;
y = y / 3;
z = z + 5;

Anything that is of the form:


<variable> = <variable> <operator> <value>;

Can be replaced with:


<variable> <operator>= <value>;

So, the three operations given as examples above can be replaced


with:
x *= 2;
y /= 3;
z += 5;

There are a few subtleties here, but nothing worthy of mention-


ing at this point.

Increment and Decrement Operators

If there is one thing that has been done more than anything else in
a program in the history of programming, it is increasing the value
of a variable by one. So, there is a special shorthand for increasing
and decreasing the value of a variable by one. Admittedly, decre-
menting is done much less than incrementing, but it kind of tagged
along because of symmetry.
The increment operator is ++ and the decrement operator is
-, and these are unary operators - meaning they act on a single
variable.
The following line
x += 1;

can be replaced with


x++;

which is called the "post-increment form", or


++x;
variables, literals and mathematical operators 67

which is called the "pre-increment form". The same can be said


for decrementing:
x -= 1;
x--;
--x;

When used on a line by itself like this, the pre-increment (or


decrement) and post-increment (or decrement) forms do exactly the
same thing. The traditional use is the post-increment form. The programming language C++
So, why the two forms? The expressions x++ and ++x in addi- gets its name from this operator - it
is "one more" than C, or the "the next
tion to their effect of incrementing the value of x, they have a value generation" of the C language.
which can be used within another calculation. That value is differ-
ent for the two cases. Consider the following code snippet:
int x = 1;
int y;

y = 3 + x++;

System.out.println(x);
System.out.println(y);

With the post increment form, when trying to calculate y, the


computer will use the old value of x, before the increment happens.
The name "post increment"" means exactly that; the increment
happens post-calculation. So, y will get the value of 3 + 1 = 4, and
at the time of printing x will have the value of 2.
We can change things to pre-increment, like in the following
code snippet:
int x = 1;
int y;

y = 3 + ++x;

System.out.println(x);
System.out.println(y);

Now, while calculating y, the computer will use the new value of
x, after the increment happens. So, y will get the value of 3 + 2 =
5, while the value of x at the time of printing will still be 2.
In some computer courses, some professors tend to ask students
to resolve things like:
int x = 7;
int y = 8;

y = 3 + ++x + x++ + ++y + y++;


x = y++ + ++y + x++;

System.out.println(x);
System.out.println(y);

Can you guess what my answer to this is? I do not care. Do not
write code like that. Writing complicated code is easy. Analyzing "Debugging is twice as hard as writing
it is as futile as it is difficult. Trying to do too many things in one the code in the first place. Therefore,
if you write the code as cleverly as
line is really bad programming. How many is too many? Three is possible, you are, by definition, not
too many. Using more than one variable with an increment and smart enough to debug it." – Brian
Kernighan
68 programming with java

decrement on it on a single line is bad form, unless the operation


being done is very clear.

Literals

Literals are values you type directly into the program. These also
have their types, and are subject to conversion rules, quite similar to
but not exactly the same as variables.

Integer Literals

Any number typed into the program composed of only decimal


digits with an optional minus sign on the left, and does not contain a
decimal point is interpreted as an int.
However, the conversion rules are a bit different. If you try to
assign an integer (int) literal to a short or a byte, automatic con-
version will work if the value is within the limits of the variable’s data
type.
So, the following assignments will work just fine:
short t = 6779; // -32768 <= 6779 <= 32767, so this works.
byte w = -45; // -128 <= -45 <= 127, so this works.

However, the following assignments will result in errors:


short u = 68444; // 68444 > 32767, so this fails to compile.
byte x = 300; // 300 > 127, so this fails to compile.

Any int literal can always be assigned to a long variable without


problems. After all, long is longer than int.
long h = 777555342; // This always works.

Trying to type an integer value larger than the limits of int even
if it is assigned to a long variable fails:
long j = 4000000000; // This fails, since number is too large for int.

To use a long literal, you type a number composed of decimal


digits not containing a decimal point, and append the letter L to the
end. Appending L makes that literal of type long quite strictly. So,
the following actually works:
long j = 4000000000L; // This works; the L makes the literal a long.

But the following assignments fail, because the "relaxed" down-


conversion does not happen with long literals:
short b = 1L; // Fails, because 1 is a long now.
byte q = 0L; // Fails, because 0 is also a long now.

Note that the letter L can be lower case (l) as well, but this can be
easily confused with the digit one (1) so using the upper case L is
strongly recommended.
variables, literals and mathematical operators 69

Integers in Other Bases


Normally, the numbers you type are in base 10. However, some-
times it is practical to use hexadecimal, octal, or binary literals.
Hexadecimal numbers are prepended with 0x, octals numbers are
prepended with a leading zero (this can cause really nasty errors,
as mentioned in the chapter on codes), and binary numbers are
prepended with 0b. Here is an example for each:
int x = 0x00FF; // Decimal 255, given in hexadecimal.
short y = 025; // Decimal 21, given in octal.
byte z = 0b1101; // Decimal 13, given in binary.

Floating-Point Literals
Floating-point literals are more complicated than integer literals.
They can certainly have an integer part, but they may also have a
fractional part separated with a decimal point. In addition, they can
also have an exponential part, which denotes the power of ten.
Let us just look at a few examples:
double x = 4.45;

This literal contains a decimal point, an integer part and a dec-


imal part. The existence of the decimal point makes this literal
automatically interpreted as a double. In fact, any floating-point lit-
eral is interpreted as a double unless the letter F (or f) is appended
to its end.
float y = 4.45; // This fails because 4.45 is interpreted as a double.

The compiler will not attempt to put a double literal into a float
variable. Here is how to make that work:
float y = 4.45F; // This works. Appending F makes the literal a float.

You can also make use of an exponential part to express large (or
small) numbers as follows:
double z = 6.022E23;

This makes z = 6.022 × 1023 . As evident from the example, the


exponent part is separated from the significand with the letter E
(the lowercase letter e can also be used).
The inclusion of an exponential part makes the literal automati-
cally a floating-point literal. So, the following will fail:
int k = 1E3;

Although the literal just means 1000, and it contains no fractions,


its type is still a double.
If you append the letter D (or d) to the end of a literal, you can
force it to be interpreted as a double. Since interpretation as a dou-
ble is the default, this is normally not needed. However, in some
rare instances, there may be use for it. For example the following
code fails to compile:
70 programming with java

double x = 1_000_000_000_000_000_000_000; // Fails with "Integer number


too large"

The error message is "Integer number too large". The follow-


ing does not fix it either:
double x = 1_000_000_000_000_000_000_000L; // Fails with "Long number too
large"

One possible fix is to put a decimal point at the end of the num-
ber, which will make the literal interpreted as a double.
double x = 1_000_000_000_000_000_000_000.; // This works.

But, you may prefer to use the suffix D in this case:


double x = 1_000_000_000_000_000_000_000D; // This also works.

Use of Underscores in Literals

Since Java 7, the use of underscores (_) are allowed in any numeric
literals between any digits. This is very useful for making numbers
more readable for humans. So, taking a previous example:
long j = 4000000000L; // Hard to read exactly what this is.

You can actually add underscores to make it readable:


long j = 4_000_000_000L; // Much better.

Character Literals

Character literals are literals you can assign to char variables. They
are used to represent a single character. A character literal is simply
a character surrounded by single quotes:
char c = ’x’;

This assigns the character x into the char variable c. Note that
you are not allowed to put more than one character into a character
literal. The following will not compile:
char c = ’xy’; // This will not compile.

Boolean Literals

Boolean literals are perhaps the simplest of them all. There are only
two literals you can use in the code, which are true and false.
boolean x = true;
boolean y = false;
variables, literals and mathematical operators 71

Casting

Casting has multiple meanings. The most basic meaning is "to


throw". The relevant meaning here is "pouring something into a
mould to give it a specific shape". In general, when we talk about
casting in programming languages, we mean converting one data
type to another forcefully.
What do we mean by forceful here? Normally, Java will only au-
tomatically convert between types where conversion is considered
to be relatively safe. When done forcefully, it will convert any nu- It will happily put an int into a
float and a long into both a float
meric type to any other numeric type.
and double even though these can
For future reference, remember that in the general case, forcing potentially cause loss of precision.
may not always work. If the types are not compatible, trying to
cast one data type into another will cause an error (and throw an
Exception).
Casting is done by putting the type name placed in parentheses
to the left of a numeric value; literal or expression. It will convert
the value to the type in parentheses. Note that casting can never
change the type of a variable, that is fixed at the declaration. Cast-
ing only does type conversion for use in the current calculation.
A simple example is when you have a value inside an int vari-
able which you know will fit just fine into a byte. Without a cast,
Java will refuse to do this. As such, the following code will not
compile. Java will complain that this is possibly a lossy conversion:
public class Main {
public static void main(String[] args) {
int x = 7;
byte b;

b = x; // This will not compile.

System.out.println(b);
}
}

Using a cast will tell Java to "go ahead and do the conversion
anyway, if there is any loss, it is my problem". Here is the version
that will compile:
public class Main {
public static void main(String[] args) {
int x = 7;
byte b;

b = (byte)x; // This will now compile.

System.out.println(b);
}
}

Now, there is the cast operator (byte) that forcefully converts


the int value into a byte. If you run this, the output will be cor-
rectly 7.
Of course, Java is correct in the first place in refusing to do this
automatically. If the value of the original int variable was larger
than what can fit into a byte, there can really be loss. Consider the
72 programming with java

following program:
public class Main {
public static void main(String[] args) {
int x = 255; // This will not fit into a byte.
byte b;

b = (byte)x; // The cast forces it

System.out.println(b);
}
}

If you run the program, you will see that it prints −1. You might
have expected it to print 127, which is the largest value a byte can
hold, but instead the bit pattern is forced into the variable, drop-
ping any excess bits. If you have understood the chapter on codes,
you should be able to figure out why this prints a −1.
When you do a conversion from a floating-point type to a integer
type, you always need a cast. Such a cast acts a bit "more intelli-
gently" than the integer casts.

Exercises

Exercise 1 Declare a float variable and give it an initial value of


2.5.

Exercise 2 Consider the following slightly modified version of the public class Main {
program in the chapter: public static void main(String[] args) {
short x = 9;
x = (short)(x + 2);
public class Main {
System.out.println(x);
public static void main(String[] args) { }
short x = 7; }

x = x + 2;

System.out.println(x);
} main reason of the compilement error was the given
} +2 is an integer, thats why it takes the x as an integer
the second time that we wrote it

The only difference is that the variable x is now declared to be


a short. Do you think this should compile and run just as before?
Try it on the computer. Explain why the error occurs. Fix it some-
how.

Exercise 3 Consider the mathematical expression:

262196 · 28813
101
Verify that this works out to be an integer, and is exactly:

74 798 548

Execute the following program:


public class Main {
public static void main(String[] args) {
variables, literals and mathematical operators 73

System.out.println(262196*28813/101);
System.out.println(262196/101*28813);
System.out.println(28813/101*262196);
}
}

Do they all print the same value? Explain the three results.

Exercise 4 Assign the value -128 to a byte variable. Assign the


negative of this value to another byte variable. Print this value on
the screen.
What is the result?
7 More Operators and Branching

Branching is the technical term for making decisions in the code to


control the flow of things. We are going to introduce the famous if
statement; but before we do we need to learn about things to decide
on - hence the operators producing or combining boolean variables
go first.

Comparison Operators

Java provides six comparison operators. The result of these com-


parison operators is always a boolean value. These should be pretty
intuitive; we will examine in detail only the possibly surprising
cases. You can see all comparison operators in Figure 7.1.

Operator Function Types of operands Figure 7.1: Comparison operators in


Java, their function, and types they can
< Less than Number/Number be used with.
> Greater than Number/Number
>= Greater than or equals to Number/Number
<= Less than or equals to Number/Number
== Equal to Number/Number or
boolean/boolean
!= Not equal to Number/Number or
boolean/boolean

Any comparison is still subject to the promotion rules. The dif-


ference is that the result is always a boolean value.
With integers, things work pretty much as expected. Given you expect that things will work
With floating point numbers, if you are testing for equality or in unexpected ways when overflows
and underflows occur.
inequality, you are probably doing something wrong. Very small
differences due to rounding errors may prevent two numbers being
equal. For the proper way to handle such cases, you will have to
read on a few chapters.
Floating point values can be Inifinity and NaN. Infinities work
fine. However, NaN is an interesting case. NaN is considered un-
ordered, so the operators <, >, >=, <= will all result in a false value
if either or both of the operands is NaN. The only case where x==x
will be false is when x is NaN. The operator != always returns
true when at least one of the operands is NaN.
76 programming with java

Branching

Now that we have operators to use to make decisions, we can in-


troduce the concept of branching. So far, all the programs we have
seen and tried ran in sequential execution. Sequential execution means
that every line in the program gets executed in the exact order that
they were typed into the program. While it is useful and whole
classes of problems can be solved with sequential execution, we do
Figure 7.2: Sequential execution. Every
need to execute different instructions depending on conditions. The statement gets executed one after the
action of "selecting which branch to execute next" is aptly called other, with nowhere else to go but
forward...
"branching". Java, like many similar languages, has two fundamen-
tal constructs to handle branching; if/else and switch. We will
examine both.

The if Statement
The general form of the if statement is as follows:
if (<condition>) {
<true block> Figure 7.3: The if/else statement
} else { branching. A value of true goes left
<false block> (if), and a value of false goes right
} (else). They eventually merge, of
course.

When execution reaches the if statement, the <condition> is


evaluated, which must evaluate to a boolean value. If that value is
true, statements in the <true block> are executed. If it is false,
statements in the <false block> are executed.
If there is nothing to be done in the else block, it can be omitted,
reducing the form to:
if (<condition>) {
<true block>
}

If either the <true block> or the <false block> is a single line,


the braces surrounding them can be omitted. This is another of the
practices that I want to bury under a large pile of rock, under lava,
under a mountain so that nobody will ever ever do it again. Please
do not use if statement without braces. It makes reading them
difficult, also makes them error prone. Think of the braces as part
of the syntax, and spread the word.
While it does not introduce any new constructs, one particular
pattern to use the if statement is the if/else if ladder. Here is
what it looks like:
if (<condition1>) {
<block1>
} else if (<condition2>) {
<block2>
} else if (<condition3>) {
<block3>
} else {
<else block>
}
more operators and branching 77

When structured like this, you can be sure one and only one of
the blocks will be executed. As above, you can omit the final else
block, in which case at most one of the blocks will be executed. If
multiple conditions are true, the first one encountered from the top
is the one that "counts".
Here is a quick example:
public class Main {
public static void main(String[] args) {
int x = 2;
int y = 7;

if (x > y) {
System.out.println("x > y");
} else if (x < y) {
System.out.println("x < y");
} else {
System.out.println("x = y");
}
}
}

When you run this, when the computer reaches the if statement,
it evaluates x > y. Because 2 > 7 is false, it skips the first block.
Then the second condition, which is x < y is evaluated. Because 2
< 7 is true, that block is executed, printing "x < y" on the screen.
The final else block is skipped entirely.
We can modify the program a little to see some interesting be-
havior. Now consider the following slighly modified program:
public class Main {
public static void main(String[] args) {
int x = 3;
int y = 3;

if (x >= y) {
System.out.println("x >= y");
} else if (x <= y) {
System.out.println("x <= y");
} else {
System.out.println("x = y");
}
}
}

Now, the conditions in two if tests are not mutually exclusive. This
means that they can be true at the same time. Note that the values
of the variables are now the same, so both conditions are true. 3>=3
is true and 3<=3 is also true. If there is a voice in your head telling
Which block will execute? The execution starts from the top, you that 3=3 so 3>=3 is false, tell that
voice to shut up.
so the very top condition will be checked first. Since it is true,
that block will execute, and the other condition will not even get a
chance to get checked.
When using an if-else-if-else ladder like this, there are a few
things to consider about the order of the conditions (and the blocks
they control).

• If at all possible, make your conditions mutually exclusive. This


makes it much easier to understand and modify later. All code, unless deleted from existence
will be modified.
78 programming with java

• If the conditions are mutually exclusive, the order in which they


appear will not matter functionally.

• Even though premature optimization is a bad idea, be aware that


placing cases that are more likely happen towards the top of the
ladder will make your code run faster.

The switch Statement


The if statement is mainly a two-way branching statement - if true, Figure 7.4: The switch statement
do this, if false, do that. Switch is a multi-way branching statement. branching. This train can choose
any of the three ways depending on
Instead of a boolean value, it uses an integer to make the decision. the value of the switch. The switch
(Well, there is more to it, but this is the most basic form.) statement can split any number of
The structure of the switch statement is shown below: ways - I suspect trains are limited to a
triple choice - this is as far as the train
switch(<decision value>) { analogy goes.
case <value1>:
<statements1>;
break;
case <value2>:
<statements2>;
break;
...
default:
<statements3>;
break;
}

A switch statement has entry points and exit points. Entry points
are the case labels and the default label. The exit points are the
break statements, or the end of the switch statement.
The choice of entry point depends on the decision value, which
is computed at the beginning of the switch. A lot of the time it is
just the name of a variable, but it can also be something computed
like x+2. The only constraint is that it has to evaluate to an integer
value.
Once the decision value is known, the entry point is chosen. If
there is a matching case label, execution jumps there. If there are
no matching case labels, but there is a default label, execution
jumps there. If the decision value does not match any case label,
and there is no default label, the switch block is skipped entirely.
Once execution is inside the switch block, sequential execution
resumes until an exit point is reached. During this sequential exe-
cution, coming across an entry point (which is either a case label or
a default label) is a "no operation" - an entry is not an exit.
If you do have a default case, it is good style to have it as the
last thing. Also, while not necessary, it is also good style to put in
the last break.
The switch statement is used less often than it should probably
because it is somewhat confusing, and a bit dangerous. Let us look
at a couple of examples to see typical cases, and typical errors.
public class Main {
public static void main(String[] args) {
int day = 3;
more operators and branching 79

switch (day) {
case 1:
System.out.println("Sunday");
break;
case 2:
System.out.println("Monday");
break;
case 3:
System.out.println("Tuesday");
break;
case 4:
System.out.println("Wednesday");
break;
case 5:
System.out.println("Thursday");
break;
case 6:
System.out.println("Friday");
break;
case 7:
System.out.println("Saturday");
break;
default:
System.out.println("Unknown day");
break;
}
}
}

This first example is to print the name of the day of the week
given its number. It has seven cases, one for each day of the week,
and a default case for anything else. Every case ends with a break.
If you run this example, it will print out "Tuesday".
As for our second example, we will consider the game known as
"craps". The rules of craps are a bit involved, but the beginning roll,
also known as the "come out" roll are simple. Two dice are rolled,
and the result is determined according to their sum as follows:

• On a total of 2, 3, or 12 the player loses.

• On a total of 7 or 11, the player wins.

• On 4, 5, 6, 8, 9, 10 player moves to the second roll.

public class Main {


public static void main(String[] args) {
int total = 7;

switch (total) {
case 2:
case 3:
case 12:
System.out.println("Player loses!");
break;
case 7:
case 11:
System.out.println("Player wins!");
break;
case 4:
case 5:
case 6:
case 8:
case 9:
case 10:
80 programming with java

System.out.println("On to second roll!");


break;
}
}
}

This is an example of stacking multiple case labels together to


make them do the same thing. There is no default label, which
means if you give total a value like 13 and run the program, noth-
ing will get printed.
This design of the switch statement has been inherited from the
C language. The most common error is the unintended omission of
a break statement. Consider the following code:
public class Main {
public static void main(String[] args) {
int total = 7;

switch (total) {
case 2:
case 3:
case 12:
System.out.println("Player loses!");
break;
case 7:
case 11:
System.out.println("Player wins!");
case 4:
case 5:
case 6:
case 8:
case 9:
case 10:
System.out.println("On to second roll!");
break;
}
}
}

The break statement after the line that prints "Player wins!" has
been omitteed. If you run this, you will see the following output:
Player wins!
On to second roll!

Here is what happened: Execution reached the switch statement.


The value of total was 7, so execution jumped there. The next line
is case 11: which is just a label, so ignored. Then, "Player wins!"
is printed. The next three lines are also labels, so they are ignored
as well. (This is called fall-through.) Then, "On to second roll!" is
printed.
This fall-through is considered a design error of the C program-
ming language. Sometimes it is useful, but most of the time it
causes problems. The moral of the story is to remember your break
statements. Or, you can make use of the enhanced switch statement
which was recently introduced.
Since Java 14, multiple case labels can be combined using com-
mas, making code shorter, more clear, and less error-prone. The
following code works exactly as the correct version of the craps
code above:
more operators and branching 81

public class Main {


public static void main(String[] args) {
int total = 7;

switch (total) {
case 2, 3, 12:
System.out.println("Player loses!");
break;
case 7, 11:
System.out.println("Player wins");
break;
case 4, 5, 6, 8, 9, 10:
System.out.println("On to second roll!");
break;
}
}
}

Since Java 14, there is also an "enhanced version" of the switch


statement. The enhanced version of the switch statement which
works without break statements and the possibility of fall-through.
Our example of craps done with the enhanced switch looks like the
following:
public class Main {
public static void main(String[] args) {
int total = 7;

switch (total) {
case 2, 3, 12 -> System.out.println("Player loses!");
case 7, 11 -> System.out.println("Player wins");
case 4, 5, 6, 8, 9, 10 -> System.out.println("On to second
roll!");
}
}
}

This introduces the so-called arrow operator ->. Having the arrow
operator implies just one thing is to be done, and that is it. No Do not go looking for the arrow key
more continuing down. on your keyboard! Believe it or not,
this has happened before. The arrow
What if I wanted to do more than one thing? Just for the sake of operator is just a minus sign follwed
the argument, let us say I wanted to print another line, saying "We by the "greater than" symbol.
are sorry." in case the player loses. Here is how you do it:
public class Main {
public static void main(String[] args) {
int total = 7;

switch (total) {
case 2, 3, 12 -> {
System.out.println("Player loses!");
System.out.println("We are sorry.");
}
case 7, 11 -> System.out.println("Player wins!");

case 4, 5, 6, 8, 9, 10 -> System.out.println("On to second


roll!");
}
}
}

What we did is, we combined the two lines into a single code
block. A code block is any number of statements enclosed in a pair
82 programming with java

of braces {}. You have seen blocks in multiple places already; the
new information here is that you can use them anywhere a statement
is expected. Any code block in Java also defines a scope. Any local
variables defined within a certain scope can only be accessed within
that scope. This is a case of "What happens in Vegas stays in Vegas".
Let us see what we mean by that with an example:
public class Main {
public static void main(String[] args) {
int total = 7;

switch (total) {
case 2, 3, 12 -> {
System.out.println("Player loses!");
System.out.println("We are sorry.");
}
case 7, 11 -> System.out.println("Player wins!");

case 4, 5, 6, 8, 9, 10 -> System.out.println("On to second


roll!");
}

System.out.println("The total was: " + total);


}
}

This works just fine. However, it is possible to add another pair


of braces to define a scope within the scope of the main method as
follows:
public class Main {
public static void main(String[] args) {
{ // <-- Beginning of new scope
int total = 7;

switch (total) {
case 2, 3, 12 -> {
System.out.println("Player loses!");
System.out.println("We are sorry.");
}
case 7, 11 -> System.out.println("Player wins!");

case 4, 5, 6, 8, 9, 10 -> System.out.println("On to second


roll!");
}
} // <-- End of new scope

System.out.println("The total was: " + total);


}
}

This code will not compile. You will end up getting an error in
the last println statement, saying "Cannot resolve symbol total".
This is because the variable total was defined within the new
scope introduced, and is not available (visible to the compiler)
outside of the scope.

Exercises

Exercise 1 Write code to calculate the income tax according to


Turkish tax code as of 2024. The tax code reads as follows:
more operators and branching 83

• For income up to 110,000 TL: A tax rate of 15% is applied.

• For income up to 230,000 TL: A fixed tax of 16,500 TL is levied


on the first 110,000 TL, and a 20% tax rate is applied to the por-
tion between 110,000 TL and 230,000 TL.

• For income up to 870,000 TL: A fixed tax of 40,500 TL is levied


on the first 230,000 TL, and a 27% tax rate is applied to the por-
tion between 230,000 TL and 870,000 TL.

• For income up to 3,000,000 TL: A fixed tax of 213,300 TL is levied


on the first 870,000 TL, and a 35% tax rate is applied to the por-
tion between 870,000 TL and 3,000,000 TL.

• For income over 3,000,000 TL: A fixed tax of 958,800 TL is levied


on the first 3,000,000 TL, with a 40% tax rate applied to any
amount greater than 3,000,000 TL.

Write a program that starts with a given amount of income in a


double variable and computes the total income tax.
8 Looping

Unlike humans, computers are very good at doing repetitive work.


As such, every programming language provides structures to If you think humans are good at
execute any block of code repeatedly. This is called "looping". Java, repetitive work, just ask any friend or
family member to sweep a room three
similar to many programming languages, provides three distinct times. Observe how many times they
looping structures. actually sweep the room in secret. If
anyone actually does sweep the room
three times, please report to me for
immediate publication!
The for Loop

Understanding the structure of the for loop is going to take some


time. This is because it is the most complex out of the three loop
structures; but once we have mastered the for loop, the other two
are going to be really easy to understand. I promise.
You probably heard the story of Gauss. He (allegedly) aston-
ished and amazed his teacher with how quickly he could add all in-
tegers from 1 to 100. He circumvented repetitive addition with the
method he invented. Here we will do the opposite - even though
we have a closed form formula, we will make the computer do the
repetitive work, and add all the numbers.
So, here is our program that does the task of little Gauss in the
story using a for loop:
public class Main {
public static void main(String[] args) {
int sum = 0;

for (int i = 1; i <= 100; i++) {


sum += i;
}
Carl Friedrich Gauss (1777-1855) was
a mathematical prodigy. The story is
System.out.println("The sum is: " + sum);
unverified; and most likely not entirely
}
true. However, he certainly was not the
}
first person to dicover the formula for
the sum of first n integers. Then again,
You know what this code does (you can run it) but you do not he was a mathematical superstar. To
say the least, he was to number theory
know how and why it actually works. The whole mystery lies in
what Newton was to gravity.
the line where the for is. The general form of the for statement is
as follows:
for (<initializer>; <condition>; <increment>) {
<loop body>
}

The semicolons (";") are part of the structure of the for state-
ment.
86 programming with java

This "classic" for loop in its most typical usage, makes use of
an integer counter variable to repeat its body a pre-determined There is what is called a "for each"
number of times. To be sure this is not the only possibility, but this loop too. However we are not ready to
comprehend that yet.
kind of usage is behind the naming of the three parts.
The first part is the initializer. Here, the counter variable is given
its starting value with an assignment. As in our example, it is also
possible to declare the variable here, in which case its scope will be
the for loop.
Technically speaking, the initializer is executed once and only
once when execution reaches the for loop. It can be any kind of
statement, and is not limited to an assignment to a counter variable.
The second part is the condition. This is very similar to the condi-
tion of an if statement - it must be some expression that evaluates
to a boolean value. The loop body is repeatedly executed as long as
this value is true. The check is done before every execution, so it is
possible that the loop body is not executed at all.
The third part is the increment. Every time the execution of the
loop body is complete, the statement in the increment part is ex-
ecuted. Typically, this contains a statement that will update the
counter variable, although just like the initializer, it is not really
restricted - any kind of operation can be performed here.
So, here is how the computer executes a for loop:

• Execute the initializer.

• Check the condition. If false, skip the loop, and proceed to


statement following the for loop.

• Execute the loop body.

• Execute the increment.

• Go back to the condition-check step.

Looking back at our example code with this new information, we


can say the following: In case you are wondering, the sum is
5050.
• The loop variable is i, which is initially set to 1.

• As long as it is less than or equal to 100, the loop body executes.

• Every time an execution completes, the value of i is incremented


by one.

• The loop body adds every value of i into a variable called sum.

The while Loop

The while loop is very similar to the for loop, except that it only
has the condition part. In other words, you can consider it to be an
if statement that keeps repeating.
The general form of the while loop is as follows: Of course, I am lying to you. The
braces are not really part of the form.
If the loop body is a single statement,
you do not need the braces. However, I
never use any loop without the braces,
and neither should you.
looping 87

while (<condition>) {
<loop body>
}

There is an unsolved mathematical problem, most commonly


known as the Collatz conjecture. It defines sort of a number game,
as follows:

• Pick any integer n > 1.

• If n is even, replace n with n/2.

• If n is not even, replace n with 3n + 1

• If n = 1, stop. Othewise, repeat the process.

For example, if you start with n = 3, you get the following


sequence:

3 → 10 → 5 → 16 → 8 → 4 → 2 → 1
The conjecture is that, no matter what n > 1 you pick, the se-
quence will always reach 1. There no known exceptions, but there As of 2020, all numbers up to 268 have
is no proof for this either. been checked, as published by David
Barina.
Here is the code that will print the sequence utilizing a while
loop.
public class Main {
public static void main(String[] args) {
int n = 3;

System.out.println(n);

while (n != 1) {
if (n % 2 == 0) {
n = n/2;
} else {
n = 3*n + 1;
}

System.out.println(" -> " + n);


}
}
}

You may also want to try 27 as the initial value for n.


Note that, at the start we do not know exactly how many times
the while loop is going to execute. This is usually the case with
while loops, whereas the for loop is utilized if the number of repe-
tition is known or fixed.
The condition we have in the while loop is that n != 1. So, the
loop will keep repeating until this is false, that is, until n == 1. In a
while loop, there must be something happening in the loop body to
eventually make the condition false. In our example, although not
mathematically proven, we know that eventually n will hit the value
1.
This behavior is in contrast to the for loop. In a properly con-
structed for loop, you can see how the loop is set up, and how
88 programming with java

it will complete in the top line. This is, in fact, the reason for the
existence of the for loop.

The do while Loop

The do while loop is the black sheep of the family. This is the kind
of loop that you will probably use the least, and see the least often
when you look at other people’s code. The general form of the do
while loop is as follows:
do {
<loop body>
} while(<condition>);

In a do while loop, the condition is checked after the loop body


is executed. If it is true, the loop is executed again. As such, the
body of a do while loop executes at least once.
This is usually useful in cases where you need to repeat some-
thing until it "succeeds" or the result is satisfactory. You have to do
it at least once to test the result.
Suppose we have the ability to generate random numbers. We
wish to generate a pair of random numbers x and y such that the I took a few hours to try to think of
point ( x, y) lies within the unit circle, that is x2 + y2 ≤ 1. a meaningful example that does not
include anything new. But, the very
The below program does this, and also includes random number nature of the do while loop requires
generation: something undeterministic. The things
that came to my mind were network
import java.util.Random; connections, user input, random num-
ber generation. I ended up picking
public class Main { random number generation, which I
public static void main(String[] args) { think introduces the least amount of
Random random = new Random(); additional confusion.

double x;
double y;

do {
x = random.nextDouble(-1.0, 1.0);
y = random.nextDouble(-1.0, 1.0);
} while (x*x + y*y > 1.0);

System.out.println("(" + x + ", " + y + ")");


}
}
There are, of course, ways of produc-
What is really new here is random number generation. Let us ing random numbers within the unit
circle without repetition and quite
examine the lines one by one:
deterministically. But they tend to get
Random random = new Random(); a bit more mathematically involved
than generating uniformly distributed
This line creates an object of type (well, class really) Random. On random numbers.

the right hand side, new is a Java keyword which is used for creat-
ing objects. The result is stored in a variable called random of type
Random.
Once we have created this Random object, it provides us with
many functionality involving random numbers. The following two
lines do exactly that:
x = random.nextDouble(-1.0, 1.0);
y = random.nextDouble(-1.0, 1.0);
looping 89

The expression random.nextDouble(-1.0, 1.0) means "take the


object labeled random. Inside that object, find the method called
nextDouble and execute it with the given arguments". The dot
operator (".") means "inside" or "under".
Here, the method we are calling, nextDouble with two argu-
ments, generates a random double between the given limits; the
lower limit is inclusive, while the upper limit is exclusive. In other
words, the generated number r will be such that −1 ≤ r < 1.
The do while loop (which is what we are interested in) will
repeat the random number generation if the point lies outside of
the unit circle. Obviously, the generation needs to happen at least
once.

The break and continue Statements

The break and continue statements work inside all three kinds of
loops. (Well, break is also used in the switch statement, which we
have already seen.)

The break Statement


The break statement does exactly what it sounds like: If it is exe-
cuted anywhere within the loop body, the loop execution is termi-
nated, and execution continues with the next statement following
the loop. As such, break is used to exit the loop "early", disregard-
ing the loop condition.
The break statement is virtually always contained within a con-
dition (like an if statement). Executing it unconditionally means
that the loop will never have the chance to repeat; which defeats the
idea of using a loop in the first place.
Here is an example, even if not very meaningful:
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
if (i % 3 == 0) {
System.out.println(i);
}

if (i > 50) {
break;
}
}
}
}

Without the if statement containing the break, this program


would print out every non-negative integer less than 100 that is
divisible by 3. With the if statement containing the break, it exits
the loop early. In the iteration where i is 51 first 51 gets printed
because it is divisible by 3. Then, since 51 is greater than 50, the
break statement executes, which causes exiting the loop.
It works exactly the same way with while and do while loops, so
we will not be providing other loop examples for break.
90 programming with java

The continue Statement


The continue statement is a bit harder than break to explain. It also
does some sort of skipping, but it only skips the current iteration of
the loop. In other words, if a continue statement is ever executed
within a loop body, the rest of the loop body is skipped, in effect,
execution jumps to the end of the loop body.
You can also read the continue statement as "skip the rest of the
loop body for this iteration".
Just like the break statement, the continue statement is virtu-
ally always contained in a conditional statement. If it was directly
executed in a loop, the lines after it would be totally unreachable.
If you do this in Java, the Java compiler will respond with an error
saying "Unreachable statement".
Below is an example in the same vein as the example provided
for the break statement:
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
continue;
}

if (i % 3 == 0) {
System.out.println(i);
}
}
}
}

Without the if block containing the continue statement, this


code would print all non-negative integers less than 100 divisible
by 3. However, when i is divisible by 2, the if statement executes
the continue which causes the rest of the iteration to be skipped -
so any integers divisible by 2 will not be printed at all. So, only odd
numbers divisible by 3 will end up being printed.

Exercises

Exercise 1 Write a program to compute the fourth powers of posi-


tive integers k such that 1 ≤ k ≤ 1000. That is, compute:

1000
∑ k4
k =1

You should be doing this by performing the actual addition


operation. You should be able to verify your result by the following
closed-form formula:

n
n(n + 1)(2n + 1)(3n2 + 3n − 1)
∑ k4 = 30
k =1
I derived this formula when I was in
high school. Still in my memory.
looping 91

Exercise 2 Write a program to compute the sum of the reciprocals


of the squares of all positive numbers. That is, write a program to
compute:

1
∑ k2
k =1
This sum actually converges, and
While it is not possible to add the whole infinite series, for the computing the sum was known as the
Basel problem.
purposes of this problem we want to get the sum correct to nine
decimal places (after the decimal point). There is more than one way to do this.
You can check the correctness of your result by comparing your
computed sum with the known value:

1 π2
∑ k2 6 =
k =1

The following line of Java code will print the "correct" value,
using Java’s constant for π:
System.out.println(Math.PI*Math.PI/6.0);

Exercise 3 Write a program to compute ln 2. This can be accom-


plished by making use of the following series:

x2 x3 x4
ln(1 + x ) = x − + − +···
2 3 4
The series is convergent for −1 < x ≤ 1, so it can be used
(marginally) to compute ln 2. This converges rather slowly, so in-
clude the first million terms to get an approximate value.
The following line of Java code will print the "correct" value,
using Java’s mathematics library to compute the natural logarithm
of 2.
System.out.println(Math.log(2));

Exercise 4 Write a program to count the divisors of a positive


integer. Give the value to an int variable.
For example, if we count the divisors of 12, we should get 6
because 12 is divisible by 1, 2, 3, 4, 6, 12, a total of 6 integers.
Use this to try to find an integer strictly larger than 1000 for
which the number of divisors is odd.

Exercise 5 A number where the sum of its proper divisors is equal


to the number itself is called a perfect number. A proper divisor
is a divisor of a number which is not equal to itself. Two small
examples are 6 = 1 + 2 + 3 and 28 = 1 + 2 + 4 + 7 + 14.
Write a program to keep searching for perfect numbers forever,
and prints them out as it finds them.
9 Logical Operators

Logical operators are operators that operate on boolean values to


change an combine them in certain ways. In a sense, these opera-
tors are simple, since both their operands and results have only two
possibilites, true and false. However, at this point you should be
aware that everything can be built upon these.

The Logical Complement Operator ! (NOT)


The official name for the operator is "The Logical Complement
Operator", but this is mostly known as the NOT operator. This is a
unary operator (takes one argument) which takes a boolean value
and produces its opposite.

x !x Figure 9.1: Truth table for the Logical


Complement Operator, also known as
true false
NOT.
false true
When working with boolean variables, it makes sense to make
a truth table for operators showing all possible inputs and their
results. You can see the truth table for the NOT operator in Figure
9.1.

The AND Operators, & and &&

Java contains two forms of the AND operator, & and &&. Both are
AND operators, and are binary operators, taking two boolean
operands. The result is also a boolean, and is true if both operands
are true. If at least one of the operands is false, the result is false.

x y x & y x && y Figure 9.2: Truth table for both forms


of the AND operator, & and &&.
true true true true
true false false false
false true false false
false false false false
As you can see from the truth table in Figure 9.2, the results for
both AND operators are exactly the same.
So why are there two operators?
Note that when you try to evaluate the result of an AND opera-
tion, if you just look at the left operand, and it is false, you already
know the result is going to be false. The && operator, called the
94 programming with java

Conditional-And Operator, takes advantage of this fact. It evalu-


ates the left operand first, and if it is false, does not even evaluate
the right operand and generates a result of false immediately.
Only if the left operand evaluates to true the right operand is even
evaluated. This can be time saving, or even functionally necessary.
Consider the following code fragment:
int x;
int y;

// More code here, things happen to x and y.

if (y != 0 && x/y == 2) {
// Something happens here.
}

When y == 0, it is an error to try to compute x/y. Having used


the Conditional-And Operator, the right operand only gets com-
puted when the left operand is true, that is, x/y is computed only
when y != 0.
The other AND operator & is called the Logical-And Operator,
and computes both operands no matter what their values are. This
is sometimes necessary when the computation contains side effects
that you need to happen no matter what.
It usually makes sense to use && as the default, unless you know
what you are doing.

The OR Operators, | and ||

Java also contains two forms of the OR operator, | and ||. The
situation here is very similar to the case of the AND operator. The
OR operator takes two boolean operands, and results in true when
at least one of the operands is true. For the result to be false both
operands must be false.

x y x | y x || y Figure 9.3: Truth table for both forms


of the OR operator, | and ||.
true true true true
true false true true
false true true true
false false false false
The truth table for both forms of the OR operator are given in
Figure 9.3. Both forms of the operator generate the same truth
values, so they are equivalent as far as the results are concerned.
They two forms of the OR operator differ in operation. Similar to
the AND operator, if the first (left) operand evaluates to true, you
already know the end result is going to be true. The Conditional-
Or Operator || takes advantage of this fact, and does not evaluate
the second operand if the first operand is true.
The Logical-Or Operator | evaluates both operands no matter
what their results are. This is again necessary because the calcula-
tion involved in evaluating the operands may have side effects that
we want to happen no matter what.
logical operators 95

Unless you know exactly what you are doing, the Conditional-Or
Operator || is the default one to use; and it is the one that you will
most often find in existing code.

The XOR Operator, ˆ

The XOR operator is the Exclusive-Or Operator, which Java has


only one. The XOR, or exclusive-or operation takes two boolean
operands, and produces a true result if and only if exactly one of
the operands is true. It is the way of saying "Either option A must
be true, or option B must be true, but not both!".

x y x ˆ y Figure 9.4: Truth table for the XOR


operator, ˆ.
true true false
true false true
false true true
false false false
If you think about this operator for a minute, you can probably
see that the expressions x ˆ y and x != y will produce the same
results and are exactly equivalent given both x and y are boolean
variables. So, in a sense, there are two XOR operators, too.
10 Strings

Java is an object-oriented language by design, almost to the exclu-


sion of other programming paradigms. We have not mentioned this
too much, because our first goal is to master the "nuts and bolts" of
programming. But, since the whole Java code library is composed
of classes, the object-orientation can and will creep up everywhere.
Strictly speaking, String is a class. Any class defines a data type.
These data types are different than the basic data types we have
previously seen (byte, int, ...). With basic types, when you declare
a variable such as:
int x;

The name x is a name for where the actual int value is in mem-
ory. The situation is not the same with a String (or any class, for
that matter). When you declare a String variable such as:
String s;

The name s is not a name for where the actual data of a String
is stored in memory. It is a name for a reference to a String object
where the actual data is stored.
If you are totally confused at this point, you are well within
your rights to get confused. This is rather hard to grasp without an
understanding of how the memory (by memory here we are talking
about RAM, Random Access Memory) of a computer works - or at
least understanding a rudimentary model of how it works. So we
will digress to understand that.

A Simplified Model of Computer Memory

Computer memory is made up of bytes. The age-old analogy is


houses and street numbers. Computer memory is a very long
street, where each house is a byte, and every byte has a street num-
ber. Unlike different arrangements around the world, numbers start
at 0 and increase one by one. This model of addresses is shown in
Figure 10.1.
If you have a very "bare bones" primitive microprocessor with
RAM connected, and you were to program in assembly, you could
pick (and sometimes indeed would) pick memory addresses to
store certain values. You would, for example say, "well, I am going
to store the counter variable at location 0x0010. You could either
98 programming with java

Figure 10.1: This is a simplified model


of computer memory. The addresses
here are 16 bits long (4 hexadecimal
digits) which would allow addressing
of 64KB of RAM. Values are bytes,
represented with two hexadecimal
digits.

remember to type 0x0010 every time you wanted to access that


value, or you could define a symbol in your assembler and use that
symbol every time.
If you are storing one byte variables, it is obvious how things
work. The value goes into the byte at that address. What happens
if you are trying to store, say, a 32-bit integer which is four bytes?
What happens is that the value takes four bytes, with the first byte
stored at the mentioned address.
So, suppose I am trying to store the value 1337 which is 0x00000539
as a 32-bit integer at memory location 0x0010. Which byte is the
first byte, really? If you think the answer is obvious, you are so
wrong, because both conventions exist. One convention is called
"little endian", which means that the "little end" is stored first. Intel
and ARM processors are well-known to be little endian systems.
You can see how this value is stored in a little-endian system in
Figure 10.2.

Figure 10.2: The 32-bit integer 1337


(0x00000539 in hex) is stored at mem-
ory address 0x0010 in little-endian
form. The little end, that is, the least
significant byte is stored first.

"Big endian" processors store the most significant byte of a multi-


byte value first. Motorola processors and IBM mainframe proces-
sors have been traditionally big-endian systems. You can see how
the same value as before is stored in a big-endian system in Figure
10.3.

Figure 10.3: The 32-bit integer 1337


(0x00000539 in hex) is stored at mem-
ory address 0x0010 in big-endian form.
The big end, that is, the most significant
byte is stored first.

The big idea occurs when you realize the addresses are also num-
bers, and they could also be stored in memory! So, my integer
value is located in memory address 0x0010 and I could store that
value in another memory location, say, 0x0008. (From this point
on we will use little-endian byte order exclusively.) The state of
memory when this is done is shown in Figure 10.4.
When the address of another memory location is stored in mem-
ory rather than a value, this is called a pointer. Following that
string s 99

Figure 10.4: The integer value itself is


stored at memory location 0x0010. The
address of the value, which is 0x0010
is stored at memory location 0x0008.
This is called a pointer because the
value there points at another memory
location, containing the value we
would want to access.

pointer value, that is, going and accessing the memory location
stored in the pointer value is called dereferencing that pointer.
The terms "pointer" and "dereferencing" are almost never used
in Java. You may also hear that "there are no pointers in Java". (You
might even believe that until you see your first NullPointerException.)
There are pointers in Java, but they are called something else. They
are called references.
Why give a different name to the same thing? The main rea-
son is that, lower level languages such as the C language, have
pointers. In such languages, you can have pointers to pointers, and
pointer arithmetic where you can modify the value of the pointers
themselves to point at different places in memory. This can be very
powerful, and at the same time very, very dangerous in the sense
that it may cause problems that are very hard to detect and fix.
For this (and other reasons, like garbage collected memory man-
agement) Java has no pointers. It has references, which are able to
store the location of objects, but there are no pointers to pointers or
pointer arithmetic. This new type of pointer with reduced function-
ality (and therefore much safer) is called a reference.
You can probably imagine the need where you need a pointer,
but you need it to "point at nowhere". It was decided that any
pointer that points at memory location zero "points nowhere" and
trying to dereference that pointer should be an error condition. A
pointer with a value of zero is known as a "null pointer".
Moving to the world of references in Java, the numeric nature of
pointers are fully hidden. The "points nowhere" value for references
is known as null and is a Java keyword, and it is not interchange-
able with the numeric value of zero.
Let us try to make sense out of all this. When we declare a
String variable we end up creating only a reference to a String
object. So, when we declare:

String s;

We end up creating a reference somewhere in memory, chosen


by the computer. Assuming our simplified 64KB memory model,
addresses will be two bytes long (in modern 64 bit machines, a
reference variable will be eight bytes long), so memory will look
something like in Figure 10.5. A perhaps better mental image is to
look at the reference like a fish hook, as shown in Figure 10.6.
How do you actually create a String object then? The answer
(again) is a bit annoying, because since Strings are so fundamental,
they get special treatment from Java in a few places. Normally, you
100 programming with java

Figure 10.5: The memory situation


with only a String s declared. The
value it contains is garbage, since it
was given no initial value. The value
is meaningless, so the compiler will
not let you use it. Since our simplifed
memory model is 64KB, and addresses
are only two bytes long, the reference
variable is two bytes long.

Figure 10.6: Another way to look at the


situation after String s is declared.
You can intrepret a reference variable
as a "fish hook", which is currently
"hooked into" nothing at all. It has the
ability to hook into a String object, or
"fish", if you like.

can create an object of any class by using the new keyword. Without
special treatment, our code fragment would look like the following:
String s;
s = new String("This is a string");

When this code fragment is executed, the computer creates a


new String object, and stores it somewhere in memory. We do not
know the internals of the String class, but the size would be large
enough to store all the characters, plus possibly a couple of more
variables; so in our case around 40 bytes. Once the computer has
placed the new String object in memory, the assignment operator
puts its address into the reference variable s. So, the final memory
situation would look something similar to Figure 10.7. And, the
fish-and-hook mental image would look like Figure 10.8.

Figure 10.7: A new String object is


created and placed at address 0x0016,
and its address assigned to reference
(String s) existing at address 0x0004.

Let us expand our code fragment to define a new String refer-


ence variable, and assign it the value of s as below:
String s;
s = new String("This is a string");
String t = s;

What happens is that a new reference variable is created some-


where else in memory, and the address already contained by the
reference variable s is copied into it. So, both variables s and t end
up being references to the same object in memory. The final situation
in memory may look something like Figure 10.9. The same final
situation represented with hooks and fish is in Figure 10.10.
string s 101

Figure 10.8: The fish-and-hook view


after the String object is created.
Objects are rectangles ("fish") and now
the hook is properly connected to it.

Figure 10.9: Another String reference


variable t is created, and assigned the
value of s. The two reference variables
end up pointing at the same String
object in memory.

The interesting part is this: Even though what we have gone


through so far is pretty close in principle to what happens under
the hood in Java, in practice you seldom see the addresses, or the
values of the pointers. The true irony lies in the fact that while
Java has gone through a lot of trouble to hide the pointers from
programmers, it is not easy to understand what exactly is going on
without understanding the nature of pointers.

Creating Strings

As mentioned in the previous section, Strings get a lot of special


treatment. The first special treatment happens in their creation.
Also as mentioned in the previous section, the "standard" way to
produce a String object would be:
String s = new String("This is a string");

But, there is a shorter version of this, which is used almost exclu-


sively, and we have already seen at the very beginning:
String s = "This is a string";

Just the double-quote delimited text is enough to create a String


object. This is what happens when we do printing as before:
System.out.println("Hello, world!");

Figure 10.10: The situation with two


references, and one object in the
hooks-and-fishes representation. Two
fish hooks hook into the one fish. In
Java, hooks can not hook into other
hooks, only fish.
102 programming with java

We are creating a String object, and giving it to the println


method so it gets printed out on the screen.

Methods of String

All classes have methods associated with them which allow us to


"do things" with them. A method is a function that is attached to an
object. Let us look at a few examples, which should make things
easier to understand.

length
The first method we will look at is length and below you can see
what the Java documentation says about it:

length
public int length()
Returns the length of this string. The length is equal to the num-
ber of Unicode code units in the string.
Specified by:
length in interface CharSequence
Returns:
the length of the sequence of characters represented by this object.

Once again, there are a few things in this documentation that are
not familiar. For now, do not worry about "interface" or CharSequence.
One thing that needs translation is "Unicode code units". This is a
(perhaps excessively) precise way of saying "characters". Here is a
code fragment showing how to use this method:
String s = "Hello, world!";

System.out.println(s.length());

If you put this fragment in a main() method and execute it, it


will print out the number 13. This is exactly how many characters
there are in the string "Hello, world!".

charAt
Another important method is charAt:

charAt
public char charAt(int index)
Returns the char value at the specified index. An index ranges
from 0 to length() - 1. The first char value of the sequence is at
index 0, the next at index 1, and so on, as for array indexing. If
the char value specified by the index is a surrogate, the surrogate
value is returned.
Specified by:
charAt in interface CharSequence
Parameters:
index - the index of the char value.
string s 103

Returns:
the char value at the specified index of this string. The first char
value is at index 0.
Throws:
IndexOutOfBoundsException - if the index argument is negative or
not less than the length of this string.

Here is a code fragment demonstrating the use of the charAt


method:
String s = "Hello, world!";

char firstCharacter = s.charAt(0);


char lastCharacter = s.charAt(s.length() - 1);

System.out.println(firstCharacter);
System.out.println(lastCharacter);

If you execute this code fragment, it will print the characters


’H’and ’!’on a line each.
Once again, there is some foreign terminology in the documenta-
tion of this method. However, one thing is important: The indexing
of the characters in a String. What we call an index is a number
indicating the order of the character in a String. As humans, when
we need to number things in a linear way, we start at the number 1,
and continue upwards. If there are five people at the service queue,
we number them as "the first person", "the second person", "the
third person" and so on.
In the world of computing, especially in general-purpose pro-
gramming languages, you will find that counting and indexing What we are talking about here is
starts at the number zero (0). It may look like the idea is to make array indexing, really. However, since
I consider that Strings are more
things hard for people, but it is not. The zero-based counting has fundamental that arrays, String comes
to do with keeping the memory addresses of things stored one af- earlier in the book. In fact, a String
does internally contain a char array.
ter the other in memory. The index of one thing is its offset from But we will do just fine.
the beginning address. So, since the first "thing" is exactly at the
beginning address, its offset is zero, and it is easier to use that as
its index. The second "thing" is at offset one (you need to skip one
thing to get the second thing) so its index is two. If there are n ob-
jects in memory, they will be indexed from 0 to n − 1.

String Comparison

This is an important topic, and as such requires and deserves a


section by itself. For all basic data types when we want to check for
equality, we use the equality operator, ==. However, trying to use
that when comparing two Strings may and will create unexpected
and unintended situations.
Let us get right to the example:
public class Main {
public static void main(String[] args) {
String a = "x";
String b = "x";
104 programming with java

if (a == b) {
System.out.println("The two Strings are equal.");
} else {
System.out.println("The two Strings are NOT equal.");
}
}
}

If you type this in, and execute it, it prints:


The two Strings are equal.

Now you are thinking, "Well, it works just fine! What is the prob-
lem?". The problem is that it works as expected, but for the wrong
reasons. We will circle back to why this seems to work. Here is the
next example you need to look at:
public class Main {
public static void main(String[] args) {
String a = "Hello";
String b = " world";
String c = "Hello world";
String d = a + b;

if (c == d) {
System.out.println("The two Strings are equal.");
} else {
System.out.println("The two Strings are NOT equal.");
}
}
}

If you type this code in, and execute it, it prints:


The two Strings are NOT equal.

You can actually check that the contents of the two Strings c and
d are exactly the same. However, checking for equality indicates
that they are not equal.
Why does this happen? This is because the == operator checks
for reference equality. That means, for the comparison to result in
a true value, the two Strings must be the same String living in
the same exact place in the computer memory. Strings residing in
different places would not be compared as equal by the == operator
even if their contents were identical.
Of course, what you usually want (and by usually we mean almost
always) is to check Strings for equality of content. How do you do
that in Java? The answer is, as is the answer for a lot of things, by
using a method. Here is the above example modified:
public class Main {
public static void main(String[] args) {
String a = "Hello";
String b = " world";
String c = "Hello world";
String d = a + b;

if (c.equals(d)) {
System.out.println("The two Strings are equal.");
} else {
System.out.println("The two Strings are NOT equal.");
}
string s 105

}
}

If you run this code with the changes, you get the following
output:
The two Strings are equal.

The equals method compares two Strings for content equality.


Now, back to the first case we tried... Why did the first example
work? The answer is a bit involved, but interesting. Java has this
mechanism called "internalization" or "interning" which is used
to combine multiple copies of the same String to a single one in
memory. This can be done manually by calling the intern method,
but it is also done automatically for you by the compiler when the
compiler sees multiple Strings at compile time with the same con-
tent. This is what happens in the first example; even though two
different Strings seem to be created with the content "x", they get
combined to a single String by the compiler. This is why the ==
operator works correctly.
Usually, you do not need to know about interning, but you
should know that the proper way to compare Strings in Java is
to use the equals() method.

Exercises

Exercise 1 Write a program that starts with a String, and checks


and prints whether that String is a palindrome or not.
A palindrome is a string that is the same forwards and back-
wards. For example, the following English words are palindromes:
"civic", "madam", "level", "rotor"
On the other hand, "motor" is not palindrome, because back-
wards it is "rotom", which is not the same as "motor".
The outline of the program should look like:
public class Main {
public static void main(String[] args) {
String s = "rotor";
boolean isPalindrome = true;

// Your code should go here and assign


// true to isPalindrome if the String s
// is a palindrome, and false if it is not.

if (isPalindrome) {
System.out.println(s + " is a palindrome.");
} else {
System.out.println(s + " is not a palindrome.");
}
}
}

You can assume all words are in lowercase English letters.

Exercise 2 Write a program that starts with a String, and prints


the number of words in that String.
106 programming with java

For example, the number of words in the following String is


eight:
"Premature optimization is the root of all evil."

The number of words in this String is four:


" Just go away, please! "

(So just counting spaces and adding one will not work.)
For this, you should make use of the following method:
boolean Character.isWhitespace(char c)
So, you give it a char, and if it is whitespace, it returns true. In
this question, it is safe to define a word as anything separated by
any number of whitespace characters. (Note that punctuation is
always adjacent to the previous word, and is always followed by at
least one space. Or at least, we assume so for the purposes of this
quesiton.)
The outline of the program should look like:
public class Main {
public static void main(String[] args) {
String s = " Just go away, please! ";
int wordCount = 0;

// Your code should go here and calculate


// the number of words in the given String.

System.out.println("The following string:");


System.out.println(s);
System.out.println("Contains " + wordCount + " words.");
}
}
11 Mixed Exercises

At this point in time, you should know enough to write things in


a single main method. Exercises about single things are fine, but
doing things to pick and choose what you need to use is the heart
and soul of programming.

Excercise 1 Write a program to print all positive integers up to


and including 1000 that are divisible by 7.

Exercise 2 Write a program to compute the sum of the squares of


all positive integers up to and including 100. (Do not use summa-
tion formulas, make the computer do the addition.)

Exercise 3 Write a program to find all three-digit positive integers


such that the sum of the cubes of the decimal digits are equal to the
number itself.

Exercise 4 Write a program to find all three digit numbers where


the sum of their decimal digits is exactly 25.

Exercise 5 Write a program that, given a positive integer finds its


integer square root and prints it out, if any. If there is no square
root, the program should print a message indicating that.

Exercise 6 Enhance the previous program so that when there


is no exact square root, it should print a message indicating the
two integers the exact square root is between. For example, when
the program is run with 75, it should say that the square root lies
between 8 and 9.

Exercise 7 Write a program that will compute the factorial of any


positive integer n (as a double), n! defined as:

n! = n × (n − 1) × (n − 2) × · · · × 3 × 2 × 1

Exercise 8 The Taylor expansion for the exponential function e x is


given below:

x2 x3 x4
ex = 1 + x + + + ...
2! 3! 4!
108 programming with java

This means, of course, that e itself can be computed as:

1 1 1
e = 1+1+ + + ...
2! 3! 4!
Write a computer program to compute e as a double. Make sure
it is correct to at least ten decimal places.

Exercise 9 Write a computer program to find the sum of the re-


ciprocal squares of all positive integers. That is, compute B given as
follows:

1
B= ∑ n 2
n =1
This is known as the "Basel Problem", and it is possible to prove
that B = π 2 /6. Verify your result.

Exercise 10 Write a computer program to compute the Fibonacci


series. The Fibonacci series is given as follows:

1 1 2 3 5 8 13 21 34 55 89 . . .

The series begins with 1 1, and every number afterwards is the


sum of the previous two numbers. More formally, we define F1 = 1,
F2 = 1, Fn+2 = Fn+1 + Fn .
So, the program should be able to compute Fn for any n.

Exercise 11 Write a computer program to calculate ln 2. Note that From the first midterm examination,
there is a series expansion for the natural logarithm as follows: Fall 2022.

x2 x3 x4
ln(1 + x ) = x − + − ···
2 3 4
This series converges for −1 < x ≤ 1. For x = 1, we get a series
for ln 2:

1 1 1
ln(2) = 1 − + − ···
2 3 4
This is also known as the alternating harmonic series. Write a com-
puter program to compute the value of ln2 using the first 1000
terms of the series.

Exercise 12 Write a computer program to find the number of From the first midterm examination,
divisors of any positive integer. Fall 2022.

Every positive integer has a set of positive integers that divide


it exactly. For example, the number 6 has exactly 4 divisors, 1, 2, 3,
and 6. Prime numbers have exactly two divisors, like 5 has 1 and 5
as its divisors.
Write a computer program that starts with any positive integer
and calculates and prints out its number of divisors.

Exercise 13 Building up on the previous exercise, write a com- From the first midterm examination,
puter program that finds and prints out all four-digit integers that Fall 2022.

have exactly 22 divisors.


12 Static Methods

Finally, we are going to be breaking the bounds of the main method.


We will do so by learning about static methods. The methods you
saw in our study of the String class were instance methods. What
this means is, when you did something like:
String s = "abc";
System.out.println(s.length());

... you were calling the length() method of that specific String
instance. That is, the length() method made use of the contents
of the particular String containing "abc", and returned the value
of 3 to be printed on the screen. So, instance methods are always
connected to objects (explicitly or implicitly) created with the new
keyword. (Here the new is implicit, again due to special treatment of
the String class.)
On the other hand, static methods are not connected with any
object instance. They belong directly to the class. Our primary
example is the main method, which is a static method as is obvi-
ous from its declaration containing the static keyword. All static
methods are called in the form <class name>.<method name>() or
just using the method name if the call is done from within the same
class.
Before we attempt to write any static methods, let us use ex-
isting static methods from the Java library first. Our primary
example here is going to be Java Math class, which contains quite a
few static methods for mathematical computations.

The Math Class

The Math class is library class containing many methods for most
mathematical operations you can think of.
exp
public static double exp(double a)
Returns Euler’s number e raised to the power of a double value.
Special cases:
If the argument is NaN, the result is NaN.
If the argument is positive infinity, then the result is positive infin-
ity.
If the argument is negative infinity, then the result is positive zero.
If the argument is zero, then the result is 1.0.
110 programming with java

The computed result must be within 1 ulp of the exact result.


Results must be semi-monotonic.
Parameters:
a - the exponent to raise e to.
Returns:
the value e a , where e is the base of the natural logarithms.

This is a method used to calculate values of the exponential


function, e x . The documentation above, taken from the official Java
documentation should be pretty clear. There is one new concept,
which the ulp. It is used as a word, but it is in fact an acronym,
standing for "Unit in the Last Place" or "Unit of Least Precision".
It means a difference of one in the last digit of the floating-point
representation. In other words, an ulp is the difference between
two consecutive numbers that can be represented by the particular
floating-point code.

log
public static double log(double a)
Returns the natural logarithm (base e) of a double value. Special
cases:
If the argument is NaN or less than zero, then the result is NaN.
If the argument is positive infinity, then the result is positive infin-
ity.
If the argument is positive zero or negative zero, then the result is
negative infinity.
If the argument is 1.0, then the result is positive zero.
The computed result must be within 1 ulp of the exact result.
Results must be semi-monotonic.
Parameters:
a - a value
Returns:
the value ln a, the natural logarithm of a.

The point is that you can call all of these static methods in the
same way. The way you call a static method is as follows:
<class name>.<method name>(<arguments>)

An example is in order. The following code fragment will print


the value of Euler’s constant, e:
System.out.println(Math.exp(1));

As another example, the following will print the square root of


two:
System.out.println(Math.sqrt(2));

Writing Your Own Static Methods

A static method is essentially a method that operates only on its


arguments and returns something as its result. This is as close as it Again, this is not strictly true. How-
ever, it is true that a static method
may not access member variables,
since it is not bound to any object.
static methods 111

gets to a mathemetical function.


Writing static methods (any method really) is a way to organize
your code into meaningful pieces with a name. The declaration of a
static method is of the following form:

public static <return type> <method name>(<argument list>) {


<method body>
}

The return type declares what the result of the method is go-
ing to be. The method name is the name you want to give your
method, which is very similar to a variable name; just as arbitrary
and just as important. The argument list is a comma separated list
of variable declarations, which represent the arguments that must
be supplied to the method while calling it.
Let us start with an example problem. Suppose we want to write
a program to calculate integer powers of integers, that is, ab . Up to
now, without static methods we would have written the program
as follows:
public class Main {

public static void main(String[] args) {


int a = 3;
int b = 4;

int pow = 1;

for (int i = 0; i < b; i++) {


pow *= a;
}

System.out.println(pow);

}
}

This specifically calculates 34 , and if you run it correctly prints


81. To calculate another power, you would have to modify the pro-
gram and change the values given to the variables a and b. This
may not be such a big deal, but if you wanted to calculate both 34
and 75 , you would need to repeat the same code block in the pro-
gram. The proper solution is to convert the part of the program
that computes the power into a static method. This can be done as
follows:
public class Main {

public static void main(String[] args) {


System.out.println(integerPower(3, 4));
}

public static int integerPower(int a, int b) {


int pow = 1;

for (int i = 0; i < b; i++) {


pow *= a;
}

return pow;
}
112 programming with java

Let us examine what we have done here. The declaration line is:
public static int integerPower(int a, int b) {

This declaration line tells us that the name of the static method is
integerPower. The int to the left of the method name indicates that
the result of this method is going to be an integer. The argument
list, which is inside the paretheses to the right of the method name
indicates that this method takes two arguments, their names are a
and b and both arguments are of type int.
The body of the methodis the part that is between the opening
and closing braces after the argument list’s closing parenthesis. The
really new part here is the last statement, which reads:
return pow;

This is what is called a return statement. The return statements


does two things: First, the value (or expression) following it be-
comes the return value, that is, the result of the method it is in. Sec-
ond, the execution of a return statement ends the execution of the
method, and continues from the point in the code that performed
the method call.
In the example, you can also see how the integerPower method
is called from the good old main method:
System.out.println(integerPower(3, 4));

We just said that static methods are called in the form <class
name>.<method name>(<argument list>). According to that, our
call should have looked like the following:
System.out.println(Main.integerPower(3, 4));

It is possible to do this, and perfectly valid. However, the class


name can be omitted when the static method you are calling is in
the same class as the calling method. This is exactly the case, so the
class name has been (rightfully) omitted.

Calling Conventions

Consider the following program:


public class Main {

public static void main(String[] args) {


int y = 1;

increment(y);

System.out.println(y);

public static void increment(int x) {


x = x + 1;
}
}
static methods 113

The method called increment seems to take an argument called


x, and then seems to add one to it. The main method calls this
method with the variable y which has an initial value of 1 and
afterwards prints this variable.
What will this print? If you try it, you will find out that this
prints 1 on the screen.
The reason is the calling convention of Java. The calling con-
vention, which means the way arguments passed to a method are There is usually some confusion about
processed, is "call by value" in Java. This means that when a value this. If you pass a reference variable as
the argument, and you make changes
is passed to another method as an argument, its value is copied into to the referenced object through that
another variable, which is the method argument. In the current variable, changes to the object will
be observable in the calling method
case, when we call increment(y); what happens is that the value 1 through the reference variable. This
contained in the varible y is copied into the argument variable x in does not mean the call is by reference;
the method. Thus, any changes made to an argument by assigning new it is still call by value.

values to it in a method has no effect on variables outside that method. In


other words, no matter what you assign to variable x, the value of y
in the calling method can not, and will not change.
In general, the calling conventions in programming languages
are "call by value" and "call by reference". Call by value is what
we have in Java, and there is no alternative. The convetion "call by
reference" means that the a reference to the variable is passed down
to the method called, and in such cases, all changes are reflected in
the original variable. This is never the case in Java.

Exercises

Exercise 1 Write a static method that takes a given String and


returns its reverse. The method should look like the following:
public static String reverse(String s) {
// Method body goes here.
}

For instance, the following code snippet


System.out.println(reverse("Hello"));

should produce the output:


olleH

Hint: You can concatenate a String and a char with the + opera-
tor.

Exercise 2 Write a static method to compute the greatest com-


mon divisor (GCD) of two positive integers. The GCD of two num-
bers a and b is defined to be the largest integer c such that c di-
vides a (without remainder) and c divides b without remainder.
This we will denote as:

gcd( a, b)
So, gcd(3, 5) = 1, gcd(10, 25) = 5, and gcd(125, 50) = 25.
114 programming with java

Note that if gcd( a, b) = c then necessarily c < a, b.


The method should look like the following:
public static int gcd(int a, int b) {
// Method body goes here.
}

Exercise 3 Consider the gcd method of the exercise above. Can


you use that method to compute the following quickly?

gcd(2 000 000 000, 2 050 000 000)


If it takes a couple of seconds, you may want to change your
algorithm. Look up the "Euclidean Algorithm" and use that to
speed up your method.
13 Arrays

An array is essentially many variables of the same type, following


each other sequentially in memory. Declaring an array is a way of
declaring many variables of the same type at once.

Declaring and Creating Arrays

Suppose I want to keep the heights of every student in class in


integral centimeters in variables. Also, let us assume there are 40
students. One (bad and impractical) way to do this would be the
following:
int height1;
int height2;
int height3;
int height4;
// Quite a few skipped
int height38;
int height39;
int height40;

A better way to do this is do declare an array, which is done as


follows:
int[] heights = new int[40];

The square brackets after the int makes the type of the variable
called heights an int array rather than a regular int. And, int[] is
pronounced as "int array".
An array variable is a reference variable. As such, it is only a
name, and the actual object (which is an array here) must be created
using the new keyword. This is what is done on the right hand side
of the assignment with new int[40]. This creates an array of 40
integer variables, and makes the name heights a reference to it.
The general way to declare an array of any type is:
<type>[] <name>;

And, to actually create an array in memory, you will need to use:


new <type>[length]

You usually use these in combination as follows:


<type>[] <name} = new <type>[length];
116 programming with java

Accessing Individual Elements of the Array

We call each of the variables contained in the array an element of the


array. Accessing elements is done through indexing which is follow-
ing the array variable name with a number in square brackets.
Here is an example to clarify:
int[] heights = new int[40];

heights[0] = 175;
heights[1] = heights[0] + 2;
heights[39] = 162;

heights[40] = 0; // Error
heights[-1] = 0; // Error

If you have created an array of length n, the indices run from 0


to n-1. The first element of the array is always at index 0, so here
the first element is heights[0]. You can do with array elements
anything you can do with regular variables, like the second element
(heights[1]) is assigned the first element, plus two.
If you try to access elements which do not exist, you will get
an error when you run your program. The last two lines in the
example both contain errors; both are outside the range of 0 . . . 39. Negative indices are never accessible
in Java.

Literal Array Initializers

Let us say you want to create an array of five integers, and assign
them the values 3, 5, 7, 11, 13. One way to do this is as follows:
int[] x = new int[5];

x[0] = 3;
x[1] = 5;
x[2] = 7;
x[3] = 11;
x[4] = 13;

Another way to do this is to make use of a literal array initializer.


Here is how:
int[] x = {3, 5, 7, 11, 13};

The general notation is:


<type>[] <name> = {<value>, <value>, <value> ...};

Such an initializer both creates an array of the needed size (you


do not put in a length, it just becomes large enough to contain the
values you list) and fills it with the provided values. Note that this
notation is only allowed in the initializer, that is, the line where the
array variable is declared. As such, while the following looks like it
should work, it will generate a compile-time error:
int[] x;

x = {3, 5, 7, 11, 13}; // Compile error


arrays 117

Automatic Initialization

We have seen that with regular variables if you never give them an
initial value, they are marked as "having no value" and the compiler
will not let you use them. Just to remember, the following code
fragment will produce an error at compile time: It says "variable m might not have
been initialized".
int m;

System.out.println(m);

In contrast to this, if you give no initializer for your array the


array is initialized automatically to its "default" value. The default
value for numbers is zero, for boolean variables false, and for
reference types null. So, the following code fragment compiles and
works just fine, printing out zero.
int[] m = new int[10];

System.out.println(m[3]);

The length Member Variable

Any array, in addition to its values which can be accessed by in-


dexing, has an additional piece of information that you can access,
which is the length of the array. The length is accessed simply as:
<array variable name>.length

This is very useful when you want to operate on arrays in gen-


eral. For example suppose you want to write a static method that
will compute the sum of all elements in an int[]. Here is how you
would write it:
public static int sum(int[] a) {
int sum = 0;

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


sum += a[i];
}

return sum;
}

Since the array carries it own length information with it I can write a
general method to sum the elements in the array.

Enhanced for Loop

You may be wondering what is a section about the for loop doing
in a chapter about arrays. Well, there is an enhanced version of the
for loop, which can only be used with collections, of which the
array is the simplest example.
If you examine the array summing example in the previous sec-
tion, you can see that the loop variable only exists because I want
118 programming with java

to access every element in the array in an ordered way. Also, I ref-


erence the length of the array only because I want to stop when I
reach the end of the array. My intention is just to access each element
in the array and perform some action on it. So, the enchanced for
loop (also named the for-each loop) exists to do that without all
the clutter. The same summation method implemented using the
enhanced for loop is below:

public static int sum(int[] a) {


int sum = 0;

for (int x : a) {
sum += x;
}

return sum;
}

Note that the loop has been reduced. The operative line is:

for (int x : a) {

This line is read as "for each int x in a". So, what we are asking
the computer to do is to run the loop body for every element inside
the array a, and pass them one by one to the loop body. In the loop
body, for every iteration the variable x gets the next value from the
array.
It is a good idea to make use of the enhanced for loop any time
you can. There are specific times when you do not want to use
this; one common reason is because you need to make use of the
index (like printing) variable; and the enchanced for loop hides
that information.

The Arrays class

Arrays in Java are reference types, but are not classes. As such,
there is a bunch of functionality that is designed to work with ar-
rays inside the Arrays class. All these are in the form of static
methods, similar to the case with the Math class static methods.
One important method is the sort method. This method takes In fact, this is a whole family of meth-
an array, and modifies the array in-place to put its elements into ods, since there is a method for each
numeric array type, plus there are
ascending order. The Java documentation states the following: variants that sort only ranges of the
given array.
sort
public static void sort(int[] a)
Sorts the specified array into ascending numerical order.
Implementation Note:
The sorting algorithm is a Dual-Pivot Quicksort by Vladimir
Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm offers
O(n log(n)) performance on all data sets, and is typically faster
than traditional (one-pivot) Quicksort implementations.
Parameters: a - the array to be sorted
arrays 119

Better Data Structures

If you look at any code used in practice, perhaps with the exception
of low-level number-crunching, nobody uses arrays any more. In-
stead, you will find a host of different classes from the Java library
used.
Studying the full extent of data structures, the algorithms that
go with these data structures, and their implementation in Java is
worth a full course on its own (almost always called "Data Struc-
tures and Algorithms"). Here, we will only examine the two Java
classes that provide "better" alternatives to using "bare" arrays;
ArrayList and LinkedList.

Figure 13.1: What a nine element


byte array would look like laid out in
memory.

An array is what I like to call a "linear" data structure. Its ele-


ments are organized like a line. In the case of the array, each ele-
ment is located in adjacent memory addresses.
In Figure 13.1 you can see how an array would normally be
laid out in memory. This specific one is a byte array, since every
element takes up just one byte in memory. The length of this array
is nine, since it contains exactly nine elements, indexed from 0 to 8. Write sections about Ar-
rayList and LinkedList.
Recap

• An array is a way to declare many variables of the same type at


once.

• An array variable is declared as <type>[] <variable name>, an


int array named x would be declared as int[] x.

• An array variable is a reference variable, the actual array must be


created using the new operator.

• The following makes x a 10-element int array: int[] x = new


int[10]. The number in brackets is the size of the array.

• Individual elements of the array are accessed as x[0], x[1] . . . .


This is called indexing.

• The index always starts at zero, and goes to one less than the
length of the array.

• For an array variable x, x.length gives the length of the array.


120 programming with java

• There is an enchanced for loop that goes through all elements


without the need for a loop variable.

• An enhanced for looks like for(int e : x), where the variable


e received all values in array x sequentially.

Exercises

Exercise 1
Write a static method that takes an int array as an argument,
and returns the second largest element as an int. The array itself
must remain unmodified, and use of Arrays.sort() is not allowed.
Use the following template program:
public class Main {
public static void main(String[] args) {
int[] x = {6, 8, 9, 2, -1, 12, 5, 17, 3, 1, 7, 11};

int secondLargest = secondLargestElement(x);

// If done correctly, this should print 12.


System.out.println("Second largest element is: " + secondLargest);
}

public static int secondLargestElement(int[] a) {


// This must find the second-largest element
// in the array a, and return it.
return 0;
}
}

Exercise 2
Write a static method that takes an int array as an argument,
and reports whether its elements can form a set, or not. This es-
sentially reports whether or not whether the array contains dupli-
cate elements. If the array does not contain duplicate elements, the
method should return true, indicating that it is a set. If the array
does contain duplicate elements, the method should return false,
indicating that is not a set.
Use the following template program:
public class Main {
public static void main(String[] args) {
int[] x = {6, 8, 9, 2, -1, 12, 5, 17, 3, 1, 7, 11};
int[] y = {6, 8, 9, 2, -1, 12, 5, 17, 9, 1, 7, 11};

// Should print true if implemented correctly.


System.out.println(isSet(x));

// Should print false if implemented correctly.


System.out.println(isSet(y));

public static boolean isSet(int[] a) {


// Return true if there are no duplicate
// elements in the array. Otherwise,
// return false.
arrays 121

return false;
}
}
14 Classes

Although we are not going to fully delve into object-oriented pro-


gramming yet, in order to claim you have "covered the basics", you
need to at least know how to define new data types making use of
classes, and writing "regular" methods to go with them.
The foundation of object-oriented programming is defining new,
composite data types and providing methods that perform opera-
tions on them. So, that is what we are going to do. Without going
into any theoretical detail, we will just jump into an example.
Java has no data type for complex numbers. And, complex num-
bers are as simple as it gets when it comes to a data type; a com-
plex number is just two numbers.

Defining the Class

When you want to define a new data type, you create a new class.
Any class goes into a file matching its name, as you already know.
So, we start by creating a new file Complex.java with the class
Complex inside it.
public class Complex {

Member Variables

Right now it is empty. Now, the next thing we want to do is create


member variables. Member variables, as opposed to local variables,
go directly inside the class definition, and not inside a method.
They essentially define what the class "has" as data. So, when we
talk about a complex number, we say "a complex number has a real
part" and "a complex number has an imaginary part", so these are
the things that will become member variables. When we add these
in, the class looks like the following:
public class Complex {
public double real;
public double imaginary;
}

The public keyword essentially means that these member vari-


ables can be accessed from anywhere in the program; they are
public information.
124 programming with java

Just by doing this, we have defined a new data type which we


can use from other parts of the program. Here is a separate Main
class with a main method that makes use of it:
public class Main {

public static void main(String[] args) {


Complex u = new Complex();
u.real = 3;
u.imaginary = 4;
}
}

The first line is creating a reference variable u, and assigns to it a


new Complex object created in memory. In the next lines, we assign
values to the newly created Complex object’s member variables by
accessing them directly.
What have we accomplished? We just created two variables
packaged together, we gave both the variables names and a name
to the package itself. Does this make it a complex number? Well,
yes and no. It has the right parts, but what really makes a complex
number a complex number is the operations we define on it.

Methods

Let us start simple: Every complex number has a magnitude, which


we define as:

z = x+i·y
q
| z | = x 2 + y2

So, we can now add a method to our complex class, to add func-
tionality to it. This is going to be a "regular", non-static method.
Here is the class after having added it:
public class Complex {
public double real;
public double imaginary;

public double magnitude() {


return Math.sqrt(real*real + imaginary*imaginary);
}
}

Now we can use it from the main method, too:


public class Main {

public static void main(String[] args) {


Complex u = new Complex();
u.real = 3;
u.imaginary = 4;

System.out.println(u.magnitude());
}
}

If you run this here, you will find that it nicely prints 5.0 since
the magnitude of 3 + i · 4 is 5.
classes 125

What other operation can we have on a single complex number?


Well, there is the complex conjugate, which we define as follows:

z = x+i·y
z∗ = x − i · y

In the case of the magnitude, the result was a regular number,


therefore we returned a double. Here, the result is another complex
number. Can we do that? Of course we can:
public class Complex {
public double real;
public double imaginary;

public double magnitude() {


return Math.sqrt(real*real + imaginary*imaginary);
}

public Complex conjugate() {


Complex c = new Complex();
c.real = real;
c.imaginary = -imaginary;
return c;
}
}

We defined a new method, called conjugate. Its return type is


Complex, because the complex conjugate of a complex number
is another complex number. Inside the method, we need a new
complex number, which we start by creating with the new operator,
and we assign it to the local variable c. The next two lines look a
little strange, but they are simple. First, we assign the real part of
the Complex number we are in to the real part of the newly created
Complex number. Then, we assign the imaginary part the same way,
except it is negated as per the definition of the complex conjugate.
There is another method that would be nice to have: Every com-
plex number, in addition to having a magnitude, also has what we
call an argument. The argument stems from the polar representation
of a complex number:

z = x+i·y
z = r · eiθ

The Cartesian representation is unique; however the polar rep-


resentation is not. We will not worry about it too much, the value
we return for the argument will not be the same as its mathematical
definition, but it will work in the sense that the following will be
valid:

x = r · cos θ
y = r · sin θ

Once again, the return value will be a number. So, here is what
the class looks like having added this method:
126 programming with java

public class Complex {


public double real;
public double imaginary;

public double magnitude() {


return Math.sqrt(real*real + imaginary*imaginary);
}

public double argument() {


return Math.atan2(imaginary, real);
}

public Complex conjugate() {


Complex c = new Complex();
c.real = real;
c.imaginary = -imaginary;
return c;
}
}

We made use of the Math.atan2() static method. You may want


to go and read its documentation at this point. In any case, here is
the Main class modified to make use of it:

public class Main {

public static void main(String[] args) {


Complex u = new Complex();
u.real = 3;
u.imaginary = 4;

double magnitude = u.magnitude();


double argument = u.argument();

System.out.println(magnitude * Math.cos(argument));
System.out.println(magnitude * Math.sin(argument));
}
}

If you run this, it prints the following:

3.0000000000000004
3.9999999999999996

It is quite close to the original real and imaginary parts, but not
exactly. Even though the precision is double numbers is good, real
computation always introduces some amount of error.
These are almost all of the things you can do with a single com-
plex number. Most of the meaning of complex numbers come from
binary operations on complex numbers - operations that operate on
two complex numbers.
The simplest of these is probably addition. The definition of
addition is simple:
classes 127

z = z x + i · zy
u = u x + i · uy
v = v x + i · vy

z = u+v

zx = ux + vx
zy = uy + vy

What we really want to do in this case is be able to write some-


thing like the following in the main method (this has no chance of
compiling): This is possible in other programming
languages where operator overloading is
public class Main {
allowed. The term overloading is used
in the sense that you assign additional
public static void main(String[] args) {
functionality to an already existing
Complex u = new Complex();
operator. The design rationale in Java
u.real = 3;
is that operator overloading may be
u.imaginary = 4;
used to create confusing situations;
such as overloading the + operator to
Complex v = new Complex();
do something unrelated to addition.
v.real = 8;
In retrospect, this is not a very good
v.imaginary = 15;
reason, but the decision stands.

Complex z;

z = u + v; // This is not possible in Java


}
}

What you can do instead is something like the following:


public class Main {

public static void main(String[] args) {


Complex u = new Complex();
u.real = 3;
u.imaginary = 4;

Complex v = new Complex();


v.real = 8;
v.imaginary = 15;

Complex z;

z = u.add(v); // This is possible

System.out.println(z.real); // Expected to print 11.0


System.out.println(z.imaginary); // Expected to print 19.0

}
}

If you just type that in, it will not compile, because we did not
implement the method yet. What we intend to do is to write a
method called add which operates on two Complex objects, one
of them is the one the method "belongs" to, the other one will be
passed as an argument. The result will be another, new Complex
object. Here is the implementation:
128 programming with java

public class Complex {


public double real;
public double imaginary;

public double magnitude() {


return Math.sqrt(real*real + imaginary*imaginary);
}

public double argument() {


return Math.atan2(imaginary, real); This is exactly why atan2 exists.
}

public Complex conjugate() {


Complex c = new Complex();
c.real = real;
c.imaginary = -imaginary;
return c;
}

public Complex add(Complex other) {


Complex sum = new Complex();
sum.real = real + other.real;
sum.imaginary = imaginary + other.imaginary;

return sum;
}
}

When you type the new add method in, the Main class should
be compiling with no errors. Now, you can run it, and the result
would be:
11.0
19.0

This is very much in line with what is expected.


What really makes complex numbers interesting (and differen-
tiates them from two dimensional vectors) is the definition of the
multiplication operator. Here is how multiplication is defined:

z = z x + i · zy
u = u x + i · uy
v = v x + i · vy

z = u·v

z x = u x · v x − uy · vy
zy = u x · vy + uy · v x

Here is the implementation of the multiplication method (the


rest of the class is skipped for brevity).
public Complex multiply(Complex other) {
Complex product = new Complex();

product.real = real * other.real - imaginary * other.imaginary;


product.imaginary = imaginary * other.real + real * other.
imaginary;
classes 129

return product;
}

Here is an obvious realization: Methods can call and use results


of other methods. This can be quite important and useful. Dividing
complex numbers is a bit more complicated, but can be defined as
follows:

z = z x + i · zy
u = u x + i · uy
v = v x + i · vy

u
z=
v
u · v∗
z=
v · v∗
u · v∗
z=
| v |2
This information should be sufficient to write the division
method. Here is one way to do this:
public Complex divide(Complex divisor) {
Complex z = multiply(divisor.conjugate());

double divisorMagnitude = divisor.magnitude();

z.real /= divisorMagnitude*divisorMagnitude;
z.imaginary /= divisorMagnitude*divisorMagnitude;

return z;
}

We made use of two existing methods here, conjugate and


magnitude. First, we create a new Complex object by multiplying
the current object with the complex conjugate of the divisor. Then,
we compute the divisor’s magnitude, and divide both the real part
of z by the magnitude squared.
Here is how we would check this:
public class Main {

public static void main(String[] args) {


Complex u = new Complex();
u.real = 3;
u.imaginary = 4;

Complex v = new Complex();


v.real = 6;
v.imaginary = 8;

Complex z = u.divide(v);

System.out.println(z.real); // Expected to print 0.5


System.out.println(z.imaginary); // Expected to print 0.0

}
}
130 programming with java

This tries to compute 3 + 4 · i divided by 6 + 8 · i. Since the


divisor is exactly twice the divident, we expect the result to be
0.5 + 0 · i, just good old real 0.5. If you run the program, you get the
following, confirming that the code works correctly:
0.5
0.0

Constructors

Consider the following code, which we have been using as a pat-


tern repeatedly:
Complex u = new Complex();
u.real = 3;
u.imaginary = 4;

We are creating an object of type Complex first, and then we set


its member variables to values we want. There is a better way to do
this, which will look like the following when we use it:
Complex u = new Complex(3, 4); // Will not compile yet.

This is how a constructor for an object is called. A constructor is


a special kind of function, which is always used together with the
new operator to create a new object, possibly taking arguments and
using them in the creation of the object in some way. In fact, the
previous call was also a constructor call:
Complex u = new Complex();

Here, we are calling a constructor that takes no arguments. The


constructor that takes no arguments is called the default constructor.
Normally, the constructor must be written in your class, just like
any other method. However, if your class contains no constructors at
all, the Java compiler automatically supplies the default constructor,
which is a default constructor that does nothing. This is exactly
why we could call a default constructor without writing one, or
even without knowing about constructors.
How does one write a constructor? Here is what the default
constructor for Complex would look like:
public class Complex {
public double real;
public double imaginary;

public Complex() {

// Rest of class skipped for brevity.


}

A constructor has the exact same name as the class in which it


is located. Also, unlike regular methods, a constructor does not
declare a return type. In fact, it is not allowed to have a return type.
It is possible to have more constructors than just the default
constructor. For example, for our Complex class, it would make
classes 131

sense to specify the real and imaginary parts of the number at


the time of object creation. Here is how such a constructor can be
implemented:
public class Complex {
public double real;
public double imaginary;

public Complex() {

public Complex(double x, double y) {


real = x;
imaginary = y;
}

// Rest of class skipped for brevity.


}

The new constructor we added here takes two arguments, and


we gave them names x and y. Their types are quite naturally
double. The constructor then takes these values, and assigns them
to the member variables.
How does this help? Well, the following code now compiles:
Complex u = new Complex(3, 4); // This will now compile.

This is now the equivalent of the following code:


Complex u = new Complex(); // Uses default constructor
u.real = 3;
u.imaginary = 4;

The this Reference Variable

There is a special reference variable name, which is a Java keyword,


called this. It is only accessible in non-static methods, and is a
reference to the object the current method "belongs to".
As an example, consider the add method we have already imple-
mented:
public Complex add(Complex other) {
Complex sum = new Complex();
sum.real = real + other.real;
sum.imaginary = imaginary + other.imaginary;

return sum;
}

We talk about sum.real, which is the real part of the Complex


object sum. We talk about other.real, which is the real part of the
Complex object other. How about the real by itself? Well, it is just
the member variable of the object we are operating on with this
method. We can make this fact explicit by making use of the this
reference variable as follows:
public Complex add(Complex other) {
Complex sum = new Complex();
sum.real = this.real + other.real;
132 programming with java

sum.imaginary = this.imaginary + other.imaginary;

return sum;
}

This does exactly the same thing; strictly speaking, in this case
using this is unnecessary. However, I believe in making use of it,
since this notation makes it obvious we are talking about a member
variable, not a local variable or a parameter.
There is an important case where it is not only useful, but also
necessary. Consider the two-argument constructor we have written
for the Complex class:
public Complex(double x, double y) {
real = x;
imaginary = y;
}

Now, I really really want to call the arguments to the constructor


real and imaginary to make it obvious to which member variable
each arguments corresponds to. I can try to do that as follows:
public Complex(double real, double imaginary) {
real = real; // Code with no effect
imaginary = imaginary; // Code with no effect
}

You may think that this would not compile, but it will, gener-
ating just a warning. This is because while you are not allowed to
use a local variable name twice (method arguments count as local
variables) you are allowed to have a local variable and a member
variable of the same name. Then, what happens when you have
both, and use the variable name? Which one are you talking about?
The answer is that the local variable takes precedence. This is called
shadowing, when you declare a local variable with the same name
as a member variable, the member variable is shadowed by the local
variable.
Is there a way to reach beyond the shadow? Yes, this is easily
accomplished by the this reference variable. The constructor can be
fixed to work properly as follows:
public Complex(double real, double imaginary) {
this.real = real; // Works fine
this.imaginary = imaginary; // Works fine
}

Now everything works just fine.


The this reference variable can be used everywhere a reference
variable can be used - except you can not assign a new value to it.
Going back to the divide method, it can be modified as follows
to make the multiplication operation more obvious:
public Complex divide(Complex divisor) {
Complex z = this.multiply(divisor.conjugate());

double divisorMagnitude = divisor.magnitude();

z.real /= divisorMagnitude*divisorMagnitude;
z.imaginary /= divisorMagnitude*divisorMagnitude;
classes 133

return z;
}

Access Modifiers and Encapsulation

The keyword public has been all over the place since the begin-
ning. It is actually what we call an access modifier. Marking some-
thing as public means pretty much that: It is accessible from any-
were in the program. We should really be saying any other class in
the program, since accessing methods or member variables from
within the same class is always possible.
One alternative to the public access modifier is the private
access modifier, which is exactly what it sounds like: Whatever you
apply it to (you can apply it to methods and member variables) can
only be accessed from within the same class, and nowhere else.
The big question is this: Why would we want to do this? The
answer, at its most fundamental is that we have limited brain ca-
pacity. A program typically contains many classes that interact with
one another. Every public part of a class is an interaction point,
leading to potential connections. We can not really remember and
track all such interactions easily, so we choose to reduce the possible
interactions, hopefully in a meaningful and orderly way.
What we call encapsulation is keeping the data safe from direct
modification from the outside world, allowing it to be only modi-
fied through well-defined means, which are the methods provided
in the class. We usually achieve this by making member variables
private, and allowing them to be accessed only through methods.
So, we change the top of our class to look like the following:
public class Complex {
private double real;
private double imaginary;

//... more things here

Once you do this, these data members can only be modified


from within the class, and not from the outside. There is no trou-
ble while creating Complex objects, because we can pass them as
arguments to the constructor, and then the constructor sets them
correctly.
However, when we try to print the values of the real and imagi-
nary parts from the main method which is in the Main class, we get
problems. The following lines do not compile any more:
System.out.println(z.real);
System.out.println(z.imaginary);

What do we do in this case? Once the member variables are


made private, but I still want to give read access to them, what can
I do? Well, we can provide methods to do that. Methods that are
134 programming with java

used to grant read access to a member variable (or something that


could be a member variable) is called a getter in Java. Getter methods
are generated according to a convention. If your member variable
is called x, its getter method is called getX(). The current example
would make excellent sense here. We can add the following two
getters into our Complex class to grant read access to the real and
imaginary parts of our complex number:
public class Complex {
//...

public double getReal() {


return this.real;
}

public double getImaginary() {


return this.imaginary;
}

//...
}

Now, we can print the real and imaginary parts from the main
method as follows:
System.out.println(z.getReal());
System.out.println(z.getImaginary());

What if you want to give write access (the ability to modify) to


the member variables, too? Well, in that case, you provide what are
called setters in Java. Of course, there is an opinion that says if you
give both read and write access, then what is the point of making
the member variable itself private? The opinion is not entirely
wrong, but let us push forward. Here is how we would add setters
for our two member variables in Java:
public class Complex {
//...

public void setReal(double real) {


this.real = real;
}

public void setImaginary(double imaginary) {


this.imaginary = imaginary;
}

//...
}

Again, if the member variable name is x, the setter method is


setX. Setters take an argument as opposed to getters which just
return a value. By convention, setters in Java do not return a value,
which is what the keyword void signifies.

Immutable Classes

The word immutable essentially means "unchangeable". Objects


whose member variables can only be set at creation time (through
the constructor’s parameters) and can be changed later in any way
classes 135

are called immutable objects. The classes of these objects are mutable
classes.
In a class, if all your member variables are private, and you
have getters but no setters, and no additional methods that assign
values to member variables, that class is immutable.
Why is this important? Again, limited cognitive capacity. Lim-
iting the ways in which objects can be modified makes thinking of
the program functionality easier in a multitude of ways. One pri-
mary example of an immutable class in Java is the good old String.
Once a String object is created, there is no way of changing its
contents.

Recap

• A class defines a type of object.

• A class contains data items, which are called member variables.

• Member variables are either primitive types (int, char, short,


double, . . . ) or other classes (defined in the program or from a
library).

• A class also contains functions that may operate on member


variables. Such functions are called methods.

• There are also methods called constructors used in conjunction


with the new keyword, used to create an object.

• The primary purpose of constructors is to set member variables


to their desired initial values.

• A constructor has no return type and always has the same name
as the enclosing class – indeed, this is how the compiler recog-
nizes a constructor.

• The keyword this is a reference variable which references the


object to which the current method belongs.

• When a member variable and a local variable or argument have


the same name, using the this keyword can be crucial.

• Usage of the same argument name as the member variable is


common in constructors.

• In addition to public, there is another access modifier, private


which makes that class element accessible only from the same
class.

• Hiding details of implementation from the outside world simpli-


fies programming. This is called encapsulation.
136 programming with java

Exercises

Exercise 1
The following is a Point class, designed as part of a two-dimensional
geometry library. Note that it has been designed to be immutable:
package tr.edu.ozyegin.cs101.geometry2d;

public class Point {


private double x;
private double y;

public Point(double x, double y) {


this.x = x;
this.y = y;
}

public double getX() {


return this.x;
}

public double getY() {


return this.y;
}
}

The next thing we want to do is to write a Line class. This will


represent a line in two dimensions. We need to choose how to
represent a line. This should be simple; since most lines require only
two parameters to represent, like:

y = m·x+n

This seems to be fine using only two parameters m (slope) and


n (y-intercept) to represent a line in two dimensions. However, it
cannot represent lines of the form x = c (which are vertical lines).
A better solution (perhaps less elegant) is to use three parameters to
represent the line, with the equation being in the following form:

a·x+b·y+c = 0

The three parameters a, b, and c are able to indeed represent


all lines in the plane. The drawback here is that unlike the two
parameter case above, the representation is not unique.
All things considered, we decide upon using this representation;
and we figure out that this is our choice of implementation, and
should not affect the overall functionality of the class. Indeed, we
can hide the implementation details from the rest of the program -
this is the heart and soul of encapsulation.
Here is the (incomplete) Line class:
package tr.edu.ozyegin.cs101.geometry2d;

public class Line {


// We are going to represent a line with parameters
// a, b, and c where
// ax + by + c = 0
classes 137

private double a;
private double b;
private double c;

/**
* Create a line with the equation ax + by + c = 0
*
* @param a the parameter a
* @param b the parameter b
* @param c the parameter c
*/

public Line(double a, double b, double c) {


this.a = a;
this.b = b;
this.c = c;
}

/**
* Create a line with the equation y = mx + n
*
* @param m slope of the line
* @param n y-intercept of the line
*/
public Line(double m, double n) {
this.a = m;
this.b = -1.0;
this.c = n;
}

/**
* Create a line that goes through both
* points p and q.
*
* @param p first point
* @param q second point
*/
public Line(Point p, Point q) {
// TO BE IMPLEMENTED
}

/**
* The parameter a for the equation ax + by + c = 0
*
* @return parameter a
*/
public double getA() {
return a;
}

/**
* The parameter b for the equation ax + by + c = 0
*
* @return parameter b
*/
public double getB() {
return b;
}

/**
* The parameter c for the equation ax + by + c = 0
*
* @return parameter c
*/
public double getC() {
return c;
}
138 programming with java

/**
* The slope of the line, m where y = mx + n
*
* @return m, the slope of the line
*/
public double getM() {
return -a/b;
}

/**
* The y-intercept of the line, n where y = mx + n
*
* @return n, the y-intercept of the line.
*/
public double getN() {
return -c/b;
}

/**
* Checks if given point p lies on this line or not.
* If it does, returns true. Otherwise, returns false.
*
* @param p the point to be checked
* @return true if p lies on this line
*/

public boolean goesThrough(Point p) {


// TO BE IMPLEMENTED

return false;
}

/**
* Finds the intersection point of this line and
* the line other given as the parameter.
*
* Returns null if this line and the other line
* are parallel.
*
* @param other the other line
* @return the intersection as a Point, null if parallel
*/

public Point intersection(Line other) {


// TO BE IMPLEMENTED

return null;
}
}

Note the ineteresting comments starting with /** before public


methods. These are called Javadoc comments, which are both in-
terpreted by your IDE to show you documentation, and can also
be extracted in the form of HTML or PDF documents for your pro-
gram. They document the "thing" coming after them.
The point of this exercise is to implement the three methods
marked as "TO BE IMPLEMENTED" in the Line class. These meth-
ods are as follows:
public Line(Point p, Point q)
Well, this is actually a constructor. In addition to the other two
constructors, this is supposed to make this Line a line that goes
through both given points. So, your goal is to set the member vari-
ables a, b, and c so that the line represented goes throught both
classes 139

points. This should work, since two points define a line. You can
assume that the two points are distinct.
public boolean goesThrough(Point p)
This method is used to check whether the line goes through the
given point. The method should return true if it does, and false if
it does not.
Note that it might be a good idea to introduce some tolerance
here. After all, it is possible that rounding errors may throw the
calculation off sufficiently so that the method returns false erro-
neously. On the other hand, what do we really mean by "going
through" when dealing with doubles? How close is close enough?
These are not easy questions to answer.
public Point intersection(Line other)
This should return the intersection point of this Line and the
other Line, provided as a parameter.
If the lines are parallel, it should return null.
Note that this can also suffer from precision problems when
trying to check for parallelism. Even if the two lines were meant to
be parallel, you might get an intersection at a very distant point.
Again, difficult questions to deal with.
Make sure your implementations work as specified by calling
them from a main() method.
15 Miscellanea

This chapter is a collection of things. Some have wide applicability,


while others are quite specific to Java. They are not large enough
to warrant a full chapter, however they are important enough to be
included.

Packages

You already know that classes have names. Naming classes is al-
ready hard, but another difficulty comes into play when programs
written by different people (or groups of people) come together in
a program. What if our classes have the same name? For example, I
might have a class named Point in my code to represent a point in
three dimensional place, and you might have a class named Point
to represent a geographical point (a point on the surface of the
Earth).
It is in general possible for class names to clash, if all names were
to live in the same space – a collision, somewhere, sometime is all
but inevitable.
The generic name for the solution is introducing namespaces.
If everyone has their separate namespace, naming clashes can be
avoided entirely.
Java’s version of namespaces is called packages. Packages have a
bit more to them than just providing a namespace, though.
A package name looks something like:
tr.edu.ozyegin.cs101.geometry

It is a multi-part name, and each part must be a valid Java name


(a name that you could give a variable, for example). Parts of a
package name are by convention all lowercase. This kind of name
actually implies a package hierarchy. In fact, what it means is:
"There is a package called tr. It contains a subpackage called
edu. That package contains a subpackage called ozyegin. And yet,
that package contains a subpackage called cs101. And finally, that
package contains a subpackage called geometry.
How do you put a class inside a package? You declare the pack-
age at the top of the .java file. For example, here is a Point class
living inside Point.java:
package tr.edu.ozyegin.cs101.geometry; // Will produce an error.
142 programming with java

public class Point {


public double x;
public double y;
}

If you just create a Point class as usual, without taking care of


business, and you put the package declaration on top as we did
here, you get an error message. The error message on my screen is:
Package name ’tr.edu.ozyegin.cs101.geometry’ does not correspond to the
file path ’’

This is because Java enforces that the package hierarchy de-


scribed by the package declaration at the top of the file must match
the folder structure where the file is located. I prefer to keep things IDE indepen-
dent, but this will be an exception.

Figure 15.1: The class Point is right


under the src folder, which means it
is in the default package. Inside the
file we declared the package to be
tr.edu.ozyegin.cs101.geometry, so
Java will refuse to compile this.

You can see in Figure 15.1 that the file Point.java is directly un-
der the src folder of the project. This means that the file is phys-
ically in the default package. The default package has an empty
name, and is not declared at all at the top of the file – in other
words it is the package you end up using if you have no idea that
packages exist. Now, the physical location of the file means it is
in the default package, while the declaration says it belongs in
tr.edu.ozyegin.cs101.geometry. This is the cause of the error.
Every IDE out there provides means to create a Java package. So,
if I go and create the package I have already named tr.edu.ozyegin.cs101.geometry,
it will become visible in the user interface.

Figure 15.2: The package called


tr.edu.ozyegin.cs101.geometry
is created and visible on screen. The
Point class is still in the wrong place.

You can see the end result in Figure 15.2. The package is now
created, but the error will persist, since the Point class is not in
there yet. In almost every IDE there is, you can perform a drag-
drop on the class to move it to a different package. Once you do
that, the error should disappear, given we have typed everything
correctly so far.

Figure 15.3: This is what the di-


rectory structure looks like on my
MacBook when viewed with the so-
called "Finder". The package name
tr.edu.ozyegin.cs101.geometry has
been translated to a series of nested
directories on the filesystem.
miscellanea 143

You can see in Figure 15.3 that the package name we entered has
been automatically converted to a series of nested (one inside the
other) directories in the filesystem. When you write Java code, you
will be working almost exclusively with IDEs, and normally do not
need to care about where the actual files are stored; the IDE will
perform the task correctly. However, in some cases you may need to
find out where exactly your dear source files are (or need to be) and
this is how things work in Java.

Javadoc
Write section about Javadoc.
16 Advanced Exercises

At this point, you should have sufficient knowledge of Java to do


more advanced programming tasks. In these exercises, you will
either need to make use of advanced language features, or meet
with concepts not covered in this book. The idea here is to learn
how to explore unknown territory.

Exercise 1
Write a (static) method that takes an uppercase English letter as
a char as its argument and returns its letter value in the game of
Scrabble as an int.
Scrabble letter values are as follows:

• 1 point - A, E, I, O, U, L, N, S, T, R.

• 2 points - D, G.

• 3 points - B, C, M, P.

• 4 points - F, H, V, W, Y.

• 5 points - K.

• 8 points - J, X.

• 10 points - Q, Z.

• 0 points - Blank tiles.

Blank tiles are represented with an underscore "_".


Two advanced topics in here:
Make use of a switch expression. A switch expression is similar
to a switch statement, however instead of mapping values to actions,
it maps values to other values, and itself has a value.
Here is some code, using a switch statement: Well, an enhanced switch statement
to be exact. Switch expressions were
public static void main(String[] args) { first introduced in the enhanced form,
int n = 3; so we start with this example. The
regular form is also possible, using the
String faceName; yield keyword.

switch (n) {
case 1 -> faceName = "One";
case 2 -> faceName = "Two";
case 3 -> faceName = "Three";
case 4 -> faceName = "Four";
case 5 -> faceName = "Five";
case 6 -> faceName = "Six";
146 programming with java

default -> faceName = "Not a dice value.";


}

System.out.println(faceName);
}

Here is a version of this made using a switch expression instead:


public static void main(String[] args) {
int n = 3;

String faceName = switch (n) {


case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
case 4 -> "Four";
case 5 -> "Five";
case 6 -> "Six";
default -> "Not a dice value.";
};

System.out.println(faceName);
}

In the expression case, we do not have assignments. Just values


are mapped to other values (of different type) and the value of
the whole switch expression is that value pointed to by the arrow
operator. Now that the whole thing is an assignment, it ends with a
semicolon at the end.
Throw an exception. You have already met with exceptions. This
is the way a Java program informs other parts of the program (and
possibly the user) that some error condition has occurred. Now,
we are going to throw an exception. In the case that the value we
received is not a proper dice value, we will report the problem with
an exception. Here is how this is done:
public static void main(String[] args) {
int n = 7;

String faceName = switch (n) {


case 1 -> "One";
case 2 -> "Two";
case 3 -> "Three";
case 4 -> "Four";
case 5 -> "Five";
case 6 -> "Six";
default -> throw new IllegalArgumentException("Not a valid dice
value: " + n);
};

System.out.println(faceName);
}

So, your method should look like the following:


public static int valueForLetter(char c) {
// This is where your code goes.
}

It should throw an exception if anything other than an uppercase


English letter or an underscore is given as its argument c.
advanced exercises 147

Exercise 2
Write a (static) method that takes a pattern (as a String) and a
word (also as a String) as arguments, and returns whether or not
the pattern matches the string.
The pattern may consist of uppercase English letters or the as-
terisk (’*’). The asterisk is a wildcard that can match any uppercase
English letter. Uppercase English letters can match only themselves.
The following are various patterns:
**A**
QU***
**R

The first pattern matches all five-letter words with the letter ’A’
in the middle. The second pattern matches all five-letter words
beginning with the letters ’QU’. The third pattern matches all three-
letter words ending with ’R’.
The word may consist of uppercase English letters only. It does
not matter whether it is an actual English word or not, just that it
consists only of uppercase English letters.
You can use the following code as a template (which also con-
tains tests for you).
public class PatternMatcher {
public static void main(String[] args) {
boolean result;

result = match("QU***", "QUEEN");


System.out.println(result ? "SUCCESS" : "FAIL");

result = match("**R", "CAR");


System.out.println(result ? "SUCCESS" : "FAIL");

result = match("C*LL", "CELLO" );


System.out.println(!result ? "SUCCESS" : "FAIL");

try {
result = match("---", "BREAK");
System.out.println("FAIL");
} catch (IllegalArgumentException e) {
System.out.println("SUCCESS");
}
}

public static boolean match(String pattern, String word) {


// Your code goes here.
return false; // Pacifier for the compiler.
}
}

Now, you should throw an exception if either argument does not


conform to the rules, that is, if the pattern contains anything other
than uppercase English letters or the asterisk, or the word contains
anything other than uppercase English letters. The Java Character
class contains methods to check whether a given character is upper-
case or not, but no methods to check for English.
The usual trick makes use of the fact that a char, although it rep-
resents a character, is numeric in nature. Therefore, the following
static method works for checking uppercase English letters:
148 programming with java

public static boolean isUpperCaseEnglish(char c){


return c >= ’A’ && c <= ’Z’;
}

This also makes use of the fact that the letters form one contigu-
ous block in the ASCII character set.
In case characters that should not exist are there, it makes sense
to throw an IllegalArgumentException. In fact, one of the tests in
the template code checks for this exception being thrown.
17 Exam Questions

When exam time comes, one question that worries students a lot,
and therefore I get asked a lot is "what kind of questions will there
be on the exam", or "what will the exam be like" in short.
My policy, as much as I can keep it up, is to make the exam
questions public. I am a firm defender of freedom of information,
and will never attempt to hide information from students (unless
my hand is forced - which has happened).
While writing this book, I also thought that just giving out the
questions is not good enough. There should also be solutions. That
is also not good enough. We need to have solutions, the rationale of
having asked that question in the first place, and my evaluation of
whether this was a good question to ask in retrospect.
Since I wish give the student the chance to try the problems
for themselves first, this chapter only contains the questions. The
answers to the questions, and all other information are in the next
chapter.
As they say, "Do not try to do everything at once", but I have
never been one to let others dictate what I can or cannot do. So,
here we go.

Fall 2022, First Midterm Exam

Question 1
The exam rules are as follows:
You are allowed to use the course book, any materials on your
computer, and your programming environment (IntelliJ Idea) for
this exam.
In principle, it is closed Internet and Google, but in case of panic
you may consult Google.
You can not contact another person by any means under any
circumstances during the exam. This is considered cheating.
It is also an Honor System exam.
Honor System:
1. I will not cheat off of anyone in this exam.
2. I will not permit anyone to cheat off of me in this exam.
Select one:

• I reject the honor system.


150 programming with java

• I accept the honor system and promise to conduct myself accord-


ingly during the exam.

Question 2
Which of the following is not true for the Java programming lan-
guage?
Select one:

• Java is an object-oriented language.

• The JRE (Java Runtime Environment) includes the JVM and Java
class libraries.

• Running Java code on a different platform requires recompiling


the Java source code.

• Java bytecode is executed by the JVM (Java Virtual Machine)

• Java was designed to be a "write once, run anywhere" program-


ming language.

Question 3
Which of the following is not true about Java source code and
source structure?
Select one:

• A method can be written outside of a class in Java source file.

• The main method must look like public static void main(String[]
args)

• The starting point of a Java program is the main method.

• All Java source code must be put in files with a .java extension.

• The name of the file must match the name of the public class
contained in the file.

Question 4
The "default" integer type to use in a Java program, unless you have
reason to use something else is:
Select one:

• long

• byte

• short

• int
exam questions 151

Question 5
If I know I need to store integers from 0 to 1000, which is the small-
est integer type I can use?
Select one:

• long

• byte

• short

• int

Question 6
Which one of the following types can store the largest integer val-
ues exactly, without loss?

• float

• int

• double

Question 7
A binary number has 22 binary digits.
If converted to hexadecimal, how many hexadecimal digits will it
have?

• 4

• 5

• 6

• 7

• 8

Information
Consider the following program:
public class Main {

public static void main(String[] args) {

for (float x = 0; x < 20_000_000; x++) {


System.out.println((int)x);
}
}
}

Answer the following two questions accordingly:


152 programming with java

Question 8
What is the largest value that will get printed?
Select one:

• 19999999

• 20000000

• 16777216

• 20000001

• 2147483647

Question 9
How many times does the loop body execute?
Select one:

• 20000000

• 2000001

• 1999999

• 16777216

• It is an infinite loop.

Question 10
Mathematically, the natural logarithm of a number has a the follow-
ing series expansion:
2 3 4
ln(1 + x ) = x − x2 + x3 − x4 · · ·
This series does not converge for all values of x, but it does con-
verge for x = 1, giving a series for ln 2:
ln(2) = 1 − 21 + 13 − 14 · · ·
Write a program to compute the value of ln(2) as a double, using
the first 1000 terms of the series.
Do not attempt to use library methods. Also, your code should
only print the final computed value of ln(2) as its output.
Using more or less than 1000 terms of the series is considered to
be an error. Obeying specifications is important.
Paste your whole class into the space given below.

Question 11
Every positive integer n has divisors which can divide it exactly.
For example, the number 6 has 4 divisors:
1, 2, 3, 6
Prime numbers have exactly two divisors. Like 5 has 2 divisors:
1, 5
Write a Java program that will start with an integer, and find its
number of divisors, and print it on the screen.
The form of your code should be as follows:
exam questions 153

public class Main {

public static void main(String[] args) {


int n = 12;
int numDivisors = 0;

// Your code goes here.

System.out.println(numDivisors);
}
}

Do not alter existing lines. Only add code where the comment is.
Your code should work for n=12, as well as any positive integer n.

Question 12
Write a Java program that will print out all four digit numbers with
exactly 22 divisors.

Spring 2023, First Midterm Exam

Question 1
The exam rules are as follows:
You are allowed to use the course book, any materials on your
computer, and your programming environment (IntelliJ Idea) for
this exam.
In principle, it is closed Internet, but in case of panic you may
consult the Internet.
You can not contact another person by any means under any
circumstances during the exam. This is considered cheating.
It is also an Honor System exam.
Honor System:
1. I will not cheat off of anyone in this exam.
2. I will not permit anyone to cheat off of me in this exam.
3. If I see anyone violating the above two rules, I will report
them to the assistants and/or professor.
Select one:

• I accept the honor system, and promise to act honorably during


the exam.

• I reject the honor system.

Question 2
Which of the following contain the Java compiler?
Select one or more:

• JDK

• JRE

• JVM
154 programming with java

Question 3
Which of the following contains the Java core libraries?
Select one or more:
• JDK

• JVM

• JRE

Question 4
If we know a number has 15 octal digits (in its octal representation),
what is the fewest hexadecimal digits it may have (in its hexadeci-
mal representation)?

Question 5
Take any 32-bit bit pattern.
First, interpret it as a two’s complement, signed integer value.
Next, interpret it as a IEEE-754 32-bit floating-point number.
Given both values are non-zero, and are actual numbers, the two
values will always have the same sign.
Select one:
• True

• False

Question 6
Consider the floating-point values
7.5 30.0 960.0
What fields do they differ in?
Select one or more:
• Sign

• Exponent

• Significand

Question 7
Consider the following 16-bit shorts in Java:
00000000 00101101 00000000 01011010 00000000 10110100 00000001
01101000
Check all that are true.
Select one or more:
• They form a geometric progression.

• They are all positive.

• They form an arithmetic progression.

• Their sum is divisible by 15.


exam questions 155

Question 8
In Java, taking a byte value, and either:
Dviding it by 2 eight times
or
Multiplying it by 2 eight times
always results in the same value.
Select one:

• True

• False

Information
Consider the following program:
public class Main {
public static void main(String[] args) {
double sumOfInverseCubes = 0.0F;

for (int i = 1; i <= 1_000_000; i++) {


sumOfInverseCubes += 1.0/(i*i*i);
}

System.out.println(sumOfInverseCubes);
}
}

Answer the following questions accordingly.

Question 9
Here is a transcript of my chat with ChatGPT:

What does the following Java program print?

public class Main {


public static void main(String[] args) {
double sumOfInverseCubes = 0.0F;
for (int i = 1; i <= 1_000_000; i++) {
sumOfInverseCubes += 1.0/(i*i*i);
}
System.out.println(sumOfInverseCubes);
}
}

———-
This Java program calculates the sum of the inverse cubes of the
integers from 1 to 1,000,000 using a for loop and the formula 1/(i*i*i).
The sum is stored in a double variable sumOfInverseCubes and is
printed using System.out.println().
The output of the program will be a double-precision floating-
point number, which represents the sum of the inverse cubes. It is
expected to be approximately equal to 1.2020569031595931. This
value is the sum of the infinite series 1 + 1/8 + 1/27 + 1/64 + ...,
which converges to a value known as Apéry’s constant. Its exact
value is currently unknown, but it is believed to be an irrational
number.
156 programming with java

——-
I tried it. It prints Infinity.
——-
Thank you for letting me know. The program indeed produces a
value that exceeds the maximum finite value that can be represented
by a double variable, which is approximately 1.7976931348623157
× 10ˆ308. This occurs because the sum of inverse cubes converges
very slowly. When a double variable overflows, it is set to positive
or negative infinity, depending on the sign of the value. In this case,
since the sum is positive, the program prints Infinity.

Is ChatGPT correct?
Select one:

• True

• False

Question 10
Consider modifying the number of terms used in the calculation,
which is 1,000,000 terms in the first form of the program.
For example, we can try taking only 100 terms as in the follow-
ing program:
public class Main {
public static void main(String[] args) {
double sumOfInverseCubes = 0.0F;

for (int i = 1; i <= 100; i++) {


sumOfInverseCubes += 1.0/(i*i*i);
}

System.out.println(sumOfInverseCubes);

}
}

This print the value:


1.2020074006596781

Which is quite close to the actual value. What is the maximum


number of terms that can be used without getting the infinity out-
put as in the previous term?

Question 11
Modify the given program to create a fixed version that works well
even with a million terms.
Your changes to the code should be minimal. If you submit
totally new code, you will get no points.
Paste the complete class below:

Information
A salesman works on commission.
When asked how much he earns on a sale, he explains it as fol-
lows:
exam questions 157

"All my sale prices are whole numbers. I like whole numbers, and
never settle for any fractions.
If the sale amount is divisible by seven, my commision is two
sevenths of the sale amount.
If the sale amount is divisible by five, my commission is one fifth
of the sale amount.
If the sale amount is divisible by eleven, my commission is three
elevenths of the sale amount.
If two or more of the above happen, I my commission is the
largest possible, but I can not get two commissions on one sale.
And if none of the above happen, I get no commission at all."

When asked how much money he made last year, he says:

"Well, I never know because I spend them as soon as I earn them. But
I remember this:
The largest sale amount I had was 1000, and there was only one of
that.
There were 2 sales of 999 each. 3 sales of 998 each; and this went
just like this all the way down. The least sales amount I had was 550,
and I can not remember how many of that happened, but I am sure
you can figure that out, being a computer scientist and everything."

Question 12
Write a Java program to compute the total commission the salesman
earned in the last year.

Question 13
Run your program, and obtain the result.
How much commission did he earn last year?

Information
The following program has been written:
public class Main {
public static void main(String[] args) {
int sum = 0;

for (int i = 1; i <= 120; i++) {


sum += i*i*i*i;

System.out.println(sum);
}
}

Answer the following questions accordingly.

Question 14
Here is a transcript of my chat with ChatGPT:

What is the mistake in the following program?


158 programming with java

public class Main {


public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 120; i++) {
sum += i*i*i*i;
}
System.out.println(sum);
}
}

——–
There is no mistake in the program. It correctly calculates the sum
of the fourth powers of the integers from 1 to 120, and prints the
result.

Is ChatGPT correct?
Select one:

• True

• False

Question 15
Calculate the sum of fourth powers of integers from 1 to 120.
(If you think ChatGPT is correct, you can just run the program. If
you think ChatGPT is wrong, fix the program first.)

Information
Consider the following program:
public class Main {

public static void main(String[] args) {

for (int a = 1; a < 10; a++) {


for (int b = 0; b < 10; b++) {
for (int c = 0; c < 10; c++) {
for (int d = 0; d < 10; d++) {
if (a + b + c + d == 15) {
System.out.println(1000 * a + 100 * b
+ 10 * c + d);
}
}
}
}
}
}
}

Answer the following questions accordingly:

Question 16
How many numbers does the program print?

Question 17
What is the sum of the numbers the program prints?
exam questions 159

Question 18
Consider the following program:
public class Main {
public static void main(String[] args) {
float sum = 0.0F;
float fraction = 0.25F;

for (int i = 0; i < 1000; i++) {


sum += fraction;
}

if (sum == 1000*fraction) {
System.out.println("Equal");
} else {
System.out.println("Different");
}
}
}

Consider changing the value of the variable fraction and running


the program with different values.
For which values of fraction will the program print "Equal"?

• 0.1

• 0.2

• 0.25

• 0.4

• 0.5
18 Solutions to Exam Questions

This chapter contains the solutions to the exam questions, a dis-


cussion of the problem, their evalution in retrospect, and even the
questions themselves. In fact, this you can use just this chapter as a
complete resource. The previous chapter exists only as a means to
give you a chance to work on the problems first.

Fall 2022, First Midterm Exam

Question 1
The exam rules are as follows:
You are allowed to use the course book, any materials on your
computer, and your programming environment (IntelliJ Idea) for
this exam.
In principle, it is closed Internet and Google, but in case of panic
you may consult Google.
You can not contact another person by any means under any
circumstances during the exam. This is considered cheating.
It is also an Honor System exam.
Honor System:
1. I will not cheat off of anyone in this exam.
2. I will not permit anyone to cheat off of me in this exam.
Select one:

• I reject the honor system.

• I accept the honor system and promise to conduct myself accord-


ingly during the exam.

Discussion
This is always the first question in my exams. When exams are
done on paper, the practice is to make students write down "Honor
System is observed" on their exam paper and get them to sign it.
When things happen on the computer, this seemed to be the correct
way to go.

Solution
Technically speaking, there is no correct answer here. And, there
is no grade associated with this question. The whole point of being
"honorable" is doing the right thing regardless of apparent short
162 programming with java

term gain, carrying the understanding that being honorable is bet-


ter for everyone in the long run.
Honorably speaking, the correct answer is to accept the honor
system.

Retrospect
In retorspect, I find that putting this question at the beginning of
every exam is helpful, as a reminder of the importance of doing the
right thing. Yorgo İstafanopulos, or with his full
title Prof. Dr. Yorgo İstafanopulos,
The original form of the Honor System, as I first heard and saw
is one of the legendary professors
it applied by Yorgo İstefanopulos has three point rather than two, of Boğaziçi University of my time
the third point being: there as a student (and later Işık
University). He was a great influence
3. If I see anyone cheating, I will report them. on me, showing me what is possible,
I found this rather unsettling - making people responsible for and what the right approach is for
their own actions is one thing, making them responsible for the ac- engineering.

tions of other is... Something else. So, I removed this part, reducing
the Honor System to the format above.
Now I understand that what I have done is not helpful. Peo-
ple must be responsible for what they are doing first, that is quite
correct. However, they should also be responsible for their sur-
roundings, and beyond. The professor is not a police officer, and
the burden of maintaining an honorable environment should be
shared between the student body and the professor.
In addition, a lot of students just complain anonymously when
they see people cheating, shifting the burden to the professor in-
stead of doing something about it themselves. All in all, I have
decided that I am going to restore the Honor System to its full form
looking forward.

Question 2
Which of the following is not true for the Java programming lan-
guage?
Select one:

• Java is an object-oriented language.

• The JRE (Java Runtime Environment) includes the JVM and Java
class libraries.

• Running Java code on a different platform requires recompiling


the Java source code.

• Java bytecode is executed by the JVM (Java Virtual Machine)

• Java was designed to be a "write once, run anywhere" program-


ming language.

Discussion
This question is meant to be an easy question. Like other ques-
tions of this type, if you have understood what Java is, and how it
solutions to exam questions 163

functions you should be able to answer this very quickly. Other-


wise, you would need to look it up, and spend some extra time on
it.

Solution
The statement that is false here is "Running Java code on a differ-
ent platform requires recompiling the Java source code." The Java
compiler compiles source code into bytecode, which can be run
without modification on any platform having a JVM.

Retrospect
As an intended "easy question", it did its job pretty well. Most
people got this right.
ChatGPT can solve this problem.

Question 3
Which of the following is not true about Java source code and
source structure?
Select one:

• A method can be written outside of a class in Java source file.

• The main method must look like public static void main(String[]
args)

• The starting point of a Java program is the main method.

• All Java source code must be put in files with a .java extension.

• The name of the file must match the name of the public class
contained in the file.

Discussion
This one is another easy question. The main thing that the stu-
dent needs to know to answer this question is the fact that every-
thing in Java goes into a class, and Java is known as "the kingdom
of nouns". "Everything goes into a class" is not strictly true, how-
ever any other "top level" stuff is at the lowest level some form of
class.

Solution
The false statement is the first one: "A method can be written
outside of a class in Java source file." All methods have to go into a
class.
Interestingly enough, ChatGPT gets this question wrong. It even
provides an (incorrect) example of Java code with a method outisde
of a class.

Retrospect
Good enough. Most people got this question right, as intended.
164 programming with java

Question 4
The "default" integer type to use in a Java program, unless you have
reason to use something else is:
Select one:

• long

• byte

• short

• int

Discussion
This is a question that is borderline between Java design and
conventions. There is nothing preventing a programmer from using
a long for everything, for instance. However, when using libraries
this may become hard to use. Using a short can also cause prob-
lems and require additional care. If the student has been listening
at all in class, this is easy to answer.

Solution
The default integer type, even obvious by name, is int.

Retrospect
Good, simple question. Perhaps a bit "too easy", since this can
be answered only by just having listened partially in class, or just
having looked over the example code.

Question 5
If I know I need to store integers from 0 to 1000, which is the small-
est integer type I can use?
Select one:

• long

• byte

• short

• int

Discussion
This is one level harder, and meant to test knowledge in prac-
tice. However, it is not really meant as an "evil" question, since the
number given falls cleanly between limits, and not close to them.

Solution
A byte goes only up to 127, so it is not large enough. A short
can store a maximum of 32767, so it is large enough. The correct
answer is short.
solutions to exam questions 165

Retrospect
Good question, with potential to be re-used with different num-
bers, even if the actual answer remains the same. Making the cor-
rect answer a long would make the hardest question, especially if
the value limit is close to the variable limit.

Question 6
Which one of the following types can store the largest integer val-
ues exactly, without loss?

• float

• int

• double

Discussion
This is one question that requires better understanding of data
types. The float and double data types store floating-point num-
bers, they have great range, but limited precision. An int is for
integers, and stores everything it can exactly.

Solution
An int variable has 32 bits, and has the range of 31 bits, since
one bit is effectively used to store the sign.
A float variable also has 32 bits, but uses 23 bits to store the
significand. So, it is more limited than int values.
On the other hand, a double variable has 64 bits, and uses 52 of
these bits to store the significand. This is larger than 32, so it can
store larger integers than int exactly. This makes double the correct
answer.

Retrospect
In retrospect, this is a very good selective question. The answer
to the question is not a matter of opinion or convention, and can
be demonstrated. A good understanding enables a quick answer,
whereas a lack of understanding makes this nigh-impossible to
answer.
Variants are also possible, so this question can be asked again in
different forms.

Question 7
A binary number has 22 binary digits.
If converted to hexadecimal, how many hexadecimal digits will it
have?

• 4

• 5
166 programming with java

• 6

• 7

• 8

Discussion
I came up with this question instead of asking a regular con-
version problem. The purpose is to make the required calculation
simpler, while making the required understanding greater. The key
knowledge being tested here is that every four bits (binary digits,
that is) correspond to one hexadecimal digit.

Solution
If a binary number has 22 digits, since every four bits will re-
quire a hexadecimal digit, it will have 22/4=5.5 digits. This means
it will not fit into five hexadecimal digits, and will spill to the sixth
digit, but will definitely not exceed the sixth digit. So, the answer is
plain 6.

Retrospect
In retrospect, this is a good question - it asks about the rationale
behind the existence of hexadecimal numbers (or their utilization).
If you have an understanding, the answer can just be computed
with a division operation, rounded up.

Information
Consider the following program:
public class Main {

public static void main(String[] args) {

for (float x = 0; x < 20_000_000; x++) {


System.out.println((int)x);
}
}
}

Answer the following two questions accordingly:

Discussion
This is a multi-part question, which is done by making an "in-
formation" part first, and then asking multiple questions about it.
The key point here is that a float variable is being used as the loop
variable, rather than the obvious int.
I expect the student to guess that something fishy is going on
at this point. If everything went exactly how they look, this would
have been a rather simple-to-answer question. As a general rule
(though not always adhered to) questions go from easy to hard as
the examination progresses. So, a problem this easy should not be
here.
solutions to exam questions 167

Solution
This is the information part, but the part that you need to "get"
here is the fact that a float can not hold integers larger than "sixteen
million something" exactly, and it will lose precision. Something
strange is bound to happen here.

Retrospect
This question as a whole stresses the fact that floats are not
magic, and if you try to use them without thinking instead of inte-
ger variables of the appropriate size, unexpected behavior can be
the result.

Question 8
What is the largest value that will get printed?
Select one:

• 19999999

• 20000000

• 16777216

• 20000001

• 2147483647

Discussion
Well, if "everything was working as usual", the answer would
have been 19999999, since the loop condition says that x is less than
twenty million. But nothing is working as usual, so the correct
answer is not that.
Note that it is possible to solve this question by copying the code
into your programming environment and running it.

Solution
If you just run the code, and wait for a while (it needs to print
millions of values, but it does not take more than a couple of min-
utes), you can see that things run normally for a while, but then the
value 16777216 gets printed repeatedly.
This is the correct answer. The reason is that, this number,
16777216 is actually 224 , which is 0b1000000000000000000000000,
that is a 1 followed by 24 zeros. In a float, you skip the leading
1, and store the rest in 23 bits. So, the last bit (rightmost bit) can
not be stored. When you add 1 to this number, you (would) get
16777217 which is 0b1000000000000000000000001, but since the fi-
nal bit still does not fit, the 1 at the end is dropped, and the stored
number is still 16777216. So, in the loop, no matter how many times
you add 1, it keeps printing the same number.

Retrospect
168 programming with java

This question is a good demonstration of the situation with float-


ing point numbers. And, it is not as difficult as it looks, since it is
possible to just run the code and see what happens. As such, it can
be solved without much understanding. However, at this point it
tests the ability of the student to effectively use their programming
envorinment to try out code.
If you can not figure out that something fishy is going on, you
may very well get the wrong answer.

Question 9
How many times does the loop body execute?
Select one:

• 20000000

• 2000001

• 1999999

• 16777216

• It is an infinite loop.

Discussion
After the previous question, this is rather obvious. However, if
you got the previous question wrong, you are very likely to get this
question wrong, too. If you actually bothered to run the code, you
know the answer already.

Solution
Well, due to reasons explained in the solution to the previous
problem, this code never stops running. It is a bit of a trick ques-
tion, and the correct answer is "It is an infinite loop".

Retrospect
In retrospect, having two questions here with very strong corre-
lation is not that great. However, I think the option "It is an infinite
loop" in this question is an additional hint to the student that some-
thing fishy may be going on.

Question 10
Mathematically, the natural logarithm of a number has a the follow-
ing series expansion:
2 3 4
ln(1 + x ) = x − x2 + x3 − x4 · · ·
This series does not converge for all values of x, but it does con-
verge for x = 1, giving a series for ln 2:
ln(2) = 1 − 21 + 13 − 14 · · ·
Write a program to compute the value of ln(2) as a double, using
the first 1000 terms of the series.
solutions to exam questions 169

Do not attempt to use library methods. Also, your code should


only print the final computed value of ln(2) as its output.
Using more or less than 1000 terms of the series is considered to
be an error. Obeying specifications is important.
Paste your whole class into the space given below.

Discussion
This is the first question in the exam where writing code is re-
quired. As such, it is the computation of a pretty straightforward
series; a Google search would very easily produce code that could
at least be of partial use here.
The question even states the exact series to be calculated, rather
than just giving the general formula (which the Taylor series for
ln(1 + x )).
Also, rather than requesting convergence, a fixed number terms
is asked for. And, that part is strict, and there is a warning about
obeying specifications, because if you are asked to use 1000 terms
for calculation, you are not allowed to use 10,000 terms, and say
you did better.

Solution
Here is one possible solution:
public class Main {
public static void main(String[] args) {
double ln2 = 0;
double sign = 1.0;

for (int i=1; i <= 1000; i++) {


ln2 += sign/i;
sign = -sign;
}

System.out.println(ln2);
}
}

One thing is that it is a good idea to go with a double rather than


a float when you need a floating-point number.
The other important point is how I handle the sign flipping here
- with a variable which is negated at the end of every loop. This
makes it obvious that the first sign is positive, and it is flipped
every time.
Also, the for loop makes it clear that exactly 1000 terms are
calculated.
Another possible solution would be:
public class Main {
public static void main(String[] args) {
double ln2 = 0;

for (int i=1; i <= 1000; i++) {


if (i % 2 == 0) {
ln2 -= 1.0/i;
} else {
ln2 += 1.0/i;
}
170 programming with java

System.out.println(ln2);
}
}

The main difference here is that the sign flipping is handled


with an if/else rather than a sign variable. This produces the
exact same result as the other code, and is just as obvious about the
number of terms.
One critical point is to type 1.0/i rather than 1/i. Doing 1/i
would cause an integer division, breaking the whole calculation.

Retrospect
Not a bad question, but perhaps not a great one, either. The
good part is that you either have to figure out how to do sign flip-
ping with every term, or use an if/else inside the for properly.
The bad part is once again, very similar code can be found directly.
Interestingly, ChatGPT writes code for this, but ends up calcu-
lating ln(1/2) rather than ln(2). And, in the code it uses library
methods, and includes a lot of unnecessary variables and code.

Question 11
Every positive integer n has divisors which can divide it exactly.
For example, the number 6 has 4 divisors:
1, 2, 3, 6
Prime numbers have exactly two divisors. Like 5 has 2 divisors:
1, 5
Write a Java program that will start with an integer, and find its
number of divisors, and print it on the screen.
The form of your code should be as follows:
public class Main {

public static void main(String[] args) {


int n = 12;
int numDivisors = 0;

// Your code goes here.

System.out.println(numDivisors);
}
}

Do not alter existing lines. Only add code where the comment is.
Your code should work for n=12, as well as any positive integer n.

Discussion
This question is rather simple to express. The goal is just to find
the number of divisors of an integer, and print it on the screen.
However, a lot needs to be told and shown to make sure it is under-
stood correctly, and the resulting code is in the correct format.
This question also forms a pair with the next one; solving this
question is part of the solution of the next problem.
solutions to exam questions 171

Solution
What is really expected here is a simple solution; the idea is to
try every integer k such that 1 ≤ k ≤ n and try each one. This is a
rather simple for loop application.
public class Main {

public static void main(String[] args) {


int n = 12;
int numDivisors = 0;

for (int i = 1; i <= n; i++) {


if (n % i == 0) {
numDivisors++;
}
}

System.out.println(numDivisors);
}
}

This solution is not good algorithmically, since it tries all integers


up to n, doing n loops. In fact, one need only check for divisors
up to the square root of n, since divisors come in pairs above and
below the square root. However, that requires more coding knowl-
edge, and more mathematics knowledge. This is neither intended,
nor required.

Retrospect
This is not a bad question, yet again it is susceptible to finding
example code on the Internet. ChatGPT is also able to write good
code. However, such code normally contains more advanced solu-
tions. But, it is possible to prompt ChatGPT to write this kind of
code by telling it to rewrite the code without using square root.

Question 12
Write a Java program that will print out all four digit numbers with
exactly 22 divisors.

Discussion
This is more or less a continuation of the previous question.
Given you have been able to write code to count the number of
divisors of a number, then half of your work here is done. However,
if you have not, you are unlikely to be able to solve this problem.
This question is also purposefully expressed in short form, with-
out examples. This is intended to be one of the discriminatory
questions.

Solution
What we need to do here is to loop over all four digit numbers.
That means looping from 1000 to 9999. Then, we need to use the
code in the previous question to count the number of divisors of
each number, and if the calculated number of divisors is exactly 22,
we print the number in question.
172 programming with java

The below code accomplishes this:


public class Main {
public static void main(String[] args) {
for (int n=1000; n <= 9999; n++) {
int numDivisors = 0;

for (int i = 1; i <= n; i++) {


if (n % i == 0) {
numDivisors++;
}
}

if (numDivisors == 22) {
System.out.println(n);
}
}
}
}

Retrospect
Good question, sharing the weakness that close solutions can be
found on Google, and ChatGPT can be prompted to write this exact
code.
In retrospect, asking questions requiring students to submit code
creates a lot of work reading and grading the solutions. Also, there
is a lot of questions from students asking why they did not score
the points they thought they should.
Towards the future, I intend to ask for larger code pieces in fewer
questions that should be sufficient to grade the coding style of
the student. Anything else should in the "proof-of-work" format,
containing original problems that ChatGPT will be mostly useless.
19 Projects

The Rules of Project Work

Towards the end of every term, students are asked to work on a


term project. My philosophy towards term projects is a bit surpris-
ing to most students. Also, I found out that it is unsettling for a
few students as evidenced by what they have written in the end-of- One student outlined the whole set
term feedback forms. of rules and wrote it in the form of
a complaint. I found that particularly
Here is how the projects work: interesting.

• I split the class into groups of five students each. I do this fully
randomly.

• I do not provide any help about how they are supposed to get a
hold of each other.

• I do not care who does the actual work within the group. They
all get the same grade.

• They have 15 minutes to present their whole work, including


analysis, code, and demonstration.

• The exception to grading is that the students present at the pre-


sentation can unanimously vote the non-present members of the This has actually happened.
group (if any) to get a grade of zero.

Why is this unsettling? Students believe this to be unfair. Well,


it is unfair in a certain sense, and that sense is that each student
should be weighed and measured individually and should also be
graded individually.
However, the world is not like that at all. Out in the world, your
work is inextricably linked with the work of others, and can be Almost every project these days is
affected positively or negatively. Out in the real world, most prob- developed by teams of various sizes.
I often jokingly say that the last time
lems are social problems as opposed to technical problems. Being anyone could write a software project
able to work as a team is perhaps the most valuable thing there is. on their own was back in the 1980s.
While we still hear of people writing
Just doing your own work and then shutting down all communica- things single-handedly from time to
tion with the world has very little value, if any at all. time, these are the rare exceptions
If you are on the more compenent side technically, earning the rather than the rule.

respect of your teammates, and motivating them to do better is an


important skill. If you feel less technically competent, again earning
the respect of your teammates, motivating them, and finding ways
to contribute rather than giving up is an important skill.
174 programming with java

Unfortunately, we do not have the time or resources to make


this into a full education on these matters. However, it is a small Maybe one day a course where the
laboratory to learn about the human side of things, if only for those whole point is to do project work...

interested in learning.

Project Level and Content

Although teamwork is the most important part of the projects, the


level and content of the projects are also important.
My goals when trying to come up with these project were, and
still are, as follows:

• The project level should be suitable so that students do not feel


overwhelmed with it.

• Still, it should still require them to learn a few programming


constructs on their own. It is monumental that they learn how to
learn.

• The project should ultimately show the students that they can
actually do more than they think they can do.

• It should require some amount of analysis and not just pure


code-writing. This is what makes a project worthwhile.

• It should not require much, or if possible, any heavy user-


interface work. This is not because the user interface is unim-
portant, but it is not the focus of this course. In this course, I do
not want to shift the focus of the course to UI/UX and aesthetics.

This chapter contains only the projects, as they were presented to


the students in their complete form.

Project: Mastermind with Six Digits

Description of the Game Rules


This is a more complicated version of the well-known game of
Mastermind. Here is how it works:
The game is played between two players. One of them is the
code maker, and the other is the code breaker. The code maker
creates a six digit number, where leading zeros are allowed, as
opposed to the usual convention. For example, the code could be:
074311
The code is not known to the code breaker. The code breaker
then comes up with a guess, and suppose they produce the num-
ber:
111000
Now, the code maker compares the guess and the actual code
and provides feedback. The feedback consists of two numbers:
The first indicates "direct hits", that is, correct digits in the correct
position. The second number is "indirect hits", that is, digits that
projects 175

are in the code but are in the wrong position in the guess. For this
example guess, the feedback would be:
0, 3
There are no direct hits, that is, no digits match exactly between
the code and the guess. There is a zero in the wrong place, and
there are two ones in the wrong place. This makes a total of three
indirect hits.
The code breaker tries to take advantage of the information from
the feedback, and produces another guess:
223344
Now, the feedback is:
1, 1
This is because 3 is a direct hit and there is an indirect hit for 4.
The game ends when the feedback is:
6, 0

The Goal of the Project

The goal of the project (probably contrary to your expectations) is


to make the computer play as the code breaker, and the user as the
code maker.
So, in essence, as the game starts, the computer presents you
with a guess immediately, and expects feedback. You compare it to
your secret code number (which you never enter into the computer)
and give two numbers as your feedback. Then, the computer gives
you another guess. At the end, the computer guesses your number.
The conditions are as follows:
The computer should play efficiently. You may or may not come
up with the best algorithm to play the game, but it should not be
random guessing by the computer. If you give inconsistent answers
(after all, you can respond 0, 0 to every guess) the computer should
be able to detect this, and tell you you are being inconsistent. The
input and output should occur on the console. No need to program
any fancy user interface. If you do, it will not earn you any extra
points.

Required Work

Analysis
You should perform an analysis of the problem, and write it up.
Specifically, you should explain what kind of algorithm you are
using, and how efficient you think it is. Proofs are not necessary
but would be appreciated.

Design
Once you have analyzed the problem and come up with an al-
gorithm, you should document your implementation design. What
176 programming with java

components your program will have, why and how it will be orga-
nized.

The Program
You can write your code in any IDE of your choice, but the pre-
ferred IDE is IntelliJ IDEA. Clean and clear code is important, as is
obeying naming and coding conventions observed throughout the
course.

The Presentation
You should be well prepared as a team to present your analysis,
design, program, and perform a demonstration in 15 minutes.
15 minutes will be a sharp cut-off point. Anything you have not
presented in the allotted 15 minutes is nonexistent.
projects 177

Project: Scrabble Assistant

Description of the Project


Scrabble is a well-known board game in which multiple players
compete to place letters on the board, make words, and gain points.
For the game rules, and the game’s history, visit the Wikipedia page
for Scrabble.
Players take turns to play, and place a word on the board. Any
word must share at least one letter with an existing word on the
board (except the very first word, which is placed at the center of
the board).
Each letter tile has its points. Here are their values:

• 1 point - A, E, I, O, U, L, N, S, T, R.

• 2 points - D, G.

• 3 points - B, C, M, P.

• 4 points - F, H, V, W, Y.

• 5 points - K.

• 8 points - J, X.

• 10 points - Q, Z.

• 0 points - Blank tiles.

Here is how the program is supposed to work:


First, the user enters the tiles in their hand, up to seven. This
is allowed to be less, since that is what happens at the end of the
game. It is possible that the user has blank tiles (which act as wild-
cards) in which case the user needs to enter an underscore (’_’).
Then, the user types in the pattern they are trying to put a word
in. For example, if they wish to find a word crossing the letter ’A’
in the third place, and a total of five letters long, they should enter
**A**. The ’*’ represents the letters that need to filled from the hand
of the of the user.
Then, the program should search all words in the dictionary and
list all words that match the given pattern, and can be made using
the letters the user has in their hands.
Of course, it should also list how many points are to be gained
(points are earned for letters already on the board) by making each
word. Note that blank tiles used do not contribute to the points.
The listing of words should be in descending order of points.
Here are a few notes:

• There are repeated words in the dictionary. It is okay to repeat


them in the output if this is the case.

• There can not be an underscore in the pattern, since once a blank


tile is assigned a letter, it does act as that letter.
178 programming with java

• It is probably a good idea to convert everything to uppercase


before using, including user input and the dictionary.

• Words do not have fixed values. If blank tiles are used, no points
are earned for those points.

Here are a few sample runs for this project:


Enter your hand. At most seven tiles, use _ for blank tiles.
QNLEETG
Enter the pattern to match. Use * for tiles you will place.
*U**N
------
14 : QUEEN

The following contains repetitions due to repetitions in the dic-


tionary file:
Enter your hand. At most seven tiles, use _ for blank tiles.
TRGWNJO
Enter the pattern to match. Use * for tiles you will place.
*E***
------
12 : TEJON
12 : TEJON
8 : REOWN
6 : GENRO
6 : NEGRO
6 : NEGRO
5 : TENOR

Enter your hand. At most seven tiles, use _ for blank tiles.
TROUNG_
Enter the pattern to match. Use * for tiles you will place.
*****G
------
7 : DUGONG
7 : GORING
7 : GUNONG
7 : URGING
6 : OUTING
6 : OUTRIG
6 : ROTANG
6 : STRONG
6 : STRUNG
6 : THRONG
6 : UNTRIG

Required Work

Analysis
You should think about how to handle the problems in this
project. Specifially, consider the problem of blank tiles being used
as wildcards.

Design
Once you have analyzed the problem and come up with an al-
gorithm, you should document your implementation design. What
components your program will have, why, and how it will be orga-
nized.
projects 179

The Program
You can write your code in any IDE of your choice, but the pre-
ferred IDE is IntelliJ IDEA. Clean and clear code is important, as is
obeying naming and coding conventions observed throughout the
course.

The Presentation
You should be well prepared as a team to present your analysis,
design, program, and perform a demonstration in 15 minutes.
15 minutes will be a sharp cut-off point. Anything you have not
presented in the allotted 15 minutes is nonexistent.

You might also like