AP CSA Unit 2 Notes: Decision-Making with Booleans and Conditionals

Algorithms with Selection and Repetition

An algorithm is a step-by-step process for solving a problem. In programming, you rarely write a straight-line algorithm that runs the same steps every time—real programs need to choose between actions and repeat actions until some goal is met. That’s where selection and repetition come in.

Selection means your algorithm makes a decision and follows one path or another. In Java, selection is primarily implemented with if statements (and if-else, else-if chains, and nested ifs). Repetition means your algorithm performs a step multiple times. In Java, repetition is implemented with loops (while, for, and enhanced for).

Why this matters: AP Computer Science A isn’t just about knowing Java syntax—it’s about being able to trace and reason about how an algorithm’s control flow changes based on conditions. Many exam questions test whether you can predict what code does when different conditions are true or false.

How selection and repetition work together

Selection and repetition often combine in patterns like:

  • Input validation: Keep asking for input while it’s invalid (repetition), and decide whether it’s valid using a condition (selection).
  • Search: Repeatedly examine items, and if you find the target, take a special action.
  • Counting with rules: Loop through data and increment counters only when certain conditions hold.

The key concept underneath both selection and repetition is the Boolean expression—an expression that evaluates to true or false. Conditions control whether an if block runs and whether a loop continues.

Example: repetition controlled by a Boolean condition

You don’t need to memorize lots of loop patterns in this section, but you do need to recognize the role of Boolean conditions.

int x = 1;
while (x < 5) {
    System.out.println(x);
    x++;
}
  • The loop repeats while the condition x < 5 is true.
  • Once x becomes 5, x < 5 is false, and the loop stops.

A common conceptual error is thinking the loop “runs 5 times because 5 is in the condition.” It actually runs based on the truth value of the condition each time it is checked.

Exam Focus
  • Typical question patterns:
    • Trace code that uses if conditions inside loops (e.g., “How many times is this printed?”).
    • Determine which inputs cause a loop to stop or continue based on a Boolean test.
    • Predict final values of counters/variables after conditional updates.
  • Common mistakes:
    • Forgetting that loop/if conditions are re-evaluated each time control reaches them.
    • Confusing assignment (=) with comparison (==) when reading conditions.
    • Off-by-one reasoning errors (e.g., misunderstanding when x < 5 becomes false).

Boolean Expressions

A Boolean expression is any expression in Java that evaluates to a boolean value: true or false. Booleans are the foundation of decision-making in code because every if statement and every loop condition depends on one.

Why this matters: You can write perfectly valid Java code that still behaves incorrectly if your Boolean expressions don’t match the logic you intended. On the AP exam, many questions are essentially “Do you understand what this condition really means?”

Where Boolean values come from

You can get a boolean value from:

  1. Boolean literals: true, false
  2. Boolean variables:
   boolean isFinished = false;
  1. Relational comparisons (compare numbers/characters):
    • <, <=, >, >=
    • ==, !=
  2. Logical operators applied to booleans:
    • && (and), || (or), ! (not)
  3. Method calls that return boolean (common with String and other objects):
    • str.equals("hi") returns true or false

Relational operators (comparisons)

Relational operators compare two values and produce a boolean result.

int score = 82;
boolean passed = score >= 60; // true

Important details:

  • With primitive types like int, double, and char, == compares actual values.
  • With objects (like String), == compares whether two references point to the same object, not whether they have the same characters. For String content comparison, you almost always want .equals(...).
String a = new String("cat");
String b = new String("cat");

System.out.println(a == b);        // usually false (different objects)
System.out.println(a.equals(b));   // true (same characters)

This is a classic AP CSA misconception: students use == for strings and get surprising results.

Logical operators (combine or modify conditions)

Java’s main logical operators are:

  • NOT: !condition flips true to false and false to true.
  • AND: A && B is true only if both A and B are true.
  • OR: A || B is true if at least one of A or B is true.

These operators let you write conditions closer to how you think about rules in real life—“if the password is correct and the account is active…”, “if the user is an admin or a moderator…”.

Operator precedence (what gets evaluated first)

When multiple operators appear in a single expression, Java applies a precedence order. A practical subset you should rely on:

  1. Parentheses (...)
  2. NOT !
  3. Relational operators < <= > >=
  4. Equality == !=
  5. AND &&
  6. OR ||

Even when you know precedence, using parentheses can prevent mistakes and make your intent clearer.

Exam Focus
  • Typical question patterns:
    • Evaluate the result of Boolean expressions for given variable values.
    • Identify whether a condition is true/false for specific inputs.
    • Distinguish == vs .equals() in string/object comparisons.
  • Common mistakes:
    • Using = instead of == when reading/writing conditions.
    • Using == to compare String contents instead of .equals().
    • Misreading precedence in expressions like a > b && b > c (or forgetting parentheses when needed).

If Statements and Control Flow

An if statement is a selection structure that runs a block of code only when its condition is true.

Why this matters: Control flow is how your program “decides what to do next.” The hardest part for many learners is not writing an if, but predicting exactly which lines run and which lines are skipped—especially with nesting, missing braces, or multiple conditions.

The basic structure

if (condition) {
    // runs only if condition is true
}

The parentheses must contain a boolean expression. Unlike some languages, Java does not treat 0/1 as false/true—your condition must actually be boolean.

What “control flow” means in practice

When Java executes an if statement:

  1. It evaluates the condition to either true or false.
  2. If true, it executes the body (the statement or block after the if).
  3. If false, it skips the body.
  4. Execution continues with the next statement after the if block.

Braces and single-statement bodies

Braces { } define a block—multiple statements treated as one unit.

If you omit braces, the if controls only the next single statement:

if (x > 0)
    System.out.println("positive");
System.out.println("done");

Here, "done" prints regardless of x. This is a common bug when you intend both prints to be conditional.

Nested if statements

You can put an if inside another if to represent decisions that depend on earlier decisions.

Conceptually: you’re refining the path—“If the user is logged in, then check whether they are an admin.”

if (loggedIn) {
    if (isAdmin) {
        System.out.println("admin panel");
    }
}

The “dangling else” idea (why indentation isn’t enough)

Java pairs an else with the closest previous unmatched if. Indentation doesn’t matter to the compiler—only braces do.

If you ever feel unsure about which if an else belongs to, add braces. That both clarifies meaning and prevents exam-trace mistakes.

Example: tracing an if statement

int temperature = 72;
if (temperature > 80) {
    System.out.println("hot");
}
System.out.println("end");

Output:

  • temperature > 80 is false, so "hot" is skipped.
  • "end" always prints.
Exam Focus
  • Typical question patterns:
    • Trace which statements execute given values (often with nested ifs).
    • Determine final variable values after conditional updates.
    • Identify the effect of missing braces.
  • Common mistakes:
    • Assuming indentation changes behavior (it doesn’t).
    • Forgetting that without braces only the next statement is controlled.
    • Mis-pairing an else with the wrong if when braces are missing.

If-Else Statements

An if-else statement is a two-way selection structure: exactly one of two paths runs.

Why this matters: Many real rules are naturally two-way—either you meet a condition or you don’t. if-else is also essential for writing mutually exclusive logic, where you want one outcome, not potentially many.

Basic structure

if (condition) {
    // runs if condition is true
} else {
    // runs if condition is false
}

The key idea: the else block is not “optional extra code”—it’s the alternative path.

Else-if chains (multi-way selection)

When you have more than two cases, you often use an else-if chain:

if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else if (score >= 70) {
    grade = "C";
} else {
    grade = "F";
}

How it works:

  • Conditions are checked top to bottom.
  • The first true condition’s block executes.
  • The rest of the chain is skipped.

This ordering is not a style detail—it’s part of the logic. If you reorder these tests incorrectly, you can make later cases unreachable.

For example, if you start with if (score >= 70) then scores of 90 would match that first condition, and you’d never reach the A case.

Choosing between separate ifs and an if-else chain

Separate if statements are appropriate when multiple independent actions might occur.

if (x > 0) { System.out.println("positive"); }
if (x % 2 == 0) { System.out.println("even"); }

Both could print.

An if-else-if chain is appropriate when you want exactly one category.

Example: avoiding overlapping cases

Suppose you want to label a number as negative, zero, or positive:

if (n < 0) {
    System.out.println("negative");
} else if (n == 0) {
    System.out.println("zero");
} else {
    System.out.println("positive");
}

This guarantees one label.

A common mistake would be writing three separate ifs, which would still work here (since the conditions are mutually exclusive), but in many problems conditions overlap and you might accidentally print multiple labels.

Exam Focus
  • Typical question patterns:
    • Determine which branch executes in an if-else or else-if chain.
    • Identify unreachable code or redundant conditions in an else-if chain.
    • Predict outputs when conditions overlap.
  • Common mistakes:
    • Putting conditions in the wrong order so earlier tests “steal” later cases.
    • Using multiple separate ifs when the situation requires mutually exclusive behavior.
    • Forgetting that exactly one branch of an if-else runs (never both).

Compound Boolean Expressions

A compound Boolean expression combines simpler boolean expressions using logical operators like &&, ||, and !.

Why this matters: Most interesting program rules aren’t single comparisons. You often need to express things like “in range,” “valid input,” or “allowed access,” which require combining conditions. AP CSA frequently tests whether you understand how compound expressions evaluate, including short-circuit behavior and negation.

AND (&&): requiring multiple conditions

Use && when all conditions must be true.

A classic pattern is a range check:

if (age >= 13 && age <= 19) {
    System.out.println("teen");
}

This reads like English: age is at least 13 and at most 19.

OR (||): allowing alternative conditions

Use || when any one condition being true is enough.

if (day.equals("Sat") || day.equals("Sun")) {
    System.out.println("weekend");
}

NOT (!): reversing a condition

! flips the truth value:

if (!isEmpty) {
    // runs when isEmpty is false
}

A common readability issue is double negatives such as if (!notReady). You can often simplify by renaming variables (e.g., isReady) or rewriting the logic.

Short-circuit evaluation (very important in Java)

Java uses short-circuit evaluation for && and ||:

  • For A && B: if A is false, Java does not evaluate B.
  • For A || B: if A is true, Java does not evaluate B.

Why this matters:

  • It can improve efficiency (skip unnecessary checks).
  • It can prevent runtime errors by guarding a risky expression.

Example: guarding against division by zero

if (denominator != 0 && numerator / denominator > 2) {
    System.out.println("large ratio");
}

If denominator is 0, the left side is false, so Java skips the division and avoids an ArithmeticException.

Another common guard is checking for null before calling a method:

if (name != null && name.length() > 0) {
    System.out.println(name);
}

If name is null, Java skips name.length() and prevents a NullPointerException.

Mixing && and || (use parentheses to control meaning)

When expressions get more complex, parentheses matter. Consider:

if (isMember || isEmployee && hasCoupon) {
    // ...
}

Because && has higher precedence than ||, this is interpreted as:

  • isMember || (isEmployee && hasCoupon)

If you meant:

  • (isMember || isEmployee) && hasCoupon

…you must add parentheses.

Worked example: tracing a compound expression

int x = 5;
int y = 10;
boolean result = (x < 0) || (y / x > 1);
System.out.println(result);

Step-by-step:

  1. Evaluate (x < 0)5 < 0 is false.
  2. Because it’s ||, Java must evaluate the right side (left side wasn’t true).
  3. Evaluate (y / x > 1)10 / 5 is 2, and 2 > 1 is true.
  4. false || truetrue prints.

If x had been 0, then the division would be dangerous. You’d rewrite the condition as a guarded check, for example:

boolean safe = (x != 0) && (y / x > 1);
Exam Focus
  • Typical question patterns:
    • Evaluate compound expressions for given values (including precedence).
    • Determine whether a risky right-hand expression is evaluated (short-circuit reasoning).
    • Rewrite conditions to correctly represent rules like “in range” or “not in range.”
  • Common mistakes:
    • Forgetting short-circuit behavior and assuming both sides always evaluate.
    • Mixing && and || without parentheses, leading to unintended logic.
    • Writing impossible range checks like x > 10 && x < 5 (always false).

Equivalent Boolean Expressions

Two Boolean expressions are equivalent if they produce the same true/false result for all possible inputs. In other words, they represent the same logical rule even if they look different.

Why this matters: On the AP exam, you’re often asked to simplify logic, correct conditions, or identify which expression matches a described behavior. In real coding, recognizing equivalence helps you write clearer conditions, avoid bugs, and correctly negate conditions.

De Morgan’s Laws (the most tested equivalences)

De Morgan’s Laws describe how negation distributes over && and ||:

  • !(A && B) is equivalent to (!A || !B)
  • !(A || B) is equivalent to (!A && !B)

These are essential when you need to rewrite a negated compound condition.

Example: negating a range check

Suppose you have:

boolean inRange = (x >= 0 && x <= 10);

What does “not in range” mean?

  • Start with the negation: !(x >= 0 && x <= 10)
  • Apply De Morgan’s Law:
    • x < 0 || x > 10

So you can write:

boolean outOfRange = (x < 0 || x > 10);

A common mistake is to negate each comparison but keep &&, producing x < 0 && x > 10, which can never be true.

Negating comparisons correctly

Negating a simple comparison flips the operator:

OriginalNegation
x < yx >= y
x <= yx > y
x > yx <= y
x >= yx < y
x == yx != y
x != yx == y

This becomes powerful when combined with De Morgan’s Laws.

Rewriting to improve clarity (without changing meaning)

Even when two expressions are equivalent, one may be easier to understand.

Example: avoid an unnecessary negation

if (!(count == 0)) {
    // ...
}

Equivalent, clearer version:

if (count != 0) {
    // ...
}

Example: express “between” as a range
Instead of:

if (x > 3 && x < 7) {
    // ...
}

This is already clear, but if you see the longer form:

if (!(x <= 3 || x >= 7)) {
    // ...
}

You should recognize it as the same idea (inside the interval).

Using truth tables (conceptual tool)

A truth table lists all possible truth values of component conditions and the resulting truth value of the full expression. You usually don’t need to draw full truth tables on AP CSA, but the reasoning style is helpful:

  • If two expressions match for every possible combination of inputs, they are equivalent.
  • If you find even one case where they differ, they are not equivalent.

Example: spotting a common equivalence

Consider the statement: “The student does NOT have both missing homework and a failing test.”

Let:

  • A = missing homework
  • B = failing test

“NOT (A AND B)” is:

  • !(A && B)

By De Morgan, this is equivalent to:

  • (!A || !B)

In words: either they are not missing homework, or they are not failing the test (or both). That matches “it is not the case that both problems happen simultaneously.”

Equivalence pitfalls in programming context

Equivalence is about logic, but code adds a practical twist: side effects and short-circuiting.

For example, these are logically equivalent:

  • !(A && B)
  • (!A || !B)

But if A or B includes a method call that changes state (side effect), the two versions might call methods in different patterns due to short-circuiting. On AP CSA, conditions are typically free of side effects, but it’s still good practice to keep conditions simple and avoid embedding work inside them.

Exam Focus
  • Typical question patterns:
    • Choose which expression is equivalent to a given one (often using De Morgan’s Laws).
    • Rewrite a condition to represent “not (something)” correctly.
    • Identify which rewritten condition correctly describes a range or category.
  • Common mistakes:
    • Negating each comparison but forgetting to switch && and || (De Morgan’s Laws).
    • Incorrectly negating operators (e.g., thinking !(x < 5) becomes x <= 5 instead of x >= 5).
    • Creating impossible conditions when describing “outside a range” (using && instead of ||).