Code-Tracing Playbook for AP CSA

What You Need to Know

Code tracing is predicting exactly what Java code does: the final output, return value, or final state of variables/objects after execution. On AP CSA, most “gotcha” questions aren’t about syntax—they’re about evaluation order, loop mechanics, and reference vs. primitive behavior.

The core rule set you trace with
  • Java is pass-by-value (always):
    • Primitives: the value is copied.
    • Objects/arrays: the reference is copied (so both variables can point to the same object).
  • Expression evaluation is left-to-right for operands in Java (important with method calls and i++).
  • Control flow is literal:
    • return exits the current method immediately.
    • Loop order is fixed (e.g., for update runs after body, before next condition check).

Critical reminder: If two variables store the same object reference, mutating through one name affects what you see through the other name. Reassigning a variable does not “follow” to the other name.


Step-by-Step Breakdown

Use this routine every time. It’s fast and prevents classic AP CSA tracing mistakes.

1) Identify what you’re being asked
  • Output lines? Final values? Returned value? Contents of an array/ArrayList? Which line throws an exception?
  • Circle the last line that matters (e.g., the System.out.println(...) or the return).
2) Create a mini “state tracker”

Make a quick table (mentally or on paper):

  • Locals (primitives + references)
  • Objects (fields / array contents / ArrayList contents)
  • Call stack (only if methods are involved)

A simple layout that works well:

  • Left: variable names → current values/references
  • Right: object boxes labeled with their contents
3) Walk line-by-line and update state

For each line, do exactly one of these:

  • Assignment updates a variable
  • Method call may update objects (side effects) and/or return a value
  • Conditional/loop changes which line runs next
4) For expressions, evaluate in the right order
  1. Parentheses
  2. Unary (!, casts, ++x, x++ as operators)
  3. Multiplicative * / %
  4. Additive + -
  5. Relational < <= > >=
  6. Equality == !=
  7. Logical && then || (with short-circuit)
  8. Assignment =, compound assignment

Then apply left-to-right among same-precedence pieces.

5) For loops, use the “loop checkpoint” pattern
for (init; condition; update)

Trace in this exact cycle:

  1. init (once)
  2. check condition
  3. run body if true
  4. run update
  5. repeat from step 2
while (condition)

Cycle:

  1. check condition
  2. run body if true
  3. repeat
Enhanced for (for (Type x : arr))
  • x is a copy of each element (for primitives) or a copy of each reference (for objects).
  • Reassigning x doesn’t change the array/ArrayList element.
6) For method calls, do a quick call-frame

For ans = f(a, b);:

  1. Evaluate actual arguments left-to-right.
  2. Create new frame with parameters (copies).
  3. Execute until return.
  4. Replace call with returned value.
  5. Assign into ans.
Worked micro-trace (method + reference)
public static void bump(int x, int[] a) {
  x += 1;
  a[0] += 1;
}

int n = 5;
int[] arr = {5};
bump(n, arr);
System.out.println(n + " " + arr[0]);
  • n is primitive, so parameter x is a copy.
  • arr is a reference; parameter a copies the reference to the same array.
  • After call: n stays 55, arr[0] becomes 66.
  • Output: 5 6.

Key Formulas, Rules & Facts

Primitive vs. reference behavior (the backbone of tracing)
ConceptWhat happensTracing note
Primitive assignmentcopies the valuelater changes don’t affect the other variable
Object/array assignmentcopies the referenceboth names can point to same object
Java parameter passingpass-by-valueobject parameters still share the same underlying object
Mutate vs reassignmutation changes object; reassign changes one referencelist.add(...) mutates; list = new ArrayList<>() reassigns
== vs .equals(...)
Type== meansUse .equals(...)?Notes
primitivesvalue equalitynot applicablesafe
object referencessame object?yes, for contentAP CSA loves String traps
Stringsame String object?yes"a".equals(b) checks text
Numeric rules that affect results
RuleExampleResult/Note
Integer division truncates toward 007/233
Mod sign follows left operand-7 % 21-1
Mixed arithmetic promotes1 + 2.03.0 (double)
Cast happens immediately(int) 3.933
Compound assignment includes implicit castint x=0; x+=2.7;x becomes 22
Increment/decrement (high-frequency trap)
FormValue used in expressionVariable afterward
++inew valueincremented
i++old valueincremented
Short-circuit logic
OperatorStops early when…Why you care
&&left side is falseright side may have side effects (like i++)
||left side is trueavoids evaluating risky code (like arr[i] out of bounds)
Arrays vs. ArrayList (core API differences)
FeatureArrayArrayList
Sizefixeddynamic
Lengtharr.lengthlist.size()
Accessarr[i]list.get(i)
Setarr[i] = xlist.set(i, x)
Addnot applicablelist.add(x) / list.add(i, x)
Removenot applicablelist.remove(i) or list.remove(obj)

Warning: ArrayList<Integer> has two remove overloads. remove(1) removes index 11, not the value 11.

Strings you must trace correctly
  • String is immutable: methods like toUpperCase() return a new String.
  • substring(a, b) includes index a and stops before b.
  • + with String forces concatenation once a String is involved.

Examples & Applications

Example 1: i++ inside an expression (evaluation order)
int i = 1;
int x = i++ + ++i;
System.out.println(i + " " + x);

Trace:

  • Start i = 1
  • i++ contributes old value 11, then i becomes 22
  • ++i increments first: i becomes 33, contributes 33
  • x = 1 + 3 = 4
  • Final: i is 33, x is 44 → output 3 4
Example 2: ArrayList<Integer> remove overload trap
ArrayList<Integer> a = new ArrayList<>();
a.add(1); a.add(2); a.add(1);
a.remove(1);
System.out.println(a);
  • remove(1) removes element at index 11 (the value 22).
  • List becomes [1, 1].

Fix if you wanted to remove the value 11:

a.remove(Integer.valueOf(1));
Example 3: Reference aliasing vs reassignment
int[] p = {1, 2};
int[] q = p;
q[0] = 9;
q = new int[]{7, 7};
System.out.println(p[0] + " " + q[0]);

Trace:

  • q = p means both point to same array [1,2]
  • q[0] = 9 mutates shared array → p[0] now 99
  • q = new int[]{7,7} reassigns q only
  • Output: 9 7
Example 4: 2D array nested loops (order matters)
int[][] m = {
  {1, 2, 3},
  {4, 5, 6}
};
int sum = 0;
for (int r = 0; r < m.length; r++) {
  for (int c = 0; c < m[0].length; c++) {
    if ((r + c) % 2 == 0) sum += m[r][c];
  }
}
System.out.println(sum);
  • m.length is 22 rows; m[0].length is 33 cols.
  • Add cells where r+c even: positions (0,0)=1, (0,2)=3, (1,1)=5.
  • sum = 1 + 3 + 5 = 9 → prints 99.

Common Mistakes & Traps

  1. Confusing mutation with reassignment

    • Wrong: thinking q = new int[]{...} changes p if p and q used to alias.
    • Why wrong: reassignment only changes one variable’s reference.
    • Fix: ask “Did we change the object’s contents (mutation) or the variable’s pointer (reassignment)?”
  2. Using == for String content

    • Wrong: if (s1 == s2) to compare text.
    • Why wrong: checks if they are the same object.
    • Fix: use s1.equals(s2).
  3. Forgetting integer division truncation

    • Wrong: expecting 5/2 to be 2.52.5.
    • Why wrong: both operands are int, so result is int 22.
    • Fix: force a double (5/2.0 or (double)5/2).
  4. Mis-tracing loop order (especially for)

    • Wrong: applying the update before the body or skipping the condition check.
    • Why wrong: Java’s for order is strict: init → condition → body → update.
    • Fix: use the loop checkpoint pattern every time.
  5. Off-by-one with substring(a, b) and loop bounds

    • Wrong: thinking substring(1,3) includes index 33.
    • Why wrong: end index is exclusive.
    • Fix: remember “start in, end out.”
  6. ArrayList.remove overload confusion

    • Wrong: list.remove(1) to remove the value 11.
    • Why wrong: calls remove(int index).
    • Fix: remove(Integer.valueOf(1)) for value-based removal.
  7. Enhanced for loop reassignment doesn’t write back

    • Wrong:
     for (int x : arr) { x = 0; }
    
    • Why wrong: x is a copy.
    • Fix: use an index-based loop to modify array elements.
  8. Missing short-circuit effects

    • Wrong: assuming both sides of && / || always run.
    • Why wrong: right side may not execute.
    • Fix: check left operand first; only evaluate right if needed.

Memory Aids & Quick Tricks

Trick / mnemonicWhat it helps you rememberWhen to use it
“Primitives copy value; objects copy address”reference vs primitive assignment/paramsany aliasing/method call question
“Start in, end out”substring(a, b) end is exclusiveall substring problems
“Init, Test, Body, Update”exact for loop execution orderany for trace
&& stops on false; || stops on true”short-circuit stopping conditionconditions with side effects / bounds checks
“Pre uses new, post uses old”++i vs i++mixed increment expressions
“length vs size”arr.length vs list.size()arrays/ArrayList questions
“Remove: int is index”remove(1) chooses index overloadArrayList<Integer> specifically

Quick Review Checklist

  • You can explain pass-by-value and why object parameters can still mutate caller-visible state.
  • You trace aliasing: two references, one object.
  • You apply for loop order: init → condition → body → update.
  • You handle ++i vs i++ correctly (especially inside larger expressions).
  • You remember: integer division truncates, and % is remainder with truncation toward 00.
  • You use .equals(...) for String content, not ==.
  • You know substring(a, b) is end-exclusive.
  • You distinguish array vs ArrayList APIs (length vs size(), [] vs get/set).
  • You watch for short-circuit skipping the right-hand side.

You’ve got this—trace slowly, update state consistently, and the “trick” questions stop being tricky.