Skip to content

第2章:变量与类型

本章只讲 Java。Python 和 C++ 的变量会在第5章末尾做统一对比。

你将学会
变量声明·8种基本类型·类型转换
前置知识
第1章 第一个程序
预计时间
30 分钟
需要准备
JDK + 文本编辑器
完成标志
能用变量写计算程序

一个故事开头

假设你是奶茶店老板,要记每天的销售情况。

"今天小明买了一杯波波奶茶,小红买了一杯杨枝甘露,小张买了两杯..."

第三天你就记不住了。你拿出一个本子,在本子上写下:

波波奶茶库存: 50杯
杨枝甘露库存: 30杯
今日销售额: 0元

然后每卖出一杯,你就划掉旧数字,写上新的。

变量就是程序里的"本子"——用来记数据的东西。不同的是,电子本子比纸质本子聪明得多:它知道自己只能记什么类型的数据,而且你报错的时候它会直接告诉你。


1. 从一道麻烦题开始

第1章我们写的程序是死的——输出什么内容,代码里就写了什么内容。

java
System.out.println("你好, 小明!");
System.out.println("小明, 你的分数是 95");

现在我想改成"小红"怎么办?把 "小明" 一个个找出来替换成 "小红"。

烦不烦?如果有 100 处地方要改呢?

变量解决的就是这个问题——别把数据写死,把它存在一个有名字的"盒子"里,用到的地方都引用这个盒子的名字。数据变,只改一处。

java
String name = "小明";
System.out.println("你好, " + name + "!");
System.out.println(name + ", 你的分数是 95");

换人?只改第一行 name = "小红",全自动更新。


2. 变量是什么——一个带标签的盒子

把变量想成一个纸箱

┌─────────────────────────┐
│  标签: name              │ ← 变量的名字
│  类别说明: 只能装文字     │ ← 变量的类型
│  ┌───────────────────┐  │
│  │ "小明"             │  │ ← 里面装的值
│  └───────────────────┘  │
└─────────────────────────┘

每个变量都有三个要素:名字、类型、值。一个不能少。

在 Java 里,声明一个变量就是做这样一个盒子:

java
// 声明 + 初始化一步到位
int age = 25;         // 一个叫 age 的盒子,只能装整数,里面装了 25
double price = 19.99; // 一个叫 price 的盒子,只能装小数
boolean ready = true; // 一个叫 ready 的盒子,只能装 true/false

💥 拆了它:试试往错误类型的盒子里塞东西

java
int age = 25;
age = "二十五";  // ← 试试这个

编译结果:incompatible types: String cannot be converted to int

Java 说:"你这个盒子标明了只能放整数,你非要塞文字进去,我搞不了。"

这就是静态类型语言的意思——在程序跑起来之前,编译器就帮你检查了类型是否匹配。看着烦人,但省下的调试时间远远更多。


3. 声明、初始化、赋值——三件事

很多人搞混这三个,其实区别特别简单:

java
public class BoxDemo {
    public static void main(String[] args) {
        // 声明:做一个空盒子,贴上标签
        int age;     
        // 此时盒子里啥也没有——实际上 Java 不让你拿空盒子的东西
        // System.out.println(age);  // ❌ 编译错误:没初始化

        // 赋值(第一次 = 初始化):往盒子里放东西
        age = 25;    

        // 可以用它了
        System.out.println(age);  // 25

        // 重新赋值:倒掉旧的,放新的
        age = 30;    
        System.out.println(age);  // 30

        // 声明+初始化一步到位(最常用的写法)
        int quantity = 3;
    }
}

💥 拆了它:试试不初始化直接用

把上面的 int age; 后面的两行全删掉,直接 System.out.println(age);。看编译器怎么说。

error: variable age might not have been initialized

Java 的规矩是:变量在初始化之前不能拿来用。这不是故意刁难你——未初始化的变量里面的值是未定义的垃圾数据,拿来用会生产莫名其妙的 bug。Java 干脆直接拦住你,不让你犯错的机会。


4. 八种基本类型

Java 有 8 种内置的基本类型(Primitive Type)。它们不是类,就是纯粹的"值"——直接存在内存里的数据。

你不需要全部记住,但需要知道都有什么以及什么时候用。

整数家族:4 种

类型大小能存的范围什么时候用
byte1 字节-128 ~ 127存大量小数字(图片像素、文件格式)
short2 字节±3.2 万很少用
int4 字节±21 亿95% 的情况用这个
long8 字节±9.2 × 10¹⁸数字超过 21 亿(时间戳、天文数字)
java
byte b = 100;             // 小数字
short s = 30000;          // 不大不小
int i = 2000000000;       // 日常够用
long l = 9000000000000L;  // 超大的数,后面要加 L

注意 long 的字面量要加 L(或小写 l,不然 Java 以为你在写 int

小数家族:2 种

类型大小精度什么时候用
float4 字节约 7 位有效数字极度省内存时(图形学)
double8 字节约 15 位有效数字99% 的情况用这个
java
float f = 3.14f;    // 后面要加 f,不然 Java 以为你是 double
double d = 3.14;    // 默认小数就是 double

其他:2 种

java
boolean isReady = true;   // 只有 true 和 false
char letter = 'A';        // 一个 Unicode 字符,用单引号
char chinese = '中';      // char 支持中文

一个小结论

实际开发中,你 90% 的时间只用三种:

java
int count = 42;          // 整数
double total = 19.99;    // 小数
boolean paid = false;    // 是/否

另外五种有它们的用途,但初学者不必刻意去记。

💥 拆了它:试试超过 int 上限

java
int x = 3000000000;  // 30亿,int 上限约21亿

编译结果:integer number too large

改成 long x = 3000000000L; 就好了。


5. 类型转换——小盒子和大盒子

不同类型的变量之间可以互相转。规则只有两条:

小范围 → 大范围:自动转,安全。大范围 → 小范围:手动转,可能丢数据。

自动转换(从小的往大的装)

java
int i = 100;
long l = i;       // int → long:自动的(long 比 int 大)
double d = i;     // int → double:自动的,100 → 100.0

// 运算时也会自动提升
int a = 5;
double b = 2.0;
double result = a + b;  // int + double → int 先转 double 再算 → 7.0

💥 拆了它:整数除法的陷阱

java
double result = 5 / 2;
System.out.println(result);  // 猜猜是多少?

你可能以为是 2.5,但结果是 2.0

为什么?因为 52 都是 int,Java 先做整数除法 5/2 = 2(整数除法丢弃小数部分),再把结果 2 转成 double2.0

java
double correct = 5.0 / 2;    // 2.5 ✅
double correct2 = 5 / 2.0;   // 2.5 ✅
double correct3 = (double) 5 / 2;  // 2.5 ✅

记好:整数除法只留整数部分。 只要运算中有一个数是 double,结果就是 double

强制转换(从大的往小的装)

从大范围往小范围转是不安全的——可能会溢出或丢精度。所以 Java 不让你自动转,你必须手动写 (类型)

java
double pi = 3.14159;
int intPi = (int) pi;    // 3(小数直接砍掉,不是四舍五入!)
java
int big = 300;
byte small = (byte) big;  // 300 超过了 byte 的范围(-128~127)
System.out.println(small); // 44!溢出了,数据静默丢失

强制转换的风险:Java 让你编译通过,但不保证结果有意义。

🧪 动手试试

java
// 猜猜每行的输出,再跑一遍验证
double price = 19.99;
int dollars = (int) price;
System.out.println(dollars);

int x = 10;
int y = 3;
System.out.println(x / y);
System.out.println((double) x / y);

6. 常量:final——做了就不能改

如果你有一个值,定义了就不想让人改——用 final

java
final double PI = 3.141592653589793;
final int MAX_USERS = 1000;

PI = 3.14;        // ❌ 编译错误!final 变量不能重新赋值
MAX_USERS = 500;  // ❌ 编译错误!

常量的命名惯例:全大写,单词之间用下划线。

java
final double TAX_RATE = 0.08;
final int MAX_RETRY = 3;
final String APP_NAME = "团子奶茶店";

这样一眼就能看出 "哦,这个值不会变"。


7. 变量名——好的名字就是最好的注释

java
// ❌ 烂命名——三天后你自己都看不懂
int a = 10;
int b = 5;
int c = a * b + a / b;

// ✅ 好命名——看到名字就知道要干嘛
int studentCount = 30;
int groupSize = 5;
int totalGroups = studentCount / groupSize;

Java 命名规矩:

种类写法例子
变量名小驼峰studentCount
常量名全大写下划线MAX_SIZE
类名大驼峰GradeCalculator
包名全小写com.example.shop

💥 拆了它:试试中文变量名

Java 其实支持中文变量名——Unicode 字符都行:

java
int 年龄 = 25;
String 名字 = "小明";
System.out.println(名字 + "今年" + 年龄 + "岁");

能跑。但不要这么写——中文编码在不同环境下可能出问题,而且英文变量名是行业通用标准。


8. 完整例子:奶茶店的点单计算

java
public class MilkTeaOrder {
    public static void main(String[] args) {
        // 常量
        final double TAX_RATE = 0.08;
        final String SHOP_NAME = "团子奶茶店";
        
        // 变量
        String drink = "波波奶茶";
        int quantity = 2;
        double unitPrice = 15.0;
        
        // 计算
        double subtotal = quantity * unitPrice;
        double tax = subtotal * TAX_RATE;
        double total = subtotal + tax;
        
        // 输出
        System.out.println("===== " + SHOP_NAME + " =====");
        System.out.println(drink + " × " + quantity);
        System.out.println("小计: " + subtotal + "元");
        System.out.println("税:   " + tax + "元");
        System.out.println("总计: " + total + "元");
    }
}

输出:

===== 团子奶茶店 =====
波波奶茶 × 2
小计: 30.0元
税:   2.4元
总计: 32.4元

⚠️ 需要知道:这个例子用 double 算金额只是为了演示变量类型。在真实的收银系统里,千万别用 doublefloat 存钱——浮点数精度不够,0.1 + 0.2 可能不等于 0.3。正确的做法是用 BigDecimal 或把金额按"分"存成整数。你现在刚起步,知道有这个问题就行,后面的章节会细讲。


9. 关于 String

这一章主要讲 8 种基本类型。但还有一个东西你第一章就用上了——String(字符串)。

java
String name = "小明";

String 不是基本类型,它是一个类。但 Java 给了它特殊待遇,让你可以像基本类型一样直接用字面量(双引号字符串),不用 new

关于 String 的更多内容,第9章会整章讲。现在你就知道它是"装一段文字用的就行了"。


我猜你会问

Q: 为什么要有 8 种基本类型,3 种不够吗? A: 历史原因。1980 年代内存很贵,省一个字节都很重要。现在你只管用 intdoubleboolean 就够了。但 byte 在文件解析里还在用,long 在时间戳里避不开,char 在处理文本时需要。

Q: float f = 3.14; 为什么报错? A: 因为 3.14 默认是 double 类型。doublefloat 是缩小转换,需要你手动强制。写 float f = 3.14f; 就好了——f 后缀告诉 Java "这是个 float 字面量"。

Q: int 为什么最大值是 21 亿? A: int 占 4 字节 = 32 位,能表示 2³² = 约 43 亿个不同的值。一半给负数,一半给非负数。具体范围:-2,147,483,648 到 2,147,483,647。

Q: 什么时候需要关心变量占多少内存? A: 刚开始完全不需要。等你写百万级数据的程序时才有意义——比如处理一张 4K 图片,选对类型能省几百 MB。


你现在学会的东西

  1. 变量 = 带标签的盒子,有名字、类型、值三个属性
  2. Java 有 8 种基本类型,平时 90% 用 intdoubleboolean
  3. 声明 ≠ 初始化,没初始化的变量不能读
  4. 小范围 → 大范围自动转,反过来要手动强制且可能丢数据
  5. 整数除法只留整数部分,想要精确除法让至少一个操作数是 double
  6. final 变量不能改,常量名全大写
  7. 变量名要让人能读懂


✅ 验收标准

完成本章后,你应该能:

  • [ ] 声明 intdoublebooleanString 变量并初始化
  • [ ] 解释编译器为什么拒绝把文本赋给整数变量
  • [ ] 区分自动类型转换和强制类型转换,至少各举一个例子
  • [ ] 写出整数除法的正确写法(5.0 / 2 而不是 5 / 2
  • [ ] 用 final 声明常量并说明为什么它有用

📌 常见卡点

  • 忘记初始化就读变量——编译报错,先赋值再用
  • 整数除法丢掉小数——至少一个操作数改成 double
  • `float` 后面忘加 `f`——默认小数是 `double`,`double → float` 需要强转
  • 中文变量名——JVM 支持但行业不用,坚持英文

🔜 现在不需要理解

  • 引用类型和基本类型的本质区别——第7章面向对象会讲
  • `String` 为什么不是基本类型——第9章字符串专题
  • 栈和堆上的内存分配——第三卷深入

🧪 练习

1. 声明变量:创建一个 Student 类(不能运行的——写一个片段就行),包含 name(String)、age(int)、score(double)三个字段,并初始化它们。

2. 类型转换:写一段代码,用 intdouble 演示

  • 自动类型转换
  • 强制类型转换
  • 整数除法陷阱(5 / 2)及正确写法

3. 常量:声明一个 final double TAX_RATE = 0.08,然后试着给它重新赋值——看看编译器报什么错。


下一篇

第3章 表达式与操作符

用 ❤️ 构建 | Software Systems Atlas