BSCS and BSIT students taking an Object-Oriented Programming or Java course can download the complete textbook “Introduction to Programming Using Java” by David J. Eck, free from its official source at Hobart and William Smith Colleges. It’s a comprehensive, 13-chapter book that starts from genuine Java basics and grows into more advanced territory — GUI programming with JavaFX, generics and the Collections Framework, file and network I/O, and multithreading.
This book has been continuously maintained and updated by its author for over two decades, with this edition (Version 9.0) explicitly covering Java 17 and JavaFX — making it both a solid introductory text and a useful reference to keep through a later, more advanced Java or systems course.
Book Overview
| Course | Object-Oriented Programming / Java Programming |
| Degree Programs | BSCS, BSIT, and any program’s OOP or Java course |
| Level | University — beginner through advanced, assumes no prior Java experience |
| Edition | Version 9.0, JavaFX Edition (May 2022, covering Java 17) |
| Author | David J. Eck (Hobart and William Smith Colleges) |
| Structure | 13 chapters — Chapters 1–5 cover core Java and OOP fundamentals, Chapters 6–13 cover GUI programming, data structures, generics, collections, I/O, networking, threads, and advanced GUI/graphics |
| Exercises | Every chapter ends with programming exercises and self-test quizzes on the official site |
| Language | English |
| License | Creative Commons Attribution-Noncommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0) — Model: Link-only |
| Format | Free PDF (linked and print editions), EPUB, and MOBI, plus a free browsable HTML edition |
Chapter List
Chapter 1: Overview: The Mental Landscape
Difficulty: Beginner · OOP / Java — Week 1 · Key topics: What a computer is, machine language, the operating system, programming languages, the idea of object-oriented programming
Before any Java syntax appears, this chapter builds the conceptual background needed to make sense of it — what a computer actually does at the hardware level (machine language, the CPU, memory), what an operating system is for, why high-level languages like Java exist, and what compiling and running a program actually means. It closes by introducing object-oriented programming as an organizing idea, well before the book asks a student to write a class.
Key Points:
- A computer’s CPU only executes machine language — a sequence of numeric instructions; high-level languages like Java exist to make writing programs humanly feasible.
- Java source code is compiled into bytecode, which then runs on the Java Virtual Machine (JVM) — this is what gives Java its “write once, run anywhere” property.
- An operating system manages hardware resources (memory, files, devices) and provides the environment every other program, including the JVM, runs inside of.
- Object-oriented programming organizes a program around objects that bundle data and the operations on that data together, rather than around a sequence of instructions acting on separate data.
- This chapter is conceptual, not hands-on — no code is written yet, but the vocabulary introduced here (compiler, bytecode, JVM, object, class) is used constantly from Chapter 2 onward.
Memory Tip: Don’t rush this chapter to “get to the real coding” — the compile-to-bytecode-then-run-on-the-JVM model explains a lot of Java-specific behavior (like needing a JDK installed, or why a .class file appears after compiling) that otherwise seems arbitrary later.
Common Mistake: Confusing the Java compiler’s output (bytecode, in .class files) with actual machine language — bytecode is a separate, portable instruction set that only the JVM understands and translates further at run time, not something the CPU executes directly.
Important Questions:
- Q1: What does it mean that Java is a “compiled and interpreted” language? Java source code (
.javafiles) is first compiled into an intermediate form called bytecode (.classfiles). That bytecode is then run by the Java Virtual Machine (JVM), which interprets (or further compiles, via JIT) it into the actual machine instructions for the specific computer it’s running on — this two-step process is what makes the same compiled Java program portable across different operating systems. - Q2: What is the relationship between a CPU, machine language, and a high-level language like Java? A CPU can only directly execute machine language — raw numeric instructions specific to that processor’s architecture. A high-level language like Java lets a programmer write in a form much closer to human logic and vocabulary; a compiler is what translates that high-level code down toward something a machine (via the JVM, for Java) can ultimately execute.
Chapter 2: Programming in the Small I: Names and Things
Difficulty: Beginner · OOP / Java — Weeks 1–2 · Key topics: Variables, primitive types, expressions, assignment, basic I/O with System.out and simple input
This chapter starts real Java syntax: declaring and using variables, Java’s primitive data types (int, double, boolean, char, etc.), the distinction between primitive types and objects (deferred more fully to later chapters), writing and evaluating expressions, and basic output with System.out.println(). Unlike dynamically-typed languages, every Java variable must be declared with an explicit type before use — a foundational rule this chapter establishes early.
Key Points:
- Every variable must be declared with an explicit type before use:
int count;ordouble price = 9.99;. - Java’s primitive types include
intandlong(whole numbers),doubleandfloat(decimals),boolean(true/false), andchar(a single character). System.out.println(x)prints a value followed by a newline;System.out.print(x)prints without one.- The
=operator in Java is assignment, not mathematical equality —x = x + 1;means “compute x+1 and store it back into x”. - Type casting (e.g.
(int) 3.9) explicitly converts one type to another, and is required when converting from a wider type (likedouble) to a narrower one (likeint).
Memory Tip: Read every declaration as “type, then name” out loud while learning — int total reads as “total is an int”. This habit prevents the common early confusion of reversing the order.
Common Mistake: Assuming integer division behaves like normal division — 7 / 2 in Java evaluates to 3 (integer division truncates), not 3.5, unless at least one operand is explicitly a double, e.g. 7.0 / 2.
Important Questions:
- Q1: Why does the following fail to compile: int x; System.out.println(x);? In Java, a local variable must be explicitly initialized before it’s used — simply declaring
int x;reserves the name but gives it no defined value, so the compiler reports “variable x might not have been initialized” when it’s referenced before an assignment. - Q2: What is the result of the expression 7 / 2 in Java, and why? The result is
3, not3.5. Since both7and2areintliterals, Java performs integer division, which truncates (discards) any fractional part rather than rounding.
Chapter 3: Programming in the Small II: Control
Difficulty: Beginner · OOP / Java — Weeks 2–3 · Key topics: if/else, switch, while, do-while, for loops, boolean expressions, blocks
This chapter covers Java’s control structures: conditional execution with if, if/else, and switch; and repetition with while, do-while, and for loops. It explains boolean expressions and the logical operators (&& || !), the required curly braces around multi-statement blocks, and the important distinction between while (which may run zero times) and do-while (which always runs at least once).
Key Points:
- An
ifstatement’s body is a single statement unless wrapped in{ }— always use braces even for one-line bodies, to avoid a very common bug when editing later. for (int i = 0; i < n; i++)is the standard counting loop — initialization, condition, and increment, separated by semicolons.whilechecks its condition before the first iteration and may run zero times;do-whilechecks after, so its body always runs at least once.switchstatements test one value against several possible cases; eachcaseneeds abreakor execution “falls through” into the next case.- Logical operators
&&(and),||(or), and!(not) combine boolean expressions;&&and||short-circuit, skipping the second operand when the result is already determined.
Practice Tip: Always wrap if/loop bodies in curly braces, even single statements — if a second statement is later added without braces, only the first line is actually part of the condition, and the bug is easy to miss on a quick read.
Common Mistake: Forgetting a break; at the end of a switch case, causing execution to “fall through” and run the following case's code too — unlike an if/else chain, a switch does not stop automatically after one matching case.
Important Questions:
- Q1: What is the key difference between a while loop and a do-while loop? A
whileloop checks its condition before each iteration, including the first, so its body may run zero times if the condition starts false. Ado-whileloop checks its condition after each iteration, so its body always runs at least once, even if the condition is false from the start. - Q2: Rewrite this loop using a for statement: int i = 0; while (i < 10) { System.out.println(i); i++; }
for (int i = 0; i < 10; i++) {— the initialization, condition, and increment from the while-loop version are combined into the for-loop's three-part header.
System.out.println(i);
}
Chapter 4: Programming in the Large I: Subroutines
Difficulty: Beginner · OOP / Java — Weeks 3–4 · Key topics: Methods (static), parameters, return values, method overloading, scope, the API/black-box idea
This chapter introduces subroutines (called methods in Java) as a way to organize a growing program into named, reusable, independently understandable pieces — the beginning of “programming in the large.” It covers writing a static method with parameters and a return type, calling it from elsewhere in the program, method overloading (same name, different parameter lists), variable scope, and the important idea of treating a method as a black box defined by its API (what it does), not how it's internally implemented.
Key Points:
- A method signature is its name plus parameter types:
static int add(int a, int b)can coexist with a differently-typed overload of the same name. - A method with return type
voidperforms an action but returns no value; any other return type requires a matchingreturnstatement on every path through the method. - Parameters are local to the method — changes to a primitive parameter inside a method do not affect the caller's original variable (Java passes primitives by value).
- Method overloading lets several methods share one name as long as their parameter lists differ in type or number — Java picks the matching version based on the arguments passed.
- Thinking of a well-written method as a black box — defined by what it does (its documented behavior), not how it's implemented internally — is a core principle of managing complexity in larger programs.
Memory Tip: Write a method's documentation comment (what it does, its parameters, its return value) before writing its body — committing to the black-box description first keeps the implementation honest to what it promises, and makes the method easier to reuse correctly later.
Common Mistake: Expecting a method to modify a primitive-type argument passed to it and have that change visible back in the caller — Java passes primitive values by value (a copy), so reassigning a parameter inside the method has no effect on the caller's original variable.
Important Questions:
- Q1: What is method overloading, and how does Java decide which overloaded version to call? Method overloading is defining multiple methods with the same name but different parameter lists (different number or types of parameters). Java determines which version to call based on the number and types of the arguments provided at the call site, matched against each overload's parameter list.
- Q2: Does the following code change the value of x in main after callIt(x) runs? void main() { int x = 5; callIt(x); } static void callIt(int n) { n = 99; } No,
xinmainis still5afterward. Java passes primitive arguments by value, meaningninsidecallItis an independent copy ofx's value — reassigningnhas no effect on the originalx.
Chapter 5: Programming in the Large II: Objects and Classes
Difficulty: Intermediate · OOP / Java — Weeks 4–6 · Key topics: Classes, objects, instance variables, constructors, instance methods, inheritance, polymorphism, interfaces
This is the core object-oriented programming chapter — defining a class as a template for objects, creating instances with new, instance variables and instance methods, constructors for initializing new objects, and the central OOP mechanisms of inheritance (a subclass extending a superclass with extends) and polymorphism (a method call resolving to the correct overridden version at run time based on the object's actual type). It also introduces interfaces as a way to specify required behavior without an implementation.
Key Points:
- A class is a template; an object (instance) is created from it with
new ClassName(...), which calls a constructor. - Instance variables belong to each individual object; unlike
staticvariables, each object gets its own separate copy. - A subclass declared with
extendsinherits its superclass's fields and methods, and can override a method to provide its own version. - Polymorphism means a method call on a variable of a superclass type actually runs the overridden version defined in the object's real, runtime class — decided dynamically, not by the variable's declared type.
- An interface declares method signatures with no implementation; a class that
implementsan interface promises to provide working versions of every method it declares. - Constructors share the class's name and have no return type — they run automatically when
newcreates an object, to set up its initial state.
Memory Tip: Think of a superclass reference holding a subclass object as looking through a narrower window — you can only directly call methods the superclass declares, but polymorphism guarantees the actual (subclass) version of any overridden method still runs underneath.
Common Mistake: Confusing method overriding (a subclass replacing an inherited method's implementation, same signature) with method overloading (multiple methods sharing a name but with different parameter lists) — these are two entirely different mechanisms that happen to sound similar.
Important Questions:
- Q1: What is the difference between a class and an object in Java? A class is the template or blueprint that defines what fields and methods objects of that type will have. An object (an instance of the class) is a specific, individual thing created from that template using
new— many separate objects can be created from the same class, each with its own independent instance variables. - Q2: If class Dog extends Animal and overrides a method makeSound(), what happens when Animal a = new Dog(); a.makeSound(); is executed? The overridden version defined in
Dogruns, not the one inAnimal. This is polymorphism — even thoughais declared as typeAnimal, Java resolves the method call based on the object's actual runtime type (Dog), not its declared reference type.
Chapter 6: Introduction to GUI Programming
Difficulty: Intermediate · OOP / Java — Weeks 6–7 · Key topics: JavaFX basics, Stage/Scene, event-driven programming, basic controls and layout, event handlers
This chapter shifts from console programs to graphical user interfaces using JavaFX (this edition's GUI toolkit, replacing the older Swing used in earlier editions of the book). It introduces the core JavaFX structure (a Stage containing a Scene, which holds a hierarchy of nodes), basic controls (buttons, labels, text fields), layout panes for arranging them, and — most importantly — event-driven programming, where code runs in response to user actions rather than top to bottom.
Key Points:
- A JavaFX application extends the
Applicationclass and overridesstart(Stage stage), which is where the GUI is built and shown. - A
Stageis the top-level window; aSceneholds the actual content displayed inside it, built from a tree of nodes (controls and layout containers). - Layout panes like
VBox,HBox, andBorderPanearrange child nodes automatically, rather than requiring manual pixel positioning. - Event-driven programming means the program's flow is determined by user actions (a button click, a key press) rather than running straight through from start to finish.
- An event handler (often written as a lambda expression) is code attached to a control that runs only when a specific event, like a button click, occurs.
Memory Tip: Think of building a GUI as building a tree: the Scene is the root, layout panes are branches, and controls (buttons, labels) are leaves — this mental model makes nested layout code much easier to read and debug.
Common Mistake: Writing GUI logic as if it runs top to bottom like a console program, then being confused when nothing happens until a button is clicked — event-driven code only executes when its associated event actually occurs, not in the order it appears in the source file.
Important Questions:
- Q1: What is the relationship between a Stage, a Scene, and the nodes in a JavaFX application? A
Stageis the actual window shown on screen. It displays oneSceneat a time, which represents the content area. ThatSceneis built from a tree of nodes — layout panes containing controls like buttons and labels — that together make up everything visible inside the window. - Q2: What does it mean that JavaFX (and GUI programming generally) is event-driven? Instead of executing instructions strictly from top to bottom like a simple console program, a GUI program mostly waits, and specific blocks of code (event handlers) only run in response to specific user actions, such as a button click or a key press, whenever and in whatever order those actions actually happen.
Chapter 7: Arrays, ArrayLists, and Records
Difficulty: Intermediate · OOP / Java — Weeks 7–8 · Key topics: Arrays (fixed-size), multi-dimensional arrays, ArrayList
This chapter covers Java's fixed-size arrays (declared with a type and length, indexed from zero), multi-dimensional arrays (arrays of arrays), and the more flexible, resizable ArrayList<T> from the standard library, along with the generics syntax (<T>) that lets a single collection class work with any object type safely. It closes with records, a newer, concise Java feature (added in Java 16) for defining simple immutable data-holding classes with far less boilerplate than a traditional class.
Key Points:
- An array's length is fixed at creation:
int[] scores = new int[10];creates room for exactly 10intvalues, indexed 0 through 9. - Accessing an index outside an array's valid range (e.g.
scores[10]for a length-10 array) throws anArrayIndexOutOfBoundsExceptionat run time. ArrayList<String>can grow and shrink dynamically viaadd()andremove(), unlike a plain array — but it can only hold object types, not primitives directly (autoboxing handles the conversion automatically).- Generics (the
<T>syntax) let a class likeArrayListbe written once and reused safely for any type, with the compiler enforcing type consistency. - A record (
record Point(int x, int y) {}) automatically generates a constructor, accessor methods,equals(),hashCode(), andtoString()for a simple immutable data class.
Memory Tip: Default to ArrayList over a plain array unless you specifically need a fixed size or primitive-type storage for performance — the flexibility of growing and shrinking dynamically avoids a whole class of off-by-one and resizing bugs common with raw arrays.
Common Mistake: Assuming an array automatically resizes when it needs more room, and getting an ArrayIndexOutOfBoundsException when trying to add a value past its fixed original length — unlike ArrayList, a plain array's size is permanently fixed once created with new.
Important Questions:
- Q1: What is the key practical difference between an array and an ArrayList in Java? An array has a fixed size determined when it's created and cannot grow or shrink afterward. An
ArrayListcan dynamically grow and shrink at run time via methods likeadd()andremove(), at some added memory and performance overhead compared to a plain array. - Q2: What does the record keyword automatically generate that a normal class would require writing by hand? A record automatically generates a constructor that sets all its fields, accessor methods for each field, and working
equals(),hashCode(), andtoString()implementations — all boilerplate that would otherwise need to be written manually for a simple immutable data-holding class.
Chapter 8: Correctness, Robustness, Efficiency
Difficulty: Intermediate · OOP / Java — Week 9 · Key topics: Program testing and debugging, exceptions (try/catch/finally), throwing exceptions, basic algorithmic efficiency
This chapter turns from writing programs that merely work to writing programs that work correctly and robustly. It covers testing and debugging strategies, and Java's exception-handling mechanism in depth — try/catch/finally, the distinction between checked and unchecked exceptions, throwing your own exceptions with throw, and declaring a method's checked exceptions with throws. It closes with a first, informal look at algorithmic efficiency — why some approaches to a problem are meaningfully faster than others as input size grows.
Key Points:
try { risky code } catch (SomeException e) { handling }lets a program recover from a runtime error instead of crashing outright.- Checked exceptions (like
IOException) must be either caught or declared withthrowsin a method's signature; unchecked exceptions (likeNullPointerException) have no such requirement. finallyruns regardless of whether an exception occurred, commonly used to release a resource like a file or network connection.- A program can deliberately raise its own exception with
throw new SomeException("message");to signal that a specific error condition has occurred. - Algorithmic efficiency matters as input grows large — a correct-but-slow approach (e.g. an unnecessary nested loop) can become impractical on realistic data even though it works fine on tiny test cases.
Practice Tip: Test a program deliberately with edge cases — empty input, the smallest and largest legal values, unexpected types — rather than only the “happy path” input you expect a user to provide. Most real bugs live at the edges, not in the middle.
Common Mistake: Catching an overly broad exception type (like a bare Exception e) just to make the compiler stop complaining, which silently swallows genuine bugs along with the specific error the catch block was meant to handle, making problems much harder to diagnose later.
Important Questions:
- Q1: What is the difference between a checked and an unchecked exception in Java? A checked exception (subclassing
Exceptionbut notRuntimeException) must be either caught with atry/catchor explicitly declared withthrowsin the method signature, enforced by the compiler. An unchecked exception (subclassingRuntimeException, likeNullPointerException) has no such compiler requirement and can occur without being declared. - Q2: What is the purpose of a finally block in a try statement? Code inside
finallyruns no matter what — whether thetryblock completed normally, an exception was thrown and caught, or an exception was thrown and not caught. It's most often used to guarantee cleanup, such as closing a file or network connection, regardless of how the block exits.
Chapter 9: Linked Data Structures and Recursion
Difficulty: Advanced · OOP / Java — Weeks 10–11 · Key topics: Linked lists, recursion, recursive data structures, binary trees, basic recursive algorithms
This chapter covers two closely related topics: linked data structures (built from objects that reference other objects, like a linked list or binary tree, rather than the contiguous memory of an array) and recursion (a method that calls itself to solve a smaller version of the same problem). It works through building a linked list node by node, and classic recursive algorithms like tree traversal, showing how the two ideas reinforce each other — recursive data structures are often most naturally processed with recursive algorithms.
Key Points:
- A linked list node typically holds a value and a reference to the next node; the list itself is just a reference to its first node.
- A recursive method must have a base case (a condition where it stops calling itself) and a recursive case that moves toward that base case, or it never terminates.
- A binary tree node holds a value and references to (up to) two child nodes, forming a branching, hierarchical structure rather than a straight-line chain.
- Recursive algorithms are often the most natural way to process recursively-defined structures — e.g. traversing a tree by recursively visiting its left subtree, then its right subtree.
- Every recursive call adds a new frame to the call stack; a recursion with no reachable base case (or one that never gets closer to it) eventually causes a
StackOverflowError.
Memory Tip: When writing a recursive method, write the base case first and make sure it's actually reachable — then write the recursive case assuming the smaller subproblem it calls already works correctly (this is called the “recursive leap of faith”), rather than trying to trace through every call by hand.
Common Mistake: Writing a recursive method whose recursive case doesn't actually move the input closer to the base case (e.g. calling itself with the same argument, or one that never reaches the stopping condition), which leads to infinite recursion and a StackOverflowError at run time.
Important Questions:
- Q1: What two things must every correct recursive method have? A base case — a condition under which the method returns a result directly without calling itself again — and a recursive case that calls itself with an input that is measurably closer to that base case. Without both, the recursion either never actually recurses (wrong) or never terminates (a stack overflow).
- Q2: In a singly linked list, how do you access the third node from the first node reference? Follow the
nextreference from the first node to reach the second node, then follownextagain from the second node to reach the third: something likefirst.next.next, assuming each node exposes a publicnextfield or accessor.
Chapter 10: Generic Programming and Collection Classes
Difficulty: Advanced · OOP / Java — Weeks 11–12 · Key topics: Writing your own generic classes/methods, the Java Collections Framework (List, Set, Map), iterators
This chapter goes deeper into generics — not just using generic classes like ArrayList<T>, but writing your own generic classes and methods with type parameters. It then tours the broader Java Collections Framework: the List, Set, and Map interfaces and their common implementations (ArrayList, HashSet, HashMap, and others), and the Iterator interface that lets any collection be traversed uniformly with a for-each loop.
Key Points:
- A generic class or method declares a type parameter, e.g.
class Box<T> { T contents; }, letting the same code work safely across different types chosen at use time. Listpreserves insertion order and allows duplicates;Setholds only unique elements;Mapassociates unique keys with values — the three core Collections Framework interfaces.HashMapandHashSetoffer fast average-case lookup via hashing but make no guarantee about iteration order.- The enhanced for-loop (
for (String s : list)) works on any class implementingIterable, which every standard collection does. - An
IteratorexposeshasNext()andnext(), letting code step through any collection's elements without needing to know its internal structure.
Memory Tip: Choose a collection type by what guarantee you actually need: order and duplicates allowed → List; uniqueness → Set; key-value lookup → Map. Picking the right interface up front usually simplifies the rest of the code significantly.
Common Mistake: Using a raw type (a generic class with no type parameter specified, like just ArrayList list = new ArrayList();) instead of a properly parameterized one, which disables compile-time type checking and can lead to a ClassCastException at run time that a generic type parameter would have caught at compile time instead.
Important Questions:
- Q1: What is the key difference between List, Set, and Map in the Java Collections Framework?
Listis an ordered collection that allows duplicate elements.Setholds only unique elements with no duplicates.Mapis not a collection of elements at all but a collection of key-value pairs, where each key maps to exactly one value and keys must be unique. - Q2: What must a class implement to be usable in Java's enhanced for-each loop? It must implement the
Iterableinterface, which requires providing aniterator()method returning anIteratorobject withhasNext()andnext(). Every standard Collections Framework class (ArrayList,HashSet, etc.) already implements this.
Chapter 11: Input/Output Streams, Files, and Networking
Difficulty: Advanced · OOP / Java — Week 12 · Key topics: Streams (byte and character), reading/writing files, Scanner, basic network sockets
This chapter covers Java's I/O model: byte streams and character streams as the two fundamental categories, reading from and writing to files using classes like FileReader, BufferedReader, and PrintWriter, and the convenient Scanner class for parsing text input. It closes with an introduction to basic network programming using sockets, showing that network communication in Java builds on the exact same stream abstractions used for ordinary files.
Key Points:
- Byte streams (
InputStream/OutputStream) handle raw binary data; character streams (Reader/Writer) handle text, with proper character encoding. - Wrapping a basic reader in a
BufferedReadersignificantly improves performance for line-by-line file reading by reducing the number of actual I/O operations. Scannerprovides convenient parsing methods (nextInt(),nextLine(), etc.) over an underlying stream or string, useful for both file and console input.- Always close streams when finished (ideally with try-with-resources) to release underlying system resources like file handles, even if an exception occurs partway through.
- A network
Socketprovides anInputStreamandOutputStreamjust like a file, so the same stream-processing code and patterns apply to reading and writing over a network connection.
Memory Tip: Use try-with-resources (try (BufferedReader in = new BufferedReader(...)) { ... }) for every stream you open — it automatically closes the resource when the block ends, even on an exception, eliminating an entire class of resource-leak bugs.
Common Mistake: Forgetting to close an opened file or network stream (or not using try-with-resources), which can leave file handles or network connections open and eventually exhaust available system resources, especially in a program that opens many streams over its lifetime.
Important Questions:
- Q1: What is the difference between a byte stream and a character stream in Java's I/O model? A byte stream (
InputStream/OutputStream) reads and writes raw 8-bit bytes, suitable for binary data like images. A character stream (Reader/Writer) reads and writes text, handling character encoding (like UTF-8) so multi-byte characters are correctly interpreted as text rather than raw bytes. - Q2: Why is try-with-resources preferred over manually calling close() on a stream? Try-with-resources automatically closes the resource when the block finishes, whether it completed normally or an exception was thrown partway through. Calling
close()manually at the end of a block means it can be skipped entirely if an exception occurs earlier, leaving the resource open.
Chapter 12: Threads and Multiprocessing
Difficulty: Advanced · OOP / Java — Week 13 · Key topics: Threads, the Thread class and Runnable, race conditions, synchronization, basic concurrency
This chapter introduces concurrent programming: creating and starting threads (via the Thread class or the Runnable interface) so multiple parts of a program can run seemingly at the same time. It covers the dangers of shared mutable state across threads — race conditions, where the outcome depends unpredictably on timing — and Java's synchronized keyword as the basic tool for protecting shared data from concurrent, unsafe access.
Key Points:
- A new thread can be started by extending
Threadand overridingrun(), or by implementingRunnableand passing it to aThreadobject — the latter is generally preferred. - Calling
start()actually begins a new thread of execution; callingrun()directly just runs the code on the current thread like an ordinary method call. - A race condition occurs when multiple threads read and write shared data without coordination, producing results that depend unpredictably on the exact timing of execution.
- The
synchronizedkeyword ensures only one thread at a time can execute a given block or method on a given object, preventing race conditions on the data it protects. - Concurrency bugs are notoriously hard to reproduce and debug because they often depend on timing that varies between runs — code can appear to work correctly in testing and still fail unpredictably later.
Memory Tip: Any time two or more threads might read AND write the same shared variable, assume a race condition exists until proven otherwise with proper synchronization — concurrency bugs that “probably won't happen often” still eventually do, usually at the worst possible time.
Common Mistake: Calling a Thread object's run() method directly instead of start(), which executes the code synchronously on the current thread (like an ordinary method call) rather than actually starting a new, separate thread of execution as intended.
Important Questions:
- Q1: What is the difference between calling thread.start() and thread.run() directly?
start()creates a new thread of execution and callsrun()on that new thread, so the calling code and the thread's code can genuinely run concurrently. Callingrun()directly just executes that method's code on the current thread, exactly like any ordinary method call — no new thread is created at all. - Q2: What is a race condition, and what Java keyword helps prevent one? A race condition occurs when multiple threads access and modify shared data without coordination, so the final result depends unpredictably on the exact timing of each thread's execution. The
synchronizedkeyword, applied to a method or a block, ensures only one thread at a time can execute that protected code on a given object, preventing unsafe concurrent access.
Chapter 13: GUI Programming Continued
Difficulty: Advanced · OOP / Java — Week 14 · Key topics: More advanced JavaFX: custom drawing (Canvas), animation, MVC pattern, more complex layouts and controls
This final chapter extends the GUI programming introduced in Chapter 6 with more advanced JavaFX topics: custom drawing using the Canvas and GraphicsContext classes, basic animation using JavaFX's timing classes, and more elaborate layouts and controls for building complete, non-trivial applications. It also introduces the Model-View-Controller (MVC) design pattern as a way to keep an application's data, its visual presentation, and its interaction logic cleanly separated as a GUI program grows in complexity.
Key Points:
- A
Canvasnode combined with itsGraphicsContextallows direct pixel-level drawing — shapes, lines, text, and images — unlike the built-in controls used in Chapter 6. - JavaFX's
AnimationTimerorTimelineclasses drive animation by repeatedly calling code on a regular schedule, redrawing or updating the scene each frame. - The Model-View-Controller (MVC) pattern separates an application's data and logic (the model) from its visual presentation (the view) and its user-interaction handling (the controller), making each piece independently testable and modifiable.
- As a GUI grows more complex, keeping the model (application data) separate from the view (how it's drawn) prevents a tangled program where data and display logic are inseparably mixed together.
- This chapter's techniques are typically applied to a substantial final project — a simple game, drawing application, or interactive simulation — that draws on most of the book's earlier chapters at once.
Practice Tip: Before writing any drawing code, sketch out which data belongs to your application's model (what state actually needs to be remembered) versus what's purely a matter of how it's drawn — this separation, done early, makes a Canvas-based project dramatically easier to extend later.
Common Mistake: Mixing application state directly into drawing code (e.g. recalculating game logic inside the paint/draw method itself), which makes the program's actual data hard to track and the drawing code hard to reuse — the MVC separation this chapter introduces exists specifically to avoid this tangle.
Important Questions:
- Q1: What is the purpose of the GraphicsContext class in JavaFX? It's obtained from a
Canvasnode and provides methods for direct, pixel-level drawing — shapes, lines, text, images — onto that canvas, offering far more drawing flexibility than the pre-built controls (buttons, labels) covered in Chapter 6. - Q2: In the Model-View-Controller pattern, what is the responsibility of the "model"? The model holds the application's actual data and core logic, independent of how that data is displayed or how user input is handled. Keeping it separate from the view (the visual presentation) and the controller (interaction handling) lets the underlying data and logic be changed, tested, or reused without needing to touch the GUI code at all.
Download Introduction to Programming Using Java PDF (Free)
This book is free from its official source — author David J. Eck publishes it on his own Hobart and William Smith Colleges faculty site under a Creative Commons licence. Click below to download the complete PDF — free EPUB, MOBI, and browsable HTML editions are also available on the official site.
↓ Download PDFHow to Study This Book
This book is organized in two halves: Chapters 1–5 (Overview through Objects and Classes) cover core Java syntax and object-oriented fundamentals and should be worked through in strict order. Chapters 6–13 branch out into more specialized topics (GUI, data structures, generics, I/O, threads) that can largely follow your own course syllabus once the fundamentals are solid.
Chapter 5 (Objects and Classes) is the conceptual heart of the book — inheritance, polymorphism, and interfaces introduced here are assumed knowledge in every chapter that follows, so it's worth slowing down and working through its examples by hand rather than skimming.
This edition uses JavaFX for all GUI examples (Chapters 6 and 13), replacing the Swing toolkit used in older editions — if following an older syllabus or course notes that reference Swing, be aware the GUI code will look different, though the underlying object-oriented concepts transfer directly.
The official site includes a self-test quiz and programming exercises at the end of every chapter — working through these, not just reading the text, is how this book is actually meant to be used.
Chapters 9 through 12 (recursion, generics/collections, I/O, and threads) are considerably harder than the earlier chapters and are often spread across the second half of a two-semester introductory sequence — don't expect to move through them at the same pace as Chapters 1–4.
Used In These Programs
This book is used as an introductory and intermediate Java/OOP text in: BSCS, BSIT, and any program's Object-Oriented Programming or Java coursework. Browse all Programming books or all Computer Science category books.
Who Should Read This
Introduction to Programming Using Java is written for a genuine beginner in Chapters 1–4, but grows significantly more advanced by its later chapters, covering topics (generics, collections, networking, threads, advanced GUI/graphics) more typical of a full university CS1/CS2 sequence than a single introductory course. It suits BSCS/BSIT students taking Object-Oriented Programming or a dedicated Java course, and is especially useful for students who want one comprehensive reference spanning both introductory syntax and more advanced topics like concurrency and networking.
Applicable Universities
This book is useful for students at Pakistani universities offering BSCS or BSIT programs with an Object-Oriented Programming, Java Programming, or CS1/CS2 course, including Punjab University, Virtual University, COMSATS, FAST, UET, NUST, GIKI, and other HEC-recognized institutions, and for self-learners who want a single, comprehensive, free Java reference.
FAQs
Is Introduction to Programming Using Java free?
Yes. The complete book is free from its official source, published by the author, David J. Eck, on his own faculty site at Hobart and William Smith Colleges, under a Creative Commons Attribution-Noncommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0) licence. Free PDF, EPUB, and MOBI editions are all available, along with a free browsable HTML edition.
Do I need any prior programming experience for this book?
No for the early chapters — Chapters 1–4 are written for a genuine beginner. The book grows significantly more advanced from Chapter 5 onward, though, eventually covering topics (generics, networking, threads) more typical of an intermediate or CS2-level course.
Does this book use Swing or JavaFX for GUI programming?
This edition (Version 9.0) uses JavaFX throughout, in Chapters 6 and 13. Older editions of this same book used Swing instead — if following course material referencing Swing, be aware the GUI code will differ, though the underlying object-oriented concepts carry over directly.
Does this book cover data structures and algorithms in depth?
It covers the basics needed for a first or second programming course — linked lists, recursion, binary trees, and the built-in Java Collections Framework (Chapters 9–10) — but it is not a dedicated data structures and algorithms textbook. It's a strong foundation before a more specialized algorithms course.
Is this book still current, given Java has had many releases since it was written?
Yes for its purpose — this edition (Version 9.0, May 2022) explicitly covers Java 17 and JavaFX, and core Java syntax, OOP concepts, and the Collections Framework covered here remain directly applicable to current Java versions.
Does this book cover multithreading and networking?
Yes — Chapter 11 covers file I/O and basic network sockets, and Chapter 12 covers threads, race conditions, and synchronization, giving a solid introduction to both topics before a more specialized systems or concurrent programming course.
Related Books
- Think Java – Allen B. Downey and Chris Mayfield (a gentler, more concise alternative first Java course)
- Open Data Structures – Pat Morin (a dedicated data structures and algorithms follow-up)
Introduction to Programming Using Java is a comprehensive, free Java and object-oriented programming textbook spanning both introductory syntax and more advanced topics like GUI programming, generics, networking, and threads. Browse more Computer Science books for the rest of your semester.
Introduction to Programming Using Java, by David J. Eck. Free under a Creative Commons Attribution-Noncommercial-ShareAlike 4.0 licence. Access for free at https://math.hws.edu/javanotes/
© Copyright Policy
Freebooks.pk is an educational resource for students in Pakistan. Textbooks and other books belong to their respective boards, authors and publishers, and all rights remain with them. If you are the copyright holder of any material on this page and want it changed or removed, please contact us or file a request under our DMCA policy. We review every request and remove reported content promptly.