AP CSA Practice Exam – Section I: 42 Multiple Choice Questions
Practice Exam — Section I
Multiple Choice • 4-Unit Curriculum • College Board Level Difficulty
Finished? Continue to the free response section. Section II: Free Response → 4 FRQ questions • 90 minutes • 50% of exam score
• 42 questions across all 4 AP CSA units — Unit 1: 10 • Unit 2: 12 • Unit 3: 7 • Unit 4: 13.
• 90 minutes. Timer starts when you click “Start Exam.”
• You may return to and change any answer before submitting.
• The Java Quick Reference (PDF) → is permitted, just as on the real exam. Open it in a separate tab.
• Unless stated otherwise, assume parameters are not null and all preconditions are satisfied.
• No penalty for guessing — answer every question before submitting.
• After submitting, your score and estimated AP score (1–5) will appear above, then continue to Section II: Free Response →
What is the value of result after executing the following code segment?
int a = 13; int b = 5; double result = (double)(a / b) + a % b;
Parentheses force
a / b to evaluate first as integer division: 13 / 5 = 2. The (double) cast then converts 2 to 2.0. Next, a % b = 13 % 5 = 3. Finally, 2.0 + 3 = 5.0. The common mistake is thinking the cast makes the division decimal (which would give 2.6), but the cast applies after division completes.
What is the value of result after executing the following code segment?
String msg = "AP Computer Science";
int idx = msg.indexOf("Computer");
String result = msg.substring(idx, idx + 4);
indexOf("Computer") returns 3 (the index where "Computer" starts). Then substring(3, 7) extracts indices 3, 4, 5, 6 — which is "Comp". Remember: substring(start, end) includes start but excludes end.
Which expression produces a random integer from 25 to 75, inclusive?
Formula:
(int)(Math.random() * range) + offset where range = number of values and offset = starting value. From 25 to 75 inclusive: range = 75 − 25 + 1 = 51, offset = 25. Option A generates 25–99. Option B generates 25–74 (misses 75). Option D generates 26–75 (misses 25).
Consider the following code segment.
String x = new String("hello");
String y = new String("hello");
System.out.println(x.equals(y));
System.out.println(x == y);
What is printed?
.equals() compares content and returns true since both contain "hello". The == operator compares references (memory addresses). Using new String() creates two separate objects, so == returns false. Always use .equals() to compare String content.
What is printed by the following code segment?
int x = 11; int y = 4; System.out.println(x / y * y + x % y);
Evaluate left to right:
x / y = 11 / 4 = 2. Then 2 * y = 8. Then x % y = 3. Finally 8 + 3 = 11. This always reconstructs the original dividend: (x/y)*y + x%y == x for positive integers.
Consider the following class.
public class Widget {
private int val;
public Widget(int v) {
val = v;
}
public int getVal() {
return val;
}
public static int doubleIt(int n) {
return n * 2;
}
}
The following code appears in another class.
Widget w = new Widget(5);
Which of the following will cause a compile-time error?
getVal() is an instance method — it requires an object. Calling Widget.getVal() attempts to call it on the class itself, which is only valid for static methods. Options A and D correctly call the static method. Option B correctly calls an instance method on an object.
What is the value of result after executing the following?
int x = 3; int y = 4; String result = x + y + " total " + x + y;
Evaluates left to right.
x + y = 7 (both ints → arithmetic). 7 + " total " = "7 total " (int + String → String). From here all + is concatenation: "7 total " + 3 = "7 total 3", then + 4 = "7 total 34".
Consider the following method.
/** Returns a random integer between low and high, inclusive */
public static int randRange(int low, int high) {
return /* missing code */ ;
}
Which of the following should replace /* missing code */ so the method works as intended?
Range =
high - low + 1 values; offset = low. Option B misses the highest value. Option D starts one too high, missing low.
Consider the following code segment.
ArrayListnums = new ArrayList (); nums.add(5); nums.add(10); int total = nums.get(0) + nums.get(1);
Which of the following is true about this code?
Java automatically converts
int to Integer (autoboxing) for add(), and Integer back to int (unboxing) for arithmetic. This allows seamless use of primitives with ArrayList.
What is printed by the following code segment?
String a = "test"; String b = a; a = a.toUpperCase(); System.out.println(a + " " + b);
Strings are immutable.
a.toUpperCase() creates a new String object "TEST" and assigns it to a. b still references the original "test". String methods never modify the original — they always return new objects.
What is printed by the following code segment?
int[] arr = {2, 4, 6};
int idx = 5;
if (idx < arr.length && arr[idx] > 0) {
System.out.print("A");
} else {
System.out.print("B");
}
Short-circuit evaluation prevents the error.
idx < arr.length → 5 < 3 → false. With &&, Java stops immediately — arr[idx] is never evaluated. The else branch executes and prints "B".
Which of the following expressions is equivalent to !(a > 0 && b <= 10)?
De Morgan's Law:
!(A && B) = !A || !B. Negate each part: !(a > 0) → a <= 0, and !(b <= 10) → b > 10. Flip && to ||. Option B uses && instead of ||. Option D negates incorrectly (> should become <=, not <).
What is the value of result after executing the following code segment?
int result = 0;
for (int j = 1; j <= 4; j++) {
for (int k = j; k <= 4; k++) {
result++;
}
}
The inner loop starts at
k = j. j=1: 4 iterations. j=2: 3 iterations. j=3: 2 iterations. j=4: 1 iteration. Total: 4 + 3 + 2 + 1 = 10.
What is the value of result after executing the following code segment?
String result = "";
for (int k = 0; k < 4; k++) {
if (k % 2 == 0) {
result += k;
} else {
result += "-";
}
}
k=0 (even → "0"), k=1 (odd → "-"), k=2 (even → "2"), k=3 (odd → "-"). Result: "0-2-". The loop runs 4 times (0 through 3), appending a digit or dash each iteration.
Consider the following code segment.
for (int k = 0; k < 20; k++) {
if (k % 4 == 0) {
System.out.print(k + " ");
}
}
Which of the following code segments produces the same output?
Original outputs: 0 4 8 12 16. Option A: starts at 0, increments by 4, stops before 20 → same output. Option B misses 0. Option C includes 20. Option D prints 4 8 12 16 20 (misses 0, includes 20).
Consider the boolean variable inRange that should be true when x is between 1 and 100 inclusive, and false otherwise. Which of the following correctly assigns inRange?
- I.
inRange = (x >= 1) && (x <= 100); - II.
inRange = !(x < 1) && !(x > 100); - III.
inRange = !(x < 1 || x > 100);
All three are logically equivalent. I is the direct range check. II negates each boundary:
!(x < 1) → x >= 1. III applies De Morgan's: !(A || B) = !A && !B, expanding to the same result as II.
What is printed by the following code segment?
int a = 1;
int b = 10;
while (a < b) {
a++;
b--;
}
System.out.println(a + " " + b);
Trace: (1,10) → (2,9) → (3,8) → (4,7) → (5,6) → (6,5). Now
a < b is 6 < 5 → false, loop exits. Both update in the same iteration, so they cross each other rather than meeting at 5,5.
The following method is intended to return the index of the last occurrence of target in arr, or -1 if not found. Which best describes the error?
public static int findLast(int[] arr, int target) {
for (int k = arr.length; k >= 0; k--) {
if (arr[k] == target) {
return k;
}
}
return -1;
}
Valid indices are 0 to
arr.length - 1. Starting at arr.length means the first access is arr[arr.length], which throws ArrayIndexOutOfBoundsException. Fix: int k = arr.length - 1. Right-to-left traversal is correct for finding the last occurrence.
Consider the following method intended to return the input string with all vowels removed.
public static String removeVowels(String str) {
String result = "";
for (int k = 0; k < str.length(); k++) {
String ch = str.substring(k, k + 1);
/* missing code */
}
return result;
}
Which of the following should replace /* missing code */?
We keep characters that are not vowels.
indexOf(ch) returns -1 when not found. Option A keeps vowels (wrong direction). Option C compares one character to a 5-character string — always unequal, keeps everything. Option D fails for 'a' since indexOf("a") == 0, and 0 > 0 is false, so 'a' is incorrectly kept.
What is printed by the following code segment?
for (int r = 1; r <= 3; r++) {
for (int c = r; c <= 3; c++) {
System.out.print("*");
}
System.out.println();
}
Inner loop starts at
c = r. r=1: c goes 1,2,3 (3 stars). r=2: c goes 2,3 (2 stars). r=3: c goes 3 (1 star). Decreasing triangle.
What is printed by the following code segment?
int x = 1;
for (int k = 0; k < 4; k++) {
x = x + x;
}
System.out.println(x);
x = x + x doubles x each pass. 1 → 2 → 4 → 8 → 16. After 4 iterations x = 24 = 16.
What happens when the following code segment is executed?
int n = 100;
while (n > 0) {
if (n % 2 == 0) {
n -= 3;
}
}
System.out.println(n);
n=100 (even) → n=97 (odd). Now
n % 2 == 0 is false, so the if-body never executes again — but n > 0 is still true. The loop runs forever with n stuck at 97.
Consider the following class.
public class Item {
private String name;
private double price;
public Item() {
name = "Unknown";
price = 0.0;
}
public Item(String n, double p) {
name = n;
price = p;
}
}
Which of the following declarations will compile without error?
- I.
Item a = new Item(); - II.
Item b = new Item("Widget", 9.99); - III.
Item c = new Item("Widget");
I matches the no-arg constructor. II matches the two-arg constructor
(String, double). III passes only one String — no constructor with that signature exists, so it does not compile.
Consider the following class.
public class Account {
private double balance;
public Account(double initial) {
balance = initial;
}
public double getBalance() {
return balance;
}
public void deposit(double amt) {
balance += amt;
}
}
The following code appears in a different class: Account acct = new Account(100.0);
Which of the following will cause a compile-time error?
balance is private and cannot be accessed directly outside the class. acct.balance will not compile. This is encapsulation: access is controlled through public methods only.
Consider the following two classes.
public class Point {
private int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public class Rectangle {
private Point corner;
private int width, height;
public Rectangle(Point p, int w, int h) {
corner = p; width = w; height = h;
}
public boolean contains(Point p) {
return p.getX() >= corner.getX() &&
p.getX() <= corner.getX() + width &&
p.getY() >= corner.getY() &&
p.getY() <= corner.getY() + height;
}
}
What is printed?
Point p1 = new Point(2, 3); Rectangle rect = new Rectangle(p1, 5, 4); Point p2 = new Point(7, 8); System.out.println(rect.contains(p2));
Rectangle: corner (2,3), width 5, height 4. x-range [2,7], y-range [3,7]. p2 = (7,8). Check: 72 ✓, 77 ✓, 83 ✓, but 87 is false. One false with
&& makes the whole expression false.
Consider the following class.
public class Counter {
private static int total = 0;
private int value;
public Counter(int v) {
value = v;
total += v;
}
public static int getTotal() {
return total;
}
public int getValue() {
return value;
}
}
What is printed by the following code segment?
Counter c1 = new Counter(5); Counter c2 = new Counter(3); Counter c3 = new Counter(7); System.out.println(Counter.getTotal() + " " + c2.getValue());
total is static — shared across all objects. Constructors add: 0+5=5, 5+3=8, 8+7=15. getTotal() = 15. c2.getValue() = 3 (c2's own instance variable). Static = shared; instance = per-object.
this
Consider the following class.
public class Pair {
private int x;
private int y;
public Pair(int x, int y) {
x = x;
y = y;
}
public int getSum() {
return x + y;
}
}
What is printed by the following code segment?
Pair p = new Pair(3, 7); System.out.println(p.getSum());
The constructor uses
x = x and y = y, assigning each parameter to itself — the instance variables are never set. They retain their default value of 0. Fix: use this.x = x; this.y = y;. This is a common bug when parameter names shadow instance variable names.
Consider the following class.
public class Tracker {
private int val;
public Tracker(int initial) {
val = initial;
}
public void step() {
if (val % 2 == 0) {
val = val / 2;
}
else { val = val * 3 + 1; }
}
public int getVal() {
return val;
}
}
What is printed by the following code segment?
Tracker t = new Tracker(6); t.step(); t.step(); t.step(); System.out.println(t.getVal());
val=6 (even) → 3. val=3 (odd) → 3×3+1=10. val=10 (even) → 5.
getVal() returns 5. (This is the Collatz sequence.)
Consider the following class.
public class Temperature {
private double degrees;
public Temperature(double d) {
degrees = d;
}
public double getDegrees() {
return degrees;
}
public double toFahrenheit() {
degrees = degrees * 9.0 / 5.0 + 32;
return degrees;
}
}
A programmer stores a Celsius value and calls toFahrenheit() multiple times. Which best describes the problem?
toFahrenheit() overwrites degrees with the converted value. A second call applies the formula to an already-converted Fahrenheit number, giving a wrong result. Fix: use a local variable — return degrees * 9.0 / 5.0 + 32; — without modifying the instance variable.
What is printed by the following code segment?
int[] arr = {5, 3, 8, 1, 9, 2};
int x = arr[0];
for (int k = 1; k < arr.length; k++) {
if (arr[k] < x) {
x = arr[k];
}
}
System.out.println(x);
Find-minimum: x starts at arr[0]=5. 3<5 → x=3. 1<3 → x=1. No subsequent value beats 1. Final: 1.
Consider the following method.
public static int mystery(int[] arr) {
int x = 0;
for (int k = 0; k < arr.length; k = k + 2) {
x = x + arr[k];
}
return x;
}
Given int[] vals = {3, 6, 1, 0, 1, 4, 2};, what value is returned by mystery(vals)?
Visits indices 0, 2, 4, 6: values 3, 1, 1, 2. Sum = 7. The method sums elements at even-numbered indices only.
Consider the following code segment.
int[][] grid = {{1, 2, 3},
{4, 5, 6},
{7, 8, 9}};
int result = 0;
for (int r = 0; r < grid.length; r++) {
result += grid[r][r];
}
System.out.println(result);
grid[r][r] accesses the main diagonal: grid[0][0]=1, grid[1][1]=5, grid[2][2]=9. Sum = 15.
The following method is intended to return a new array containing only the positive values from data, in order.
public static int[] getPositives(int[] data) {
int count = 0;
for (int val : data) {
if (val > 0) {
count++;
}
}
int[] result = new int[count];
int idx = 0;
for (int val : data) {
if (val > 0) {
/* missing code */
}
}
return result;
}
Which of the following should replace /* missing code */?
Place val at
result[idx] then increment idx. Option B uses count which already equals the full size — immediate out-of-bounds. Option C never increments, overwriting index 0 every time. Option D treats val as an index rather than a value.
Consider a method hasDuplicates that returns true if a sorted array contains any duplicate adjacent values. Which of the following implementations work correctly?
-
I.
for (int k = 0; k < arr.length - 1; k++) { if (arr[k] == arr[k+1]) { return true; } } return false; -
II.
for (int k = 1; k < arr.length; k++) { if (arr[k] == arr[k-1]) { return true; } } return false; -
III.
for (int k = 0; k < arr.length; k++) { if (arr[k] == arr[k+1]) { return true; } } return false;
I safely accesses
arr[k+1] by stopping at length - 1. II safely accesses arr[k-1] by starting at 1. III uses k < arr.length starting at 0 with arr[k+1] — when k = length-1, it accesses arr[length] → ArrayIndexOutOfBoundsException.
Consider the following code segment.
int[][] mat = {{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}};
int result = 0;
for (int c = 0; c < mat[0].length; c++) {
result += mat[mat.length - 1][c];
}
System.out.println(result);
mat.length - 1 = 2 (last row index). mat[0].length = 4 (columns). Sums row 2: 9+10+11+12 = 42. Key: mat.length = rows; mat[r].length = columns.
Consider the following code segment.
ArrayListwords = new ArrayList (); words.add("cat"); words.add("dog"); words.add("bird"); words.add("cat"); words.add("fish"); for (int k = words.size() - 1; k >= 0; k--) { if (words.get(k).equals("cat")) { words.remove(k); } } System.out.println(words);
Backward traversal safely removes all matches. Both "cat" entries are removed. Result: [dog, bird, fish]. Forward traversal would skip elements when indices shift after each removal.
Consider the following method.
public static void mystery(int[] arr) {
for (int k = 0; k < arr.length - 1; k++) {
arr[k + 1] = arr[k] + arr[k + 1];
}
}
Given int[] vals = {5, 2, 1, 3, 8};, what are the contents of vals after the call?
Each step uses the already-modified value. k=0: arr[1]=5+2=7. k=1: arr[2]=7+1=8. k=2: arr[3]=8+3=11. k=3: arr[4]=11+8=19. Running cumulative sum. Option B incorrectly uses the original unmodified values.
Consider the following classes.
public class Student {
private String name;
private int score;
public Student(String n, int s) {
name = n; score = s;
}
public int getScore() {
return score;
}
public String getName() {
return name;
}
}
public class Gradebook {
private ArrayList roster;
public Gradebook() {
roster = new ArrayList();
}
public void addStudent(Student s) {
roster.add(s);
}
public String topStudent() {
Student best = roster.get(0);
for (Student s : roster) {
if (s.getScore() {
> best.getScore()) { best = s;
} }
}
return best.getName();
}
}
What is printed?
Gradebook gb = new Gradebook();
gb.addStudent(new Student("Jo", 88));
gb.addStudent(new Student("Al", 95));
gb.addStudent(new Student("Mo", 91));
System.out.println(gb.topStudent());
Find-max: best starts as Jo (88). Al has 95>88 → best=Al. Mo has 91, not >95. Returns Al. Option D is a trap — return type is String (name), not int (score).
Consider the following binary search method and sorted array.
public static int bSearch(int[] arr, int target) {
int lo = 0, hi = arr.length - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (arr[mid] == target) {
return mid;
}
else if (arr[mid] < target) {
lo = mid + 1;
}
else hi = mid - 1;
}
return -1;
}
int[] data = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91};
How many iterations of the while loop execute during bSearch(data, 23)?
Iter 1: lo=0, hi=9, mid=4, arr[4]=16<23 → lo=5. Iter 2: lo=5, hi=9, mid=7, arr[7]=56>23 → hi=6. Iter 3: lo=5, hi=6, mid=5, arr[5]=23 → found! Returns 5 after 3 iterations.
The following method is intended to return the sum of all elements in a 2D array.
public static int totalSum(int[][] mat) {
int total = 0;
for (int r = 0; r < mat.length; r++) {
for (int c = 0; c < /* missing code */; c++) {
total += mat[r][c];
}
}
return total;
}
Which of the following should replace /* missing code */?
The inner loop iterates over columns of the current row
r. mat[r].length gives the number of columns in that row. Option A gives the number of rows. Option B uses c (column counter) as a row index — incorrect. Rule: mat.length = rows; mat[r].length = columns in row r.
An ArrayList named nums contains [3, 7, 2, 9, 4]. Which of the following code segments will correctly print all elements?
- I.
for (int val : nums) System.out.print(val + " "); - II.
for (int k = 0; k < nums.size(); k++) System.out.print(nums.get(k) + " "); - III.
for (int k = 0; k < nums.size(); k++) System.out.print(nums[k] + " ");
I uses enhanced for with unboxing — correct. II uses
.get(k) — the proper ArrayList accessor. III uses nums[k] bracket notation, which only works for arrays, not ArrayLists — compile-time error. Remember: arrays use arr[k]; ArrayLists use list.get(k).
Consider the following code segment.
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
for (int k = 3; k < arr.length - 1; k++) {
arr[k] = arr[k + 1];
}
System.out.println(Arrays.toString(arr));
What is printed?
Loop shifts left starting at index 3: arr[3]=5, arr[4]=6, arr[5]=7, arr[6]=8. The last element (8) is never overwritten, leaving a duplicate at the end. Arrays have fixed size, so length stays 8. Option B is wrong — the array still has 8 elements.
Get in Touch
Whether you're a student, parent, or teacher — I'd love to hear from you.
Just want free AP CS resources?
Enter your email below and check the subscribe box — no message needed. Students get daily practice questions and study tips. Teachers get curriculum resources and teaching strategies.
Message Sent!
Thanks for reaching out. I'll get back to you within 24 hours.
tanner@apcsexamprep.com
Courses
AP CSA, CSP, & Cybersecurity
Response Time
Within 24 hours
Prefer email? Reach me directly at tanner@apcsexamprep.com