Skip to content

01.1 — 数据类型与运算符

定位: 一切 Java 程序的类型基石 — 8种基本类型的设计动机与使用规则 面试高频度: ⭐⭐⭐⭐ 考查方式: 类型取值范围、自动/强制类型转换、float/double 精度陷阱

一、这是什么?为什么需要它?

是什么

Java 有 8 种基本类型(primitive type),分为四组:

组别类型内存默认值
整数byte8 bit0
short16 bit0
int32 bit0
long64 bit0L
浮点float32 bit0.0f
double64 bit0.0d
字符char16 bit空字符
布尔boolean~8 bit JVM依赖false

为什么需要它们?

没有基本类型会怎样?

如果只有 Object,每个 int 值都需要 new Integer() 包装:

  • 内存暴涨 3-5 倍(对象头 + 实例数据 + 对齐)
  • 性能下降(堆分配 + GC)
  • 无法直接映射到 CPU 寄存器操作

所以:基本类型是 Java 与硬件之间的桥梁,直接对应 CPU 的寄存器和 ALU 操作。

┌─────────────────────────────────────────────────────────────┐
│         Java 类型系统:基本类型 vs 引用类型                     │
│                                                              │
│  基本类型:                                                  │
│  int a = 10;   →  栈上直接存值 [10]                          │
│                                                              │
│  引用类型:                                                  │
│  Integer b = new Integer(10);                                │
│  栈                   堆                                     │
│  [引用] ──────────→ [Integer 对象: value=10]                 │
│                                                              │
│  结论:基本类型 = 直接操作值,引用类型 = 间接操作对象            │
└─────────────────────────────────────────────────────────────┘

二、原理拆解

2.1 整型设计——为什么是这 4 种?

byte  (8)  : 最小存储单位 — 文件流、网络协议
short (16) : 兼容 16 位系统 legacy
int   (32) : 默认整数类型 — CPU 32/64 位效率最高
long  (64) : 需要大范围时(时间戳、ID)

为什么 int 是默认整数类型?

java
byte b = 100;        // OK
byte c = 200;        // 编译错误:int 不能隐式转 byte
int  d = 100;        // 最自然
long e = 100L;       // 需要 L 后缀

为什么 long 需要 L 后缀? 因为 100 字面量是 int 类型,int → long 是隐式转换(安全),显式用 L 告诉编译器 + 读者"这个数可能超出 int 范围"。

2.2 浮点设计——float/double 为什么不精确?

java
0.1 + 0.2  // 0.30000000000000004  (不是 0.3!)

原因:IEEE 754 二进制无法精确表示某些十进制小数。

十进制 0.1 = 二进制 0.00011001100110011... (无限循环)

float/double 只能存有限位 → 截断 → 精度损失

解决方案:精确计算用 BigDecimal(String 构造器!)

java
// ✅ 正确
new BigDecimal("0.1").add(new BigDecimal("0.2"));

// ❌ 错误(double 构造器仍然有精度问题)
new BigDecimal(0.1).add(new BigDecimal(0.2));

2.3 类型转换规则

自动转换(小→大,安全):  byte → short → int → long → float → double

                                     char (unsigned)

强制转换(大→小,可能溢出):double → float → long → int → short → byte

自动转换的动机:不丢失信息的转换,编译器自动做。

强制转换的陷阱

java
int big = 300;
byte small = (byte) big;  // 300 % 256 = 44

// 溢出原理:
// int 300 = 00000000 00000000 00000001 00101100
// byte 截断低8位 = 00101100 = 44
┌─────────────────────────────────────────────────────────────┐
│                    类型转换方向图                              │
│                                                              │
│                          double                              │
│                         ↑  ↑                                 │
│                      float  long                             │
│                         ↑    ↑                               │
│                        int  ←── char (无符号特殊)              │
│                         ↑                                    │
│                       short                                  │
│                         ↑                                    │
│                       byte                                   │
│                                                              │
│  自动:箭头方向 (小→大)  强制:反方向 (大→小,可能丢失)        │
└─────────────────────────────────────────────────────────────┘

三、图解全景

┌─────────────────────────────────────────────────────────────┐
│                Java 运算的隐式类型提升                         │
│                                                              │
│  表达式中的自动提升规则:                                      │
│                                                              │
│  byte/short/char → int (最小精度)                             │
│                                                              │
│  例:                                                         │
│  byte a = 10, b = 20;                                        │
│  int  c = a + b;   // a+b 先提升为 int                       │
│  byte d = a + b;   // ❌ 编译错误:int 不能隐式转 byte          │
│                                                              │
│  结合性 & 优先级:                                             │
│                                                              │
│  int x = 1 + 2 * 3;   // 7  (乘优先于加)                      │
│  int y = (1 + 2) * 3; // 9  (括号改变优先级)                  │
│  int z = 1 + 2 + "3"; // "33" (String 拼接优先)              │
└─────────────────────────────────────────────────────────────┘

四、实战验证

4.1 各类型取值范围验证

java
// byte 范围
System.out.println(Byte.MIN_VALUE + " ~ " + Byte.MAX_VALUE);
// -128 ~ 127

// int 溢出观察
int max = Integer.MAX_VALUE;  // 2147483647
System.out.println(max + 1);  // -2147483648 (回绕)

// float 精度损失
System.out.println(0.1 + 0.2);              // 0.30000000000000004
System.out.println(1.0f - 0.9f);            // 0.100000024

4.2 类型转换验证

java
// 自动转换
int i = 100;
long l = i;        // OK:int → long 自动
float f = l;       // OK:long → float 自动(可能有精度损失)
System.out.println(l == (long)f); // false! long 转 float 可能丢精度

// 强制转换
int big = 256;
byte small = (byte) big;
System.out.println(small);  // 0

// 复合赋值陷阱
int x = 10;
x += 3.5;  // 隐含强制转换: x = (int)(10 + 3.5) = 13

五、面试视角

追问答案要点
float 和 double 的区别?精度:float 7位,double 15位;内存:32 vs 64;默认浮点字面量是 double
为什么 long 转 float 可能有精度损失?long 63位尾数 > float 23位尾数,高位截断
switch 支持哪些类型?演进过程byte/short/char/int (JDK5) → enum (JDK5) → String (JDK7)
i++ 和 ++i 的区别?i++ 先取值后自增;++i 先自增后赋值;底层字节码不同
& 和 && 的区别?& 位运算/逻辑与(不短路);&& 短路与(左边 false 不计算右边)

📚 相关链接

  • **String深度解析** — String 相关的类型转换
  • **Object类与通用方法** — Object 类中的通用方法
  • **包装类详解** — 包装类与基本类型的互转
  • ← 返回 **语言基础索引**

Knowledge4J — Java 知识库