Learn how computers think, what programming languages are, and how code goes from human-readable text to something a CPU can actually run.
At its core, every computer has three key parts that work together.
The brain. Performs calculations and runs instructions — billions per second. Think of it as a chef following a recipe.
Short-term memory. Super fast, but temporary — everything in RAM disappears when you turn off the computer.
Long-term memory. Slower, but permanent — where your files and programs live when not running.
The CPU repeats this cycle billions of times per second. It's fast, not smart.
A program is just a very long, very precise recipe for the computer to follow.
Just like a recipe, skip a step or do them out of order and you get something unexpected — or nothing at all!
Computers do exactly what you tell them. Bugs happen because you wrote the wrong step — not because the computer made a mistake.
Every comma, bracket, and letter counts. Programming teaches you to think clearly and precisely.
Your program never talks directly to hardware. There's a layer in between — the Operating System.
Manages CPU time, RAM, storage, and devices so multiple programs can share hardware without crashing into each other.
When Java prints to the screen, it asks the OS, which tells the display driver, which controls the monitor. Java handles this chain — you just write println().
CPUs only understand 0s and 1s — programming languages bridge the gap between humans and machines.
Created by James Gosling at Sun Microsystems to solve a big problem: code written for one OS wouldn't run on another.
Java compiles to bytecode, which the JVM (Java Virtual Machine) can run on any computer — Windows, Mac, Linux.
Android apps, Minecraft, bank systems, government software — and it's the language used in AP Computer Science A!
Different languages take different paths to the CPU. Java's approach is a middle ground — next slide we'll compare it to C and Python.
C, Python, and Java each take a completely different path from source code to running program.
Fastest at runtime. Compiles to machine code your OS runs directly. Not portable — you recompile per platform.
No compile step. The interpreter reads and runs each line. Great for quick scripts; slower at runtime than compiled languages.
Compiles once to portable bytecode. The JVM handles the final step on each platform. Best of both worlds for most use cases.
How does code actually run? Two very different approaches.
The entire program is translated before it runs. The compiler checks for errors first — nothing runs until it's clean.
✅ Catches errors early · Often faster
Instructions are read and run one line at a time as the program executes. More flexible, but errors only show up when that line runs.
✅ Easier to test · No compile step
Java makes you label what type of data a variable holds. Python figures it out on the fly.
☕ Java — Statically Typed
You must declare the type. The compiler enforces it before running.
🐍 Python — Dynamically Typed
No type labels needed. More flexible but errors show up later.
You will see all three this course. Here's what each one means.
Caught before the program runs. Java won't even create a .class file. Syntax mistakes, type mismatches.
Easiest to fix — the compiler points right at it.
The program starts, but crashes mid-run. Examples: dividing by zero, accessing something that doesn't exist.
The error message shows where it crashed.
The program runs fine and produces output — but the output is wrong. Nothing tells you something broke.
Hardest to find — requires testing and thinking.
We'll set up and run this in Lesson 4 — for now, let's read it and understand every piece.
Main.java.
You don't need to run this yet. Just understand the journey a Java program takes.
How programs remember information — and why the type of data matters.
A variable is a named slot in RAM that your program can read or change while it runs.
Name — what you call it (score, lives)
Type — what kind of data it holds
Value — the actual data inside
Use camelCase: playerScore, isGameOver. Can't start with a number. Can't use Java keywords like int or class.
That's why they're called variables. A game score starts at 0 and goes up — same box, different value each time.
Java has 8 primitive types — these four cover nearly everything you'll need.
Whole numbers. Scores, counts, ages.
⚠️ 7 / 2 gives 3, not 3.5 — decimals get chopped!
Decimal numbers. Prices, averages, measurements.
8 bytes; tiny rounding quirks at extreme precision.
Exactly two values: true or false.
Powers every if statement and loop you'll ever write.
A single character in single quotes.
Single quotes = char. Double quotes = String.
String is not a primitive — it's a class. But it's so essential it gets its own slide.
The + operator joins strings together. Mix a String with any other type and Java converts it automatically.
String is capitalized because it's a class, not a primitive. Java is case-sensitive — string won't work.
Never use == to compare Strings. Use .equals() instead. We'll cover why in Lesson 9.
Three things you'll do with every variable — understand the difference between each.
Declaration creates the box. Initialization puts something in it. Java will error if you try to use a variable before it has a value.
Writing int lives = 2; a second time causes "variable already defined." Drop the type keyword on reassignment.
println adds a newline at the end. print doesn't — useful for printing things side by side.
Same math you know — with a few programming twists.
10 / 3 gives 3 — the decimal is thrown away. Make one number a double to get 3.333.
10 % 3 = 1 because 10 ÷ 3 = 3 remainder 1. Classic use: n % 2 == 0 checks if a number is even.
+= -= *= and ++ / -- are everywhere in Java code — get comfortable with them now.
The most common shortcuts in Java — used everywhere. Master these and you'll recognize half the code you see.
Uses the current value first, then increments. Like: "use it, then bump it."
Increments first, then uses the new value. Like: "bump it, then use it."
In loops, it doesn't matter — i++ and ++i do the same thing. But when assigning: y = x++ behaves very differently from y = ++x!
These combine an operation and an assignment into one shorthand — they're everywhere in real code.
Shorter to write and read. Compare: playerScore += 50 vs. playerScore = playerScore + 50. The first is clearer and faster to type.
+= -= *= /= %=
Each one is equivalent to: x = x ⊕ value
Increment a counter, decrement health, multiply by a power-up multiplier. These shortcuts make game logic clean and readable.
The most underrated operator. It's not just for math class — it's essential for real-world programming problems.
10 % 3 asks: "If I divide 10 by 3, what's left over?" Answer: 10 = (3 × 3) + 1 → remainder is 1.
Even/odd check: n % 2 == 0
Every nth iteration: if (i % 5 == 0) runs every 5 steps
Wrapping indices: (index % arrayLength)
A deck of cards cycles: cardIndex % 52 wraps back to card 0 after card 51. A timer loop: frame % 10 triggers every 10 frames.
A quick reference for everything you've learned about operators — use this as a bookmark!
x++ → use, then +1++x → +1, then usex-- → use, then -1--x → -1, then useAll equivalent to: x = x ⊕ value
+= add-= subtract*= multiply/= divide%= modn % 2 == 0 even?n % 2 == 1 odd?i % 5 == 0 every 5th?index % size wrap aroundi++ dozens of times per program. Comfort comes with repetition.
So far we've stored one value per variable. But what if we need 100 test scores or a deck of 52 cards? Enter arrays.
Same type, accessed by index (position). scores[0] is the first value, scores[1] is the second.
This is crucial — the first element is always at index 0, not 1. A common source of "off-by-one" bugs!
When you create an array, you decide how many slots it has. You can't add more later (not in this course, anyway).
An int[] holds only integers. A String[] holds only strings.
The syntax looks odd at first, but you'll type it hundreds of times and it becomes automatic.
[] syntaxRead "int square-bracket" as "array of int." The [] goes after the type, not the variable name.
The new keyword creates the array in RAM. The number in brackets is the size.
int[] → 0, double[] → 0.0, boolean[] → false, String[] → null
Every array has a .length (not a method!). Perfect for loops: i < array.length
You access elements by index — the same syntax you saw in the diagrams.
If the array has 3 elements (indices 0, 1, 2), asking for scores[3] crashes with ArrayIndexOutOfBoundsException.
You can use variables, math, or even method calls inside brackets: scores[i+1] or scores[random()].
Once created, you can change individual elements as many times as you want, but you can't resize the array.
This is why loops exist! In Lesson 2, you'll loop through arrays with for (int i = 0; i < scores.length; i++).
A cheat sheet for everything arrays. Bookmark this one!
int[] whole numbersdouble[] decimalsString[] textboolean[] true/falsePrograms become useful when they can react to the user — read what they type and decide what to do next.
Read what the user types from the keyboard.
Run different code depending on a condition.
Compare values: equal, greater, less than.
Combine conditions: AND, OR, NOT.
The Scanner class lets your program pause and wait for the user to type something.
Scanner lives in the java.util package. You must import it at the top before you can use it.
nextLine() → String
nextInt() → int
nextDouble() → double
Just like System.out sends to the screen, System.in reads from the keyboard.
The most fundamental decision-making tool in any programming language.
true.
These are the building blocks of every condition you'll ever write.
Comparison operators — return true or false
Logical operators — combine conditions
&& = AND (both), || = OR (either), ! = NOT (flip it). You read ! as "not" — so !isGameOver means "game is not over."
Putting it all together — Scanner + if/else in one small program.
Variables (int secret, int guess), Scanner to read input, and if/else to react.
The player only gets one chance. In Lesson 4 we'll add a loop so they can keep guessing until they get it right.
Can you modify it to also tell the player how far off their guess was? Hint: subtraction and Math.abs().
We'll use Terminal to create the project folder, then use mise to pin Java 21 to it.
Press ⌘ Space, type Terminal, press Enter. A black or white window with a command prompt opens.
labs is your alias for ~/Development/labs. mkdir lab1 creates this lab's folder, and cd lab1 moves you into it. Your prompt should now end with lab1.
This creates a .mise.toml file in your project folder that tells mise which Java version to use here. If Java 21 isn't installed yet, mise will install it automatically.
You should see:
If you see a different version or an error, make sure you ran cd into the project folder first.
Still in Terminal — one command opens the whole project in VS Code.
The . means "this folder." VS Code opens with lab1 loaded in the Explorer panel on the left.
In VS Code's Explorer panel (left sidebar), hover over the folder name and click the New File icon (📄). Type GuessingGame.java and press Enter.
Click inside the new file and paste the GuessingGame code from the previous slide. Save with ⌘ S.
VS Code will highlight the code with colors — that's syntax highlighting telling you the code was parsed correctly.
.mise.toml file mise created — it locks Java 21 to this project so anyone who opens it gets the right version automatically.
The Java extension adds a ▶ Run button above your main method — no terminal commands needed.
Look just above the line public static void main — VS Code shows a small ▶ Run link. Click it. VS Code compiles and runs your program automatically.
VS Code opens the integrated terminal at the bottom of the screen and runs your program there. You'll see the prompt Guess a number: — click in the terminal and type your guess.
Open the terminal with ⌃ ` and run it yourself:
CPU, RAM, Storage, the OS layer. Fetch-Decode-Execute cycle. Programming languages bridge humans and binary. C, Python, Java each take a different path to the CPU.
Variables are named RAM slots. Primitives: int, double, boolean, char. Strings concatenate with +. Integer division truncates. % gives the remainder.
Scanner reads from the keyboard. if/else if/else branches on a condition. Comparison operators produce booleans. && / || / ! combine them.
Loops — while, do-while, for, and for-each — and we'll finally set up Java and run real code.
Computers are great at repeating things — millions of times without getting tired. Loops are how you tell them to do it.
Without loops, you'd have to write the same line of code thousands of times. Loops let you say it once and let the computer repeat it.
😩 Without a loop — printing 1 to 5:
✅ With a loop — same result, one idea:
Every game runs a loop — check input, update state, draw screen — thousands of times per second.
Go through every student's grade, every order in a store, every message in an inbox.
Keep asking the user for a valid answer until they give one — a natural loop.
Repeat a block of code as long as a condition stays true. Check the condition before each iteration.
Like a while loop — but the body runs at least once before the condition is ever checked.
If the condition starts false, the body never runs. Use when you might not need to run at all.
Body always runs at least once, then checks. Use when you need to do something before you can decide to continue.
Menu systems, input validation, game rounds — anywhere the user must act before the program can decide what to do next.
These will happen to you. Knowing what they look like makes them easy to fix.
🔴 Infinite Loop — never stops
Program runs forever, terminal freezes, fan spins up. Fix: press ⌃ C to stop it, then make sure the loop variable actually changes inside the body.
🟡 Off-by-One — one too many or few
Loop runs one iteration too many or too few. Often caused by using < when you needed <= (or vice versa). Extremely common — even experienced developers make this mistake.
Update your guessing game so the player can keep guessing until they get it right.
Added a while loop around the guess logic. The loop keeps running until guess == secret.
attempts++ increments a counter each iteration — the loop naturally counts how many tries it took.
Add a max of 7 guesses. If the player doesn't guess in 7 tries, reveal the answer. Hint: add && attempts < 7 to the condition.
When you know exactly how many times to repeat, for packs the setup, condition, and update into one tidy line.
Creates the loop variable before the first iteration. Usually int i = 0. The variable only exists inside the loop.
If false, the loop ends immediately. Same as a while loop's condition.
Usually i++ to move forward, but can be any expression — i--, i += 2, etc.
Both can do the same job — but one is usually a better fit depending on what you know upfront.
You know exactly how many times to loop — iterating over a list, counting from A to B, repeating N times.
You don't know how many times — keep going until some event happens (user input, a condition changes, a file ends).
A loop inside a loop. The inner loop runs completely for every single iteration of the outer loop.
Two keywords that give you fine-grained control over what a loop does mid-iteration.
⛔ break — exit the loop immediately
You found what you were looking for and there's no point continuing — searching a list, hitting a sentinel value, a game-over condition.
⏭️ continue — skip this iteration only
You want to skip certain items but keep looping — filtering out invalid data, skipping blank lines, ignoring certain values in a calculation.
A cleaner syntax for looping over every item in a collection — no index variable needed.
The variable takes on each value in the collection one by one. You declare its type on the left side of the colon.
When you just need each element's value and don't care about the index — reading, printing, summing, searching.
When you need the index — modifying elements in place, going backwards, skipping by 2s, comparing adjacent elements.
A classic programming challenge. Simple rules, great practice for loops and conditions working together.
Write a program that prints every number from 1 to 100, but:
Use the % (modulo) operator to check divisibility. n % 3 == 0 means n is divisible by 3. Check FizzBuzz first — order matters!
Your starter code:
1. Add a counter — print how many FizzBuzzes there were at the end
2. Make it go 1 to N where N is entered by the user (Scanner)
3. Add a 4th rule: divisible by 7 prints "Bazz"
~/Development/labs/lab2/ — use labs → mkdir lab2 && cd lab2 → code .
Checks condition first. Runs zero or more times. Use when the count is unknown — keep going until something happens.
Runs the body first, then checks. Guarantees at least one execution. Ideal for input validation and menus.
Init, condition, update — all in one line. Use when you know the count. Can count up, down, or by any step size.
Cleanest syntax for visiting every element in a collection. No index to manage — just the value.
Infinite loop — forgot to update the loop variable. Off-by-one — used < when you needed <=.
break exits the loop entirely. continue skips the rest of this iteration and moves to the next.
Inner loop runs completely for each outer iteration. Total executions = outer count × inner count. Great for grids and tables.
Methods — breaking code into reusable named blocks, parameters, return types, and scope.
Stop repeating yourself. Package logic into a reusable, named block you can call again and again — with different data each time.
Without methods, every calculation gets copy-pasted everywhere you need it. Methods let you write the logic once.
😩 Without a method — repeated logic
✅ With a method — write once, reuse
Write the logic once, call it as many times as you need — with different data each time.
A well-named method like calculateTax() explains itself — no comment needed.
One bug in one place — fix it once and every caller benefits, instead of hunting down every copy.
Every method you write has the same five parts, no matter what it does.
What kind of value comes back out — int, double, String... or void if nothing comes back.
camelCase, just like variables. Should be a verb — add, calculateAverage, printReceipt.
The inputs the method needs, in parentheses. Can be zero, one, or many — separated by commas.
The code that runs when the method is called, wrapped in { }.
Defining a method doesn't run it — you have to call it by name for its code to execute.
add(5, 3) pauses main, runs add's body completely, then main resumes exactly where it left off.
Parameters let the same method work with completely different data every time it's called.
Parameters are the placeholders in the definition (int a, int b). Arguments are the real values you pass in when calling (5, 3).
Arguments are matched to parameters by position, not by name. The 1st argument fills the 1st parameter, and so on.
Inside the method, a and b behave like regular variables — you can even reassign them without affecting the caller's originals.
A method can hand a value back to whoever called it — or hand nothing back at all, using void.
Returns a value
Returns nothing — void
return exits immediatelyThe moment Java hits return, the method ends — any code after it in that path never runs.
If the method says int, you must return an int. Returning a String from an int method is a compile-time error.
A void method does something (prints, changes something) but doesn't hand back a value you can store.
If a method promises int, every possible branch (if/else) must eventually hit a return — or it won't compile.
Where you declare a variable decides where it's visible — and where it disappears.
Declared inside a method. Only exist while that method is running — created fresh on each call, destroyed when it returns.
Declared outside any method, directly in the class. Shared and visible across every method in that class.
Trying to use a local variable from one method inside a different method — Java says "cannot find symbol." It simply doesn't exist there.
Keep variables as local as possible. Only make something a field if multiple methods genuinely need to share it.
A recursive method solves a problem by breaking it into a smaller version of the same problem, until it's simple enough to answer directly.
The simplest version of the problem, answered directly with no further recursive call. Without one, recursion never stops.
Calls itself with a smaller or simpler input, moving one step closer to the base case each time.
Each call waits, paused, for the one below it to finish — then they all resolve in reverse order, like stacked plates.
Recursion is elegant for the right problems — but a missing base case is the loop-forgot-to-update-i of recursion.
🔴 Missing base case — crashes
Recursion's version of an infinite loop. Each call takes up space on the call stack — with no base case, Java eventually runs out and crashes.
✅ Another example — countdown
Anything recursion can do, a loop can also do — and loops usually use less memory. Reach for recursion when a problem is naturally self-similar (folders inside folders, branches of a tree).
Rebuild a basic calculator, but this time every operation is its own method.
Write four methods, each taking two double parameters and returning a double result:
add(a, b)subtract(a, b)multiply(a, b)divide(a, b)Call all four methods from main with the same two numbers, and print each labeled result with println.
Your starter code:
1. Add a modulus(a, b) method for the remainder
2. Guard divide against dividing by zero — print an error instead of crashing
3. Add a Scanner so the user types their own two numbers
~/Development/labs/lab3/ — use labs → mkdir lab3 && cd lab3 → code .
Write logic once, reuse it anywhere. Improves readability and makes bugs easier to fix — one place, not many.
Return type, name, parameters, body — static <type> name(params) { }.
Inputs passed by position. Local to the method — separate from the caller's original variables.
return sends a value back and exits immediately. void means no value comes back.
Local variables live and die inside one method call. Class-level fields are shared across every method.
A method that calls itself needs a base case (stops it) and a recursive case (moves toward the base case).
Recursion's version of an infinite loop — a missing or unreachable base case means the calls never stop piling up.
Built a calculator with a dedicated method per operation — the same pattern real programs use everywhere.
ArrayLists — a resizable, more powerful alternative to arrays, plus the Collections framework.