AP CSA Practice Exam Section 2 Free Response

AP Computer Science A — Practice Exam

Section II: Free Response

4 Questions • 90 Minutes • 50% of Exam Score

4 Questions
36 Total Points
90 Minutes
Directions
  • Write your answers clearly in the scratch area below each question. Only the code you write will be graded — not your scratch work.
  • Write your solution using Java. You may use any standard Java library methods and constructors that are part of the Java Quick Reference.
  • Show all your work. Partial credit is awarded on every question.
  • Assume that import statements and class declarations are provided where needed.
  • Click Show Solution only after you have attempted each part completely.
Your Running Score
0 / 36 pts
Questions Viewed
0 / 4
1
LicensePlate — Methods and Control Structures
Unit 1 & 2 String Methods Loops Conditionals
9 pts

A state motor vehicle department uses a software system to validate license plate codes. A LicensePlate class stores a single plate code as a String. A partial implementation is shown below.

A plate code may contain any combination of letters and digits. Assume plateCode is never null and always has at least one character.

public class LicensePlate { private String plateCode; public LicensePlate(String code) { plateCode = code; } /** Returns the number of digit characters ('0'-'9') in plateCode. * For example, if plateCode is "ABC123", returns 3. * If plateCode is "VANITY", returns 0. */ public int countDigits() { // Part (a) - to be implemented } /** Returns true if this plate is considered valid; false otherwise. * A plate is valid when ALL of the following are true: * - its total length is between 5 and 8 characters, inclusive * - it contains exactly 3 digit characters * - it does NOT begin with a digit character * You must call countDigits in writing this method. */ public boolean isValid() { // Part (b) - to be implemented } // Other methods not shown }
Part (a): countDigits 4 pts

Write the countDigits method. This method examines each character in plateCode and returns the total number of digit characters ('0' through '9'). Non-digit characters are ignored.

For example:

new LicensePlate("ABC123").countDigits() // returns 3 new LicensePlate("7XY99Z").countDigits() // returns 3 new LicensePlate("VANITY").countDigits() // returns 0

You may find Character.isDigit(char c) useful. It returns true if c is a digit character.

Your Answer — Part (a)

Part (b): isValid 5 pts

Write the isValid method. This method returns true if the plate meets all three of the following rules; otherwise it returns false:

  • The total length of plateCode is between 5 and 8 characters inclusive.
  • The plate contains exactly 3 digit characters.
  • The first character is not a digit.

You must call countDigits in your solution.

For example:

new LicensePlate("ABC123").isValid() // true (len=6, 3 digits, starts with letter) new LicensePlate("1ABC23").isValid() // false (starts with digit) new LicensePlate("AB12").isValid() // false (len=4, too short) new LicensePlate("ABCD1234").isValid() // false (only 4 digits, not exactly 3)
Your Answer — Part (b)
Complete Solution — Question 1
Part (a): countDigits
public int countDigits() { int count = 0; for (int i = 0; i < plateCode.length(); i++) { if (Character.isDigit(plateCode.charAt(i))) { count++; } } return count; }
Part (b): isValid
public boolean isValid() { int len = plateCode.length(); boolean rightLength = len >= 5 && len <= 8; boolean threeDigits = countDigits() == 3; boolean letterFirst = !Character.isDigit(plateCode.charAt(0)); return rightLength && threeDigits && letterFirst; }

Calling countDigits() is required. A solution that re-implements the digit-counting logic without calling countDigits() does not earn the method-call point.

Scoring Rubric (9 points)
+1
Part (a): Iterates over every character in plateCode using charAt(i) and length() without going out of bounds.
+2
Part (a): Correctly identifies digit characters (using Character.isDigit() or an equivalent comparison) and increments a counter for each one found.
+1
Part (a): Returns the correct count.
+1
Part (b): Calls countDigits() and uses its return value to check that exactly 3 digits are present.
+1
Part (b): Correctly checks that length is between 5 and 8 inclusive (both bounds required).
+2
Part (b): Correctly checks that the first character (charAt(0)) is not a digit using negation.
+1
Part (b): Returns true only when all three conditions are satisfied; returns false in all other cases.
Total: 9 points
Common Mistakes

Off-by-one in loop: Writing i <= plateCode.length() instead of i < plateCode.length() causes a StringIndexOutOfBoundsException on the last iteration.
Not calling countDigits in Part (b): Re-writing the counting logic from scratch instead of calling countDigits() loses the required method-call point and often introduces new errors.
Missing the “not starts with digit” check: Students often check length and digit count but forget the third condition. Read every precondition carefully.

Key Insight

FRQ 1 almost always requires Part (b) to call the method written in Part (a). Even if your Part (a) solution is slightly wrong, you can still earn full credit on Part (b) if your logic correctly uses the return value of countDigits(). Write Part (b) as if Part (a) works perfectly.

2
ShelterAnimal — Class Writing
Unit 3 Class Writing Encapsulation Constructors
9 pts

A local animal shelter wants to track the animals in its care. Each animal has a name and a species. When an animal first enters the shelter, it has not yet been vaccinated. The shelter's system needs to be able to mark an animal as vaccinated and check its vaccination status at any time.

The shelter charges an adoption fee that depends on the species. Dogs cost $150, cats cost $100, and all other species cost $75. The fee never changes once set.

Write the complete ShelterAnimal class described below. Your class will be graded on correct encapsulation, constructor behavior, and method implementations.

The ShelterAnimal class must contain:

  • Appropriate private instance variables to store the animal's name, species, vaccination status, and adoption fee.
  • A constructor that takes the animal's name and species as parameters. The constructor must set the vaccination status to not vaccinated and compute the adoption fee based on the species rules described above.
  • A vaccinate method that marks the animal as vaccinated.
  • An isVaccinated method that returns whether the animal has been vaccinated.
  • A getAdoptionFee method that returns the adoption fee.
  • A toString method that returns a String with the animal's name, species, and vaccination status in a readable format of your choosing.

The following code should work correctly with your class:

ShelterAnimal a = new ShelterAnimal("Rex", "dog"); System.out.println(a.isVaccinated()); // false System.out.println(a.getAdoptionFee()); // 150 a.vaccinate(); System.out.println(a.isVaccinated()); // true ShelterAnimal b = new ShelterAnimal("Nibbles", "rabbit"); System.out.println(b.getAdoptionFee()); // 75
Complete ShelterAnimal Class 9 pts
Your Answer
Complete Solution — Question 2

Note: variable names and exact String format in toString are your design choices — the rubric scores behavior, not names.

public class ShelterAnimal { private String name; private String species; private boolean vaccinated; private int adoptionFee; public ShelterAnimal(String animalName, String animalSpecies) { name = animalName; species = animalSpecies; vaccinated = false; // not vaccinated on arrival if (species.equals("dog")) { adoptionFee = 150; } else if (species.equals("cat")) { adoptionFee = 100; } else { adoptionFee = 75; } } public void vaccinate() { vaccinated = true; } public boolean isVaccinated() { return vaccinated; } public int getAdoptionFee() { return adoptionFee; } public String toString() { return name + " (" + species + ") - Vaccinated: " + vaccinated; } }
Scoring Rubric (9 points)
+2
Declares appropriate private instance variables with correct types for name (String), species (String), vaccination status (boolean), and fee (int or double). Variable names are the student's choice.
+1
Constructor correctly initializes name and species from parameters.
+1
Constructor initializes vaccination status to false (not vaccinated) without being told to do so by a parameter.
+2
Constructor correctly computes the adoption fee using an if/else chain: dog = 150, cat = 100, all others = 75. All three cases handled correctly.
+1
vaccinate sets vaccination status to true; isVaccinated returns the current vaccination status.
+1
getAdoptionFee returns the fee that was set in the constructor.
+1
toString returns a String that includes the name, species, and vaccination status in any readable format.
Total: 9 points
Common Mistakes

Computing fee in getAdoptionFee instead of the constructor: The problem says the fee is set when the animal enters the system (i.e., in the constructor). Recomputing it in the accessor works but misses the design intent — and if a grader is strict, it may cost a point.
Using == to compare Strings: species == "dog" compares object references, not content. Always use .equals() for String comparison in Java.
Making vaccinate() return a value: The method is described as updating state only — the return type should be void.

Key Insight

On the real AP exam, FRQ 2 never tells you what to name your variables. You are graded on whether your class behaves correctly given the described scenario — not on specific variable names. Focus on: private fields, correct constructor logic, and methods that do exactly what the description says.

3
WordList — ArrayList Manipulation
Unit 4 ArrayList Remove While Iterating String Methods
9 pts

A WordList class manages a list of words. A partial implementation is shown below.

Assume import java.util.ArrayList; is provided. The words list is never null but may be empty.

public class WordList { private ArrayList<String> words; public WordList(ArrayList<String> wordList) { words = wordList; } /** Removes from words all strings with length strictly less than minLen. * @param minLen the minimum length a word must have to stay in the list */ public void removeShorterThan(int minLen) { // Part (a) - to be implemented } /** Returns the number of unique (case-sensitive) strings in words. * For example, if words = ["apple", "Apple", "apple", "fig"], * this method returns 3. */ public int getUniqueCount() { // Part (b) - to be implemented } // Other methods not shown }
Part (a): removeShorterThan 4 pts

Write the removeShorterThan method. This method removes every String from words whose length is strictly less than minLen.

For example, if words contains ["cat", "elephant", "ox", "dog", "rhinoceros"] and minLen is 4, then after calling removeShorterThan(4), words contains ["elephant", "rhinoceros"].

Your Answer — Part (a)

Part (b): getUniqueCount 5 pts

Write the getUniqueCount method. This method returns the number of distinct (case-sensitive) strings in words. Duplicates count only once.

For example, if words contains ["apple", "Apple", "apple", "fig", "fig"], this method returns 3 (the distinct strings are "apple", "Apple", and "fig").

You may use an ArrayList as a helper structure. You may not modify the words instance variable.

Your Answer — Part (b)
Complete Solution — Question 3
Part (a): removeShorterThan
public void removeShorterThan(int minLen) { int i = 0; while (i < words.size()) { if (words.get(i).length() < minLen) { words.remove(i); // do NOT increment i after a remove } else { i++; // only advance if nothing was removed } } }

An enhanced-for loop cannot be used to remove elements from an ArrayList during iteration. The index-based while loop is the standard correct approach.

Part (b): getUniqueCount
public int getUniqueCount() { ArrayList<String> seen = new ArrayList<String>(); for (String word : words) { if (!seen.contains(word)) { seen.add(word); } } return seen.size(); }
Scoring Rubric (9 points)
+1
Part (a): Accesses words elements using .get() and checks .length() correctly against minLen.
+2
Part (a): Uses a loop that correctly handles ArrayList shrinkage during removal — does not increment the index after a remove() call (index-based while loop or reverse for loop).
+1
Part (a): Removes all and only the elements whose length is strictly less than minLen (uses <, not <=).
+1
Part (b): Creates a helper structure (ArrayList or similar) to track seen words; does not modify words.
+2
Part (b): Traverses words and adds each word to the seen list only if it is not already present (uses .contains() or equivalent check; case-sensitive).
+1
Part (b): Returns the correct count of unique elements.
+1
Part (b): Handles the empty list case (returns 0) without error.
Total: 9 points
Common Mistakes

Enhanced-for during removal: Using a for-each loop and calling words.remove() inside it throws a ConcurrentModificationException. Always use an index-based while loop.
Incrementing after every remove: Writing i++ after every removal skips elements because the ArrayList shifts left when an item is removed.
Modifying words in Part (b): The precondition says do not modify words. Use a separate helper list.

Key Insight

ArrayList removal while iterating is the single most commonly tested ArrayList concept on the AP exam. The while-loop-with-conditional-increment pattern is the expected answer and must be memorized.

4
SurveyGrid — 2D Arrays
Unit 4 2D Arrays Nested Loops Row/Column Traversal
9 pts

A research team collects survey responses on a 1–5 scale. The responses are stored in a 2D array where each row represents one participant and each column represents one survey question. A partial implementation is shown below.

Assume data is always a rectangular 2D array with at least one row and one column. All values in data are integers between 1 and 5 inclusive.

public class SurveyGrid { private int[][] data; // rows = participants, cols = questions public SurveyGrid(int[][] responses) { data = responses; } /** Returns the sum of all values in the given column. * @param col the column index to sum * Precondition: 0 <= col < data[0].length */ public int getColumnSum(int col) { // Part (a) - to be implemented } /** Returns true if ANY row in data is strictly increasing * (each element greater than the element before it). * A row with only one element is considered strictly increasing. * Returns false if no such row exists. */ public boolean hasIncreasingRow() { // Part (b) - to be implemented } // Other methods not shown }
Part (a): getColumnSum 3 pts

Write the getColumnSum method. This method returns the sum of all values in the column at index col.

For example, if data is {{3, 1, 4}, {1, 5, 9}, {2, 6, 5}} and col is 1, the method returns 12 (the values 1, 5, and 6 in column 1).

Your Answer — Part (a)

Part (b): hasIncreasingRow 6 pts

Write the hasIncreasingRow method. A row is strictly increasing if each element is greater than the element immediately before it. The method returns true if at least one row is strictly increasing; otherwise false.

For example, given:

data = {{ 2, 3, 5 }, // row 0: strictly increasing (2 < 3 < 5) { 4, 4, 6 }, // row 1: NOT strictly increasing (4 == 4) { 7, 2, 9 }} // row 2: NOT strictly increasing (7 > 2)

hasIncreasingRow() returns true because row 0 is strictly increasing.

If data is {{5, 3, 1}, {4, 4, 4}}, the method returns false.

Your Answer — Part (b)
Complete Solution — Question 4
Part (a): getColumnSum
public int getColumnSum(int col) { int sum = 0; for (int row = 0; row < data.length; row++) { sum += data[row][col]; } return sum; }
Part (b): hasIncreasingRow
public boolean hasIncreasingRow() { for (int row = 0; row < data.length; row++) { boolean increasing = true; // assume this row is increasing for (int col = 1; col < data[row].length; col++) { if (data[row][col] <= data[row][col - 1]) { increasing = false; break; // no need to check the rest of this row } } if (increasing) { return true; // found at least one increasing row } } return false; }
Scoring Rubric (9 points)
+1
Part (a): Correctly iterates over all rows using data.length (number of rows, not columns).
+1
Part (a): Correctly accesses data[row][col] with the provided col parameter held constant.
+1
Part (a): Returns the correct column sum.
+2
Part (b): Outer loop correctly traverses each row; inner loop correctly starts at column index 1 and compares data[row][col] to data[row][col-1].
+1
Part (b): Uses a boolean flag (or equivalent) correctly initialized to true per row and set to false when a non-increasing adjacent pair is found.
+1
Part (b): Correctly uses <= (not just <) to detect both equal and decreasing pairs as violations of strict increase.
+2
Part (b): Returns true immediately upon finding a strictly increasing row; returns false only after all rows are checked and none qualify.
Total: 9 points
Common Mistakes

Swapping row/col in 2D access: Writing data[col][row] instead of data[row][col] causes an ArrayIndexOutOfBoundsException on non-square arrays.
Using < instead of <=: The condition must be data[row][col] <= data[row][col-1] to correctly reject equal adjacent elements (not strictly increasing).
Inner loop starting at 0: Starting the inner column loop at index 0 causes a comparison to data[row][-1], which throws an exception. Start at index 1.

Key Insight

The boolean flag pattern (assume true, set false when condition fails) is the cleanest way to check a property across an entire row. Early returns in the outer loop are efficient and encouraged — return immediately when the answer is found.

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.

Typically responds within 24 hours

Message Sent!

Thanks for reaching out. I'll get back to you within 24 hours.

🏫 Welcome, fellow educator!

I offer curriculum resources, practice materials, and study guides designed for AP CS teachers. Let me know what you're looking for — whether it's classroom materials, a guest speaker, or Teachers Pay Teachers resources.

Email

[email protected]

📚

Courses

AP CSA, CSP, & Cybersecurity

Response Time

Within 24 hours

Prefer email? Reach me directly at [email protected]