BSCS and BSIT students can download the complete Java textbook “Think Java” by Allen B. Downey and Chris Mayfield. The 2nd edition is free to download as a PDF or read online, and it takes you from the first line of code to object-oriented design in seventeen short chapters.
What makes this book unusual is its discipline about size. Each chapter is twelve to fourteen pages and is written to cover exactly one week of a college course, so the whole text fits a single semester. It also uses what the authors call the “objects late” approach: you spend the first eight chapters learning variables, methods, conditionals, loops, arrays and recursion properly, and only then meet objects and classes. That order suits Pakistani BSCS programmes well, where Programming Fundamentals comes first and Object Oriented Programming follows in the next semester.
Book Overview
| Course | Programming Fundamentals / Object Oriented Programming (Java) |
| Degree Programs | BSCS, BSIT |
| Level | University — no prior programming experience assumed |
| Edition | 2nd edition, Version 7.1.0 (2020) |
| Authors | Allen B. Downey and Chris Mayfield |
| Structure | 17 chapters plus 4 appendices; each chapter covers one week of a course |
| Exercises | 84 across the book, with code available on GitHub |
| Java Version | Examples tested on OpenJDK 11 and still compile on current Java |
| Language | English |
| License | CC BY-NC-SA 4.0 — Model: Link-only |
| Format | Free PDF, free online HTML, and an interactive browser version |
Chapter List
Chapter 1: Computer Programming
Difficulty: Easy · Key topics: what a computer is, high-level vs low-level languages, compiling, the Hello World program, escape sequences, source formatting, debugging
The chapter opens with the book’s stated goal — to teach you to think like a computer scientist, combining the formal languages of mathematics, the design sense of engineering and the hypothesis testing of science. It defines a computer broadly as any device that stores and processes data, then defines a program as a sequence of instructions. The Hello World program arrives early and is dissected line by line: public class, public static void main, and System.out.println. From there it explains why Java needs compiling at all, introducing source code, byte code and the virtual machine that makes Java portable. Formatting comes next, with the point that most whitespace is optional for the compiler and essential for the reader. Escape sequences let one statement print several lines. The chapter closes by defining computer science, bugs and debugging, and encourages you to deliberately break the Hello World program to see what each error looks like.
Key Points:
- A high-level language such as Java must be translated before it runs; the translation is called compiling.
- Java compiles to byte code, which a virtual machine interprets — that indirection is what makes Java portable across operating systems.
- Every Java program needs a class and a
mainmethod; execution always begins at the first statement ofmain. printlnadds a newline after the text;printdoes not.- An escape sequence is two characters of source code that represent one character of output, such as a newline.
- Most whitespace and newlines are optional to the compiler, but organisations publish strict style guidelines because code is read far more often than it is written.
Practice Tip: Read this chapter sitting at a computer with the Hello World program open, and deliberately introduce the errors the chapter suggests — delete a brace, drop a semicolon, misspell println. Learning to read the compiler’s complaint in week one saves hours in week six.
Common Mistake: Assuming the compiler and the interpreter are the same thing. Compiling produces byte code and reports syntax errors; running the byte code is a separate step where a different class of error appears.
Important Questions:
- Why is Java called a portable language? Because the compiler produces byte code rather than machine code for one processor, and any machine with a Java virtual machine can interpret that byte code.
- What is the difference between source code, byte code and an executable? Source code is what you write; byte code is the intermediate form the Java compiler produces; an executable is machine code a processor runs directly.
Chapter 2: Variables and Operators
Difficulty: Easy · Key topics: declaring and assigning variables, memory diagrams, arithmetic operators, floating-point numbers, rounding error, string concatenation, order of operations, the three kinds of error
A variable is defined as a named location in memory that stores a value, and the chapter separates declaration from assignment carefully, using memory diagrams to show what each statement actually does. It makes an important warning early: the = symbol is assignment, not equality. In mathematics a = 7 means 7 = a is also true, but in Java the left side must always be a storage location. Arithmetic operators follow, and with them integer division — the reason minute / 60 gives zero when you expect a fraction. Floating-point numbers solve that, and immediately introduce rounding error, since most fractions cannot be represented exactly. The + operator is shown doing two different jobs depending on operand type: addition for numbers, concatenation for strings. The chapter ends by naming the three kinds of programming error — compile-time, run-time and logic — a distinction the rest of the book keeps returning to.
Key Points:
- Declaring a variable creates a named storage location; assigning gives it a value. They are separate operations.
- Assignment is not commutative:
a = 7is legal,7 = ais not. - When both operands are integers, Java performs integer division and discards the remainder.
- Most floating-point values are only approximate, so rounding error is normal rather than a bug.
- The
+operator concatenates when either operand is a string, which is why"5" + 1is not 6. - Compile-time errors break the rules of the language, run-time errors appear while the program runs, and logic errors produce a program that runs but does the wrong thing.
Memory Tip: Draw the memory diagram by hand for any assignment you find confusing. The book uses them throughout, and the habit pays off enormously in Chapter 7 when array references appear and again in Chapter 10 when aliasing does.
Common Mistake: Writing int average = total / count; where both are integers and expecting a decimal result. Java truncates. Cast one operand to double first.
Important Questions:
- What are the three kinds of programming error? Compile-time errors violate Java’s syntax rules and stop compilation; run-time errors occur during execution and usually raise an exception; logic errors compile and run but produce the wrong result.
- Why does 1/3 not give an exact answer in floating point? Repeating fractions and irrational numbers cannot be represented exactly in a finite number of bits, so the computer rounds to the nearest representable value, and the difference is the rounding error.
Chapter 3: Input and Output
Difficulty: Easy · Key topics: the System class, Scanner, packages and imports, literals and constants, printf and format specifiers, type casting, the remainder operator, the Scanner bug
This is where programs stop being one-way. System is explained as a class providing access to the environment, with System.out and System.in as its values. Scanner, from the java.util package, wraps System.in and provides nextLine, nextInt and nextDouble. That leads naturally into packages, imports and the layered structure of a Java program — packages contain classes, classes define methods, methods contain statements. The worked example converts inches to centimetres, which introduces literals and the idea of a magic number: a bare 2.54 in the middle of an expression should be a named constant. printf and its format specifiers give control over decimal places. Type casts and the remainder operator finish the conversion program by turning inches into feet and inches. The chapter ends with a genuinely useful warning about a Scanner behaviour that catches almost every beginner.
Key Points:
- A package groups related classes;
importtells the compiler where to find a class such asScanner. - A magic number is an unexplained literal in an expression; replacing it with a named constant makes code readable and maintainable.
printftakes a format string with specifiers such as%.2f, followed by the values, separated by commas rather than joined with+.- A type cast converts a value from one type to another, and casting a
doubleto aninttruncates rather than rounds. - The remainder operator gives what is left after integer division, which is how a quantity is split into larger and smaller units.
- Mixing
nextIntandnextLinecauses the leftover newline to be read as an empty line — the chapter’s “Scanner bug”.
Practice Tip: Type the Scanner bug example in yourself and watch the program skip your input. Every student meets this in a lab eventually; meeting it deliberately, with the explanation in front of you, means you will recognise it instantly instead of losing an hour.
Common Mistake: Joining printf arguments with + instead of commas. It compiles, so the compiler says nothing, and then the program throws a MissingFormatArgumentException at run time.
Important Questions:
- What is a magic number and why should it be avoided? A literal value appearing in an expression with no explanation; it makes code hard to read and hard to maintain if the value ever changes, so it should be replaced by a named constant.
- What happens when you cast a double to an int? The fractional part is truncated, not rounded, so 2.9 becomes 2.
Chapter 4: Methods and Testing
Difficulty: Medium · Key topics: defining methods, flow of execution, parameters and arguments, stack diagrams and frames, scope, the Math class, composition, return values, incremental development
Until now every program had one method. This chapter breaks programs into several and explains what happens when one calls another. The flow of execution is stressed: a program does not run top to bottom through the source file, it starts at main and jumps wherever invocations take it. Parameters and arguments are distinguished carefully, including the common beginner error of writing types in the invocation rather than only in the definition. Stack diagrams arrive here — a frame per running method, holding its parameters and local variables — and with them the idea of scope. The Math class shows that most work is already done for you in the library. Return values follow, with the distinction between void methods and value-returning methods. The chapter ends on incremental development, the book’s core working method: start with something that runs, add a little, test, repeat.
Key Points:
- Execution always begins at
main, regardless of wheremainappears in the file. - A parameter is named in the method definition; an argument is the value supplied at the invocation.
- Each running method gets a frame on the stack holding its own parameters and local variables.
- The scope of a variable is the part of the program where it can be used; local variables exist only inside their own method.
- A value-returning method declares a return type and must return a value of that type on every path.
- Incremental development means adding small amounts of working code and testing as you go, rather than writing everything and then debugging.
Practice Tip: Draw the stack diagram for any program with three or more nested method calls before you run it, then check your drawing against the output. This is the single best preparation for Chapter 8, where recursion makes the stack the whole point.
Common Mistake: Declaring the types of arguments at the invocation, as in printTime(int hour, int minute);. That is a syntax error — the types belong only in the method definition.
Important Questions:
- What is the difference between a parameter and an argument? A parameter is the variable named in the method’s definition; an argument is the actual value passed in when the method is invoked.
- What is a stack diagram and what is a frame? A stack diagram shows the methods currently running; each method has a frame containing its parameters and local variables, with the frame for
mainat the top.
Chapter 5: Conditionals and Logic
Difficulty: Easy · Key topics: relational operators, if-else, chaining and nesting, switch, logical operators, De Morgan’s laws, boolean variables and methods, input validation
Programs so far did the same thing every run. This chapter gives them the ability to react. The six relational operators produce a boolean, a type named after George Boole. The if statement follows, then chains of else if and nested conditionals, with the practical note that long chains become unreadable and a switch statement is often the better structure. The three logical operators are introduced with their English meanings, along with short-circuit evaluation. De Morgan’s laws get their own section as the clean way to negate a compound condition instead of wrapping it in ! and parentheses. Boolean variables store the result of a comparison, and boolean methods hide a test behind a readable name — the chapter’s convention is to name them like yes-or-no questions. It closes with input validation, making the blunt point that you should never assume users will type the right kind of data, whether by accident or on purpose.
Key Points:
- Relational and logical operators both evaluate to
trueorfalse, so their result can be stored in abooleanvariable. - Chained
else ifblocks are fine but become hard to read;switchhandles many possible values of one expression more cleanly. - De Morgan’s laws:
!(A && B)is the same as!A || !B, and!(A || B)is the same as!A && !B. - The logical operators short-circuit — the second operand is not evaluated if the first already decides the result.
- Boolean methods are conventionally named as questions, such as
isSingleDigit. - Input must always be validated; a hacker may deliberately enter unexpected values to break a program.
Memory Tip: De Morgan’s laws are easiest to remember as “push the not inside and flip the operator”. Write both forms out once and test them on paper with a truth table — examiners ask for exactly that.
Common Mistake: Writing if (x = 5) instead of if (x == 5). In Java this is caught by the compiler unless x is a boolean, in which case it silently assigns instead of comparing.
Important Questions:
- State De Morgan’s laws with an example.
!(A && B)equals!A || !Band!(A || B)equals!A && !B; so!(x > 0 && y > 0)is the same asx <= 0 || y <= 0. - What is short-circuit evaluation? When the value of a logical expression is already determined by the first operand, Java does not evaluate the second one at all.
Chapter 6: Loops and Strings
Difficulty: Medium · Key topics: while and for loops, increment and decrement, nested loops, the char type, string iteration, indexOf, substring, string comparison, String.format
The chapter starts from the observation that repeating a task without error is what computers do well and people do poorly. The while loop comes first, then the increment and decrement operators, then the for loop as a more concise expression of the same three-part pattern: initialise, test, update. Nested loops build a multiplication table and introduce the vocabulary of outer and inner loop variables. The second half of the chapter applies loops to text. The char type and charAt let you reach individual characters; length bounds the traversal; indexOf searches; substring extracts, with the second index excluded. Then comes one of the most important warnings in the book: == almost never works for comparing strings, because it tests whether two references point at the same object rather than whether the text matches. String.format closes the chapter, producing a formatted string instead of printing one.
Key Points:
- Any
forloop can be rewritten as awhileloop and vice versa;forsuits a definite number of repetitions,whilesuits an indefinite one. ++and--add and subtract one;+=and-=change a variable by any amount.substring(a, b)includes the character at indexabut excludes the one at indexb.indexOfreturns the index of the first occurrence, or-1if the character is not present.- Use
equals, never==, to compare the contents of two strings. String.formattakes the same arguments asprintfbut returns the string instead of displaying it.
Practice Tip: Write the same loop three ways — while, for, and the enhanced for once you reach Chapter 7 — over the characters of a string. Seeing the same logic in three shapes makes loop questions in exams almost automatic.
Common Mistake: Comparing strings with ==. The code compiles and runs, and the comparison is simply always false for strings the user typed, which makes it one of the hardest bugs for a beginner to spot.
Important Questions:
- Why should strings be compared with
equalsrather than==? Because==checks whether both operands refer to the same object in memory, whileequalschecks whether they contain the same sequence of characters. - When should you use a
forloop instead of awhileloop? When you know at the start how many times the loop will repeat — a definite loop;whileis for indefinite loops whose end depends on a condition discovered as you go.
Chapter 7: Arrays and References
Difficulty: Medium · Key topics: creating and accessing arrays, references and aliasing, copying arrays, the length constant, traversal, search, reduce, random numbers, histograms, the enhanced for loop
This is the chapter where the mental model has to change. An array variable does not hold the array; it holds a reference to it. The chapter makes that explicit with memory diagrams and then shows the consequence: assigning one array variable to another copies the reference, not the array, so both names now refer to the same data and a change through either is visible through the other. Elements are initialised to zero by new, and length is a constant on arrays rather than a method as it is on strings — a small difference the book calls out deliberately. Three traversal patterns are named: traversal, search, and reduce with an accumulator. Random numbers introduce the terms deterministic, nondeterministic and pseudorandom, and are used to build a histogram, which is presented as a set of counters. The enhanced for loop arrives at the end as the compact way to say “for each value in values”.
Key Points:
- An array variable stores a reference; the array itself is a separate object in memory.
- Assigning one array variable to another creates an alias — two names for one array, not two arrays.
a.lengthis a constant with no parentheses;s.length()on a string is a method with parentheses.- Traversal, search and reduce are the three patterns almost every array algorithm is built from.
- Computers generate pseudorandom numbers, which look random but come from a deterministic algorithm.
- The enhanced
forloop is compact and readable, but gives you the values, not the indexes.
Memory Tip: Whenever an array appears in an exam question about “what does this program print”, draw the boxes and arrows first. Almost every trick question in this area turns on aliasing, and the diagram makes the answer obvious.
Common Mistake: Writing b = a; to copy an array. That copies the reference, so both variables point at the same array. To copy the contents you must loop through it or use a library method.
Important Questions:
- What is aliasing and why is it dangerous? Aliasing is when two or more variables refer to the same object; it is dangerous because a change made through one variable is visible through the others, which is easy to overlook.
- What is the difference between
lengthfor an array andlength()for a string? For an arraylengthis a built-in constant accessed without parentheses; for a stringlength()is a method and requires them.
Chapter 8: Recursive Methods
Difficulty: Hard · Key topics: recursive void methods, recursive stack diagrams, value-returning recursion, the leap of faith, factorial, Fibonacci, binary numbers, CodingBat
Recursion is introduced as a method that invokes itself to solve a smaller version of the same problem. The countdown example is traced through its stack diagram so you can see a frame created for each call, and the base case is identified as the thing that stops the process. Value-returning recursion follows with factorial, where each call multiplies its argument by the result of a smaller call. Then comes the section that makes the chapter work: the leap of faith. Instead of tracing every level, you assume the recursive call returns the right answer and check only that the base case is correct and the problem gets smaller — the same trust you already place in library methods. Fibonacci shows a case where tracing is hopeless and the leap of faith is the only practical way to read the code. The binary number system section then uses recursion to convert an integer to binary, and the chapter ends by pointing at CodingBat as free practice.
Key Points:
- Every recursive method needs a base case that returns without recursing, or it never stops.
- Each recursive call gets its own frame with its own copy of the parameters and local variables.
- The leap of faith means assuming the recursive call works correctly, rather than tracing every level by hand.
- Moving the recursive call before or after the print statement reverses the order of the output — a small change with a large effect.
- Naive recursive Fibonacci recomputes the same values many times, which is why it becomes unusably slow for large inputs.
- Infinite recursion eventually exhausts the stack and produces a
StackOverflowError.
Memory Tip: For any recursive method, write down two things before anything else: what the base case is, and how the argument gets smaller on each call. If you cannot state both, the method is wrong, and if you can, the leap of faith takes care of the rest.
Common Mistake: Writing a base case that is never reached — for example decrementing towards zero but testing for exact equality when the argument might start negative. The result is infinite recursion and a stack overflow.
Important Questions:
- What is a base case and why is it essential? The base case is the condition under which a recursive method returns without calling itself; without one the method recurses forever until the stack overflows.
- Why is the recursive Fibonacci method inefficient? Because each call spawns two more calls that recompute values already computed elsewhere in the tree, so the amount of work grows exponentially with
n.
Chapter 9: Immutable Objects
Difficulty: Medium · Key topics: primitives versus objects, the null keyword, string immutability, wrapper classes, command-line arguments, argument validation, BigInteger, encapsulation and generalization
The book’s “objects late” approach ends here. Not everything in Java is an object: int, double, char and boolean are primitive types stored directly in memory, while object variables store references. null is introduced as the value meaning “no object”, along with the NullPointerException that follows from using it carelessly. Strings are then shown to be immutable — toUpperCase does not change the string, it returns a new one — which explains a bug almost every beginner writes at least once. Wrapper classes give each primitive an object counterpart with methods and the ability to be null. Command-line arguments finally explain the args parameter that has been sitting in main since Chapter 1, and lead into argument validation. BigInteger shows what to do when values exceed long. The chapter closes with the design process the authors call encapsulation and generalization: write code, wrap it in a method, replace literals with parameters.
Key Points:
- Primitive variables store values directly; object variables store references to objects.
nullmeans “no object”; invoking a method on anullreference throws aNullPointerException.- Strings are immutable, so methods like
toUpperCasereturn a new string rather than modifying the original. - Each primitive type has a wrapper class —
Integer,Double,Character,Boolean,Long— providing methods and allowingnull. argsinmainis an array of the strings typed after the class name on the command line.- Encapsulation and generalization: wrap working code in a method, then replace its literals with parameters.
Practice Tip: Write s.toUpperCase(); on its own line, print s, and watch nothing change. Then write s = s.toUpperCase();. That two-minute experiment fixes the idea of immutability permanently.
Common Mistake: Calling a string method and ignoring the return value, expecting the original string to have changed. Strings never change; you must assign the result.
Important Questions:
- What does it mean that strings are immutable? Once created, a string’s contents cannot be changed; methods that appear to modify a string actually return a new string, leaving the original untouched.
- What is a wrapper class and why is it needed? A class that represents a primitive value as an object —
Integerforint, for example — needed because primitives cannot benulland do not provide methods.
Chapter 10: Mutable Objects
Difficulty: Medium · Key topics: Point and Rectangle, attributes and dot notation, objects as parameters and return values, aliasing revisited, reading library source, UML class diagrams, shadowing, garbage collection, StringBuilder
Where Chapter 9 used immutable objects, this one uses mutable ones: Point and Rectangle from java.awt. Attributes are the variables belonging to an object, reached with dot notation. Objects are then passed as parameters and returned from methods, which makes code shorter and more readable than passing four coordinates around. Because rectangles are mutable, aliasing returns with sharper consequences than in Chapter 7 — two variables referring to one rectangle means moving it through either one moves it for both. The chapter encourages you to read the Java library’s own source code, since it is written in Java and available. UML class diagrams summarise a class’s attributes and methods. Scope is revisited to distinguish parameters, local variables and attributes, and shadowing explains what happens when names collide. Garbage collection answers when an object stops existing. StringBuilder closes the chapter as the case where a mutable object is genuinely more efficient than an immutable one.
Key Points:
- Attributes are the variables that belong to an object and are accessed with dot notation, as in
blank.x. - Mutable objects can be changed after creation, either directly through an attribute or through a method.
- Aliasing is more dangerous with mutable objects, because a change through one reference is visible through every other.
- UML class diagrams show a class’s name, attributes and methods, with private members marked by a minus sign.
- An object becomes eligible for garbage collection when no references to it remain.
- Repeatedly concatenating strings in a loop is inefficient because each
+creates a new string;StringBuilderavoids that.
Practice Tip: Open the Java library source for Rectangle.translate as the chapter suggests. Reading real library code written by professionals, at a point where you can actually understand it, is a rare opportunity that most textbooks never offer.
Common Mistake: Building a long string by concatenating inside a loop. It works for ten iterations and becomes painfully slow for ten thousand, because every concatenation copies the whole string again.
Important Questions:
- What is the difference between a mutable and an immutable object? A mutable object’s attributes can be changed after it is created; an immutable object’s cannot, so any apparent modification produces a new object instead.
- What is garbage collection? The automatic reclaiming of memory used by objects that no longer have any references pointing to them.
Chapter 11: Designing Classes
Difficulty: Medium · Key topics: defining a class, instance variables, constructors, overloading, getters and setters, information hiding, toString, equals, instance versus static methods
Now you write the classes instead of using them. The chapter builds a Time class from scratch and uses it to introduce every part of a class definition in order. Instance variables come first, declared private so that other classes cannot reach them — the chapter’s definition of information hiding. Constructors follow, with three rules that make them different from ordinary methods: same name as the class, no return type, no static. Constructors can be overloaded, so a class usually offers both a default constructor and a value constructor. Getters and setters provide controlled access to private variables, and a class that uses another class is called a client. toString is explained as a method every object already has, which println calls automatically, so overriding it is the fastest way to make an object printable. equals then draws the distinction the chapter cares most about: == tests whether two references are identical, equals tests whether two objects are equivalent. The chapter ends by adding times, comparing a static approach with an instance-method approach.
Key Points:
- Defining a class creates a new type with the same name; the class is a template and each object is an instance.
- A constructor has the same name as the class, declares no return type, and is not
static. - Constructors can be overloaded — Java chooses one by matching the arguments to the parameters.
- Declaring instance variables
privateand providing getters and setters is information hiding. - Overriding
toStringmakesprintlndisplay something meaningful instead of a memory address. ==tests identity (same object);equalstests equivalence (same values).
Memory Tip: Learn the three constructor rules as a single sentence — same name, no return type, no static. It is asked in almost every OOP paper, and it is also the fastest way to diagnose why your constructor “isn’t being called”: if you accidentally give it a return type, Java treats it as an ordinary method.
Common Mistake: Writing public void Time() instead of public Time(). Adding a return type turns the constructor into a normal method, and the compiler then supplies a default constructor that leaves everything at zero.
Important Questions:
- What is a constructor and how does it differ from a method? A constructor initialises a new object; it has the same name as the class, has no return type and is not declared
static, whereas an ordinary method has its own name and a declared return type. - What is the difference between
==andequalsfor objects?==is true only when both references point to the same object, whileequalscan be defined to be true when two distinct objects hold the same values.
Chapter 12: Arrays of Objects
Difficulty: Medium · Key topics: designing a Card class, encoding values as integers, class variables and static, compareTo, arrays of references, sequential search, binary search
The next three chapters build one connected program: playing cards, decks, and finally a working card game. This chapter designs the Card class and makes a genuine design decision in the open — ranks and suits could be strings, but integers make comparison and sorting far easier, so the values are encoded as numbers and decoded for display using arrays of strings. Those decoding arrays are the motivation for class variables: values shared across all instances, declared static, since every card can share one copy of the suit names. compareTo extends the equality idea from Chapter 11 into ordering, returning a negative number, zero or a positive number. Then arrays of cards, with the reminder that the array holds references and starts full of null. The chapter closes with two search algorithms: sequential search, which checks every element, and binary search, which halves the range each time — presented through the familiar example of looking up a word in a dictionary.
Key Points:
- Encoding ranks and suits as integers makes cards easy to compare and sort; string arrays decode them for display.
- A class variable is declared
staticand is shared by every instance of the class. compareToreturns a negative number, zero, or a positive number depending on the ordering of two objects.- An array of objects contains references; before you fill it, every element is
null. - Sequential search checks elements one at a time and returns
-1if the target is absent. - Binary search requires a sorted array and halves the search range on each step, so it is far faster on large collections.
Practice Tip: Add the trace print statement the chapter suggests inside the binary search loop and run it on a sorted deck. Watching the range shrink line by line is worth more than reading the algorithm three times.
Common Mistake: Running binary search on an unsorted array. It compiles, runs, and confidently returns the wrong answer — a logic error with no error message at all.
Important Questions:
- What is the difference between an instance variable and a class variable? Every object has its own copy of an instance variable, while a class variable is declared
staticand one copy is shared by all instances of the class. - Compare sequential search and binary search. Sequential search examines each element in turn and works on any array; binary search requires a sorted array but eliminates half the remaining elements at every step, so it is much faster for large arrays.
Chapter 13: Objects of Arrays
Difficulty: Hard · Key topics: the Deck class, shuffling, selection sort, merge sort, subdecks, recursive sorting, static context, ArrayList, the game of War
Chapter 12 put cards in an array; this chapter wraps that array in a Deck class, which is the point where encapsulation starts paying off. Shuffling comes first and is done by swapping each card with a randomly chosen one, rather than imitating how humans shuffle. Sorting follows with two algorithms deliberately contrasted. Selection sort repeatedly finds the lowest remaining card and swaps it into place, taking time proportional to n squared. Merge sort splits the deck into halves, sorts each, and merges the sorted halves, taking time proportional to n log n — and the chapter is explicit that the difference becomes enormous as n grows. Merge sort is built up in pieces: a subdeck helper, a merge helper, then recursion with a one-card base case. Static context explains why helper methods that touch no instance variables are declared static. ArrayList then arrives as the library’s resizable alternative to a fixed array, and the chapter finishes by implementing the card game War.
Key Points:
- The
Deckclass encapsulates an array ofCardobjects, so code that uses a deck never touches the array directly. - Selection sort takes time proportional to n squared; merge sort takes time proportional to n log n.
- Merge sort is naturally recursive: split, sort each half, merge — with a single-card deck as the base case.
- A method that does not read or write any instance variables belongs in a static context.
- An
ArrayListgrows and shrinks as needed, unlike an array whose length is fixed at creation. - Top-down design means starting from the high-level goal and breaking it into smaller problems, which is exactly how the sort is built.
Memory Tip: Remember the two sorts by their shape rather than their code — selection sort makes one pass per element, merge sort halves the problem each time. That is also the answer to the standard exam question about why one is faster.
Common Mistake: Writing mergeSort without a base case, or with a base case that only handles a one-card deck and not an empty one. The chapter points out that handling both is what makes the recursion convenient.
Important Questions:
- Compare selection sort and merge sort. Selection sort traverses the array once per element and takes time proportional to n squared; merge sort recursively splits the array in half and merges the sorted halves, taking time proportional to n log n, which is far faster for large n.
- What is the difference between an array and an ArrayList? An array has a fixed length set when it is created; an
ArrayListis a library class that grows and shrinks as elements are added and removed.
Chapter 14: Extending Classes
Difficulty: Medium · Key topics: CardCollection, inheritance, subclass and superclass, overriding, dealing cards, the Player and Eights classes, bottom-up design, HAS-A versus IS-A
This chapter is the payoff for the previous two, and the clearest treatment of inheritance in the book. The problem is stated concretely: a deck, a discard pile, a draw pile and a player’s hand are all collections of cards with a lot in common, and Deck and Pile have become two versions of nearly the same class. The solution is to write a CardCollection superclass holding the shared behaviour, then define Deck, Pile and Hand as subclasses that extend it. A subclass inherits everything from its superclass and adds or overrides what it needs. The game of Crazy Eights is then implemented on top, with a Player class holding the strategy and an Eights class running the game — built bottom-up, assembling small pieces into larger ones, in contrast to the top-down design of Chapter 13. The chapter ends by naming the two relationships between classes precisely: composition is HAS-A, inheritance is IS-A.
Key Points:
- A subclass extends a superclass, inheriting its attributes and methods and adding or overriding as needed.
- Inheritance removes duplicated code: shared behaviour lives once in the superclass.
- Composition is a HAS-A relationship — an
Eightsobject containsPlayerandHandobjects. - Inheritance is an IS-A relationship — every
Handis aCardCollection. - Top-down design breaks a goal into smaller problems; bottom-up design assembles small pieces into a larger program.
- Overriding replaces an inherited method with a version specific to the subclass.
Memory Tip: HAS-A means composition, IS-A means inheritance. When you are unsure which to use in a design question, say the sentence aloud — “a car has an engine”, “a car is a vehicle” — and the right relationship is obvious.
Common Mistake: Using inheritance where composition is meant, simply because it removes duplication. The test is whether the subclass genuinely IS-A kind of the superclass, not just whether it needs the same methods.
Important Questions:
- Explain the difference between composition and inheritance with an example. Composition is HAS-A: an
Eightsgame containsPlayerobjects. Inheritance is IS-A: aHandextendsCardCollection, so every hand is a card collection. - What does it mean to override a method? To define a method in a subclass with the same signature as one in the superclass, so that the subclass version is used instead of the inherited one.
Chapter 15: Arrays of Arrays
Difficulty: Hard · Key topics: Conway’s Game of Life, the Cell class, two-dimensional arrays, row-major order, GridCanvas, the simulation loop, exception handling with try-catch, counting neighbours
The last three chapters use 2D graphics to teach advanced object-oriented ideas, and this one builds Conway’s Game of Life. It is introduced properly as a zero-player game: you set the initial conditions and then watch it run. A Cell class holds a location, a size and a state. A grid of cells is a two-dimensional array, created by specifying rows and columns, and stored in row-major order — an array of rows, each of which is an array. GridCanvas extends Canvas from java.awt, which is the chapter’s demonstration of specialisation: you inherit the drawing machinery and override paint. The simulation loop updates the grid and repaints it at each time step. Exception handling is introduced exactly where it is genuinely needed: counting a cell’s live neighbours goes out of bounds at the edges of the grid, and a try-catch statement handles that far more cleanly than checking every boundary case. Updating the grid then requires counting all neighbours before changing any cell, because the rules specify simultaneous update.
Key Points:
- A two-dimensional array in Java is an array of arrays, stored in row-major order.
array.lengthgives the number of rows;array[0].lengthgives the number of columns.GridCanvasextendsCanvasand overridespaint— an example of specialisation through inheritance.- A
try-catchstatement runs the code in thetryblock and transfers control tocatchif an exception occurs, instead of ending the program. - Catching
ArrayIndexOutOfBoundsExceptionhandles grid edges more cleanly than testing every boundary condition. - The Game of Life rules require counting neighbours for all cells before updating any of them, so the grid is traversed twice.
Practice Tip: Get the grid drawing on screen before you write a single rule of the game. The book builds it in that order deliberately, and being able to see the state of your program is what makes the rest of the chapter debuggable.
Common Mistake: Updating cells as you count them. The rules require all cells to change simultaneously, so updating during the counting pass produces a different and incorrect simulation.
Important Questions:
- What is row-major order? The convention that a 2D array is stored as an array of rows, so the first index selects a row and the second selects a column within that row.
- What is exception handling and when should it be used? Using
try-catchto intercept a run-time exception so the program can continue; it is appropriate when the exceptional case is expected, such as reaching the edge of a grid.
Chapter 16: Reusing Classes
Difficulty: Medium · Key topics: Langton’s Ant, refactoring, the Automaton superclass, abstract classes and abstract methods, concrete classes, UML
This is a short chapter with one clear lesson. Langton’s Ant is another zero-player game: an ant on a grid turns right on a white cell and left on a black one, flipping the cell as it goes. The rules are trivial, and yet after roughly ten thousand steps the ant begins building a highway — which is why it is worth implementing. But the Langton class ends up with almost the same main and mainloop methods as Conway from Chapter 15, and the chapter treats that duplication as a problem to be fixed rather than tolerated. Refactoring pulls the shared code into an Automaton superclass. That raises a design question: Automaton has no sensible standalone existence and its update method cannot be written without knowing which simulation it is. The answer is an abstract class with an abstract method — a class that cannot be instantiated, declaring a method that every concrete subclass must provide.
Key Points:
- Refactoring means restructuring existing code to remove duplication without changing what it does.
- An abstract class cannot be instantiated; it exists to be extended.
- An abstract method has no body and must be implemented by every concrete subclass.
- A concrete class is one that provides implementations for all inherited abstract methods and can therefore be instantiated.
- Repeated code in two subclasses is the signal that a shared superclass is needed.
- Langton’s Ant produces complex emergent behaviour from two simple rules — a useful illustration that simple rules are not the same as simple outcomes.
Memory Tip: An abstract class is a promise, not a thing: it says “every subclass will have this method” without saying how. If a class has an abstract method, the class itself must be abstract — that pairing is the most commonly examined fact in this chapter.
Common Mistake: Trying to create an object of an abstract class with new. The compiler rejects it, and the reason is deliberate — the class is incomplete by design.
Important Questions:
- What is an abstract class and why would you define one? A class that cannot be instantiated and exists to be extended; it is defined to hold behaviour shared by several subclasses when the superclass itself has no meaningful standalone instance.
- What is refactoring? Restructuring existing code — typically to remove duplication or improve design — without changing its external behaviour.
Chapter 17: Advanced Topics
Difficulty: Hard · Key topics: Polygon objects, DrawablePolygon and RegularPolygon, generalization versus specialization, constructor chaining with this and super, interfaces, polymorphism, event listeners, Timer
The final chapter draws together the object-oriented ideas from the whole book. It begins by naming the two directions inheritance can be used in, both of which have already appeared: generalization, where common behaviour is pulled up into a superclass as in Chapters 14 and 16, and specialization, where an existing class is extended to add features as in Chapter 15. Polygons demonstrate specialization — DrawablePolygon adds colour and a draw method to the library’s Polygon, and RegularPolygon extends that further. Constructors are chained using this to call another constructor in the same class and super to call one in the superclass. Then the chapter reaches interfaces, its main new idea: Drawing should not be tied to polygons, so an Actor interface declares the methods any drawable, steppable object must provide, and the drawing works with anything that implements it. That is polymorphism. Event listeners and the Timer class close the book by replacing the hand-written animation loop with the mechanism Java actually provides.
Key Points:
- Generalization pulls shared behaviour up into a superclass; specialization extends an existing class to add behaviour.
- Constructors are not inherited; a subclass constructor calls the superclass one with
super. thisinside a constructor invokes another constructor of the same class, which avoids duplicating initialisation code.- An interface declares a set of methods without implementing them; a class that implements the interface must provide them all.
- Polymorphism means code written against an interface works with any object that implements it, without knowing its actual class.
- A
Timerexecutes code at regular intervals and is the proper way to animate, replacing awhile(true)loop withThread.sleep.
Memory Tip: The exam distinction between an abstract class and an interface is worth memorising here: an abstract class can hold state and partial implementations and a class extends exactly one; an interface declares behaviour and a class can implement many.
Common Mistake: Expecting a subclass to inherit its superclass’s constructors. It does not — if you define no constructor, the compiler generates an empty one, which is why objects sometimes come out with all their fields unset.
Important Questions:
- What is an interface and how does it differ from a class? An interface declares methods without implementing them and cannot be instantiated; a class provides implementations, and one class can implement many interfaces while extending only one class.
- What is polymorphism? The ability to write code that operates on an interface or superclass type and works correctly with objects of any class that implements or extends it.
Appendix A: Tools
Appendix · Difficulty: Easy · Key topics: installing an IDE, the interactions pane, the command-line interface, command-line testing, Checkstyle, using a debugger, JUnit
This appendix covers the practical tooling the main text deliberately leaves out: how to compile and run code, how to check style, how to trace execution and how to write automated tests. One part of it has dated. The appendix recommends DrJava as the beginner’s IDE, and DrJava’s last stable release was in 2014 with a preview in 2019; it is no longer maintained and does not work reliably with current Java versions. Everything else in the appendix stands. Use VS Code with the Java extension pack, IntelliJ IDEA Community Edition, or Eclipse instead — all free. The appendix itself opens by noting that browser-based Java environments can handle almost everything in the book, which remains true and is the easiest starting point if you cannot install software. The command-line sections, the Checkstyle instructions, the explanation of breakpoints and stepping, and the JUnit introduction all apply to any modern setup.
Key Points:
- The command-line interface is a direct interface to the operating system and is worth learning properly — many development tools are available only there.
- Checkstyle is a command-line tool that checks whether source code follows a set of style rules and flags common design problems.
- A debugger lets you set a breakpoint, step through code one line at a time, and watch variables change.
- JUnit replaces hand-written test code in
mainwith test methods that scale and report failures clearly. - Redirecting input and output from the command line lets you test a program against saved input files.
- DrJava, the IDE this appendix recommends, is no longer maintained — use VS Code, IntelliJ IDEA Community or Eclipse, all of which support everything the appendix describes.
Practice Tip: Install one modern IDE and also learn to compile with javac and run with java from a terminal. University labs and viva examiners frequently ask for the command-line version, and it is the only way to understand what the IDE is doing for you.
Common Mistake: Following the DrJava installation steps literally and concluding that Java is broken when it will not run on a current JDK. The problem is the IDE, not your setup.
Important Questions:
- What is a breakpoint and how is it used in debugging? A line where you tell the debugger to pause execution, so you can step through the code from there and inspect the values of variables as they change.
- What is the purpose of unit testing with JUnit? To write test methods that automatically check whether each part of a program produces the expected result, so that errors are caught and reported clearly as the code grows.
Appendix B: Javadoc
Appendix · Difficulty: Easy · Key topics: reading Java documentation, the three comment types, documentation comments, Javadoc tags, a complete example source file
This appendix teaches two connected skills: reading the official Java documentation and writing your own. It starts by walking through the documentation page for Scanner — the package line, the class name, the summary, the method table — so that the layout of every Java library page becomes familiar. It then distinguishes the three kinds of comment in Java: end-of-line comments starting with //, multiline comments in /* */, and documentation comments in /** */. The first two are written for yourself; the third is written for other programmers and is extracted automatically by the Javadoc tool into HTML pages. Tags such as @author, @param and @return organise the generated documentation into sections. The appendix ends with a complete, professionally formatted source file showing where the copyright statement, class documentation and method documentation each belong.
Key Points:
- Java has three comment types:
//end-of-line,/* */multiline, and/** */documentation comments. - Documentation comments are written for other programmers, not for yourself, and describe how to use a class or method.
- The Javadoc tool scans source files and generates HTML documentation from these comments.
- Tags begin with
@and include@author,@version,@paramand@return. - A documentation comment should start with a description of what the class or method does, before any tags.
- The official Java library documentation follows the same format for every class, so learning to read one page teaches you all of them.
Practice Tip: Add Javadoc comments to one of your own lab assignments and run the javadoc tool on it. Seeing your own code turned into documentation pages that look like the official Java ones makes the habit stick.
Common Mistake: Writing documentation comments with one star, /*, instead of two, /**. Javadoc silently ignores them, so you get no documentation and no error message.
Important Questions:
- What are the three types of comments in Java? End-of-line comments starting with
//, multiline comments enclosed in/* */, and documentation comments enclosed in/** */which Javadoc extracts. - What is the purpose of Javadoc tags? To organise generated documentation into sections; for example
@paramdescribes a parameter and@returndescribes the return value.
Appendix C: Graphics
Appendix · Difficulty: Easy · Key topics: Canvas and Graphics, the AWT package, the graphical coordinate system, pixels, drawing methods, bounding boxes, colour
This short appendix supplies the graphics background that Chapters 15 to 17 depend on, which is why the book suggests reading it before Chapter 15. It introduces java.awt.Canvas as a blank rectangular area to draw on and java.awt.Graphics as the object providing the drawing methods — drawLine, drawRect, fillOval, drawString. The most important idea is the coordinate system: unlike the Cartesian plane, Java’s origin is the upper-left corner and y increases downward, so all coordinates are positive integers measured in pixels. Drawing methods that produce ovals and rectangles are specified by a bounding box, which makes Rectangle objects a natural way to describe what to draw. Colour is set on the Graphics object and stays in effect until changed. The worked example draws a Mickey Mouse silhouette from three ovals, which is more instructive than it sounds because it forces you to compute the bounding boxes.
Key Points:
Canvasis the drawing surface;Graphicsprovides the methods that draw on it.- Java’s graphical origin is the upper-left corner, with x increasing to the right and y increasing downward.
- Coordinates are measured in pixels, so they are always positive integers.
- Ovals and rectangles are specified by a bounding box, given as x, y, width and height.
- Setting a colour on the
Graphicsobject affects everything drawn afterwards until it is changed. - Read this appendix before Chapter 15, as the last three chapters assume it.
Memory Tip: The single fact to hold on to is that y increases downward. Almost every “why is my drawing upside down” problem in a Java graphics lab comes from assuming the Cartesian convention.
Common Mistake: Drawing directly instead of overriding paint. The window repaints itself whenever it is resized or uncovered, and anything not drawn inside paint disappears.
Important Questions:
- How does Java’s graphical coordinate system differ from the Cartesian one? The origin is at the upper-left corner rather than the centre, and y increases downward rather than upward, so all screen coordinates are positive.
- What is a bounding box? The rectangle that encloses a shape, given as x, y, width and height, and used to specify where and how large to draw ovals and rectangles.
Appendix D: Debugging
Appendix · Difficulty: Easy · Key topics: compile-time errors, run-time errors, logic errors, common exceptions, diagnostic questions
The book scatters debugging advice throughout, and this appendix collects it and adds more, organised by the three error types from Chapter 2. It opens with the most useful point of all: the best debugging is the debugging you avoid, and incremental development is how you avoid it — start from a working program and add small pieces, so that when something breaks you already know where. The compile-time section works through the situations beginners actually hit and what the compiler’s message really means. The run-time section explains how to attack a program that hangs, which usually means an infinite loop or infinite recursion, and lists the exceptions that appear most often with their causes: NullPointerException, ArrayIndexOutOfBoundsException, StackOverflowError, FileNotFoundException and ArithmeticException. Logic errors get the longest treatment, because the compiler and the interpreter give you nothing at all — the appendix instead supplies a list of questions to ask yourself in order to form a hypothesis about what the program is really doing.
Key Points:
- Incremental development is the best defence against debugging: add small amounts of code and test constantly.
- A hanging program is usually caught in an infinite loop or an infinite recursion.
NullPointerExceptionmeans a method was invoked on a reference that isnull.ArrayIndexOutOfBoundsExceptionmeans an index was negative or greater than or equal to the array’s length.StackOverflowErroralmost always means recursion without a reachable base case.- Logic errors produce no message, so the method is to form a hypothesis about what the program is doing and test it with print statements.
Memory Tip: Learn the five common exceptions with one cause each. Being able to say instantly what a NullPointerException means, without reading the stack trace twice, is the difference between a five-minute fix and a lost evening.
Common Mistake: Adding more code while an existing bug is unresolved. The appendix’s whole argument is the opposite: get back to a working state, then move forward in small steps.
Important Questions:
- What causes a
NullPointerExceptionand how do you fix it? Invoking a method or accessing an attribute on a reference whose value isnull; the fix is to ensure the variable refers to an actual object before it is used. - How do you debug a logic error? By forming a hypothesis about what the program is actually doing, then testing that hypothesis with print statements or a debugger, since neither the compiler nor the interpreter reports anything.
Download Think Java PDF (Free)
This book is free from its official source. Click below to open the Green Tea Press page for the 2nd edition, where you can download the full PDF, read the online HTML version, or open the interactive version that runs code in your browser.
↓ Download PDFHow to Study This Book
The book is written to be read in order, one chapter per week, and that pacing genuinely works — seventeen chapters fit a semester almost exactly. Do not skip ahead to objects; the “objects late” structure is deliberate, and Chapters 9 to 14 depend on you being comfortable with methods, arrays and references first.
For a Programming Fundamentals course, Chapters 1 to 8 are your syllabus. They cover variables, input and output, methods, conditionals, loops, strings, arrays and recursion, which is almost exactly the standard first-semester outline. Chapter 8 on recursion is the hardest of the eight; give it two weeks if you need to, and rely on the “leap of faith” section rather than trying to trace every call.
For an Object Oriented Programming course, start at Chapter 9. Chapters 9 to 11 build the concepts — objects, references, classes, constructors, encapsulation — and Chapters 12 to 14 apply them to one connected program that ends in a playable card game. Chapter 14 on inheritance and Chapter 17 on interfaces and polymorphism are the two most exam-relevant chapters in the whole book.
Chapters 15 to 17 use graphics. Read Appendix C first, as the book itself advises, or those chapters will be harder than they need to be. If your course does not cover Java graphics, you can still take Chapter 16’s abstract classes and Chapter 17’s interfaces and polymorphism from them, since those ideas are examined regardless of the drawing.
One caution about Appendix A. It recommends DrJava, which is no longer maintained and does not work reliably with current Java. Use VS Code with the Java extension pack, IntelliJ IDEA Community Edition, or Eclipse instead — all free, and everything else in that appendix applies to them unchanged. The rest of the book is not affected: the examples were tested on OpenJDK 11 and still compile without modification on current Java versions.
The book also predates some newer Java conveniences — var, records, switch expressions, text blocks and sealed classes are not covered. None of them are usually required in a first or second semester course, and none of them change anything the book does teach, but you should know they exist when you move on to professional Java.
Do the exercises. There are 84 of them, the code for every example is on GitHub, and the chapters are short precisely so there is time left for the practice. Chapter 8 also points at CodingBat, which is free and gives immediate feedback on exactly the methods, loops, strings, arrays and recursion problems the first half of the book covers.
Used In These Programs
This book is used for Programming Fundamentals and Object Oriented Programming courses in: BS Computer Science · BS Information Technology. Browse all Java books, all OOP books, or all Computer Science books.
Who Should Read This
Think Java is written for someone who has never programmed before, and it keeps that promise — it defines every term when it first appears and collects them in a glossary at the end of each chapter, which makes it unusually good for students studying in English as a second language. It suits a first-semester BSCS or BSIT student whose Programming Fundamentals course uses Java, and it suits a second-semester student meeting Object Oriented Programming for the first time, since Chapters 9 to 14 are a complete OOP course on their own. It also works for a student who already knows C++ and needs Java quickly: the first eight chapters can be read in a few days because the concepts transfer, and the real work starts at Chapter 9 where Java’s reference model and class design differ from C++. It is not the right book if you want a comprehensive Java reference — the authors say plainly that it is not meant to be one, and it leaves out language features on purpose to stay small enough to finish. Students preparing for the AP Computer Science A exam or the Java SE Programmer I certification will find nearly every required topic covered.
Applicable Universities
This book is useful for students at Pakistani universities offering BSCS and BSIT including Punjab University, Virtual University, COMSATS, FAST, UET, NUST, and other HEC-recognized institutions.
FAQs
Is Think Java free?
Yes. Think Java is free under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International licence. You can download the complete PDF, read the online HTML version, or use the interactive version hosted by Trinket, all at no cost. The licence also allows you to copy, distribute and adapt the text for non-commercial use.
Which edition is this?
The 2nd edition, Version 7.1.0, published in 2020. It is the current edition; the LaTeX source is maintained publicly on GitHub.
Is Think Java still current in 2026?
The book itself is. Its examples were developed and tested on OpenJDK 11 and still compile unchanged on current Java versions, because the core language is backward compatible. One part has dated: Appendix A recommends DrJava as the IDE, and DrJava is no longer maintained — use VS Code with the Java extension pack, IntelliJ IDEA Community Edition or Eclipse instead. The book also predates var, records, switch expressions, text blocks and sealed classes, none of which are usually required in a first or second semester course.
Is Think Java good for complete beginners?
Yes. It assumes no prior programming experience, defines every term when it first appears, and collects the terms in a glossary at the end of each chapter. That vocabulary discipline makes it particularly useful for students studying in English as a second language.
Does it cover object-oriented programming?
Yes, thoroughly. Chapters 9 to 14 cover objects, references, classes, constructors, encapsulation, inheritance and class relationships, and Chapter 17 covers interfaces and polymorphism. The book uses an “objects late” approach, so these come after the programming fundamentals in Chapters 1 to 8.
How many chapters and exercises does it have?
Seventeen chapters plus four appendices, with 84 exercises across the book. Each chapter is twelve to fourteen pages and is written to cover one week of a college course. The code for every example is available in a public GitHub repository.
Related Books
- Think Python – Allen B. Downey (3rd Edition)
- Object Oriented Programming Using C++ – IT Series BSCS
- Problem Solving with Algorithms and Data Structures using Python
Think Java is the best free option for a Java course that has to fit one semester. It is short by design, it defines its terms carefully, it teaches fundamentals before objects, and it ends with a playable card game and a working simulation rather than a list of features. Browse more Computer Science books for the rest of your semester.
Think Java, 2nd Edition, Version 7.1.0, by Allen B. Downey and Chris Mayfield, Green Tea Press. Free under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International licence. Official source: https://greenteapress.com/wp/think-java-2e/