← All Units← 返回单元列表 ← Course Hub← 课程主页
A P  C O M P U T E R  S C I E N C E  A
Unit 1 · Solutions第 1 单元 · 解析

Using Objects and Methods — Solutions使用对象与方法 —— 解析

Companion to the AP-Style MC Practice SetAP 风格选择题练习的解析配套

MEDIUM HARD AP MC

Unit 1: Using Objects and Methods1 单元:使用对象与方法CSA



MULTIPLE CHOICEWorked Answers详细解析

Multiple Choice — Worked Answers选择题(Multiple Choice)—— 详细解析

Each item restates the prompt and choices, marks the correct letter, and gives a brief justification. Trap distractors are called out where useful.每道题重述题干与选项、标出正确答案字母,并给出简明解析;对容易混淆的干扰项(trap distractor)单独提示。

Q1MEDIUMAP MCInteger Ops

Print: x / y + x % y with x = 17, y = 4?x = 17, y = 4 时,x / y + x % y 的输出是?

Answer:答案: (B)
Both x and y are int, so / is integer division and % is the integer remainder.
  • 17 / 4 = 4 (truncate 4.25 toward zero)
  • 17 % 4 = 1 (since 17 = 4·4 + 1)
  • Sum: 4 + 1 = 5
A useful identity: for non-negative integers, x == (x/y) * y + (x%y). Trap (A) skips the modulus.
xy 都是 int,所以 / 是整除(integer division),% 是整数取模(modulus)。
  • 17 / 4 = 4(将 4.25 向零截断,truncate toward zero
  • 17 % 4 = 1(因为 17 = 4·4 + 1
  • 求和:4 + 1 = 5
一个常用恒等式:对非负整数,x == (x/y) * y + (x%y)。陷阱选项 (A) 漏掉了取模那一项。
Q2MEDIUMAP MCMath.random()

Random even integer 2..10 inclusive?2 到 10(含端点)之间的随机偶数?

Answer:答案: (A)
Count the target values: {2, 4, 6, 8, 10} — five evenly spaced integers in steps of 2. Build from a 0..4 random integer:
  • (int)(Math.random() * 5) → 0, 1, 2, 3, or 4 (five values)
  • * 2 → 0, 2, 4, 6, 8
  • + 2 → 2, 4, 6, 8, 10 ✓

Trap (B) returns any integer 2..10 (10 − 2 + 1 = 9 distinct values), not just evens. (C) maps to {2, 4, 6, 8, 10, 12} — overshoots by including 12. (D) maps to {0, 2, 4, 6, 8} — misses 10 and includes 0.

先数一数目标值集合:{2, 4, 6, 8, 10} —— 五个步长为 2 的偶数。从一个 0..4 的随机整数构造:
  • (int)(Math.random() * 5) → 0、1、2、3 或 4(五个值)
  • * 2 → 0、2、4、6、8
  • + 2 → 2、4、6、8、10 ✓

陷阱选项 (B) 返回 2..10 中的任意整数(共 10 − 2 + 1 = 9 个不同值),不仅仅是偶数。(C) 映射到 {2, 4, 6, 8, 10, 12} —— 多出 12。(D) 映射到 {0, 2, 4, 6, 8} —— 漏掉 10 而把 0 包含进来。

Q3MEDIUMAP MCString Identity

a = "Java"; b = "Ja" + "va"; c = new String("Java").a = "Java"b = "Ja" + "va"c = new String("Java")

Answer:答案: (B)
Two distinct mechanisms are at play:
  • String pool / constant folding. "Ja" + "va" is two compile-time constant operands, so the compiler folds them at compile time into the single literal "Java". That literal already lives in the String pool (because of a), so b reuses the same reference: a == b is true.
  • new String(...) bypasses the pool. c is explicitly allocated on the heap as a new object — different reference from a, so a == c is false. The character content matches, so a.equals(c) is true.
Rule: for content comparison, always use .equals(). == on objects compares references.
这里有两种不同机制在起作用:
  • 字符串池与常量折叠(String pool / constant folding)。 "Ja" + "va" 由两个编译期常量构成,所以编译器在编译期就把它折叠为单一字面量 "Java"。因为 a 已经把这个字面量放入字符串池,b 复用同一份引用:a == btrue
  • new String(...) 绕过字符串池。 c 被显式地分配到堆上作为新对象 —— 与 a 不是同一个引用,所以 a == cfalse。但字符内容相同,所以 a.equals(c)true
规则:比较对象的内容时永远用 .equals()== 比较的是引用地址(reference equality)。
Q4HARDAP MCCast Placement

int a = 10; int b = 4; double r = (double) a / b + a / b;int a = 10; int b = 4; double r = (double) a / b + a / b;

Answer:答案: (B)
The cast (double) binds tighter than /, but does not reach across the + to the second a / b. Evaluate each side of + independently:
  • (double) a / b(double) 10 / 410.0 / 42.5 (one operand double promotes the division)
  • a / b10 / 42 (both int, integer division)
Sum: 2.5 + 2 = 4.5 (mixed double + intdouble). Trap (C) treats the second a/b as also promoted (so 2.5 + 2.5 = 5.0); (D) ignores the second term.
(double) 的强制类型转换(cast)优先级高于 /,但不会跨过 + 作用到第二个 a / b。需要把 + 的左右两侧分开求值:
  • (double) a / b(double) 10 / 410.0 / 42.5(一侧为 double,整个除法被类型提升 type promotiondouble
  • a / b10 / 42(两侧均为 int,整除)
求和:2.5 + 2 = 4.5double + int 混合 → double)。陷阱 (C) 错误地把第二个 a/b 也类型提升(得到 2.5 + 2.5 = 5.0);(D) 忽略了第二项。
Q5HARDAP MCString — 4-step Chain

s = "ABCDEFG"; chain through t, u, v, then print v + u.substring(u.length() - 1).s = "ABCDEFG";依次得到 tuv,然后打印 v + u.substring(u.length() - 1)

Answer:答案: (B)
Resolve each new variable in order. The tricky part: every indexOf / length / substring call is made on the most recent reassignment, not on the original s.
  • t = s.substring(2)"CDEFG" (5 chars, indices 0..4 are C,D,E,F,G)
  • t.indexOf("E") = 2 (in t, not in s — that's the key)
  • u = t.substring(2)"EFG" (3 chars)
  • v = u.substring(0, u.length() - 1) = u.substring(0, 2)"EF"
  • u.substring(u.length() - 1) = u.substring(2)"G"
  • Print "EF" + "G" = "EFG"
Trap (A) drops the trailing "G" term. (C) double-counts the last character. (D) forgets that u already starts at 'E' — students often re-index from the start of s.
按顺序逐个解出每个新变量。难点在于:每次 indexOf / length / substring 都作用在最近一次赋值得到的字符串上,而不是原始的 s
  • t = s.substring(2)"CDEFG"(5 个字符,下标 0..4 依次是 C,D,E,F,G)
  • t.indexOf("E") = 2(注意:是在 t 里查找而非 s —— 这是关键)
  • u = t.substring(2)"EFG"(3 个字符)
  • v = u.substring(0, u.length() - 1) = u.substring(0, 2)"EF"
  • u.substring(u.length() - 1) = u.substring(2)"G"
  • 打印 "EF" + "G" = "EFG"
陷阱 (A) 丢掉了末尾的 "G"。(C) 把最后一个字符多算了一次。(D) 忘了 u 本身就是从 'E' 开始的 —— 学生常常误用 s 的下标重新数。
Q6HARDAP MCString — Aliased Reassignment Timing

a = "go"; b = a; a = a + a; b += a; println(b);a = "go"; b = a; a = a + a; b += a; println(b);

Answer:答案: (B)
The key is tracking what b refers to at the moment each statement runs.
  • After a = "go"; b = a;  →  both reference the literal "go".
  • a = a + a;  →  RHS evaluates first: "go" + "go" = "gogo". Then a is reassigned to that new String. b is unchanged — it still points to "go". Strings are immutable, so the original "go" object can't be mutated underneath b.
  • b += a;  →  expands to b = b + a. Read each operand's current value: b = "go", a = "gogo". Concatenate: "go" + "gogo" = "gogogo". Reassign b.
Trap (C) "gogogogo" happens if you mistakenly use the new value of a in place of b in b + a (i.e., compute a + a a second time). (D) doubles up further. (A) ignores the second statement entirely.
关键是跟踪 b每一条语句执行时所指向的对象。
  • a = "go"; b = a; 后  →  两者都指向字面量 "go"
  • a = a + a;  →  先求右侧:"go" + "go" = "gogo"。然后把 a 重新绑定到这个新 String 对象。b 没有变化 —— 它仍然指向 "go"String 不可变(immutable),所以底层的 "go" 对象不会在 b 之下被悄悄改写。
  • b += a;  →  等价于 b = b + a。读取每个操作数当前的值:b = "go"a = "gogo"。拼接:"go" + "gogo" = "gogogo"。重新赋值给 b
陷阱 (C) "gogogogo" 出现在你把 b + a 中的 b 误用成新值 a 时(即又一次算了 a + a)。(D) 进一步重复加倍。(A) 完全忽略了第二条语句。
Q7HARDAP MCInteger Overflow

Value of (Integer.MAX_VALUE + 1) / 2?(Integer.MAX_VALUE + 1) / 2 的值?

Answer:答案: (B)
Step through carefully:
  • Integer.MAX_VALUE = 2,147,483,647
  • MAX_VALUE + 1 overflows the 32-bit signed range and wraps to Integer.MIN_VALUE = -2,147,483,648 (this is silent — no run-time error for int overflow in Java)
  • (-2,147,483,648) / 2 = -1,073,741,824
Key fact: Java int overflow does NOT throw. The arithmetic just wraps. To detect overflow you'd use Math.addExact(...) or use long instead. Trap (A) is the mathematical answer (if there were no overflow); (C) ignores the overflow.
逐步推演:
  • Integer.MAX_VALUE = 2,147,483,647
  • MAX_VALUE + 1 超出了 32 位有符号 int 的范围,发生整数溢出(integer overflow),回绕到 Integer.MIN_VALUE = -2,147,483,648(默默回绕——Java 不会因为 int 溢出而抛错)
  • (-2,147,483,648) / 2 = -1,073,741,824
关键事实:Java 的 int 溢出不会抛出异常,算术运算只是"回绕"。要检测溢出可以用 Math.addExact(...),或者改用 long。陷阱 (A) 是不考虑溢出时的数学答案;(C) 忽略了溢出。
Q8HARDAP MCType Promotion + Cast

int x = 7; double y = 2.0; double z = (int) (x / y * 2); println(z);int x = 7; double y = 2.0; double z = (int) (x / y * 2); println(z);

Answer:答案: (B)
Evaluate the cast's operand first (inside the parens), then apply the cast, then assign to z.
  • x / y = 7 / 2.0 — mixed int / double promotes to double3.5
  • 3.5 * 2 = 7.0 (mixed → double)
  • (int) 7.0 = 7 (cast to int truncates toward zero)
  • Assigning int 7 to double z auto-widens to 7.0
So println(z) prints 7.0. Trap (A) computes (int)(7/2) = (int)3 = 3, then * 2 = 6 — i.e. misreads the parens as (int)(x / y) * 2. (C) skips the cast.
先求强制类型转换括号内的表达式,再做转换,再赋给 z
  • x / y = 7 / 2.0 —— int / double 混合,提升为 double3.5
  • 3.5 * 2 = 7.0(混合 → double
  • (int) 7.0 = 7(转换为 int 向零截断)
  • int 7 赋给 double z,自动加宽为 7.0
所以 println(z) 输出 7.0。陷阱 (A) 错误地把括号当作 (int)(x / y) * 2 解释:(int)(7/2) = (int)3 = 3,再 * 2 = 6。(C) 跳过了类型转换那一步。
Q9HARDAP MCString — Self-referential length arithmetic

s = "BAOBAB"; i = s.indexOf("OB"); a = s.substring(i); b = s.substring(i, i + a.length() - 2). Print a.length() + b.length().s = "BAOBAB"i = s.indexOf("OB")a = s.substring(i)b = s.substring(i, i + a.length() - 2)。打印 a.length() + b.length()

Answer:答案: (C)
Index map of "BAOBAB":
 B  A  O  B  A  B
 0  1  2  3  4  5
The trick: a.length() appears inside the bounds for b — you must compute a first.
  • i = s.indexOf("OB") = 2 (first match of the 2-char sequence O-B starts at index 2)
  • a = s.substring(2)  →  "OBAB", so a.length() = 4
  • b = s.substring(2, 2 + 4 - 2) = s.substring(2, 4)  →  "OB", so b.length() = 2
  • Print 4 + 2 = 6
Trap (A) ignores b.length(). (B) miscounts a (forgets the second 'B' at index 5). (D) double-includes the endpoint in b.
"BAOBAB" 的下标对照:
 B  A  O  B  A  B
 0  1  2  3  4  5
关键:a.length() 出现在 b 的边界表达式之中,必须先把 a 算出来。
  • i = s.indexOf("OB") = 2("OB" 这个 2 字符序列首次匹配的起点在下标 2)
  • a = s.substring(2)  →  "OBAB",所以 a.length() = 4
  • b = s.substring(2, 2 + 4 - 2) = s.substring(2, 4)  →  "OB",所以 b.length() = 2
  • 打印 4 + 2 = 6
陷阱 (A) 忽略了 b.length()。(B) 数错了 a(漏掉下标 5 的第二个 'B')。(D) 把 b 的右端点也算进字符里——substring(start, end) 是左闭右开(end 不包含)。
Q10HARDAP MCNegative Division & Modulus

x = -17; y = 5; println((x/y) + " " + (x%y));x = -17; y = 5; println((x/y) + " " + (x%y));

Answer:答案: (B)
Java's int division truncates toward zero (not toward minus infinity), and % follows the identity x == (x/y) * y + (x%y).
  • -17 / 5: -3.4 truncates toward zero to -3 (not -4).
  • -17 % 5: (-17) − (-3) · 5 = -17 + 15 = -2. The sign matches the dividend (-17).
So the output is "-3 -2". Trap (A) treats / as floor division (Python-style); (C) treats % as math-class modulo (always non-negative).
Java 的 int 整除是向零截断(truncate toward zero),不是向负无穷取整;% 满足恒等式 x == (x/y) * y + (x%y)
  • -17 / 5-3.4 向零截断为 -3(不是 -4)。
  • -17 % 5(-17) − (-3) · 5 = -17 + 15 = -2。结果符号与被除数(-17)相同。
所以输出 "-3 -2"。陷阱 (A) 把 / 当成 Python 的下取整除法;(C) 把 % 当成数学课上的"非负模"。
Q11HARDAP MCString — Swap without a temp

a = "AB"; b = "CD"; a = a + b; b = a.substring(0, a.length() - b.length()); a = a.substring(b.length()); println(a + b);a = "AB"; b = "CD"; a = a + b; b = a.substring(0, a.length() - b.length()); a = a.substring(b.length()); println(a + b);

Answer:答案: (B)
This is the classic "swap two values without using a temporary variable" using concatenation. Every step uses the current values of a and b, which change between statements.
  • Start: a = "AB" (len 2), b = "CD" (len 2)
  • a = a + b  →  a = "ABCD" (len 4). b still equals "CD".
  • b = a.substring(0, a.length() - b.length())  →  a.length() = 4, b.length() = 2 (b is still "CD"!). So a.substring(0, 2) = "AB". b is now "AB".
  • a = a.substring(b.length())  →  b.length() is now 2 (b is "AB"). So a.substring(2) = "CD". a is now "CD".
  • Print a + b  →  "CD" + "AB" = "CDAB".
Trap (A) ignores the swap. (C, D) collapse to whichever original value the student tracked. The deep trap is the third statement: b.length() there equals the new b (which is "AB", len 2) — not the original. The values happen to coincide here because both originals were length 2; if the originals had different lengths, the answer would be different.
这是用字符串拼接来"不使用临时变量交换两个值"(swap without a temp)的经典写法。每一步都依赖 ab 当前的值,而它们在语句间不断变化。
  • 初始:a = "AB"(长度 2),b = "CD"(长度 2)
  • a = a + b  →  a = "ABCD"(长度 4)。b 仍然是 "CD"
  • b = a.substring(0, a.length() - b.length())  →  此时 a.length() = 4b.length() = 2b 还没变!)。所以 a.substring(0, 2) = "AB"b 现在变成 "AB"
  • a = a.substring(b.length())  →  现在 b.length()2b 已经是 "AB")。所以 a.substring(2) = "CD"a 现在变成 "CD"
  • 打印 a + b  →  "CD" + "AB" = "CDAB"
陷阱 (A) 忽略了交换。(C, D) 表示只跟踪了其中一个原值。深层陷阱在第三条语句:那里的 b.length() 等于 b(即 "AB",长度 2)—— 不是原始的 "CD"。这道题恰好因为两个原值都是长度 2 而能得到正确答案;如果原值长度不同,结果会变化。
Q12HARDAP MCMath.pow — Edge inputs + Cast

Value of (int) Math.pow(-3, 3) + (int) Math.pow(-3, -1) + (int) Math.pow(0, 0)?(int) Math.pow(-3, 3) + (int) Math.pow(-3, -1) + (int) Math.pow(0, 0) 的值?

Answer:答案: (C)
Three edge-case Math.pow evaluations, each followed by truncation toward zero:
  • Math.pow(-3, 3)  =  (-3)3 = -27.0. (int) -27.0 = -27.
  • Math.pow(-3, -1)  =  1 / (-3) ≈ -0.3333. Cast to int truncates toward zero, not toward minus-infinity, so (int) -0.3333 = 0 (not -1).
  • Math.pow(0, 0)  =  1.0 by Java's convention (matches the IEEE-754 spec). (int) 1.0 = 1.
  • Sum: -27 + 0 + 1 = -26.
Trap (A) -28 truncates -0.33 toward minus-infinity to -1 (Python-style, wrong for Java). Trap (B) -27 assumes Math.pow(0, 0) is 0. Trap (D) -25 assumes Math.pow(0, 0) is 1 AND that Math.pow(-3, -1) rounds to 1.
三个 Math.pow 的边界输入(edge input),每个都伴随向零截断:
  • Math.pow(-3, 3)  =  (-3)3 = -27.0(int) -27.0 = -27
  • Math.pow(-3, -1)  =  1 / (-3) ≈ -0.3333转换为 int 时向零截断,不是向负无穷取整,所以 (int) -0.3333 = 0(不是 -1)。
  • Math.pow(0, 0)  =  1.0(Java 约定,与 IEEE-754 规范一致)。(int) 1.0 = 1
  • 求和:-27 + 0 + 1 = -26
陷阱 (A) -28-0.33 向负无穷取整为 -1(Python 风格,对 Java 错误)。陷阱 (B) -27 误以为 Math.pow(0, 0)0。陷阱 (D) -25 既假设 Math.pow(0, 0)1,又把 Math.pow(-3, -1) 当成"四舍五入"得到 1
Q13HARDAP MCConcatenation Order

s = "" + 1 + 2 + 3; t = 1 + 2 + 3 + ""; println(s.length() + t.length());s = "" + 1 + 2 + 3; t = 1 + 2 + 3 + ""; println(s.length() + t.length());

Answer:答案: (C)
The + operator is left-associative. When one operand is a String, it becomes string concatenation; otherwise it's integer addition.
  • s = "" + 1 + 2 + 3  →  ("" + 1) = "1"  →  "1" + 2 = "12"  →  "12" + 3 = "123". s.length() = 3.
  • t = 1 + 2 + 3 + ""  →  1 + 2 = 3 (int)  →  3 + 3 = 6 (int)  →  6 + "" = "6". t.length() = 1.
  • Sum: 3 + 1 = 4.
Hard-question payoff: same operands, same operator, very different result — the position of the empty string matters.
+ 操作符是左结合(left-associative)的。当某一侧是 String 时,+ 是字符串拼接;否则是整数加法。
  • s = "" + 1 + 2 + 3  →  ("" + 1) = "1"  →  "1" + 2 = "12"  →  "12" + 3 = "123"s.length() = 3
  • t = 1 + 2 + 3 + ""  →  1 + 2 = 3(int) →  3 + 3 = 6(int) →  6 + "" = "6"t.length() = 1
  • 求和:3 + 1 = 4
难题的精髓:相同的操作数、相同的操作符,结果却截然不同——空字符串的位置决定一切。
Q14HARDAP MCWrapper — Autoboxing

Integer a = 5; Integer b = 7; int c = a + b; →?Integer a = 5; Integer b = 7; int c = a + b; 的结果?

Answer:答案: (A)
Java auto-unboxes Integer to int when arithmetic operators are applied:
  • a + b → unboxes a and b5 + 712 (int)
  • Assignment to int c is straightforward — both sides are int
  • println(c) prints 12
Trap (B) is the trap of treating + as String concatenation (it would do that only if one operand were a String). (D) would only fire if one of the wrappers were null — both are valid here.
在算术操作符作用下,Java 自动拆箱auto-unboxing):把 Integer 拆为 int
  • a + b → 把 ab 拆箱 → 5 + 712int
  • 赋给 int c,两侧均为 int,直接赋值
  • println(c) 打印 12
陷阱 (B) 把 + 当成字符串拼接(只有当至少一侧是 String 时才会拼接)。(D) 仅在某个包装类(wrapper)为 null 时才会触发 —— 这里两者都有效。
Q15HARDAP MCString — compareTo, case & lex

a = "apple"; b = "Apple"; c = "apricot"; p = a.compareTo(b); q = a.compareTo(c); println((p > 0) + " " + (q > 0));a = "apple"; b = "Apple"; c = "apricot"; p = a.compareTo(b); q = a.compareTo(c); println((p > 0) + " " + (q > 0));

Answer:答案: (B)
compareTo walks character by character. The first differing position determines the sign: it returns (Unicode of this char) − (Unicode of other char).
  • "apple".compareTo("Apple")  →  first differing char is index 0: 'a' (Unicode 97) vs 'A' (Unicode 65). Returns 97 - 65 = +32. So p > 0 is true.
  • "apple".compareTo("apricot")  →  index 0: 'a' == 'a'. Index 1: 'p' == 'p'. Index 2: 'p' (112) vs 'r' (114). Returns 112 - 114 = -2. So q > 0 is false.
  • Println  →  "true false" (the booleans auto-convert to String).
Two pitfalls baked in:
  • Case sensitivity. Lowercase letters have HIGHER Unicode values than uppercase, so "apple" > "Apple". Trap (D) assumes upper-comes-first like an English dictionary.
  • Stop at first difference. Once 'p' vs 'r' resolves, the rest of the strings ("le" vs "icot") is ignored. Trap (A) compares the full strings as if they were numbers.
compareTo 逐字符比较。第一个不同位置决定结果的正负号:返回 (当前字符的 Unicode) − (对方字符的 Unicode)
  • "apple".compareTo("Apple")  →  第一个不同字符在下标 0:'a'(Unicode 97)vs 'A'(Unicode 65)。返回 97 - 65 = +32。所以 p > 0true
  • "apple".compareTo("apricot")  →  下标 0:'a' == 'a'。下标 1:'p' == 'p'。下标 2:'p'(112)vs 'r'(114)。返回 112 - 114 = -2。所以 q > 0false
  • 打印  →  "true false"(布尔值自动转换为 String)。
两个常见陷阱:
  • 区分大小写(case-sensitive)。小写字母的 Unicode 值比大写更高,所以 "apple" > "Apple"。陷阱 (D) 以为像英文字典那样"大写先排"。
  • 在第一个差异处停止。一旦 'p' vs 'r' 决出胜负,字符串剩下的部分("le" vs "icot")就被忽略。陷阱 (A) 把完整字符串当作数字一样从头到尾比较。