JavaSE(一)数据类型:八种基本类型、类型提升与 String

本文系统讲解Java八种基本数据类型,包括其字节大小、包装类命名规律及易错点(如char支持汉字但emoji需代理对、boolean无规范字节数、无隐式布尔转换);深入剖析类型提升机制(byte运算自动升为int)、float/double字面量后缀规则、String作为引用类型而非基本类型的本质,以及+号在字符串拼接中的左结合性与类型推导逻辑;最后涵盖字符串与数值互转方法及典型踩坑场景。

按知识章节整理的学习笔记,适合 Java 零基础阶段对照阅读。本篇对应「数据类型与变量」这一章。<br>每个知识点尽量回答「为什么」,而不是只给结论。

一、为什么非得有数据类型

C 语言里 int a 和 double a 的区别是"占多少字节",Java 也一样,但 Java 走得更绝一些:它的每种基本类型,字节数是被规范写死的,不随操作系统变。

比如 C 里 int 在 16 位机器上是 2 字节,32 位机器上是 4 字节。写个程序在这台机器上能跑,换台机器可能就溢出。

Java 的取舍是:用固定的字节数,换"一次编译,到处运行"。int 永远是 4 字节,不管是 Windows、Linux 还是 ARM 架构的服务器。

二、八种基本类型全表

类型字节包装类范围日常用不用
byte1Byte-128 ~ 127少见(IO、数组省内存)
short2Short-32768 ~ 32767几乎不用
int4Integer-2147483648 ~ 2147483647最常用
long8Long±9.2×10¹⁸时间戳、大数
float4Float约 ±3.4×10³⁸少见
double8Double约 ±1.7×10³⁰⁸小数默认
char2Character0 ~ 65535字符处理
boolean未规定Booleantrue/false逻辑判断
记忆抓手:包装类的命名规律

八种基本类型对应八个包装类,只有两个是"改名"的:

  • int → Integer(不是 Int)
  • char → Character(不是 Char)

其余六个都是首字母大写。所以看到 Long.MAX_VALUE、Byte.MIN_VALUE 都能直觉理解。

为什么要记包装类? 因为基本类型本身没有任何方法。想知道 int 的最大值,写 int.MAX_VALUE 是错的(编译不过),必须写 Integer.MAX_VALUE——值挂在包装类上,不在基本类型上。

三个容易记错的点

① char 是 2 字节,能存汉字

char ch1 = 'a';     // 英文字母
char ch2 = '高';    // 汉字,合法

这跟 C 不一样(C 的 char 是 1 字节,存不下汉字)。Java 用 Unicode,char 是 2 字节。

但有个坑:2 字节只够表示基本平面(BMP)的字符,emoji 需要两个 char。

System.out.println("高".length());    // 1
System.out.println("😀".length());    // 2  ← 一个表情,长度是 2

因为 emoji 超出 65535 的范围,要用"代理对"(surrogate pair)两个 char 拼起来。这个细节在后续处理文本时会踩坑,现在知道有这回事就行。

② boolean 的字节数,规范没规定

Java 规范只规定了 true/false 两个取值,没规定占多少内存。JVM 实现里通常用 1 个字节(或者用 int 的 0/1 表示),但这不属于语言规范。

面试问"boolean 占几个字节",标准答案是"规范未明确规定,取决于 JVM 实现"。答"1 字节"其实不严谨。

③ Java 里没有"非 0 即真"

if (1) { }        // ❌ 编译错误:不兼容的类型,int 无法转换为 boolean

C 语言里 if (1) 是对的,Java 直接禁止。条件必须是 boolean 类型——这是 Java 强类型的一个体现,也避免了 if (a = 1) 这种把赋值写成判断的低级错误(Java 里这个也是编译错误,因为 a = 1 的结果是 int)。

三、float 后面那个 F,以及 long 的 L

float f = 10.5F;   // ✅
float f = 10.5;    // ❌ 不兼容的类型:从 double 转换到 float 可能会有损失

Java 里的小数字面量默认是 double。 10.5 这个字面量从出生就是 double(8 字节),往 float(4 字节)里放属于窄化转换,编译器不敢替你做这个决定。

加 F 就是明确告诉编译器"这个字面量我要它当 float"。

long 的情况类似但更松一些:整数字面量默认是 int,只要不超过 int 范围,long l = 10; 直接写就行。但超过就必须加 L:

long big = 3000000000;    // ❌ 整数过大,这个数字超出了 int 的范围
long big = 3000000000L;   // ✅

注意 L 要写大写。 小写 l 和数字 1 在某些字体里长得一模一样,1l 看着就像 11。

四、类型提升:一个能让人卡住半小时的报错

byte d = 10;
byte e = 10;
byte f = d + e;    // ❌ 不兼容的类型:从 int 转换到 byte 可能会有损失

两个 byte 加起来,怎么就成了 int?

规则是二元数值提升:参与算术运算时,如果两个操作数都不是 long/float/double,就都提升为 int。

byte + byte → 提升为 int + int → 结果是 int

所以把 int 塞回 byte 变量,编译器要拦。两种改法:

int f = d + e;                  // 用 int 接
byte f = (byte) (d + e);        // 强转,括号必须包住整个表达式

注意括号不能省:

byte f = (byte) d + e;    // ❌ 还是报错,只转了 d,后面 + e 又提升回 int

同样的规则,换个方向也一样:

int a = 10;
long b = 20;
long c = a + b;    // int + long → long,结果必须是 long 接
int c = a + b;     // ❌ 装不下

一句话记住:运算结果的类型,取参与运算里"最大"的那个。 顺序是 byte → short → int → long → float → double。

设计动机值得深究:Java 之所以要把 byte 拉到 int 再运算,是因为 CPU 的整数运算单元本身就是按 32 位设计的,没有高效的 8 位加法指令。运算统一拉到 int 这个"原生宽度",既符合硬件现实,也让字节码更简单。

同一个规则还有两个延伸坑:

// ① 复合赋值隐含强制类型转换
byte b = 10;
b = b + 1;    // ❌ 报错
b += 1;       // ✅ 合法,等价于 b = (byte)(b + 1)

// ② 但它会静默溢出
byte b2 = 127;
b2 += 1;
System.out.println(b2);   // -128,编译器一句警告都没有

报错的代码会被改掉,不报错的代码可能直接上线——这是类型提升这个话题里最需要警惕的地方。

五、String 到底算不算一种"类型"

初学数据类型时,很容易把 String 当作第九种基本类型。但它不是基本类型,它是类。

String str = "hello";    // 这是创建了一个对象,不是基本类型的赋值

证据有几个:

  1. 首字母大写 —— Java 里所有类名都是大驼峰,基本类型全是小写(int、char)。String 大写就说明它是类。
  2. 它是引用类型 —— 变量里存的不是字符串本身,而是指向字符串对象的地址。
  3. 它有方法 —— 基本类型不能调方法,str.length() 能调,因为它是对象。
  4. 它没有对应的包装类 —— 因为它本身就是类,不需要包装。

这个区分为什么重要? 因为后面学集合、泛型、比较的时候,基本类型和引用类型的差别会变得非常关键。比如 == 比较 String 时比的是地址而不是内容(要用 equals),而 int 用 == 比的就是值。现在把 String 归对位置,后面就不容易乱。

六、字符串拼接:那个 + 号的执行顺序

int a = 10;
int b = 20;

System.out.println("a=" + a + b);      // a=1020
System.out.println("a=" + (a + b));    // a=30
System.out.println(a + b + "=a+c");    // 30=a+c

三行看着差不多,结果完全不同。原因是 + 从左到右执行,而它有两种含义:数值加法、字符串拼接。

  • 第一行:先算 "a=" + a → 结果已经是字符串 "a=10",再 + b 只能拼接 → "a=1020"
  • 第二行:括号优先,a + b = 30 先算出来,再拼接 → "a=30"
  • 第三行:a + b 两个都是 int,走算术加法 → 30,再和字符串拼 → "30=a+c"

规律:一旦 + 的左边出现了 String,后面所有的 + 都变成拼接。

顺便说一句:+ 在 Java 里能拼字符串,是编译器特批的。Java 并不支持运算符重载(除了这一个特例),这也是为什么 Java 里没有自定义的运算符重载。

七、字符串和数值互转

数值 → 字符串
int a = 123;
String s1 = a + "";              // 最偷懒的写法
String s2 = String.valueOf(a);   // 推荐,意图明确

a + "" 能用,但意图不清晰而且性能稍差。实际开发一律用 String.valueOf(),或者 Integer.toString(a)。

字符串 → 数值
String s = "123";
int b1 = Integer.parseInt(s);     // 返回 int
int b2 = Integer.valueOf(s);      // 返回 Integer,自动拆箱后看着一样

很多笔记会写"这两个一样",严格说不一样:

  • parseInt 返回 int(基本类型)
  • valueOf 返回 Integer(包装类对象)

在新版 JDK 里 valueOf 内部就是调 parseInt 再装箱,日常用起来没区别,但面试可能会问。而且异常行为要注意:

String s = "abc";
Integer.parseInt(s);   // NumberFormatException

String s2 = null;
Integer.parseInt(s2);  // NumberFormatException(不是空指针)

只要不是合法数字(包括 null),都抛 NumberFormatException。

八、本章踩坑记录

  1. 以为 String 是第九种基本类型,实际它是类(首字母大写就是线索)
  2. 以为 boolean 占 1 字节,实际规范没规定
  3. 以为 Integer.valueOf() 和 parseInt() 完全一样,返回值类型其实不同
  4. char 存汉字没问题,但 emoji 要占 2 个 char
  5. float f = 10.5; 不加 F 会报错,long big = 3000000000; 不加 L 也会报错
  6. byte b = 127; b += 1; 结果是 -128,编译器不报错

九、自测

  1. int 的最大值怎么打印?为什么不能写 int.MAX_VALUE?
  2. "😀".length() 等于几?为什么?
  3. byte a = 1, b = 2; byte c = a + b; 报错原因?写出两种改法。
  4. String.valueOf(1) 和 1 + "" 有区别吗?
  5. Integer.parseInt(null) 会抛什么异常?
  6. System.out.println(1 + 2 + "3" + 4 + 5); 输出什么?
  7. boolean 到底占几个字节?

下一篇:运算符——从「5 / 2 为什么等于 2」说起,包括自增自减、短路逻辑、位运算。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值