开篇:一个"物资采购结算"的小程序
第一章我们看到了 Java 程序的骨架,这一章往里走,看看装数据的"容器"——类型。还是老规矩,先看一段活的代码:煤矿物资采购的结算程序,算出各种物料的金额并汇总。
public class PurchaseApp { public static void main(String[] args) { // 物料1:锚杆 String itemName1 = "锚杆"; int quantity1 = 1200; // 数量(根) double price1 = 18.5; // 单价(元) // 物料2:液压油 String itemName2 = "液压油"; int quantity2 = 40; // 数量(桶) double price2 = 320.0; // 单项金额 = 数量 × 单价 double amount1 = quantity1 * price1; double amount2 = quantity2 * price2; // 汇总 double total = amount1 + amount2; long totalInFen = (long) (total * 100); // 折算成"分",转成整数 // 是否超过预算 double budget = 40000.0; boolean overBudget = total > budget; // 采购员等级(用单个字符表示) char buyerLevel = 'A'; System.out.println(itemName1 + " 金额:" + amount1 + " 元"); System.out.println(itemName2 + " 金额:" + amount2 + " 元"); System.out.println("合计:" + total + " 元(" + totalInFen + " 分)"); System.out.println("是否超预算:" + overBudget); System.out.println("采购员等级:" + buyerLevel); } }运行结果:
锚杆 金额:22200.0 元 液压油 金额:12800.0 元 合计:35000.0 元(3500000 分) 是否超预算:false 采购员等级:A这段代码里,Java 的八种基本类型出现了六种(int、double、boolean、char、long,外加转换),剩下的byte、short、float我们马上讲到。先立一个总纲:
Java 的类型分两大阵营:八种基本类型(primitive)是"值"本身;其余一切——String、数组、你自己写的类——都是引用类型。
这两个阵营的行为差异,是新手无数 bug 的源头。本章讲透它们。
第一部分:八种基本类型——一张表就够了
Java 有且只有八种基本类型,分四类:
| 分类 | 类型 | 占用空间 | 举例 | 日常使用频率 |
|---|---|---|---|---|
| 整数 | byte | 1 字节(-128~127) | byte b = 100; | 低 |
| 整数 | short | 2 字节 | short s = 5000; | 低 |
| 整数 | int | 4 字节(约 ±21 亿) | int age = 30; | 极高,整数默认用它 |
| 整数 | long | 8 字节 | long fen = 3500000L; | 中,int 装不下时用 |
| 小数 | float | 4 字节 | float f = 3.14F; | 低 |
| 小数 | double | 8 字节 | double price = 18.5; | 极高,小数默认用它 |
| 字符 | char | 2 字节 | char level = 'A'; | 中 |
| 布尔 | boolean | — | boolean ok = true; | 极高 |
按本系列"只讲常用"的原则,给你三条极简结论:
整数就用
int,除非确定会超过 21 亿(比如毫秒时间戳、以"分"为单位的大金额),那就用long,数值后面加L:long t = 1723000000000L;小数就用
double。float要加F后缀、精度还低,业务代码里基本不用,知道它存在即可。byte、short、float日常几乎不手写,多在底层 IO、旧代码里出现,见到了能看懂就行。
两个细节:
写整数字面量时,Java 默认当 int 处理;写小数字面量默认当 double 处理。所以
float f = 3.14;会报错(3.14 是 double,不能直接塞进更小的 float),必须写3.14F。char用单引号,只能包一个字符:'A'、'中'都合法。boolean只有true和false,没有第三态,也不能用 0 和 1 代替。
第二部分:类型转换——自动的和强制的
案例里有两处转换,正好把两种转换方式都演示了。
自动转换(小杯倒进大杯,不用管)
double amount1 = quantity1 * price1; // int × double → doublequantity1是 int,price1是 double,int 乘之前会被自动提升为 double,结果是 double。规则一句话:范围小的类型放进范围大的类型,自动转,不丢数据。转换链是:
byte → short → int → long → float → double char ↗所以int i = 'A';合法(i 的值是 65,字符的本质是 Unicode 编码数字),double d = 100;也合法。
强制转换(大杯倒进小杯,自己负责)
long totalInFen = (long) (total * 100);total * 100是 double,要装进 long——范围大的进范围小的,可能丢数据,编译器要求你显式确认,写法是在值前面加(目标类型):
double d = 9.99; int i = (int) d; // i 是 9,不是 10!强转直接砍掉小数,不做四舍五入新手必记:(int)是"截断"不是"四舍五入"。想四舍五入用Math.round(),第十章讲工具类时会细说。
第三部分:String——最常用的引用类型
基本类型讲完了,现在看另一大阵营的代表。String 是 Java 里使用频率最高的类型,没有之一,但它不是基本类型,是一个类。这个事实引出三个必知点。
必知点一:字符串是不可变的(immutable)
String s = "锚杆"; s = s + "(12m)";第二行并不是"修改了原来的字符串",而是新建了一个字符串对象,让 s 指向新对象,旧对象等待垃圾回收。日常用没问题,但在循环里反复+拼接会产生大量临时对象——性能坑,第四章讲 StringBuilder 时解决它。
必知点二:字符串拼接用+,其他类型自动转字符串
System.out.println("合计:" + total + " 元");任何类型和字符串用+拼接,都会先被转成字符串。注意一个顺序陷阱:
System.out.println("结果:" + 1 + 2); // 输出:结果:12 System.out.println("结果:" + (1 + 2)); // 输出:结果:3+从左往右算,第一行里"结果:" + 1先拼成了字符串,后面的 2 只能跟着拼;加括号让加法先算就对了。
必知点三(重点):比较字符串内容,必须用 equals,不能用 ==
这是 Java 新手最经典的坑,没有之一。看这段代码:
String a = new String("锚杆"); String b = new String("锚杆"); System.out.println(a == b); // false ← 惊不惊喜? System.out.println(a.equals(b)); // true内容明明一样,==却返回 false。原因在于两大阵营的本质区别:
基本类型:变量里存的就是值本身,
==比较的就是值,int x = 5; int y = 5;则x == y为 true,天经地义。引用类型:变量里存的是对象的地址(内存中的位置)。
a和b是两个不同的对象,地址不同,所以==为 false。==对引用类型回答的问题是"是不是同一个对象",而不是"内容一不一样"。
想比较内容,要用 String 类提供的equals方法:
String input = "维修"; if (input.equals("维修")) { // 正确姿势 System.out.println("生成维修工单"); }实际开发中更稳妥的写法是把已知的字面量放前面,避免 null 报错:
if ("维修".equals(input)) { ... }这种写法即使input是 null 也不会抛空指针异常——空指针(NullPointerException)是 Java 程序最常见的事故,第八章异常处理会专门讲。
补一个容易混淆的点:直接写双引号的字符串字面量会被 JVM 放进"字符串常量池"复用,所以:
String x = "abc"; String y = "abc"; System.out.println(x == y); // 居然是 true!常量池里同一个对象正因为这种"有时 true 有时 false"的不确定性,规矩只有一条:比较字符串内容永远用 equals,忘掉 == 在字符串上的存在。
常踩的坑(排雷清单)
字符串用 == 比较:本章最大的坑,见上。内容比较一律
equals。整数相除丢掉小数:
int result = 7 / 2;结果是 3,不是 3.5——int 除 int 得 int。想要小数,至少一边写成 double:7.0 / 2或(double) 7 / 2。强转当四舍五入用:
(int) 9.99得 9。要四舍五入请用Math.round()。long 忘记加 L:
long t = 1723000000000;编译报错,因为字面量默认 int,超范围了。加L就好。boolean 写 0/1:
if (flag == 1)在 boolean 上行不通,Java 的 boolean 和数字完全不通婚。char 和 String 混用引号:
'维修'(两个字符进单引号)编译报错;String s = 'A';也报错——类型不匹配。单引号 char、双引号 String,泾渭分明。
本章小结
Java 类型分两大阵营:八种基本类型存值本身,引用类型(String、数组、类)存地址。
日常取舍:整数
int、大额long L、小数double、真假boolean、单字符char 'A',其余三种认识即可。转换:小进大自动转,大进小加
(类型)强转,强转是截断不是四舍五入。String 是类、不可变;拼接用
+(注意从左到右的顺序陷阱)。比较字符串内容用
equals,永远不用==——引用类型的==比的是地址。
下一章:运算符与流程控制——通过一个"巡检成绩评级 + 批量统计"的案例,把 if、switch、三种循环讲透,重点拆解 switch 的 break 穿透和增强 for 为什么成为遍历的主流。