☕ 第六章 Java API
📄 一句话概括

String 是「铁憨憨」:用双引号写的都是 String 对象;它自己永远不可变,一切"修改"都返回新对象;比内容一律 equals——常量池和 == 的坑 4.5 埋的钩子这节收网。

💡 先修课

4.5 Object类(equals vs == 的本体 + String 常量池坑的出处)、6.1 包装类("比内容用 equals"刚立过规矩)、第二章 Java编程基础/2.3 Java中的运算符(+ 拼接的雏形)。


1. String 是什么?——用引号写的都是对象

String s = "abc";       // "abc" 本身就是一个 String 对象
String t = new String("abc");   // 也能 new,但不推荐(原因见下)

之前我们一直在用 String 却没正式介绍它:打印、拼接、new Person("波仔") 传名字……现在补上户口。它的三个身份:

  1. 引用类型:String 是类,变量里存的是地址不是内容
  2. 不可变(Immutable):对象内容一旦生成就改不了
  3. 自带常量池:字面量创建时进"字符串池"复用

2. 两种创建方式:房间分配完全不同

String s1 = "abc";              // 字面量:常量池里找/建 "abc",s1 指过去
String s2 = "abc";              // 池里已有,直接共用 → s1 == s2 为 true
String s3 = new String("abc");  // new:堆里再开一间新房 → s1 == s3 为 false
方式 内存里发生什么 建议
"abc" 字面量 常量池查重,有就复用 日常用这个
new String("abc") 堆里造新对象 + 常量池可能再建一间,一个 new 造两个对象 别用,浪费
⚠️ 判空字符串的标准姿势

"abc".equals(输入) 优于 输入.equals("abc")——把已知的常量写在前面,输入是 null 时返回 false 而不是炸 NPE(6.1 判空思路的 String 版)。

3. 不可变:String 是「铁憨憨」

String s = "abc";
s.replace('a', 'x');        // 白调了!返回的新对象没人接
System.out.println(s);      // abc——原对象纹丝不动

s = s.replace('a', 'x');    // 用变量接住返回值
System.out.println(s);      // xbc——s 改指了新对象,旧的"abc"等回收

没有哪个方法能改老对象的内容——所有"修改类"方法(replace/substring/toUpperCase…)都是返回新对象。这就是为什么方法必须接返回值:s = s.replace(...)。

📝 为什么设计成不可变?

① 常量池复用的前提(内容会变就没法共用);② 多线程下天然安全;③ 字符串用得极多,JVM 敢做深度优化。先知道结论,原理到后面章节自然懂。

4. == 与 equals:老规矩再上岗

常量池的巧合(s1 == s2 为 true)不能依赖——就像 6.1 里 127 的巧合一样。规矩:比字符串内容,永远写 equals,别用 ==。(4.5 埋的坑 → 6.1 包装类 → 本节,三连收网。)

5. 常用 API 速查(10 个,用熟够本)

String s = "Hello,Java";

s.length()              // 10:长度(数组的 length 没括号,String 的有!)
s.charAt(1)             // 'e':按下标取字符
s.equals("Hello,Java")  // true:比内容
s.substring(6)          // "Java":从下标 6 截到尾
s.substring(0, 5)       // "Hello":[0,5) 含头不含尾
s.indexOf("Java")       // 6:找位置,找不到返回 -1
s.replace("Java", "World")  // "Hello,World":替换(返回新对象)
s.split(",")            // ["Hello", "Java"]:按逗号切成数组
s.toUpperCase()         // "HELLO,JAVA"
s.trim()                // 去掉两端空格
⚠️ 两颗高频雷

① substring(0, 5) 含头不含尾——取下标 0~4,下标 5 不在内。 ② charAt(下标) 越界 → StringIndexOutOfBoundsException(5.2 收容所名单又 +1)。

6. 拼接:+ 能用,但别在循环里狂用

String msg = "姓名:" + name + ",成绩:" + score;   // 少量拼接,+ 舒服

+ 每拼一次就造一批新对象。循环里拼几百上千次,内存里全是垃圾——这种场景请 6.3 的 StringBuilder 出场(铺垫已埋)。

7. 动手环节(IDEA06 · Demo02)

  1. 写 Demo02,用字面量和 new 各创建一个 "abc",分别打印 == 和 equals 的结果。成功标准:false/true/true/false 四个结果说得清谁是谁。
  2. 30 秒实验 ①:不接返回值直接调 s.replace('a','x'),打印 s → 还是 "abc",亲眼看"铁憨憨"。
  3. 30 秒实验 ②:s.substring(0, 5) 打印 → 数一数含头不含尾。
  4. 30 秒实验 ③:s.charAt(100) → StringIndexOutOfBoundsException,读报错三段。
  5. 30 秒实验 ④:"abc".equals(输入) vs 输入.equals("abc"),输入传 null 对比——一个 false 一个 NPE。
  6. 综合小任务:把 "波仔,86,宜-male" 用 split 拆开,取出成绩转成 int(6.1 的 parseInt 上岗)。

8. 常见误区

⚠️ 五大误区
  1. 用 == 比字符串内容 → 常量池巧合会骗你,一律 equals。
  2. 以为 s.replace(...) 能改 s 自己 → 不接返回值等于白调,String 不可变。
  3. substring 当成"含头含尾" → [1,3) 取下标 1、2,不含 3。
  4. 数组的 length 和 String 的 length() 混淆 → 数组是属性没括号,String 是方法带括号。
  5. 循环里用 + 疯狂拼接 → 每次造新对象,该 StringBuilder 上场了。

9. 本节小结

要点 内容
身份 引用类型 + 不可变 + 自带常量池
创建 字面量(推荐,进池复用)vs new(造两个对象,别用)
不可变 所有"修改"返回新对象,必须用变量接
比较 == 比地址、equals 比内容,永远 equals
API length / charAt / substring / indexOf / replace / split 十件套
拼接 少量用 +,循环狂拼等 StringBuilder

10. 可折叠自测题

❓ Q1:`String s1="ab"; String s2="a"+"b"; s1==s2` 结果?

true。字面量 + 字面量在编译期就合并成 "ab",直接指向常量池同一个对象(编译器的优化)。但若是两个 String 变量相加,则是运行时造新对象,== 就可能是 false——又见"巧合不可依赖"。

❓ Q2:`s.substring(2, 6)` 取的是下标几到几?共几个字符?

下标 2、3、4、5,共 4 个字符——含头 [2,不含尾 6)。

❓ Q3:判断用户输入是否为 "yes",最稳妥的写法?

"yes".equals(input)——常量放前面,input 为 null 时返回 false 不炸 NPE。反过来写 input 为 null 就 NPE(String 是引用类型,null 上调方法必炸,5.1 空指针老朋友)。

❓ Q4:`String s="ABC"; s.toLowerCase(); System.out.println(s);` 打印什么?

ABC。toLowerCase 返回新对象但没人接,s 纹丝不动——不可变铁律。想变就 s = s.toLowerCase();。

❓ Q5(毕业题):把 `"语文=90,数学=85,英语=88"` 拆成三科成绩并求总分。写出代码。
String data = "语文=90,数学=85,英语=88";
String[] parts = data.split(",");
int sum = 0;
for (String p : parts) {                    // 增强 for 遍历(2.7 学的)
    String[] kv = p.split("=");
    sum += Integer.parseInt(kv[1]);         // 6.1 parseInt 上岗
}
System.out.println("总分:" + sum);          // 263

串起本节 split + 6.1 parseInt + 2.7 遍历——字符串和数字的接力棒传完了。


11. 下一步学什么


相关笔记