Mr.Mou @ ShiShi AP Center

CSA 2025年真考 MCQ 重点题型和解析

Section 1 — 2025 AP CSA MCQ Questions


Q1

Consider the following method, which is intended to return a copy of phrase with word moved to the end of phrase.

For example:

phrase = "uncomfortable"
word = "comfort"

The method should return:

"unablecomfort"

Assume that word is found exactly once in phrase.

public static String moveIt(String phrase, String word)
{
    int index = phrase.indexOf(word);
    int len = word.length();
    return /* missing expression */;
}

Which expression can replace /* missing expression */ so that the method works as intended?

(A)

phrase.substring(0, index)
+ phrase.substring(index + len) + word

(B)

phrase.substring(0, index)
+ phrase.substring(index + len - 1) + word

(C)

phrase.substring(0, index - 1)
+ phrase.substring(index + len) + word

(D)

phrase.substring(0, index - 1)
+ phrase.substring(index + len - 1) + word

(E)

phrase.substring(0, index + 1)
+ phrase.substring(index + len) + word

Q2

The code segment is intended to print the double average of the int variables a and b.

Example:

a = 3;
b = 4;

The code should print:

3.5

Code:

int a = /* initial value not shown */;
int b = /* initial value not shown */;
double avg = /* missing expression */;
System.out.println(avg);

Which expression works?

(A)

(a + b) / 2

(B)

(double) a + b / 2

(C)

(double) (a + b) / 2

(D)

(double) (a + b / 2)

(E)

(double) ((a + b) / 2)

Q3

The following method is intended to reverse the digits of an integer.

Example:

reverse(1234) → 4321

Given code:

public static int reverse(int num)
{
    int retVal = 0;     // Line 7
    int digit = 0;

    while (num > 0)     // Line 10
    {
        digit = num % 10;                  // Line 12
        retVal += (retVal * 10) + digit;   // Line 13
        num = num / 10;                    // Line 14
    }

    return retVal;
}

Which line contains the error?


Q4

Consider the following method, which is intended to return the median of three integer values. The median is the middle value when the three integers are sorted.

For example:

findMedian(1, 2, 3)

should return:

2

and:

findMedian(3, 4, 3)

should return:

3

The method does not always work as intended.

public static int findMedian(int firstVal, int secVal, int thirdVal)
{
    int largestSoFar;

    if (secVal > firstVal)
    {
        largestSoFar = secVal;
    }
    else
    {
        largestSoFar = firstVal;
    }

    if (thirdVal > largestSoFar)
    {
        return largestSoFar;
    }
    else
    {
        return thirdVal;
    }
}

Which of the following method calls can be used to show that findMedian does not always work as intended?

(A)

findMedian(3, 2, 1)

(B)

findMedian(3, 8, 7)

(C)

findMedian(5, 10, 16)

(D)

findMedian(8, 3, 3)

(E)

findMedian(10, 5, 10)

Q5

Consider the following method.

public static String enigma(String first, String second)
{
    int len;

    if (first.length() <= second.length())
    {
        len = first.length();
    }
    else
    {
        len = second.length();
    }

    String result = "";

    for (int i = 0; i < len; i++)
    {
        result += first.substring(i, i + 1)
               + second.substring(i, i + 1);
    }

    return result;
}

What is returned as a result of the call?

enigma("abcde", "WXYZ")

(A) "abcdWXYZ" (B) "aWbXcYdZ" (C) "aWbXcYdZe" (D) "WaXbYcZd" (E) "WXYZabcd"


Q6

Consider the following code segment.

int count = 0;

for (int first = 10; first < 25; first++)
{
    for (int second = 25; second > 10; second--)   // Line 4
    {
        count++;
    }
}

The condition in line 4 is changed from:

second > 10

to:

second > first

What is the effect of this modification?

(A) Infinite loop (B) Final value of count = 0 (C) Smaller final value of count (D) Larger final value of count (E) No change


Q7

Consider the following class declaration.

public class Road
{
    private String name;

    public Road()
    {
        name = "First";
    }

    public Road(String n)
    {
        name = n;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String n)
    {
        name = n;
    }
}

Code segment:

Road a = new Road();
a.setName("Pine");

Road b = new Road("Pine");

Road c = a;
Road d = c;

d.setName("Maple");

Which Boolean expression evaluates to false after the code segment has been executed?

(A)

a.getName().equals("Maple")

(B)

a.getName().equals(c.getName())

(C)

a == b

(D)

a == c

(E)

a == d

Q8

Consider the following recursive method.

public static int mystery(int n)
{
    if (n < 2)
    {
        return 2 * n;
    }
    else
    {
        return mystery(n - 1) + mystery(n - 2);
    }
}

What value is returned as a result of the call?

mystery(4)

(A) 0 (B) 2 (C) 4 (D) 6 (E) 10


Q9

Consider the following class declaration.

public class Arithmetic
{
    private int value;

    public Arithmetic(int v)
    {
        value = v;
    }

    public void update()
    {
        value = value * value;
    }

    public void update(int num)
    {
        value = value + num;
    }

    public void update(int num1, int num2)
    {
        value = value * num1 + num2;
    }

    public int getValue()
    {
        return value;
    }
}

Code segment:

Arithmetic alpha = new Arithmetic(3);
Arithmetic beta = new Arithmetic(2);

alpha.update(1);
alpha.update(2, 3);
beta.update();

System.out.println(alpha.getValue() + beta.getValue());

What is printed when the code segment is executed?

(A) 5 (B) 11 (C) 15 (D) 18 (E) 81


Q10

Consider the following code segment.

for (int j = 0; j < 3; j++)
{
    arr[j][j] = j;

    for (int k = 0; k < j; k++)
    {
        arr[j][k] = j - k;
        arr[k][j] = k - j;
    }
}

arr is a 3×3 integer array.

What are the contents of arr after execution?

(A)

{ { 0, -1, -2 },
  { 1,  0, -1 },
  { 2,  1,  0 } }

(B)

{ { 0, -1, -2 },
  { 1,  1, -1 },
  { 2,  1,  2 } }

(C)

{ {  0,  1,  2 },
  { -1,  0,  1 },
  { -2, -1,  0 } }

(D)

{ {  0,  1,  2 },
  { -1,  1,  1 },
  { -2,  2,  2 } }

(E)

{ { 0, 1, 2 },
  { 1, 1, 1 },
  { 2, 1, 2 } }

Q11

Questions Q11 and Q12 refer to the following method.

Consider the reorder method, which rearranges the elements of its ArrayList<Integer> parameter so that elements with odd values appear before all other elements.

For example, if reorder is called with an ArrayList containing:

[2, 3, 5, 8, 4, 1]

then the ArrayList would contain:

[3, 5, 1, 8, 4, 2]

after the method has been executed.

/** Precondition: data contains at least two elements and all elements
 *                are greater than or equal to 0.
 */
public static void reorder(ArrayList<Integer> data)
{
    for (int i = 0; i < data.size(); i++)
    {
        if (data.get(i) % 2 == 0)          // Line 8
        {
            int index = i + 1;

            while (index < data.size() &&
                   data.get(index) % 2 == 0)   // Line 12
            {
                index++;
            }

            if (index < data.size())
            {
                int temp = data.get(i);    // Line 19
                data.set(i, data.get(index));
                data.set(index, temp);
            }
        }
    }
}

Consider a call to reorder with an ArrayList containing the values:

[2, 6, 7, 4, 1, 8]

How many times will the statement in line 19 be executed as a result of the method call?

(A) 1 time (B) 2 times (C) 3 times (D) 4 times (E) 5 times


Q12

Consider the following modifications to the reorder method from Q11.

In line 8, the expression:

data.get(i) % 2 == 0

is changed to:

data.get(i) / 2 == 0

In line 12, the expression:

data.get(index) % 2 == 0

is changed to:

data.get(index) / 2 == 0

The original reorder method moved elements with odd values to appear before all other elements.

Which of the following best explains how making the given changes will affect the behavior of the reorder method?

(A) The modified method will rearrange the elements of data from largest to smallest.

(B) The modified method will rearrange the elements of data from smallest to largest.

(C) The modified method will rearrange the elements of data so that elements with a value equal to 0 or 1 appear after all other elements.

(D) The modified method will rearrange the elements of data so that elements with a value equal to 0 or 1 appear before all other elements.

(E) The modified method will rearrange the elements of data so that elements with even values appear before all other elements.


Q13

Consider the following class declaration. The variable count is intended to store the total number of social media posts made by all SocialMediaAccount objects. The declaration of count is missing.

public class SocialMediaAccount
{
    /* The total number of posts made by all SocialMediaAccount objects */
    /* missing declaration */

    public void post()
    {
        count++;
    }

    /* There may be instance variables, constructors,
       and methods that are not shown. */
}

Consider the following code segment, which appears in a class other than SocialMediaAccount. Assume that no other SocialMediaAccount objects have been created.

SocialMediaAccount kevin = new SocialMediaAccount();
SocialMediaAccount briana = new SocialMediaAccount();

kevin.post();
briana.post();
briana.post();

The value of count should be 3 after the code segment was executed because there were a total of three posts.

Which of the following can replace /* missing declaration */ so that the SocialMediaAccount class works as intended?

(A)

private final int count = 0;

(B)

private int count = 0;

(C)

private static count = 0;

(D)

private static final int count = 0;

(E)

private static int count = 0;

Q14

Consider the following recursive method, which is intended to compute the product of the positive integers from 1 to n.

For example, factorial(4) should return the result of the computation:

4 * 3 * 2 * 1

which is 24.

The method does not work as intended.

/** Precondition: n >= 1 */
public static int factorial(int n)
{
    if (n == 1)
    {
        return 1;
    }

    return n * factorial(n);
}

Which of the following best describes the error in the method?

(A) The method does not contain an appropriate base case.

(B) The recursive call to the method does not allow progress toward the base case.

(C) The recursive call should appear in an else statement.

(D) The method returns an incorrect value when n has the value 1.

(E) The result of the recursive call must be assigned to a variable before being used.


Q15

Consider the following code segment.

boolean a = true;
boolean b = false;
boolean c = false;

boolean b1 = a && !(b || c);
boolean b2 = !a || b && c;
boolean b3 = a && !b || !c;

Which of the following Boolean expressions evaluates to true after the execution of the code segment?

(A)

b1 == b2

(B)

b1 && b2

(C)

b1 && b3

(D)

b2 && b3

(E)

(b1 != b2) && (b1 != b3)

Q16

Consider the following code segment.

for (int j = 1; j < 7; j += 2)
{
    for (int k = j; k > 0; k--)
    {
        System.out.println("A");    // Line 5
    }
}

How many times does the code segment execute line 5?

(A) 4 times (B) 9 times (C) 16 times (D) 21 times (E) 28 times


Q17

Consider the following code segment.

double[] values = {6.5, 2.0, 3.0, 2.5, 5.0, 3.0, 7.0};
int limit = values.length / 2;

for (int k = 0; k < limit; k++)
{
    int temp1 = values.length - k - 1;
    double scratch = values[k];
    values[k] = values[temp1];
    values[temp1] = scratch;
}

for (double v : values)
{
    System.out.print(v + " ");
}

What is printed as a result of executing the code segment?

(A) 3.0 2.0 6.5 2.5 7.0 3.0 5.0

(B) 5.0 3.0 7.0 2.5 6.5 2.0 3.0

(C) 6.5 2.0 3.0 2.5 5.0 3.0 7.0

(D) 7.0 3.0 3.0 2.5 5.0 2.0 6.5

(E) 7.0 3.0 5.0 2.5 3.0 2.0 6.5


Q18

Consider the Room class, which contains two constructors.

public class Room
{
    public Room(String description)
    {
        /* implementation not shown */
    }

    public Room(String description, int numDoors)
    {
        /* implementation not shown */
    }

    // There are no other constructors.
}

Which, if any, of the following statements, appearing in a class other than Room, correctly declares and creates a Room object?

(A)

Room r1 = new Room(2);

(B)

Room r2 = new Room("kitchen", 2);

(C)

Room r3 = new Room(1, "bedroom");

(D)

Room r4 = new Room();

(E) A Room object cannot be created. The Room class will not compile because it has two constructors with the same name.


Q19

Consider the following class definitions.

public class Program
{
    private String name;

    public Program()
    {
        /* implementation not shown */
    }

    public Program(String n)
    {
        name = n;
    }

    public void setName(String n)
    {
        name = n;
    }
}
public class Computer
{
    public Computer()
    {
        /* implementation not shown */
    }

    public void run(Program p)
    {
        /* implementation not shown */
    }
}

Which of the following code segments, appearing in a class other than Program or Computer, will compile without error?

(A)

Program p = new Program("blocks");
Computer.run();

(B)

Program p = new Program("blocks");
Computer.run(p);

(C)

Computer c = new Computer();
Program p = new Program("blocks");
p.run();

(D)

Computer c = new Computer();
Program p = new Program("blocks");
c.run(p);

(E)

Computer c = new Computer();
Program p = new Program();
p.setName("blocks");
c.run();

Q20

Consider the following code segment, which is intended to print "Not the same" when the value of entered is not the same as the value of computed.

int computed = /* initial value not shown */;
int entered = /* initial value not shown */;

if ( /* missing condition */ )
{
    System.out.println("Not the same");
}

Which of the following can replace /* missing condition */ so that the code segment works as intended?

Proposed conditions:

I.   computed != entered
II.  !(computed == entered)
III. !(computed.equals(entered))

(A) I only (B) II only (C) III only (D) I and II only (E) I, II, and III


Q21

Consider the following method, which is intended to compute and return the average, arithmetic mean, of the elements of theList. The method does not work as intended.

/** Precondition: theList contains at least one element. */
public static double findAverage(ArrayList<Integer> theList)
{
    double sum = theList.get(0);        // Line 4

    for (int item : theList)
    {
        sum += item;                    // Line 7
    }

    return sum / theList.size();        // Line 9
}

Which of the following changes should be made so that the findAverage method always works as intended?

(A) In line 4, sum should be declared as an int.

(B) In line 4, sum should be initialized to 0.0.

(C) In line 7, item should be replaced with theList.get(item).

(D) In line 7, sum += item should be replaced with item += sum.

(E) In line 9, theList.size() should be cast to double.


Q22

Consider the following method.

public static int aMystery(String str)
{
    int n = str.indexOf("a");

    if (n < 0)
    {
        return 0;
    }
    else
    {
        return 1 + aMystery(str.substring(n + 1));
    }
}

What, if anything, is returned by the call?

aMystery("aardvark")

(A) 0

(B) 3

(C) 5

(D) Nothing is returned because of infinite recursion.

(E) Nothing is returned because a StringIndexOutOfBoundsException occurs.


Q23

The main diagonal of a square two-dimensional array goes from the top-left corner to the bottom-right corner of the array. The main diagonal in the figure is shaded.

Consider the following method, which is intended to return the sum of the values in a square two-dimensional array of integers, excluding those values along the main diagonal.

/** Precondition: mat is a square two-dimensional array. */
public int noDiagSum(int[][] mat)
{
    int len = mat.length;
    int sum = 0;

    /* missing code */

    return sum;
}

Which missing code correctly computes the sum of all values not on the main diagonal?

(B)

for (int j = 0; j < len; j++)
{
    for (int k = 0; k < len; k++)
    {
        sum += mat[j][k];
    }
}

for (int d = 0; d < len; d++)
{
    sum -= mat[d][d];
}

(C)

for (int j = 0; j < len; j++)
{
    for (int k = 0; k < len; k++)
    {
        if (j != k)
        {
            sum += mat[j][k];
        }
        else
        {
            sum -= mat[j][k];
        }
    }
}

Q24

Consider the following code segment.

String[] arr = {"arch", "brain", "circle", "dash",
                "energy", "frame", "guess", "hike"};

for (int i = 1; i < arr.length - 1; i += 2)
{
    System.out.print(arr[i] + "-");
}

What is printed when the code segment is executed?

(A) arch-circle-energy-

(B) arch-circle-energy-guess-

(C) brain-dash-frame-

(D) brain-dash-frame-hike-

(E) brain-circle-dash-energy-frame-guess-hike-


Q25

Consider the following class definitions.

public class Alpha
{
    public int findValue(int a, int b)
    {
        return (2 * a) + b;
    }
}
public class Omega extends Alpha
{
    public int findValue(int a, int b)
    {
        return a + b;
    }

    public int findValue(int a)
    {
        int val = super.findValue(a, a);
        return findValue(val, a);
    }
}

Consider the following code segment, which appears in a class other than Alpha or Omega.

Omega om = new Omega();

System.out.print(om.findValue(2, 3) + " ");
System.out.print(om.findValue(4));

What is printed as a result of executing the code segment?

(A) 5 8 (B) 5 12 (C) 5 16 (D) 7 12 (E) 7 16


Q26

Consider the following code segment, which is intended to create a one-dimensional array result containing the values from the two-dimensional square array letters in column-major order.

After the code segment has been executed, result should contain:

{"A", "D", "G", "B", "E", "H", "C", "F", "I"}

Code:

String[][] letters = { {"A", "B", "C"},
                       {"D", "E", "F"},
                       {"G", "H", "I"} };

String[] result = new String[9];

int k = 0;

for (int x = 0; x < letters.length; x++)
{
    for (int y = 0; y < letters.length; y++)
    {
        /* missing assignment statement */
        k++;
    }
}

Which of the following could be used as a replacement for /* missing assignment statement */ so that the code segment works as intended?

(A)

result[x] = letters[y][k];

(B)

result[x] = letters[k][y];

(C)

result[y] = letters[x][k];

(D)

result[k] = letters[x][y];

(E)

result[k] = letters[y][x];

Q27

Consider the following method.

public static int checkString(String word, String target)
{
    int num = 0;

    for (int k = 0; k < word.length(); k++)
    {
        String letter = word.substring(k, k + 1);

        if (target.indexOf(letter) != -1)
        {
            num += k;
        }
    }

    return num;
}

Which of the following best describes the value returned by the checkString method?

(A) The number of substrings of word that occur in target

(B) The number of characters of target that occur in word

(C) The number of characters of word that occur in target

(D) The sum of the indices of the characters of target that occur in word

(E) The sum of the indices of the characters of word that occur in target


Q28

Consider the following code segment.

int[] nums = {2, 5, 1, 8, 0, 6};

int a = nums[nums.length / 2];
int b = nums[nums[0]];
int c = nums[nums[2] + 2];

int[] result = {a, b, c};

Which of the following represents the contents of result after the code segment is executed?

(A) {3, 1, 8}

(B) {3, 2, 3}

(C) {8, 1, 3}

(D) {8, 1, 8}

(E) {8, 2, 8}


Q29

Consider the method countSpecial, which calls the helper method sumSeven. The sumSeven method returns true if the digits of its String parameter have a sum of 7 and returns false otherwise.

For example:

sumSeven("412")   // true, because 4 + 1 + 2 = 7
sumSeven("43")    // true, because 4 + 3 = 7
sumSeven("111")   // false
sumSeven("74")    // false

Code:

public static int countSpecial(String str)
{
    int count = 0;
    int i = 0;

    for (i = 0; i < str.length() - 1; i++)
    {
        String temp = str.substring(i, i + 2);

        if (sumSeven(temp))
        {
            count += 7;
        }
    }

    return count;
}

Consider the method call:

countSpecial("24341243")

What will the value of i be when count is assigned the value 14?

(A) 1 (B) 2 (C) 3 (D) 4 (E) 6



Section 2 — 中文解析


Q1 — moveIt 字符串移动

答案

A

phrase.substring(0, index)
+ phrase.substring(index + len) + word

考点

解题思路

题目要把 wordphrase 中间移到末尾。

可以把原字符串想成:

phrase = BEFORE + word + AFTER

目标是:

BEFORE + AFTER + word

所以:

phrase.substring(0, index)

表示 word 前面的部分。

phrase.substring(index + len)

表示 word 后面的部分。

最后再加上:

word

所以答案是 A。

关键追踪

例子:

phrase = "uncomfortable"
word = "comfort"

结构是:

"un" + "comfort" + "able"

目标:

"un" + "able" + "comfort"

也就是:

"unablecomfort"

易错点

最常见错误是写:

index + len - 1

substring 的结束位置本来就是 exclusive,所以不需要 -1。聊天记录中也强调,这类题要立刻想到“删除一段”:substring(0, start) + substring(end)


Q2 — int 平均值转 double

答案

C

(double) (a + b) / 2

考点

解题思路

题目要得到:

(3 + 4) / 2 = 3.5

如果写:

(a + b) / 2

因为 ab 都是 int,所以:

7 / 2 = 3

这会发生整数除法,.5 会丢掉。

正确做法是让除法发生前,至少一个操作数变成 double

选项 C:

(double) (a + b) / 2

先把 (a + b) 转成 double,所以:

(double) 7 / 2
= 7.0 / 2
= 3.5

选项追踪

A:

(a + b) / 2
7 / 2 = 3

错误。

B:

(double) a + b / 2

先算 b / 2

3.0 + 4 / 2
= 3.0 + 2
= 5.0

错误。

C:

(double) (a + b) / 2
= 7.0 / 2
= 3.5

正确。

D:

(double) (a + b / 2)

先算 b / 2

4 / 2 = 2
3 + 2 = 5
(double) 5 = 5.0

错误。

E:

(double) ((a + b) / 2)

先发生整数除法:

7 / 2 = 3
(double) 3 = 3.0

错误。

易错点

double 不是“优先级更高”,而是类型提升。真正关键是:cast 必须在整数除法发生前完成。聊天记录中明确区分了 precedence 和 type promotion。


Q3 — 反转整数 digit 的错误

答案

D:Line 13 中的 += 应该改成 =

正确写法:

retVal = (retVal * 10) + digit;

考点

解题思路

反转数字的标准模式是:

digit = num % 10;
retVal = retVal * 10 + digit;
num = num / 10;

题目中写成:

retVal += (retVal * 10) + digit;

这等价于:

retVal = retVal + (retVal * 10 + digit);

也就是把旧的 retVal 多加了一次。

代码追踪

num = 12 快速测试。

初始:

retVal = 0
num = 12

第一次循环:

digit = 12 % 10 = 2
retVal = 0 + (0 * 10 + 2) = 2
num = 1

第二次循环:

digit = 1 % 10 = 1
retVal = 2 + (2 * 10 + 1)
       = 2 + 21
       = 23

但正确结果应该是:

21

易错点

学生看到:

retVal += ...

容易以为是“累加结果”,但这个算法不是普通累加,而是“旧结果左移一位再加新 digit”。所以必须用:

retVal = retVal * 10 + digit;

聊天记录里也把这个当成标准 pattern recognition 题:看到反转数字,马上检查 retVal = retVal * 10 + digit


Q4 — findMedian 找反例

答案

A

findMedian(3, 2, 1)

考点

解题思路

这个方法先找出 firstValsecVal 中较大的那个:

largestSoFar

然后比较 thirdVallargestSoFar

如果:

thirdVal > largestSoFar

返回 largestSoFar

否则返回 thirdVal

所以这个方法本质上是:

返回 thirdVal 和 max(firstVal, secVal) 中较小的那个

它没有真正判断三个数的中位数。

代码追踪

选项 A:

findMedian(3, 2, 1)

第一步:

firstVal = 3
secVal = 2
thirdVal = 1

比较前两个:

largestSoFar = 3

然后:

if (thirdVal > largestSoFar)

也就是:

1 > 3

结果为 false。

所以执行:

return thirdVal;

返回:

1

3, 2, 1 排序后是:

1, 2, 3

中位数应该是:

2

所以 A 可以证明方法不总是正确。

易错点

不要把五个选项都完整 trace。更快的方法是先找失败模式:

thirdVal 是最小值时,代码可能直接返回最小值

聊天记录中也明确指出:找到 bug pattern,再匹配选项,比盲测五个选项快。


Q5 — enigma 字符串交替拼接

答案

B

"aWbXcYdZ"

考点

解题思路

方法先找两个字符串中较短的长度:

len = min(first.length(), second.length())

然后每次取:

first.substring(i, i + 1)
second.substring(i, i + 1)

所以它是在交替拼接两个字符串的字符。

代码追踪

调用:

enigma("abcde", "WXYZ")

长度:

first = "abcde"  长度 5
second = "WXYZ"  长度 4

所以:

len = 4

循环:

i first 字符 second 字符 result
0 a W aW
1 b X aWbX
2 c Y aWbXcY
3 d Z aWbXcYdZ

最终返回:

"aWbXcYdZ"

易错点

学生容易选 C:

"aWbXcYdZe"

但代码只循环到较短字符串长度,不会自动补上 first 多出来的 "e"


Q6 — 修改内层循环条件后的 count

答案

C:Smaller final value of count

考点

解题思路

原代码:

for (int first = 10; first < 25; first++)
{
    for (int second = 25; second > 10; second--)
    {
        count++;
    }
}

外层:

first = 10 到 24
一共 15 次

内层原来:

second = 25 到 11
一共 15 次

原来总次数:

15 × 15 = 225

修改后:

second > first

内层循环长度会随着 first 变大而变短。

代码追踪

first = 10

second = 25, 24, ..., 11
15 次

first = 20

second = 25, 24, 23, 22, 21
5 次

first = 24

second = 25
1 次

所以总次数小于原来的 225。

易错点

学生容易看到条件变复杂就以为次数变多,但这里新条件限制更强,内层循环会越来越短。


Q7 — 对象引用与 ==

答案

C

a == b

考点

解题思路

要画对象引用图。

代码:

Road a = new Road();
a.setName("Pine");

Road b = new Road("Pine");

Road c = a;
Road d = c;

d.setName("Maple");

ab 是两个不同对象。

c = a,所以 ca 指向同一个对象。

d = c,所以 dca 都指向同一个对象。

状态追踪

一开始:

a → Road object #1, name = "Pine"
b → Road object #2, name = "Pine"

然后:

Road c = a;
Road d = c;

所以:

a, c, d → object #1
b       → object #2

执行:

d.setName("Maple");

因为 d 指向 object #1,所以 object #1 的名字变成 "Maple"

最终:

a, c, d → same object, name = "Maple"
b       → different object, name = "Pine"

检查选项:

a.getName().equals("Maple")      // true
a.getName().equals(c.getName())  // true
a == b                           // false
a == c                           // true
a == d                           // true

所以答案是 C。

易错点

abname 一开始都可能是 "Pine",但它们不是同一个对象。== 比较的是引用是否指向同一对象,不是内容是否相同。


Q8 — 递归 mystery(4)

答案

D:6

考点

解题思路

方法:

public static int mystery(int n)
{
    if (n < 2)
    {
        return 2 * n;
    }
    else
    {
        return mystery(n - 1) + mystery(n - 2);
    }
}

它像 Fibonacci,但 base case 不一样:

mystery(0) = 0
mystery(1) = 2

代码追踪

从小往上算最快:

mystery(0) = 2 * 0 = 0
mystery(1) = 2 * 1 = 2

然后:

mystery(2) = mystery(1) + mystery(0)
           = 2 + 0
           = 2
mystery(3) = mystery(2) + mystery(1)
           = 2 + 2
           = 4
mystery(4) = mystery(3) + mystery(2)
           = 4 + 2
           = 6

易错点

不要一看到:

mystery(n - 1) + mystery(n - 2)

就自动当成标准 Fibonacci。递归关系像 Fibonacci,但 base case 不同,结果也不同。


Q9 — Method Overloading 与对象状态

答案

C:15

考点

解题思路

Arithmetic 有三个 update 方法:

update()
update(int num)
update(int num1, int num2)

Java 根据参数个数选择调用哪个方法。

根据后来更清楚的截图确认,两个参数版本是:

value = value * num1 + num2;

聊天记录里也特别修正了这一点。

代码追踪

初始:

Arithmetic alpha = new Arithmetic(3);
Arithmetic beta = new Arithmetic(2);

所以:

alpha.value = 3
beta.value = 2

执行:

alpha.update(1);

调用:

update(int num)

所以:

alpha.value = 3 + 1 = 4

执行:

alpha.update(2, 3);

调用:

update(int num1, int num2)

所以:

alpha.value = 4 * 2 + 3 = 11

执行:

beta.update();

调用:

update()

所以:

beta.value = 2 * 2 = 4

最后:

System.out.println(alpha.getValue() + beta.getValue());

输出:

11 + 4 = 15

易错点

  1. 看错 overloaded method 的公式。
  2. alphabeta 的状态混在一起。
  3. 不根据参数个数选择方法。

Q10 — 3×3 二维数组对角线填充

答案

B

{ { 0, -1, -2 },
  { 1,  1, -1 },
  { 2,  1,  2 } }

考点

解题思路

代码:

for (int j = 0; j < 3; j++)
{
    arr[j][j] = j;

    for (int k = 0; k < j; k++)
    {
        arr[j][k] = j - k;
        arr[k][j] = k - j;
    }
}

核心是:

arr[j][j] = j

填主对角线。

而:

arr[j][k]
arr[k][j]

是一组对称位置。

代码追踪

j = 0

arr[0][0] = 0

j = 1

arr[1][1] = 1

内层 k = 0

arr[1][0] = 1 - 0 = 1
arr[0][1] = 0 - 1 = -1

j = 2

arr[2][2] = 2

内层 k = 0

arr[2][0] = 2 - 0 = 2
arr[0][2] = 0 - 2 = -2

内层 k = 1

arr[2][1] = 2 - 1 = 1
arr[1][2] = 1 - 2 = -1

最终:

{ { 0, -1, -2 },
  { 1,  1, -1 },
  { 2,  1,  2 } }

聊天记录中也总结:看到 arr[j][k]arr[k][j],要想到“沿对角线对称填充”。

易错点

  1. 只填一边,忘记对称位置。
  2. 符号反了:j - k 是正数,k - j 是负数。
  3. 忘记对角线位置是 arr[j][j] = j,所以对角线是 0, 1, 2

Q11 — reorder 中 line 19 执行次数

答案

B:2 times

考点

解题思路

方法逻辑:

从左到右扫描。遇到偶数,就往右找下一个奇数。如果找到,就交换。

Line 19:

int temp = data.get(i);

只有发生 swap 时才执行。

代码追踪

初始:

[2, 6, 7, 4, 1, 8]

i = 0

data.get(0) = 2,偶数。

往右找:

index = 1 → 6 偶数,跳过
index = 2 → 7 奇数,停止

交换 27

[7, 6, 2, 4, 1, 8]

Line 19 执行第 1 次。

i = 1

data.get(1) = 6,偶数。

往右找:

index = 2 → 2 偶数,跳过
index = 3 → 4 偶数,跳过
index = 4 → 1 奇数,停止

交换 61

[7, 1, 2, 4, 6, 8]

Line 19 执行第 2 次。

i = 2, 3, 4, 5

后面只剩偶数,没有奇数可以换,所以不再执行 line 19。

最终 line 19 执行:

2 次

聊天记录中也指出,截图里选 C 很有诱惑性,但会 overcount;最后那一组偶数后面没有奇数,不能 swap。

易错点

不要把“遇到偶数”次数当成 line 19 执行次数。Line 19 只有在找到右边的奇数并进入 swap block 时才执行。


Q12 — 把 % 2 == 0 改成 / 2 == 0

答案

C

The modified method will rearrange the elements of data so that elements with a value equal to 0 or 1 appear after all other elements.

考点

解题思路

原条件:

data.get(i) % 2 == 0

表示“这个数是偶数”。

修改后:

data.get(i) / 2 == 0

这不是判断偶数,而是整数除法。

因为题目说所有元素都是非负整数,所以:

0 / 2 = 0
1 / 2 = 0
2 / 2 = 1
3 / 2 = 1
4 / 2 = 2

所以条件只对:

0 和 1

为 true。

行为分析

修改后的方法变成:

如果当前位置是 0 或 1,就往右找第一个不是 0 或 1 的值,然后交换。

所以 01 会被往后推。

因此最终效果是:

0 或 1 会出现在其他元素之后

易错点

x % 2 == 0x / 2 == 0 完全不是一回事。


Q13 — static 统计所有对象的总 posts

答案

E

private static int count = 0;

考点

解题思路

题目关键词是:

total number of posts made by all SocialMediaAccount objects

也就是所有对象共用一个总数。

Java 中“属于整个 class,而不是属于某个对象”的变量要用:

static

所以需要:

private static int count = 0;

选项分析

A:

private final int count = 0;

错误。不是 static,而且 final 不能 count++

B:

private int count = 0;

可以编译,但每个对象有自己的 count,不是总数。

如果:

kevin.count = 1
briana.count = 2

不会得到共享总数 3。

C:

private static count = 0;

错误。缺少类型 int

D:

private static final int count = 0;

错误。final 不能修改,count++ 不合法。

E:

private static int count = 0;

正确。

聊天记录中也明确总结:private int count 是每个对象一份,private static int count 是所有对象共享一份。

易错点

看到“所有对象总共”就应该立刻想到 static。看到 count++ 就不能用 final


Q14 — 错误的 factorial 递归

答案

B

The recursive call to the method does not allow progress toward the base case.

考点

解题思路

递归题先检查两件事:

1. 有没有 base case?
2. 每次递归有没有靠近 base case?

代码中 base case 是有的:

if (n == 1)
{
    return 1;
}

所以 A 错。

问题在:

return n * factorial(n);

它再次调用:

factorial(n)

参数没有变化。

如果调用:

factorial(4)

会变成:

4 * factorial(4)

又进入:

4 * factorial(4)

永远不会变成:

factorial(3)
factorial(2)
factorial(1)

所以无法靠近 base case。

正确版本

public static int factorial(int n)
{
    if (n == 1)
    {
        return 1;
    }

    return n * factorial(n - 1);
}

易错点

学生可能认为“有 base case 就安全”,但递归必须每一步都朝 base case 前进。聊天记录中也强调:factorial(n) 是坏的,factorial(n - 1) 才能前进。


Q15 — Boolean 表达式追踪

答案

OCR 有不确定。按 Section 1 当前 OCR,C 和 D 都会是 true,因此原选项 D 很可能识别有误。

如果只选一个最稳定的答案,C:b1 && b3 是 true。

考点

解题思路

已知:

boolean a = true;
boolean b = false;
boolean c = false;

计算:

boolean b1 = a && !(b || c);
boolean b2 = !a || b && c;
boolean b3 = a && !b || !c;

代码追踪

b1

b1 = a && !(b || c)

代入:

= true && !(false || false)
= true && !false
= true && true
= true

所以:

b1 = true

b2

b2 = !a || b && c

&& 优先级高于 ||,所以:

= !true || (false && false)
= false || false
= false

所以:

b2 = false

b3

b3 = a && !b || !c

先算 !,再算 &&,再算 ||

= true && true || true
= true || true
= true

所以:

b3 = true

现在:

b1 = true
b2 = false
b3 = true

检查选项:

A:

b1 == b2
true == false → false

B:

b1 && b2
true && false → false

C:

b1 && b3
true && true → true

D 如果是:

!b2 && b3

则:

!false && true = true && true = true

这也会是 true。

E:

(b1 != b2) && (b1 != b3)
(true != false) && (true != true)
= true && false
= false

Q16 — 嵌套循环打印次数

答案

B:9 times

考点

解题思路

外层循环:

for (int j = 1; j < 7; j += 2)

所以:

j = 1, 3, 5

内层循环:

for (int k = j; k > 0; k--)

每次内层执行次数就是当前的 j

代码追踪

j k values Line 5 执行次数
1 1 1
3 3, 2, 1 3
5 5, 4, 3, 2, 1 5

总次数:

1 + 3 + 5 = 9

聊天记录中也建议:不要一行一行数 print,直接列外层 j 值,再加内层次数。

易错点

外层不是 1, 2, 3, 4, 5, 6,而是每次加 2,所以只有:

1, 3, 5

Q17 — 反转 double[] values

答案

E

7.0 3.0 5.0 2.5 3.0 2.0 6.5

考点

解题思路

数组长度是 7:

int limit = values.length / 2;

所以:

7 / 2 = 3

循环:

k = 0, 1, 2

每次交换:

values[k]

和:

values[values.length - k - 1]

这就是反转数组的前半部分与后半部分。

代码追踪

初始:

[6.5, 2.0, 3.0, 2.5, 5.0, 3.0, 7.0]

k = 0

temp1 = 7 - 0 - 1 = 6

交换 index 0 和 6:

[7.0, 2.0, 3.0, 2.5, 5.0, 3.0, 6.5]

k = 1

temp1 = 7 - 1 - 1 = 5

交换 index 1 和 5:

[7.0, 3.0, 3.0, 2.5, 5.0, 2.0, 6.5]

k = 2

temp1 = 7 - 2 - 1 = 4

交换 index 2 和 4:

[7.0, 3.0, 5.0, 2.5, 3.0, 2.0, 6.5]

输出:

7.0 3.0 5.0 2.5 3.0 2.0 6.5

聊天记录里也直接总结:values[k] ↔ values[values.length - k - 1] 就是 reverse pattern。

易错点

中间元素 2.5 不会动,因为数组长度是奇数,反转时中间 index 保持原位。


Q18 — Constructor Overloading

答案

B

Room r2 = new Room("kitchen", 2);

考点

解题思路

Room 类只有两个 constructor:

Room(String description)

和:

Room(String description, int numDoors)

所以创建对象时,参数列表必须完全匹配其中一个。

选项分析

A:

Room r1 = new Room(2);

参数是一个 int,没有 Room(int),错误。

B:

Room r2 = new Room("kitchen", 2);

参数是:

String, int

匹配:

Room(String description, int numDoors)

正确。

C:

Room r3 = new Room(1, "bedroom");

参数是:

int, String

顺序不匹配,错误。

D:

Room r4 = new Room();

没有 no-argument constructor。注意:一旦 class 中写了 constructor,Java 不会再自动提供默认无参 constructor。

E 错,因为 constructor 可以 overload,只要参数列表不同即可。

聊天记录中也总结:constructor 题检查三件事:参数个数、类型、顺序。

易错点

学生容易以为“所有 class 都有默认 constructor”。如果你已经写了 constructor,默认无参 constructor 就不会自动生成。


Q19 — ProgramComputer 方法调用是否编译

答案

D

Computer c = new Computer();
Program p = new Program("blocks");
c.run(p);

考点

解题思路

Computer 中的方法是:

public void run(Program p)

这说明:

  1. run 是 instance method,要用 Computer 对象调用。
  2. run 需要一个 Program 参数。

所以必须先创建:

Computer c = new Computer();
Program p = new Program("blocks");

再调用:

c.run(p);

选项分析

A:

Program p = new Program("blocks");
Computer.run();

错误。run 不是 static,不能用 class name 调用;而且缺少参数。

B:

Program p = new Program("blocks");
Computer.run(p);

错误。还是用 class name 调用了 instance method。

C:

Computer c = new Computer();
Program p = new Program("blocks");
p.run();

错误。Program 类没有 run 方法。

D:

Computer c = new Computer();
Program p = new Program("blocks");
c.run(p);

正确。

E:

Computer c = new Computer();
Program p = new Program();
p.setName("blocks");
c.run();

错误。c.run() 缺少 Program 参数。

易错点

看到 Computer.run(p) 要警觉:除非方法是 static,否则不能用类名直接调用。


Q20 — int 不相等判断

答案

D:I and II only

考点

解题思路

题目要判断两个 int 值不相同。

I:

computed != entered

正确,直接表示不等于。

II:

!(computed == entered)

也正确,意思是“不是相等”。

III:

!(computed.equals(entered))

错误。computedint,primitive type 不能调用 .equals()

易错点

.equals() 用于对象,比如 String。对于 intdoubleboolean 这类 primitive type,不能写:

computed.equals(...)

Q21 — findAverage 第一项被加了两次

答案

B

In line 4, sum should be initialized to 0.0.

考点

解题思路

原代码:

double sum = theList.get(0);

然后:

for (int item : theList)
{
    sum += item;
}

enhanced for loop 会遍历 list 中的每一个元素,包括第一个元素。

所以第一个元素被算了两次。

代码追踪

假设:

theList = [2, 4, 6]

正确平均值:

(2 + 4 + 6) / 3 = 4.0

原代码:

sum starts as 2

循环:

add 2 → sum = 4
add 4 → sum = 8
add 6 → sum = 14

返回:

14 / 3 = 4.666...

错误。

正确做法:

double sum = 0.0;

然后让 loop 把每个元素加一次。

为什么 E 不需要

E 说把 theList.size() cast 成 double,但 sum 已经是 double,所以:

sum / theList.size()

已经是 double division。

聊天记录中也指出:真正问题不是除法,而是第一个元素被重复计入。

易错点

学生容易一看到 average 就怀疑 integer division,但这里 sumdouble,除法没问题。真正问题在 accumulator 初始值。


Q22 — aMystery("aardvark")

答案

B:3

考点

解题思路

方法:

int n = str.indexOf("a");

找到第一个 "a" 的位置。

如果没有 "a"

return 0;

如果有 "a"

return 1 + aMystery(str.substring(n + 1));

意思是:

先数到一个 "a",然后递归检查它后面的字符串。

所以这个方法是在数字符串中 "a" 出现的次数。

代码追踪

调用:

aMystery("aardvark")

"aardvark" 第一个 "a" 在 index 0:

1 + aMystery("ardvark")

"ardvark" 第一个 "a" 在 index 0:

1 + 1 + aMystery("rdvark")

"rdvark" 有一个 "a",在 index 3:

1 + 1 + 1 + aMystery("rk")

"rk" 没有 "a"

return 0

所以总数:

1 + 1 + 1 + 0 = 3

聊天记录中也总结:return 1 + method(remainingString) 通常是在“数某个东西”。

易错点

substring(n + 1) 会跳过当前找到的 "a",所以会继续找后面剩下的部分,不会无限递归。


Q23 — noDiagSum 排除主对角线求和

答案

根据聊天记录中可见选项,正确是 B。

考点

解题思路

主对角线位置是:

mat[0][0]
mat[1][1]
mat[2][2]
...
mat[d][d]

也就是:

row index == column index

题目要加总所有 不在主对角线 上的元素。

可见选项 B 的策略是:

  1. 先把所有元素都加起来。
  2. 再把主对角线元素减掉。

这样剩下的就是非对角线元素的和。

为什么 B 对

B 的结构:

for (int j = 0; j < len; j++)
{
    for (int k = 0; k < len; k++)
    {
        sum += mat[j][k];
    }
}

for (int d = 0; d < len; d++)
{
    sum -= mat[d][d];
}

第一段:

sum = 所有元素之和

第二段:

减去 mat[0][0], mat[1][1], ...

所以最终:

sum = 非主对角线元素之和

为什么 C 错

聊天记录中可见 C 是:

if (j != k)
{
    sum += mat[j][k];
}
else
{
    sum -= mat[j][k];
}

对于非对角线元素,它加进去,这部分是对的。

但对于对角线元素,它不是忽略,而是减掉。

最后结果会变成:

非对角线元素之和 - 对角线元素之和

但题目只要:

非对角线元素之和

所以 C 错。

聊天记录中明确说:如果截图显示 C,不应信任;基于可见代码,B 正确,C 不正确。

易错点

“排除主对角线”不是“遇到主对角线就减掉”。如果一开始 sum = 0,那主对角线应该 不处理,不是扣成负数。


Q24 — 数组隔项输出

答案

C

brain-dash-frame-

考点

解题思路

数组:

index: 0      1       2       3     4       5      6      7
value: arch   brain   circle  dash  energy  frame  guess  hike

循环:

for (int i = 1; i < arr.length - 1; i += 2)

arr.length = 8,所以:

i < 7

i = 1 开始,每次加 2:

i = 1, 3, 5

输出:

arr[1] = brain
arr[3] = dash
arr[5] = frame

每次后面加 "-"

所以结果:

brain-dash-frame-

聊天记录中也按 index 表追踪出答案 C。

易错点

i < arr.length - 1 意味着 i < 7,所以 index 7 的 "hike" 不会被打印。


Q25 — Alpha / Omega 继承与 super

答案

C

5 16

考点

解题思路

Omega 继承 Alpha

Alpha 中:

findValue(int a, int b)
{
    return (2 * a) + b;
}

Omega 中重写了两个参数版本:

findValue(int a, int b)
{
    return a + b;
}

并新增了一个参数版本:

findValue(int a)
{
    int val = super.findValue(a, a);
    return findValue(val, a);
}

代码追踪

第一句:

om.findValue(2, 3)

omOmega 对象,调用 Omega 的两个参数版本:

2 + 3 = 5

所以先打印:

5

第二句:

om.findValue(4)

调用 Omega 的一个参数版本。

进入方法:

int val = super.findValue(a, a);

此时:

a = 4

super.findValue(4, 4) 调用父类 Alpha 的版本:

(2 * 4) + 4 = 12

所以:

val = 12

然后:

return findValue(val, a);

注意这里没有 super,所以调用 Omega 的两个参数版本:

findValue(12, 4)
= 12 + 4
= 16

最终输出:

5 16

聊天记录中也强调:super.method(...) 用父类版本;普通 findValue(...) 仍然会匹配子类版本。

易错点

super.findValue(a, a) 只影响那一次调用。后面的:

findValue(val, a)

没有 super,所以用的是 Omega 自己的 two-parameter method。


Q26 — Column-major order

答案

E

result[k] = letters[y][x];

考点

解题思路

目标顺序:

A, D, G, B, E, H, C, F, I

这是 column-major order。

也就是:

letters[0][0], letters[1][0], letters[2][0],
letters[0][1], letters[1][1], letters[2][1],
letters[0][2], letters[1][2], letters[2][2]

列先固定,行不断变化。

代码:

for (int x = 0; x < letters.length; x++)
{
    for (int y = 0; y < letters.length; y++)
    {
        /* missing assignment statement */
        k++;
    }
}

外层 x 变得慢,适合作为 column。

内层 y 变得快,适合作为 row。

所以访问:

letters[y][x]

一维数组位置由 k 控制:

result[k] = letters[y][x];

代码追踪

x = 0

y = 0 → letters[0][0] = A → result[0]
y = 1 → letters[1][0] = D → result[1]
y = 2 → letters[2][0] = G → result[2]

x = 1

y = 0 → letters[0][1] = B → result[3]
y = 1 → letters[1][1] = E → result[4]
y = 2 → letters[2][1] = H → result[5]

x = 2

y = 0 → letters[0][2] = C → result[6]
y = 1 → letters[1][2] = F → result[7]
y = 2 → letters[2][2] = I → result[8]

得到目标数组。

聊天记录中也明确给出:column-major 需要 letters[y][x],并放入 result[k]

易错点

如果写:

letters[x][y]

就是 row-major order,会得到:

A, B, C, D, E, F, G, H, I

不是题目要的顺序。


Q27 — checkString 返回值描述

答案

E

The sum of the indices of the characters of word that occur in target.

考点

解题思路

代码遍历 word 的每一个位置:

for (int k = 0; k < word.length(); k++)

每次取 word 中 index 为 k 的一个字符:

String letter = word.substring(k, k + 1);

然后判断这个字符是否出现在 target 中:

if (target.indexOf(letter) != -1)

如果出现,就执行:

num += k;

注意不是:

num++;

而是加上 index k

所以返回值不是匹配字符的数量,而是这些匹配字符在 word 中的 index 之和。

聊天记录中也强调关键线索是 num += k,不是 num++

易错点

很多学生会选“characters 的数量”,但代码加的是位置:

num += k;

不是计数。


Q28 — 数组索引套索引

答案

D:{8, 1, 8}

注:你后来补充过 D 是 8 1 8;早先 OCR 中 D 曾可能被识别错。

考点

解题思路

数组:

int[] nums = {2, 5, 1, 8, 0, 6};

写出 index 表:

index: 0  1  2  3  4  5
nums:  2  5  1  8  0  6

代码追踪

a

int a = nums[nums.length / 2];

nums.length = 6

6 / 2 = 3

所以:

a = nums[3] = 8

b

int b = nums[nums[0]];

先算:

nums[0] = 2

所以:

b = nums[2] = 1

c

int c = nums[nums[2] + 2];

先算:

nums[2] = 1

所以:

nums[2] + 2 = 3

因此:

c = nums[3] = 8

最终:

int[] result = {a, b, c};

是:

{8, 1, 8}

易错点

这类题要从最里面的 brackets 算起:

nums[nums[2] + 2]

不是直接看 nums[2],而是先算出完整 index。


Q29 — countSpecialsumSeven

答案

B:2

考点

解题思路

方法每次取长度为 2 的 substring:

String temp = str.substring(i, i + 2);

如果这两个 digit 的和为 7,则:

count += 7;

题目问:

count 被赋值为 14 时,i 的值是多少?

注意问的是 count 变成 14 的那一次,不是最后返回值。

代码追踪

调用:

countSpecial("24341243")

字符串:

index: 0 1 2 3 4 5 6 7
char:  2 4 3 4 1 2 4 3

循环:

for (i = 0; i < str.length() - 1; i++)

逐步取两位:

i = 0

temp = "24"
2 + 4 = 6

不是 7,count 不变:

count = 0

i = 1

temp = "43"
4 + 3 = 7

所以:

count += 7
count = 7

i = 2

temp = "34"
3 + 4 = 7

所以:

count += 7
count = 14

此时 i = 2

所以答案是 B。

易错点

不要问最终有几个 pair,也不要跳到最后。题目问的是:

count 被赋值为 14 的那一刻,i 是多少?

所以要在每次 count += 7 的时刻记录 i