1: Static Checking | - Types
- Static Checking vs Dynamic Checking
- Arrays and Collections
- Iterating
- Methods
- Mutating Variables vs Reassigning Variables
- Documenting Assumptions
|
2: Basic Java | - Snapshot Diagrams
- Java Collections
- Java API Documentation
|
3: Testing | - Validation
- Test-first Programming
- Choosing Test Cases by Partitioning
- Blackbox and Whitebox Testing
- Documenting Testing Strategies
- Coverage
- Unit Testing and Stubs
- Automated Testing and Regression Testing
|
4: Code Review | - Don't Repeat Yourself
- Comments Where Needed
- Fail Fast
- Avoid Magic Numbers
- One Purpose for Each Variable
- Use Good Names
- Use Whitespace to Help the Reader
- Don't Use Global Variables
- Methods Should Return Results, Not Print Them
|
5: Version Control | - Inventing Version Control
- Git: Copy, Commit, Pull, Push, Merge
|
6: Specifications | - Why Specifications?
- Behavioral Equivalence
- Specification Structure
- Null References
- What a Specification May Talk About
- Testing and Specifications
- Specifications for Mutating Methods
- Exceptions for Signaling Bugs
- Exceptions for Special Results
- Checked and Unchecked Exceptions
- Throwable Hierarchy
- Exception Design Considerations
- Abuse of Exceptions
|
7: Designing Specifications | - Deterministic vs Undertermined Specs
- Declarative vs Operational Specs
- Stronger vs Weaker Specs
- Diagramming Specifications
- Designing Good Specifications
- Precondition or Postcondition?
- About Access Control
- About Static vs Instance Methods
|
8: Avoiding Debugging | - First Defense: Making Bugs Impossible
- Second Defense: Localizing Bugs
- Assertions
- What to Assert
- What Not to Assert
- Incremental Development
- Modularity and Encapsulation
|
9: Mutability and Immutability | - Mutability
- Risks of Mutation
- Aliasing is What Makes Mutation Risky
- Specifications for Mutating Methods
- Iterating Over Arrays and Lists
- Mutation Undermines an Iterator
- Mutation and Contracts
- Useful Implementation Types
|
10: Recursion | - Choosing the Right Decomposition For a Problem
- Structure of Recursive Implementations
- Helper Methods
- Choosing the Right Recursive Subproblem
- Recursive Problem vs Recursive Data
- Reentrant Code
- When to Use Recursion Rather Than Iteration
- Common Mistakes in Recursive Implementations
|
11: Debugging | - Reproduce the Bug
- Understand the Location and Cause of the Bug
- Fix the Bug
|
12: Abstract Data Types | - What Abstraction Means
- Classifying Types and Operations
- Designing Abstract Type
- Representation Independence
- Realizing ADT Concepts in Java
- Testing and Abstract Data Type
|
13: Abstraction Functions and Rep Invariants | - Invariants
- Rep Invariant and Abstraction Function
- Documenting the AF, RI, and Safety from Rep Exposure
- ADT Invariants Replace Preconditions
|
14: Interfaces | - Interfaces
- Subtypes
- Example: MyString
- Example: Set
- Generic Interfaces
- Why Interfaces?
- Realizing ADT Concepts in Java, Part II
|
15: Equality | - Three Ways to Regard Equality
- == vs. equals()
- Equality of Immutable Types
- The Object Contract
- Equality of Mutable Types
- The Final Rule for Equals() and hashCode()
|
16: Recursive Data Types | - Recursive Functions
- Immutable Lists
- Recursive Datatype Definitions
- Functions Over Recursive Datatypes
- Tuning the Rep
- Null vs Empty
- Declared Type vs Actual Type
- Example: Boolean Formulas
- Writing a Program with ADTs
- Recipes for Programming with ADTs
- Example: Matrix Multiplication
|
17: Regular Expressions and Grammars | - Grammars
- Regular Expressions
|
18: Parser Generators | - Parser Generators
- An Antlr Grammar
- Generating the Parser
- Calling the Parser
- Traversing the Parse Tree
- Constructing an Abstract Syntax Tree
- Handling Errors
|
19: Concurrency | - Two Models for Concurrent Programming
- Processes, Threads, Time-Slicing
- Example: Shared Memory
- Interleaving
- Race Condition
- Tweaking the Code Won't Help
- Reordering
- Example: Message Passing
- Concurrency is Hard to Test and Debug
|
20: Thread Saftey | - What Threadsafe Means
- Strategy 1: Confinement
- Strategy 2: Immutability
- Strategy 3: Using Threadsafe Data Types
- How to Make A Safety Argument
|
21: Sockets and Networking | - Client/Server Design Pattern
- Network Sockets
- I/O
- Blocking
- Using Network Sockets
- Wire Protocols
- Testing Client/Server Code
|
22: Queues and Message-Passing | - Two Models for Concurrency
- Message Passing with Threads
- Implementing Message Passing with Queues
- Stopping
- Thread Safety Arguments with Message Passing
|
23: Locks and Synchronization | - Synchronization
- Deadlock
- Developing a ThreadSafe Abstract Data Type
- Locking
- Monitor Pattern
- Thread Safety Argument with Synchronization
- Atomic Operations
- Designing a Data Type for Concurrency
- Deadlock Rears its Ugly Head
- Goals of Concurrent Program Design
- Concurrency in Practice
|
24: Graphical User Interfaces | - View Tree
- How the View Tree is Used
- Input Handling
- Separating Frontend from Backend
- Background Processing in Graphical User Interfaces
|
25: Map, Filter, Reduce | - Abstracting Out Control Flow
- Map
- Functions as Values
- Filter
- Reduce
- Benefits of Abstracting Out Control
- First-class Functions in Java
- Map/Filter/Reduce in Java
- Higher-order Functions in Java
|
26: Little Languages | - Representing Code as Data
- Building Languages to Solve Problems
- Music Language
|
27: Team Version Control | - Git Workflow
- Viewing Commit History
- Graph of Commits
- Using Version Control as a Team
|