有了第 1 章的环境,这一章开始写真正的代码:变量、八种基本类型、运算符、类型转换、控制台输入输出。这些是所有语言共有的地基,但 Java 的地基有几块和 C 长得不一样:类型长度跨平台定死、收窄转换必须显式、boolean 和整数彻底分家。本章把这些差异点讲透,它们日后都是编译器替你拦 bug 的本钱。
| 类型 | 字节 | 范围/取值 | 典型用途 |
|---|---|---|---|
| byte | 1 | -128 ~ 127 | 字节流、文件 |
| short | 2 | -32768 ~ 32767 | 少见(兼容遗留) |
| int | 4 | ±21 亿 | 默认整数 |
| long | 8 | ±922 京 | 时间戳、大数 |
| float | 4 | IEEE 754 单精度 | 少见(精度不够用) |
| double | 8 | IEEE 754 双精度 | 默认浮点 |
| char | 2 | UTF-16 码元 | 单字符 |
| boolean | - | true / false | 逻辑 |
两个和 C 的关键差异(计组课第 2 章的知识在这直接兑现)。其一,长度是规范承诺而非实现选择:C 的 int「至少 16 位,通常与机器字长相关」,Java 的 int 永远 4 字节,跨平台行为可预测。其二,boolean 独立于整数:C 里 if (1) 合法(非零即真),Java 里 if (1) 直接编译错误,「1 和 true 混用」这类 C 的经典笔误在 Java 不可能发生。
int width = 10; // 变量
final int MAX = 100; // 常量:final 修饰,再赋值编译错误
变量必须先声明后使用,局部变量还要先初始化(C 的「未初始化读是 UB」,计组第 14 章,在 Java 里直接编译错误拦下:variable x might not have been initialized)。
算术 + - * / %、赋值 = += -=、比较、逻辑 && || ! 都和 C 同款。留神两处:
整数除法向零截断:7 / 2 == 3,-7 / 2 == -3(C 同款);除零是运行时异常 ArithmeticException: / by zero,不是 C 的 UB(计组第 14 章)。浮点比较别用 ==:计组第 2 章讲过 0.1 + 0.2 的故事,Java 照样中招,判断相等用 Math.abs(a - b) < 1e-9,金额计算用 BigDecimal。
前缀后缀 ++ 的坑:单独成句没区别,混进表达式里 a[i++] 和 a[++i] 的可读性灾难,第 5 章(数组)再展开。风格建议:自增自减永远单独成句,别炫技。
int i = 42;
long l = i; // 自动(拓宽):4 字节装进 8 字节,无损
double d = i; // 自动:int → double
int j = (int) d; // 强制(收窄):必须写 (int),丢小数
byte b = (byte) 200; // 强制:溢出回绕,得 -56(补码回绕,计组第 2 章)
Java 的立场:拓宽自动,收窄必须显式。C 会给警告但放行,Java 直接编译错误强制你表态。这个设计省掉一整类事故:C 代码里「double 赋给 int 丢了小数没人发现」的 bug,Java 里根本写不出来。long 字面量要带 L 后缀(9999999999L,不带会被当 int 编译错误:integer number too large),这个报错初学每周都会见一次,见到就检查数字后面有没有 L。
import java.util.Scanner;
public class Greet {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("你叫什么?");
String name = sc.nextLine(); // 读一整行
System.out.print("几岁?");
int age = sc.nextInt(); // 读一个 int
System.out.printf("%s 今年 %d 岁%n", name, age);
}
}
输出三兄弟:print 不换行、println 换行、printf 格式化(和 C 的 printf 同一套 %d %s 模板,但 %n 是跨平台换行)。输入靠 Scanner 类(new Scanner(System.in),语法细节第 6、8 章补):nextInt 读整数、nextLine 读整行、hasNextInt 判断下一个是不是整数。
一个真实的坑我先替你踩了:nextInt 之后紧跟 nextLine 会读到空串。nextInt 只取走数字,留下换行符在缓冲区,下一个 nextLine 一口吞掉它,你得到空字符串。修法:nextInt 后补一个 sc.nextLine(); 把残留在往掉,或统一用 nextLine 读、再 Integer.parseInt 转换。
经典题「求三位数各位之和」,演示把数学翻译成代码的完整过程:
public class DigitSum {
public static void main(String[] args) {
int n = 472;
int ones = n % 10; // 个位:472 % 10 = 2
int tens = n / 10 % 10; // 十位:47 / 10 = 47,再 % 10 = 7
int hunds = n / 100; // 百位:472 / 100 = 4
System.out.println(ones + tens + hunds); // 13
}
}
取余拆位、整除去尾,是这两章最常用的组合拳。那道「不借助临时变量交换两个数」的智力题(a=a+b; b=a-b; a=a-b;)可以玩,但工程里请老老实实用临时变量(可读性第一,且加法可能溢出),Java 还有更简单的选择,第 5 章讲到数组时再提。
integer number too large,十之八九是这个。int 在 ±21 亿封顶,阶乘 13! 就爆了;要大数换 long,再大换 BigInteger。if (x = 5) 编译不过(x 是 int 不是 boolean),这是 Java 静态类型送你的礼物;但 boolean 变量的 if (flag = true) 依然合法,手滑照样有。System.out.println(Integer.MAX_VALUE) 和 Integer.MIN_VALUE 打出来是多少;Integer.MAX_VALUE + 1 的结果是什么(溢出回绕,和计组第 2 章对答案)?(int) 3.99、(int) -3.99、(byte) 130、(byte) -130 各是多少?对照计组课第 2 章的截断与回绕规则。