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
|