Java 核心笔记:第 2 章 基本数据类型与数组底层

一、基础概念全览

  • Identifier (标识符):程序中用于命名类、变量、方法的符号。必须以字母、下划线 _$ 开头(数字绝对禁止开头)。Java 使用 Unicode 字符集,因此汉字也被视为合法字母。
  • Keyword (关键字):Java 语言预留的赋予特殊语法含义的单词(如 class, public, int)。关键字全为小写,且不能用作标识符。需要注意的是,true, false, null 虽然不是关键字,但也不能作为标识符。
  • Primitive Types (基本数据类型):Java 定义的 8 种不可再分的原子数据类型,在内存中分配固定长度的空间。
  • Reference Types (引用类型):如数组 (Array) 和对象。它们在 Stack (栈) 中存放内存地址,该地址指向 Heap (堆) 中实际存放的数据实体。

二、核心语法与模型规范

1. 数据类型内存规格表

类型 (Type)内存宽度 (Bits/Bytes)默认值 (Default)
byte8 bits / 1 Byte0
short16 bits / 2 Bytes0
int32 bits / 4 Bytes0
long64 bits / 8 Bytes0L
float32 bits / 4 Bytes0.0f
double64 bits / 8 Bytes0.0d
char16 bits / 2 Bytes’\u0000’
boolean8 bits / 1 Bytefalse

2. 字符与进制规范

  • 字符型 (char):必须使用单引号 'A',不能使用双引号 "A"(那是 String 类)。char 没有负数,范围是
  • 进制前缀
    • 二进制 (Binary):0b0B (如 0b11)
    • 八进制 (Octal):0 (如 077)
    • 十六进制 (Hexadecimal):0x0X (如 0x3ABC)

3. 数组的声明与分配

  • 合法声明int[] a;int a[];(此时栈中仅有引用,值为 null)。
  • 非法声明int[12] a;(声明时绝对不能指定长度)。
  • 物理分配a = new int[4];(在堆内存中连续开辟 4 个 int 的空间,并初始化为 0)。

三、深度逻辑推导

1. 字面量与类型强转逻辑

在 Java 中,直接写出的整数(如 20)默认是 int 类型,直接写出的小数(如 3.14)默认是 double 类型。

  • 宽化转换 (Widening):低精度向高精度转换,JVM 自动完成。 double d = 10; (10 被隐式转换为 10.0)
  • 窄化转换 (Narrowing):高精度向低精度转换,必须显式声明,否则编译报错。 int x = (int) 3.14;
  • 边界推演:执行 byte b = (byte) 128;128int 补码截断最后 8 位为 10000000。在 byte 中,最高位 1 代表负数,其真值为 -128。这就是强转导致的 Overflow (溢出)。

2. 数组长度 (length) 的维度映射

length 是数组在内存创建时就固化的固有属性。

  • 一维数组int[] a = new int[12]; a.length 为 12。
  • 二维数组 (不规则矩阵)int[][] b = new int[3][6]; b.length 返回的是行数(外层指针数),值为 3。

3. IO 输出底层推演

  • 使用 System.out.println(a) 输出一个普通的 int[] 数组时,输出的不是数组元素,而是类似 [I@1b6d3586 的哈希特征码。
    • [ 代表一维数组。
    • I 代表 int 类型。
    • @ 后面的十六进制字符串是该数组在 JVM 中的 Identity Hash Code (身份哈希码),并非真实的物理内存地址,但可用于标识唯一性。
  • 特例:如果是 char[] c = {'a', 'b'};System.out.println(c) 会直接输出字符串 ab,因为 PrintStreamchar[] 做了底层重载。

四、边际陷阱与性能审计

1. Float 后缀遗漏引发的类型阻断

  • 陷阱:写出 float x = 3.14; 会触发编译错误:incompatible types: possible lossy conversion from double to float
  • 审计:由于 3.14 默认是 64 位的 double,而 float 只有 32 位。将 64 位硬塞入 32 位被 JVM 视为危险操作。必须显式添加后缀 float x = 3.14f;,告知编译器这是 32 位的浮点字面量。

2. 科学计数法导致的类型漂移

  • 陷阱:在需要 long 类型的金融计算中,为了方便写出 long amount = 1e10;
  • 审计1e10 这种指数表示法,在 Java 中会被编译器强制识别为 double 类型。虽然数值看起来没小数,但内存结构已变成浮点数。赋给 long 会报类型不兼容的错误。

3. 数组越界的延迟崩溃

  • 陷阱:声明 int[] a = new int[5]; 后执行 a[7] = 10;
  • 审计:编译器 (javac) 根本不会报错,因为它在编译时只检查类型(a 是数组,7 是整数,语法合法)。只有在 Runtime (运行期) 时,JVM 在堆内存寻址越界,才会抛出致命的 ArrayIndexOutOfBoundsException

4. 引用复制导致的数据污染

  • 陷阱int[] a = {1, 2}; int[] b = {3, 4}; a = b;
  • 后果:此时 ab 的栈内存指针指向了同一个堆内存地址 {3, 4}。后续执行 b[0] = 99;a[0] 也会随之变成 99。原先 {1, 2} 所在的堆内存块因失去所有引用,变为垃圾数据,等待 Garbage Collector (垃圾回收器) 清理。

🛑 极致压测

危机测试 1:标识符的极端边界 int 1_money = 100;int 中国 = 100; 这两行代码,哪一行会编译报错?结合 Unicode 字符集解释为什么 Java 允许其中一种看似违规的写法。

危机测试 2:浮点精度的毁灭打击 执行 double d1 = 0.1; double d2 = 0.2; System.out.println(d1 + d2 == 0.3); 输出的结果是 true 还是 false?请结合十进制小数转二进制小数的底层机制解释原因。

危机测试 3:二维数组的物理碎片化 在 C/C++ 中,二维数组在内存中是绝对连续的一整块。但在 Java 中,执行 int[][] a = new int[3][]; 为什么是合法的?请画出 Java 不规则二维数组的栈堆指针映射关系。

危机测试 4:字符变量的加法溢出 已知 char 占用 16 位,最大值为 65535。如果执行 char c = 65535; c++;c 的十进制值会变成多少?为什么它不会变成负数(与 byte/int 溢出不同)?

危机测试 5:数组打印的类型歧义 如果 char[] c = {'a', 'b'};System.out.println(c); 会输出 ab。如果我非要输出这个字符数组的堆内存哈希地址特征码,应该在代码里做怎样的强制字符串拼接处理?


topic: 编程 tags:

  • Java
  • 概念