跳到主内容
创建时间: 2026-09-16最后更新: 2026-09-16

第一篇文章已经运行了 HealthController,其中出现了变量、字符串、方法调用和 return。先运行一个结果再回头拆解语法,可以知道这些符号最终解决什么问题;但在继续学习类与 Spring 之前,还需要把 Java 程序最基本的执行规则补齐。

本章使用独立的 JavaSyntaxDemo 练习,不修改已经能够运行的心跳接口。代码只依赖 JDK,可以直接使用 javac 编译并通过 java 运行。

1. 语句与代码块

Java 程序由类型、方法和语句组成。下面的 main 方法是程序入口,JVM 从方法体的第一条语句开始顺序执行:

src/main/java/cn/usehook/spring/basics/JavaSyntaxDemo.java
1
package cn.usehook.spring.basics;
2
3
public class JavaSyntaxDemo {
4
5
public static void main(String[] args) {
6
String service = "spring";
7
System.out.println(service);
8
}
9
}

这里先把 package、class 和 main 当作运行代码所需的外围结构,只观察 main 花括号中的两条语句。类与对象、包和访问控制会在后面两篇逐项展开,不需要现在一次记住全部声明。

大多数语句以分号结束,花括号表示一个代码块。缩进不会改变 Java 语义,但统一使用四个空格可以直接看出代码所属范围。// 后面是单行注释,/* ... */ 是多行注释;注释应该解释必要的原因,而不是重复代码本身。

变量只在声明它的代码块及其子代码块中可见:

block-scope.java
1
if (service != null) {
2
String message = "checking " + service;
3
System.out.println(message);
4
}
5
6
// 这里不能访问 message

这就是局部作用域。缩小变量的作用域可以减少无关代码修改它的机会,也让读者更容易判断一个值在哪里有效。

2. 变量与数据类型

变量声明由类型、名称和可选初始值组成:

variables.java
1
String service = "spring";
2
int attempts = 3;
3
boolean available = true;

Java 是静态类型语言。attempts 被声明为 int 后,只能接收兼容的整数值,不能在运行过程中变成字符串。编译器会在程序运行前检查大部分类型错误。

Java 有八种基本类型:

类型表达的数据示例
byte、short、int、long不同范围的整数int attempts = 3
float、double浮点数double elapsed = 12.5
char单个 UTF-16 代码单元char level = 'A'
boolean真或假boolean available = true

整数业务值通常先使用 int,需要更大范围时使用 long。浮点数存在二进制表示误差,不适合直接表示要求精确的小数金额;这类数据通常使用 BigDecimal。char 使用单引号,String 使用双引号,而且 String 是引用类型,不是基本类型。

每种基本类型都有包装类型,例如 int 对应 Integer,boolean 对应 Boolean。泛型集合不能写 List<int>,需要使用 List<Integer>;自动装箱可以在很多场景下完成二者转换,但包装类型还可能是 null。

引用类型变量保存的是对象引用。引用可以暂时没有指向对象,此时值是 null:

reference.java
1
String service = null;
2
3
if (service != null) {
4
System.out.println(service.toUpperCase());
5
}

对 null 调用方法会抛出 NullPointerException。不要把 null 当作所有缺省值的通用表达;字段是否允许缺失应该在方法契约、校验规则或后面学习的 Optional 中明确说明。

局部变量的类型足够明显时可以使用 var:

local-variable.java
1
var service = "spring";

var 仍然会在编译期推断出固定的 String 类型,不会把 Java 变成动态类型语言。它只能用于有初始值的局部变量,不能用于字段、方法参数或返回类型。

3. 表达式与运算符

表达式计算出一个值。赋值号右侧的 attempts + 1 先计算,再把结果写回变量:

expressions.java
1
int attempts = 2;
2
attempts = attempts + 1;
3
4
boolean retryable = attempts < 5;
5
boolean healthy = retryable && attempts > 0;

常用运算符可以分为几类:

类型运算符作用
算术+、-、*、/、%数值计算与求余
比较==、!=、>、>=、<、<=得到 boolean 结果
逻辑&&、`
赋值=、+=、-=保存计算结果

&& 和 || 使用短路计算。left && right 在左侧为 false 时不会计算右侧,因此下面的空值检查是安全的:

short-circuit.java
1
boolean usable = service != null && !service.isBlank();

基本数值可以使用 == 比较值,引用类型的 == 比较是否指向同一个对象。字符串内容应该使用 equals(),或者把已知非空常量放在前面:

string-comparison.java
1
boolean up = "UP".equals(status);

String 的 + 可以连接文本,但复杂日志不要提前拼接字符串;日志章节会使用参数化日志,让框架按日志级别决定是否格式化内容。

4. 条件分支

if 根据布尔条件决定是否执行代码块:

condition.java
1
if (service == null || service.isBlank()) {
2
System.out.println("service is required");
3
} else {
4
System.out.println("checking " + service);
5
}

条件较多时可以继续使用 else if,但嵌套层级过深通常意味着一个方法承担了太多判断。能够提前结束的方法可以先处理非法条件,再执行正常路径。

已知选项之间的分支适合使用 switch。Java 21 的 switch 表达式可以直接产生值:

switch-expression.java
1
String message = switch (status) {
2
case "UP" -> "service is available";
3
case "DOWN" -> "service is unavailable";
4
default -> "service status is unknown";
5
};

箭头右侧是对应分支的结果,default 处理没有明确列出的值。后面的分页文章会先把外部排序选项转换成枚举,再使用 switch 选择预先写好的 SQL 排序片段。

5. 循环与数组

数组保存一组相同类型、长度固定的元素:

arrays.java
1
String[] services = {"spring", "postgresql", "redis"};

数组下标从 0 开始,services[0] 是第一个元素。访问小于 0 或大于等于数组长度的下标会抛出 ArrayIndexOutOfBoundsException。

只需要依次读取元素时,增强 for 循环最清楚:

enhanced-for.java
1
for (String current : services) {
2
System.out.println(current);
3
}

需要下标时可以使用普通 for:

indexed-for.java
1
for (int index = 0; index < services.length; index++) {
2
System.out.println(index + ": " + services[index]);
3
}

while 会在每次执行前判断条件,适合循环次数不能提前确定的场景:

while-loop.java
1
int attempts = 0;
2
3
while (attempts < 3) {
4
attempts++;
5
System.out.println("attempt " + attempts);
6
}

循环必须让结束条件最终能够改变,否则会形成无限循环。业务数据经常需要动态增加和删除,后面的集合章节会使用 List 和 Map 替代固定长度数组。

6. 方法

方法把一段操作命名,并通过参数接收输入、通过返回值交付结果:

src/main/java/cn/usehook/spring/basics/JavaSyntaxDemo.java
01
package cn.usehook.spring.basics;
02
03
public class JavaSyntaxDemo {
04
05
public static void main(String[] args) {
06
String result = normalizeService(" PostgreSQL ");
07
System.out.println(result);
08
}
09
10
static String normalizeService(String service) {
11
if (service == null || service.isBlank()) {
12
throw new IllegalArgumentException("service is required");
13
}
14
15
return service.trim();
16
}
17
}

normalizeService 的返回类型是 String,参数也是 String。执行到 return 后,当前方法结束并把结果交给调用方。返回类型为 void 的方法不返回结果;它仍然可以使用单独的 return; 提前结束。

同一个类可以声明名称相同但参数列表不同的方法,这叫方法重载:

method-overloading.java
1
static String healthMessage() {
2
return healthMessage("spring");
3
}
4
5
static String healthMessage(String service) {
6
return service + " is UP";
7
}

编译器根据参数数量和类型选择方法。仅改变返回类型不能形成重载,因为调用位置可能无法据此判断应该选择哪个方法。

编译并运行练习类:

terminal.bash
1
./mvnw compile
2
java -cp target/classes cn.usehook.spring.basics.JavaSyntaxDemo

变量让程序保存值,分支和循环控制语句执行顺序,方法把操作组织成可以复用的单元。但这些局部数据会在方法结束后离开作用域。下一篇通过类与对象把状态和行为组合成自己的类型,再讨论对象怎样创建、保存和比较。