Companion to the AP-Style MC Practice SetAP 风格选择题练习的解析配套
Unit 1: Using Objects and Methods第 1 单元:使用对象与方法CSA
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)单独提示。
Print: x / y + x % y with x = 17, y = 4?当 x = 17, y = 4 时,x / y + x % y 的输出是?
4567x 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)4 + 1 = 5x == (x/y) * y + (x%y). Trap (A) skips the modulus.
x 与 y 都是 int,所以 / 是整除(integer division),% 是整数取模(modulus)。
17 / 4 = 4(将 4.25 向零截断,truncate toward zero)17 % 4 = 1(因为 17 = 4·4 + 1)4 + 1 = 5x == (x/y) * y + (x%y)。陷阱选项 (A) 漏掉了取模那一项。
Random even integer 2..10 inclusive?2 到 10(含端点)之间的随机偶数?
(int)(Math.random() * 5) * 2 + 2(int)(Math.random() * 9) + 22 * ((int)(Math.random() * 6) + 1)(int)(Math.random() * 5) * 2{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 包含进来。
a = "Java"; b = "Ja" + "va"; c = new String("Java").a = "Java";b = "Ja" + "va";c = new String("Java")。
a == b: true; a == c: true; a.equals(c): truea == b: true; a == c: false; a.equals(c): truea == b: false; a == c: false; a.equals(c): true"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..equals(). == on objects compares references.
String pool / constant folding)。
"Ja" + "va" 由两个编译期常量构成,所以编译器在编译期就把它折叠为单一字面量 "Java"。因为 a 已经把这个字面量放入字符串池,b 复用同一份引用:a == b 为 true。new String(...) 绕过字符串池。
c 被显式地分配到堆上作为新对象 —— 与 a 不是同一个引用,所以 a == c 为 false。但字符内容相同,所以 a.equals(c) 为 true。.equals();== 比较的是引用地址(reference equality)。
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;
4.04.55.02.5(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 / 4 → 10.0 / 4 → 2.5 (one operand double promotes the division)a / b → 10 / 4 → 2 (both int, integer division)2.5 + 2 = 4.5 (mixed double + int → double). 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 / 4 → 10.0 / 4 → 2.5(一侧为 double,整个除法被类型提升 type promotion 为 double)a / b → 10 / 4 → 2(两侧均为 int,整除)2.5 + 2 = 4.5(double + int 混合 → double)。陷阱 (C) 错误地把第二个 a/b 也类型提升(得到 2.5 + 2.5 = 5.0);(D) 忽略了第二项。
s = "ABCDEFG"; chain through t, u, v, then print v + u.substring(u.length() - 1).s = "ABCDEFG";依次得到 t、u、v,然后打印 v + u.substring(u.length() - 1)。
EFEFGEFGFGFGindexOf / 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""EF" + "G" = "EFG""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""G"。(C) 把最后一个字符多算了一次。(D) 忘了 u 本身就是从 'E' 开始的 —— 学生常常误用 s 的下标重新数。
a = "go"; b = a; a = a + a; b += a; println(b);a = "go"; b = a; a = a + a; b += a; println(b);
gogogogogogogogogogogogogogob refers to at the moment each statement runs.
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."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。"gogogogo" 出现在你把 b + a 中的 b 误用成新值 a 时(即又一次算了 a + a)。(D) 进一步重复加倍。(A) 完全忽略了第二条语句。
Value of (Integer.MAX_VALUE + 1) / 2?(Integer.MAX_VALUE + 1) / 2 的值?
1073741824-1073741824Integer.MAX_VALUErun-time error)。Integer.MAX_VALUE = 2,147,483,647MAX_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,824int 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,647MAX_VALUE + 1 超出了 32 位有符号 int 的范围,发生整数溢出(integer overflow),回绕到 Integer.MIN_VALUE = -2,147,483,648(默默回绕——Java 不会因为 int 溢出而抛错)(-2,147,483,648) / 2 = -1,073,741,824int 溢出不会抛出异常,算术运算只是"回绕"。要检测溢出可以用 Math.addExact(...),或者改用 long。陷阱 (A) 是不考虑溢出时的数学答案;(C) 忽略了溢出。
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);
6.07.014.0compile-time error)。z.
x / y = 7 / 2.0 — mixed int / double promotes to double → 3.53.5 * 2 = 7.0 (mixed → double)(int) 7.0 = 7 (cast to int truncates toward zero)int 7 to double z auto-widens to 7.0println(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 混合,提升为 double → 3.53.5 * 2 = 7.0(混合 → double)(int) 7.0 = 7(转换为 int 向零截断)int 7 赋给 double z,自动加宽为 7.0println(z) 输出 7.0。陷阱 (A) 错误地把括号当作 (int)(x / y) * 2 解释:(int)(7/2) = (int)3 = 3,再 * 2 = 6。(C) 跳过了类型转换那一步。
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()。
4567"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() = 4b = s.substring(2, 2 + 4 - 2) = s.substring(2, 4) → "OB", so b.length() = 24 + 2 = 6b.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() = 4b = s.substring(2, 2 + 4 - 2) = s.substring(2, 4) → "OB",所以 b.length() = 24 + 2 = 6b.length()。(B) 数错了 a(漏掉下标 5 的第二个 'B')。(D) 把 b 的右端点也算进字符里——substring(start, end) 是左闭右开(end 不包含)。
x = -17; y = 5; println((x/y) + " " + (x%y));x = -17; y = 5; println((x/y) + " " + (x%y));
-4 -3-3 -2-4 3-3 2int 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)."-3 -2". Trap (A) treats / as floor division (Python-style); (C) treats % as math-class modulo (always non-negative).
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) 把 % 当成数学课上的"非负模"。
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);
ABCDCDABABABCDCDa and b, which change between statements.
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".a + b → "CD" + "AB" = "CDAB".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)的经典写法。每一步都依赖 a 与 b 当前的值,而它们在语句间不断变化。
a = "AB"(长度 2),b = "CD"(长度 2)a = a + b → a = "ABCD"(长度 4)。b 仍然是 "CD"。b = a.substring(0, a.length() - b.length()) → 此时 a.length() = 4,b.length() = 2(b 还没变!)。所以 a.substring(0, 2) = "AB"。b 现在变成 "AB"。a = a.substring(b.length()) → 现在 b.length() 是 2(b 已经是 "AB")。所以 a.substring(2) = "CD"。a 现在变成 "CD"。a + b → "CD" + "AB" = "CDAB"。b.length() 等于新 b(即 "AB",长度 2)—— 不是原始的 "CD"。这道题恰好因为两个原值都是长度 2 而能得到正确答案;如果原值长度不同,结果会变化。
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) 的值?
-28-27-26-25Math.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.-27 + 0 + 1 = -26.-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。-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。
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());
2346+ 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.3 + 1 = 4.+ 操作符是左结合(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。Integer a = 5; Integer b = 7; int c = a + b; →?Integer a = 5; Integer b = 7; int c = a + b; 的结果?
1257Integer + Integer is not defined.编译错误:Integer + Integer 未定义。Integer cannot be auto-unboxed.运行时错误:Integer 无法自动拆箱。Integer to int when arithmetic operators are applied:
a + b → unboxes a and b → 5 + 7 → 12 (int)int c is straightforward — both sides are intprintln(c) prints 12+ 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.
auto-unboxing):把 Integer 拆为 int:
a + b → 把 a 与 b 拆箱 → 5 + 7 → 12(int)int c,两侧均为 int,直接赋值println(c) 打印 12+ 当成字符串拼接(只有当至少一侧是 String 时才会拼接)。(D) 仅在某个包装类(wrapper)为 null 时才会触发 —— 这里两者都有效。
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));
true truetrue falsefalse truefalse falsecompareTo 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."true false" (the booleans auto-convert to String)."apple" > "Apple". Trap (D) assumes upper-comes-first like an English dictionary.'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 > 0 为 true。"apple".compareTo("apricot") → 下标 0:'a' == 'a'。下标 1:'p' == 'p'。下标 2:'p'(112)vs 'r'(114)。返回 112 - 114 = -2。所以 q > 0 为 false。"true false"(布尔值自动转换为 String)。case-sensitive)。小写字母的 Unicode 值比大写更高,所以 "apple" > "Apple"。陷阱 (D) 以为像英文字典那样"大写先排"。'p' vs 'r' 决出胜负,字符串剩下的部分("le" vs "icot")就被忽略。陷阱 (A) 把完整字符串当作数字一样从头到尾比较。