Java语法
Java 语法是 Java 编程的入口。语法看起来简单,但很多线上 bug 都来自基础细节:整数溢出、浮点精度、switch 漏写 break、== 比较引用、循环边界错误、变量作用域不清晰等。
学习语法时要同时关注三件事:
- 代码怎么写。
- 编译器怎么理解。
- 运行时可能出现什么坑。
# 1. Java 源文件基本结构
一个简单 Java 文件:
package com.example;
import java.util.List;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello Java");
}
}
结构说明:
| 部分 | 作用 |
|---|---|
package | 声明类所在包 |
import | 导入其他包中的类 |
class | 定义类 |
main | Java 应用程序入口方法 |
执行链路:
Hello.java
│ javac
▼
Hello.class
│ java com.example.Hello
▼
JVM 执行 main 方法
# 2. 标识符
标识符用于命名类、方法、变量、包等。
规则:
- 可以包含字母、数字、下划线、美元符号。
- 不能以数字开头。
- 不能使用 Java 关键字。
- 大小写敏感。
命名习惯:
| 类型 | 习惯 |
|---|---|
| 类名 | 大驼峰,如 UserService |
| 方法名 | 小驼峰,如 getUserName |
| 变量名 | 小驼峰,如 userName |
| 常量名 | 全大写加下划线,如 MAX_SIZE |
| 包名 | 全小写,如 com.example.service |
# 3. 关键字
Java 关键字是语言保留词,不能作为标识符。
常见分类:
| 分类 | 关键字 |
|---|---|
| 访问控制 | public、protected、private |
| 类和接口 | class、interface、extends、implements、enum |
| 修饰符 | static、final、abstract、synchronized、volatile、transient |
| 流程控制 | if、else、switch、case、for、while、do、break、continue、return |
| 异常 | try、catch、finally、throw、throws |
| 对象相关 | new、this、super、instanceof |
| 基本类型 | byte、short、int、long、float、double、char、boolean |
| 包相关 | package、import |
true、false、null 严格来说是字面量,不是普通标识符,也不能作为变量名。
# 4. 数据类型总览
Java 类型分为基本类型和引用类型。
Java 数据类型
├─ 基本类型
│ ├─ 整数:byte、short、int、long
│ ├─ 浮点:float、double
│ ├─ 字符:char
│ └─ 布尔:boolean
└─ 引用类型
├─ 类
├─ 接口
├─ 数组
├─ 枚举
└─ 注解
基本类型保存的是值本身,引用类型变量保存的是对象引用。
# 5. 基本类型范围
| 类型 | 位数 | 默认值 | 说明 |
|---|---|---|---|
byte | 8 | 0 | 小整数 |
short | 16 | 0 | 小整数 |
int | 32 | 0 | 默认整数类型 |
long | 64 | 0L | 大整数 |
float | 32 | 0.0f | 单精度浮点 |
double | 64 | 0.0d | 默认浮点类型 |
char | 16 | '\u0000' | UTF-16 代码单元 |
boolean | JVM 未规定具体位数 | false | 布尔值 |
注意:
long value = 10000000000L; // L 不能省
float rate = 3.14f; // f 不能省
整数字面量默认是 int,浮点字面量默认是 double。
# 6. 类型转换
# 6.1 自动类型转换
小范围类型可以自动转换为大范围类型:
int a = 10;
long b = a;
double c = b;
大致方向:
byte -> short -> int -> long -> float -> double
char -> int
# 6.2 强制类型转换
大范围转小范围需要强制转换,可能丢失数据:
int a = 130;
byte b = (byte) a;
System.out.println(b); // -126
这是因为 byte 只有 8 位,130 超出范围后发生截断。
# 6.3 表达式类型提升
byte、short、char 参与运算时通常会提升为 int:
byte a = 1;
byte b = 2;
// byte c = a + b; // 编译错误
int c = a + b;
# 7. 运算符
常见运算符:
| 类型 | 运算符 |
|---|---|
| 算术 | +、-、*、/、% |
| 关系 | >、<、>=、<=、==、!= |
| 逻辑 | &&、||、! |
| 位运算 | &、|、^、~、<<、>>、>>> |
| 赋值 | =、+=、-=、*=、/= |
| 三元 | condition ? a : b |
# 8. && 和 & 的区别
if (obj != null && obj.isValid()) {
// safe
}
&& 是短路与:左边为 false 时,右边不会执行。
& 用在 boolean 表达式时也表示与,但不短路:
if (obj != null & obj.isValid()) {
// obj 为 null 时会 NPE
}
开发中条件判断优先使用 && 和 ||。
# 9. 自增自减
int i = 1;
int a = i++; // a = 1, i = 2
int b = ++i; // i = 3, b = 3
规则:
i++:先使用,再自增。++i:先自增,再使用。
不要写过于复杂的自增表达式,可读性很差。
# 10. 流程控制
# 10.1 if-else
if (score >= 90) {
level = "A";
} else if (score >= 60) {
level = "B";
} else {
level = "C";
}
适合条件范围判断和复杂布尔表达式。
# 10.2 switch
传统写法:
switch (level) {
case "A":
System.out.println("excellent");
break;
case "B":
System.out.println("pass");
break;
default:
System.out.println("failed");
}
箭头写法:
switch (level) {
case "A" -> System.out.println("excellent");
case "B" -> System.out.println("pass");
default -> System.out.println("failed");
}
传统 switch 容易漏写 break,箭头写法能减少 fall-through 问题。
# 11. 循环
# 11.1 for
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
适合知道循环次数或需要索引的场景。
# 11.2 enhanced for
for (String item : list) {
System.out.println(item);
}
适合只读遍历,不适合在遍历中删除集合元素。
# 11.3 while
while (condition) {
doSomething();
}
适合循环次数不确定的场景。
# 11.4 do-while
do {
readInput();
} while (needContinue);
至少执行一次。
# 12. 变量作用域
public void method() {
int a = 1;
if (a > 0) {
int b = 2;
}
// b 在这里不可见
}
变量作用域由代码块 {} 决定。作用域越小,代码越容易理解,也越不容易误用。
# 13. 开发中常见坑
# 13.1 整数溢出
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // -2147483648
整数溢出不会自动抛异常。金额、库存、计数等场景要特别小心。
# 13.2 浮点精度
System.out.println(0.1 + 0.2); // 0.30000000000000004
金额计算不要使用 float 或 double,应使用 BigDecimal 或以分为单位的整数。
# 13.3 == 比较引用
基本类型用 == 比较值,引用类型用 == 比较引用地址。字符串内容比较应使用 equals()。
# 13.4 switch 漏写 break
传统 switch 如果漏写 break,会继续执行后续 case。除非明确需要 fall-through,否则要避免。
# 14. Tips 快问快答
Q:Java 中 char 能表示所有 Unicode 字符吗?
A:不能完全表示。char 是 16 位 UTF-16 代码单元,一些补充字符需要两个 char 表示。
Q:为什么 byte + byte 结果是 int?
A:Java 对小整数类型参与运算时会做整型提升,结果通常是 int。
Q:&& 和 & 在条件判断中怎么选?
A:大多数条件判断用 &&,因为它短路且更安全。
Q:金额为什么不用 double?
A:二进制浮点无法精确表示很多十进制小数,金额应使用 BigDecimal 或整数分。
# 15. 总结
Java 语法的重点不是记住每个关键字,而是理解类型、表达式、作用域和流程控制背后的规则。很多复杂 bug 都能追溯到这些基础细节。