本课作品
制作控制台聊天格式化器
一条消息只要少一个空格,玩家名和正文就会挤成一团。String 让程序能够保存文字,也能寻找、比较和整理文字。这一课,我们从最小欢迎语开始,沿着索引找到每个位置,最后做出一台能把杂乱数据整理成清楚消息的聊天格式化器。
开始前:建议先完成第 1–4 课
01 / 14
空格也是信息

广播上出现“欢迎Alex加入红队”。你大概能猜懂,可一旦名字和消息更长,猜测就不够了。电脑不会自动替我们补标点和空格。字符串里的每个字符,都参与了最后的表达。
这里的“聊天栏”是作品的故事主题。程序先在控制台输出消息;真正发送到游戏服务器,需要以后学习服务器或模组接口。先把文字组织正确,是值得单独完成的一步。
- 拼接时留意边界与空格。
- 截取前先标出索引。
- 比较内容与清理格式,选择不同的方法。
02 / 14
String 保存文字,也返回新文字

片段:放在 main 中,单独运行
String player = "Alex";
String team = "Red";
String message = "找到钻石!";
String welcome = "欢迎 " + player + "!";
System.out.println(welcome);
System.out.println(team + ": " + message);运行结果
欢迎 Alex!
Red: 找到钻石!String 的 S 要大写。"player" 是一段固定文字,player 则读取变量。"欢迎" + player 会得到“欢迎Alex”;要留空格,就明确写 "欢迎 "。
03 / 14
+ 会算,也会连接
- 队伍[RED]
- 等级Lv.8
- 玩家Alex:
- 正文Hi!
连接各部分时,空格和标点要明确写在字符串里。
片段:放在 main 中,单独运行
int level = 8;
System.out.println("等级=" + level + 1);
System.out.println("等级=" + (level + 1));
int diamonds = 3, bonus = 2;
System.out.println("总钻石=" + (diamonds + bonus));
String levelText = String.valueOf(level);
System.out.println("Lv." + levelText);运行结果
等级=81
等级=9
总钻石=5
Lv.8第一行从左到右连接:先得到“等级=8”,再接上“1”。第二行的括号先算8+1,再把9接进消息。看到数字和文字混在一起,先决定哪些部分是计算。
04 / 14
索引从0开始,长度从数量出发
| 索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 字符 | C | R | E | E | P | E | R |
length() = 7;合法索引为0到6。charAt(0)是C,charAt(6)是R。
片段:放在 main 中,单独运行
String mob = "CREEPER";
System.out.println(mob.length());
System.out.println(mob.charAt(0));
System.out.println(mob.charAt(mob.length() - 1));运行结果
7
C
R“有七个位置”和“最后的位置编号是六”并不矛盾:我们从0开始编号。charAt(7) 越过了末尾,空字符串也没有可以读取的第0个位置。
05 / 14
substring:左边拿,右边停
| 索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 字符 | D | I | A | M | O | N | D |
substring(0, 3)拿索引0、1、2,得到DIA;索引3的M不包含。
片段:放在 main 中,单独运行
String ore = "DIAMOND";
System.out.println(ore.substring(0, 3));
System.out.println(ore.substring(3));
System.out.println(ore.substring(ore.length() - 3));运行结果
DIA
MOND
OND两个参数分别是起点和结束边界。起点包含,结束边界不包含。只给一个起点时,就取到字符串末尾。先画编号,通常比反复试数字更快。
06 / 14
搜索:有没有,在哪里?
| 方法 | 所问的问题 | 返回 |
|---|---|---|
| contains | 有没有这段文字? | boolean |
| indexOf | 第一次出现在哪里? | int,未找到是-1 |
| startsWith | 开头是不是这段文字? | boolean |
| endsWith | 结尾是不是这段文字? | boolean |
片段:放在 main 中,单独运行
String msg = "I found diamond!";
System.out.println(msg.contains("diamond"));
System.out.println(msg.indexOf("diamond"));
System.out.println(msg.indexOf("emerald"));
System.out.println("/home now".startsWith("/"));
System.out.println(msg.endsWith("!"));运行结果
true
8
-1
true
true07 / 14
内容相同,未必是同一个对象

片段:放在 main 中,单独运行
String a = new String("diamond");
String b = new String("diamond");
System.out.println(a.equals(b));
System.out.println(a == b);
String role = null;
System.out.println("admin".equals(role));运行结果
true
false
falseequals 比较 String 的内容;这里的 == 比较两个引用是否指向同一个对象。这个例子明确创建了两个对象,所以内容相同,引用比较仍为 false。
08 / 14
大小写转换:接住返回的新结果
- 原始命令/Home
- 得到新结果toLowerCase
(Locale.ROOT) - 比较内容/home
与目标一致
片段:放在 main 中,单独运行
String command = "/Home";
String normalized = command.toLowerCase(java.util.Locale.ROOT);
System.out.println(normalized);
System.out.println(command.equalsIgnoreCase("/HOME"));
String team = "blue";
System.out.println(team.toUpperCase(java.util.Locale.ROOT));
System.out.println(command);运行结果
/home
true
BLUE
/Home最后一行仍是 /Home,因为我们没有给 command 重新赋值。对于固定格式的英文命令或标签,Locale.ROOT 让转换不依赖电脑当前的语言设置;暂时把它当成一个明确的转换选项。
09 / 14
两端空白与空内容,是两回事
| 数据 | 检查 | 结果 |
|---|---|---|
| " Alex " | length() | 10 |
| strip 后的 "Alex" | length() | 4 |
| "" | isEmpty() | true |
| " " | isEmpty() | false |
| " " | isBlank() | true |
片段:放在 main 中,单独运行
String rawName = " Alex ";
String cleanName = rawName.strip();
System.out.println(rawName.length());
System.out.println(cleanName.length());
System.out.println("<" + cleanName + ">");
System.out.println(" ".isEmpty());
System.out.println(" ".isBlank());运行结果
10
4
<Alex>
false
true10 / 14
转义与 null:看清隐藏的规则


片段:放在 main 中,单独运行
String quote = "Alex 说:\"Hi!\"";
String twoLines = "第一行\n第二行";
String path = "C:\\Minecraft";
System.out.println(quote);
System.out.println(twoLines);
System.out.println(path);
System.out.println("bad word".replace("bad", "good"));运行结果
Alex 说:"Hi!"
第一行
第二行
C:\Minecraft
good word反斜杠让某些字符进入字符串:\" 表示引号,\n 表示换行,\\ 表示一个反斜杠。它们在源代码里的样子,与运行后的结果不同。
片段:放在 main 中,单独运行
String player = null;
boolean valid = player != null && !player.isBlank();
System.out.println(valid);运行结果
false11 / 14
先设计聊天格式,再写代码

- 初始数据队伍、等级
玩家、消息 - 清理strip 两端空白
队伍改大写 - 组织[TEAM] Lv.等级
玩家: 消息 - 核对是否为空
是否以/开头
本作品先使用写在变量里的文字。键盘读取要到下一课才学习,所以现在只需修改这些变量,再运行核对。队伍、姓名、消息清理后都应非空;等级使用1到30的练习范围。
- 空白只在两端清理,保留消息内部的空格。
- 不对整条消息强制改大写,否则正文也会变。
- 输出格式固定,便于用样例逐字核对。
12 / 14
完整作品:清理、检查、组织
完整程序 · ChatFormatter.java
import java.util.Locale;
public class ChatFormatter {
public static void main(String[] args) {
String team = " red ", player = " Alex ";
String message = " /home ";
int level = 8;
team = team.strip().toUpperCase(Locale.ROOT);
player = player.strip();
message = message.strip();
boolean valid = !team.isBlank() && !player.isBlank()
&& !message.isBlank() && level >= 1 && level <= 30;
if (!valid) {
System.out.println("请检查队伍、名字、消息和等级");
return;
}
String chat = "[" + team + "] Lv." + level
+ " " + player + ": " + message;
System.out.println(chat);
System.out.println("命令=" + message.startsWith("/"));
}
}运行结果
[RED] Lv.8 Alex: /home
命令=true13 / 14
让测试覆盖不同边界
| 情况 | 修改项 | 主要结果 | 命令检查 |
|---|---|---|---|
| 正常 | 原始参考数据 | [RED] Lv.8 Alex: /home | true |
| 普通消息 | message = " Hi! " | [RED] Lv.8 Alex: Hi! | false |
| 大小写 | team = " ReD " | 仍是 [RED] | 不变 |
| 空名字 | player = " " | 只输出检查提示 | 不执行 |
| 越界等级 | level = 31 | 只输出检查提示 | 不执行 |
一张测试表同时写明“改什么”和“预期什么”,才能告诉你差异来自哪里。先只改一个字段,确认结果,再组合多个变化。空名字被拒绝时,不应继续读取它的首字符。
- 升级:输出 message.contains("diamond") 的结果,再测试大小写差异。
- 升级:为公告增加标题行,用 \n 将两行连起来。
- 升级:用 replace 替换一个明确的词,观察返回的新字符串。
14 / 14
把方法与意图连起来
- 组织+ 与空格
- 定位length / charAt
- 截取substring
- 比较equals
- 清理strip / 大小写
- 基础:用三个字符串变量做两行告示牌,明确写出换行和空格。
- 进阶:清理两端带空格的名字,核对原长度与新长度;非空时再取首尾字符。
- 创造:为三条探险事件写消息模板,包含玩家、地点和事件,再做一次搜索或替换。
30秒自测
① substring(1, 4) 包含哪些索引?② " ".isEmpty() 与 isBlank() 相同吗?③ 运行 name.strip() 后,name 会自动改变吗?
技术参考:Java SE 11 的 String API,docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html。
TAKE IT WITH YOU
下载本课 PDF
完整课文、代码、图解与练习,选择你需要的语言。
