开篇先聊点实在的:Java八种基本类型,这个话题我看网上已经被人写烂了,但绝大多数文章只停留在“有八种、叫什么、占几个字节”这种背答案的层面。而实际上,凡是面试问到这个点的人,后面大概率还会追一句“那Integer和int有什么区别”“为什么100等于100、1000不等于1000”这种进阶问题。你如果只背了那张类型表,基本就交代了。
所以这篇不打算只给你列一个表格,而是从设计原理、内存模型、包装类型陷阱、面试高频变体这几个维度,把这八种类型彻底摊开讲透。无论你是刚学Java的新手,还是准备跳槽刷八股文的老兵,这篇文章都能帮你把“基本类型”从死记硬背变成真正理解。你的代码能不能写出高性能、低内存的版本,你对Java语言本身的理解深不深,很大程度上就藏在这些基础细节里。
1. 基本类型全景:为什么Java要保留这八种“非对象”
1.1 先说清楚一个概念:基本类型到底是什么
Java号称“万物皆对象”,但这句话其实不严谨。如果你写过一点代码,肯定知道int a = 10;和Integer b = new Integer(10);是两种完全不同的存在。前者是基本类型,存储在栈上,不是一个对象;后者是包装类型,是一个真正的对象实例。
为什么Java不像Ruby或者Python那样,所有东西都是对象,连整数也是对象?答案很简单:性能。对象有对象头、有类元数据、有垃圾回收的负担,创建一个Integer对象可能要比直接操作一个int多付出几十倍的存储和计算成本。在Java诞生那个年代,内存和CPU资源都相当紧张,设计者必须提供一种轻量级的、直通底层硬件的数据表示方式,这就是基本类型存在的意义。
八种基本类型分别是:byte、short、int、long、float、double、char、boolean。它们覆盖了整数、浮点数、字符、布尔这四大类数据,基本能满足所有编程场景的需求。除去这些,其余全是引用类型,也就是类、接口、数组、枚举这些。
注意:
String不是基本类型,它是类。很多新手会搞混,这属于面试中非常基础的送分题,错了就太亏了。
1.2 八种基本类型的一张总表
先来一张速查总表,把范围、位数、默认值一次性记住。这张表背下来只是第一步,后面我会逐个类型讲它背后的坑。
| 类型 | 占用空间 | 取值范围 | 默认值 | 直接用途举例 |
|---|---|---|---|---|
| byte | 8位 | -128 ~ 127 | 0 | 文件流读取、节省内存的字节数组 |
| short | 16位 | -32768 ~ 32767 | 0 | 极少用,多见于底层协议解析 |
| int | 32位 | -2^31 ~ 2^31-1 | 0 | 默认整数类型,计数器、索引 |
| long | 64位 | -2^63 ~ 2^63-1 | 0L | 时间戳、大数值计算 |
| float | 32位 | 约 ±3.4E38,精度约7位 | 0.0f | 科学计算中的浮点数,注意精度损失 |
| double | 64位 | 约 ±1.7E308,精度约15位 | 0.0d | 默认浮点类型,数学函数计算 |
| char | 16位 | 0 ~ 65535(Unicode字符) | '\u0000' | 单个字符的存储 |
| boolean | 未严格定义(JVM规范建议1位) | true / false | false | 条件判断、标志位 |
这个表有个非常值得记住的点:int是Java默认的整数类型,如果你直接写123,那它就是int。同样,带小数点的字面量默认是double而不是float,所以float f = 3.14;编译会直接报错,必须写成3.14f。这两个默认值规则,是所有类型陷阱里最先踩到的。
1.3 为什么没有unsigned类型
细心的读者会发现,Java的八种基本类型中没有任何一种是无符号(unsigned)类型。C和C++里满屏的unsigned int、unsigned char在Java里完全不存在。这是Java设计者刻意为之的。
主要还是为了简化语言。无符号和有符号的混用会导致很多隐蔽的bug,比如两个无符号数相减,在C语言里一不小心就会变成很大的正数,这种问题在Java里从根本上被杜绝了。所以Java的byte、short、int、long,清一色都是补码表示的有符号数。即使是char,名义上取值范围是0到65535,但它被定位为“单个UTF-16编码单元”,用来表示字符,不参与算术运算的日常场景。
这个设计的好处是语言层面减少了一整类“符号位处理”的错误。坏处是什么?当你真的需要无符号数时,比如处理二进制协议里的无符号字段,就得自己用更大的类型去转换(比如用int接收两个byte拼出来的无符号值),多写一些代码。但整体来说,利远大于弊。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 逐个拆解八种类型:不仅要知道是什么,更要知道为什么
2.1 整型四兄弟:byte、short、int、long
整数类型有四个,按字节从小到大是byte(1字节)、short(2字节)、int(4字节)、long(8字节)。它们就像四个容量不同的容器,容器越大能装的数越大,但占用的内存也越多。
为什么Java需要四个不同的整数类型,而不是统一用int?核心答案只有两个字:内存。假设你要存储一个包含100万像素的灰度图,每个像素是0到255的灰度值。用byte只需要1MB,用int就要4MB。内存差距在这种批量场景下非常可观。尤其是在移动端、嵌入式环境中,这种节省是实实在在的。
但现实是,byte和short在Java里实际上有点“鸡肋”。因为JVM规范规定,byte和short在进行算术运算时,会先自动提升为int再运算。也就是说,你写byte a = 10; byte b = 20; byte c = a + b;是编译不过的,编译器会告诉你a + b的结果是int,不能自动窄化成byte。你只能写成byte c = (byte)(a + b);。这就是类型提升机制带来的麻烦。
int是Java中最常用的整数类型,没有之一。数组下标、循环计数、常规算术,全都默认用int。面试里常考的int范围是-2147483648到2147483647。这里藏着一个最经典的面试陷阱:Math.abs(Integer.MIN_VALUE)返回的仍然是负数。因为Integer.MIN_VALUE的绝对值超出了int的范围,取绝对值后整型溢出,还是-2147483648。
long用于真正的大数场景,比如System.currentTimeMillis()返回的时间戳,或者System.nanoTime()纳秒级别的计时,又或者文件大小的高精度统计。定义一个long类型的字面量,后面要加大写L或小写l,比如long timestamp = 1700000000000L;。强烈建议使用大写L,因为小写l在楷体字体下跟数字1极度相似,阅读代码时容易误导别人。
经典笔试题:
long a = 2147483648;编译报错,因为2147483648已经超出int范围了,必须先声明为2147483648L。这考察的就是“字面量默认是int”这个规则。
2.2 浮点双雄:float与double的精度之痛
float和double是按照IEEE 754标准实现的浮点数,前者32位、后者64位。浮点数的数据和你想的不一样,它不是用十进制直接存的,而是拆成“符号位 + 指数位 + 尾数位”存进去的。这就导致一个非常反直觉的现象:0.1 + 0.2在Java里并不等于0.3,而是约等于0.30000000000000004。
这不是Java的bug,所有遵循IEEE 754的语言都有这个问题。因为0.1在二进制里是一个无限循环小数,计算机只能取近似值。你看到0.1那个字面量,在内存里其实是另一个非常接近它的数。
对于日常的简单运算,浮点数没毛病。但如果你做的是金融计算、金额累计、计费系统,那你用float或double就是在给自己埋雷,早晚会出“差一分钱”的事故。在这种场景下,正确做法是使用BigDecimal,并用字符串构造器new BigDecimal("19.99")而不是直接传double。
java复制// 正确示例:金额计算必须用BigDecimal(字符串构造)
BigDecimal price = new BigDecimal("19.99");
BigDecimal quantity = new BigDecimal("3");
BigDecimal total = price.multiply(quantity);
// total = 59.97,精确无误
为什么不直接用double做金额?我见过有人用double跑财务系统,测试时金额不大没问题,到了线上金额大、笔数多时,累计误差被放大到几块钱甚至几十块钱。用户投诉都压不住。这类问题一旦发生,排查成本极高,因为你看到的金额是四舍五入后的,底层数据早就歪了。
浮点数的比较也有坑。你要判断两个double是否相等,千万别写if (a == b),因为运算结果的精度误差可能导致本来应该相等的结果不相等。比如0.1 + 0.2 == 0.3是false。正确做法是指定一个误差范围epsilon:
java复制double target = 0.3;
double actual = 0.1 + 0.2;
double epsilon = 1e-9;
if (Math.abs(actual - target) < epsilon) {
// 在这个误差范围内,认为相等
}
float在日常开发中用得很少,除了某些图形学库和保存模型权重时为了省内存会用到。它只有大约7位有效十进制数字,double大约是15到16位。数字越大,浮点数的精度就越差,因为指数位增长后,尾数位还要在有限的位数内表示数值,密度会下降。这个特性在写科学计算代码时要格外留意。
2.3 char类型:不只是一个字符
char在Java里的定位比较特殊。它占16位(两个字节),范围是0到65535,理论上可以表示Unicode字符集的前65536个码点。注意,是“理论上”。因为Unicode的码点总数已经超过了65536,所以Java里那些比较偏门的中日韩统一表意文字、emoji表情等,实际上需要两个char拼起来表示,这叫代理对(surrogate pair)。
所以在Java中,char变量的语义其实更像是“一个UTF-16编码单元”,而不是严格意义上的“一个字符”。这在处理字符串长度时会引起一个著名陷阱:
java复制String emoji = "😂";
System.out.println(emoji.length()); // 输出2,而不是1
因为😂这个字符的码点超过了65535,在UTF-16编码下需要两个代理项来表示。如果你用传统方式遍历字符串,就可能把一个完整的emoji截成两个毫无意义的半截字符。这也是为什么Java 8以后推荐用codePoint相关的方法来处理字符串里的复杂字符,而不是直接用char。
char可以参与算术运算,因为它在底层其实是一个无符号整数。比如char c = 'A'; int index = c - 'A';就能算出字母的下标,这种写法在算法题中很常见。但正因如此,它也继承了整数类型的窄化问题:char c = 65536;会编译报错,因为超出范围了。
2.4 boolean类型:真与假的物理存储之谜
boolean只有两个值:true和false。这是人类认知中最直观的数据类型,但它的实现细节却没那么直白。JVM规范里没有硬性规定boolean数组的每个元素占多大空间,而是留给了具体实现去决定。在实际实现中,单独的boolean变量往往被当作int处理,占4个字节;而boolean[]数组中的每个元素通常占1个字节。
这就导致一个非常微妙的结论:new boolean[1024]可能占1024字节,而new boolean[1024]如果按照“每个boolean只占1位”来算,本来只需要128字节。所以如果你要存储海量的布尔标志位,用boolean[]并不划算,得考虑用BitSet来压缩。
boolean还有一个常见面试坑:它不能直接转换为其他基本类型。有些语言里0等于false,1等于true,但在Java里,你写int i = (int) true;直接编译失败。两个世界是隔离的。如果你确实需要把boolean转成int,只能自己用三元运算符写:int flag = boolValue ? 1 : 0;。
2.5 类型转换与强制窄化:每个开发者都会踩的溢出坑
基本类型之间的转换分为两种:自动类型转换和强制类型转换。自动类型转换发生在“小范围”向“大范围”转的时候,比如int转long、float转double,编译器无脑帮你处理,不会丢失任何信息。而强制类型转换是“大范围”向“小范围”转,比如long转int、double转int,这种转换会丢精度,必须显式加上(int)这样的强转符。
强制类型转换最大的风险是溢出静默发生,编译器不会报错。比如:
java复制int big = 300;
byte small = (byte) big;
// small的结果是44,因为300转成二进制后,超出byte部分被截断
这种问题杀伤力极大,因为程序不会抛异常,数据悄悄就错了。这种 bug 一般出现在:“对方接口返回一个long,你强转成int存起来”的场景里,数据量一旦增大,百分百出问题。所以我现在对强制窄化非常警惕,任何强转都要先做范围检查。
java复制public static int safeLongToInt(long value) {
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
throw new ArithmeticException("数值超出int范围: " + value);
}
return (int) value;
}
另外还有一个很容易被忽略的点:int直接转float可能会丢失精度。因为float只有24位尾数,而int需要32位才能精确表示。虽然float的取值范围比int大,但精度上的“大”不代表“精确”,所以Java允许int到float的自动类型转换,但底层其实是可能丢精度的,只不过Java规范把这个风险“吃”进内部了。这种设计上的妥协,如果没人提醒,写代码的人一般注意不到。
3. 包装类型:基本类型的对象化与自动装箱陷阱
3.1 为什么需要包装类型
基本类型有了,为什么还要有Integer、Long、Double这一堆包装类?根本原因是泛型和集合框架的存在。你写List<int>试试,编译直接报错。泛型要求类型参数必须是引用类型,所以你想存一列整数,只能用List<Integer>。这个Integer就是int的“对象形态”,它内部包装了一个int值,并且提供了一堆工具方法,比如解析字符串、比较大小、进制转换。
除此之外,每个包装类型都提供了一些很实用的静态方法。比如Integer.parseInt("123")把纯数字字符串解析成int;Integer.toHexString(255)把整数转成十六进制字符串;Integer.MAX_VALUE和Integer.MIN_VALUE表示范围边界。这些内容在刷LeetCode或者做业务开发时,出镜率极高。
3.2 自动装箱与拆箱:语法糖背后的性能代价
Java 5引入了自动装箱(autoboxing)和自动拆箱(unboxing),写法上确实爽了不少。以前你要写list.add(Integer.valueOf(1)),现在直接list.add(1)就行;你要拿值时直接int value = list.get(0),编译器自动帮你拆箱。
但爽是要付出代价的。自动装箱本质上是调用Integer.valueOf()方法创建一个新的包装对象(或者命中缓存),自动拆箱本质上是调用intValue()方法取出值。如果这些操作发生在循环里,对象创建的开销会被放大成严重的性能问题。
java复制// 性能反例:循环里大量自动装箱
Integer sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i; // 每次都要拆箱再装箱,产生大量Integer对象
}
这个代码每次执行sum += i时,先把sum拆箱成int,加完再装箱成新的Integer,一百次循环就是一百个对象分配。虽然JIT可能做了一些优化,但遇到热点代码、大数据量循环,这种写法仍然可能成为性能瓶颈。正确写法是把求和变量声明为int,最后再转成Integer。
我实际测试过,上面这个一百万次的累加,用Integer sum比用int sum慢一个数量级,而且内存分配量翻好多倍。所以写代码时要心里有数:自动装箱是方便,不是免费。
3.3 包装类型缓存机制:100等于100,1000不等于1000
这是Java基本类型面试题中的“天王级”考点:为什么Integer a = 100; Integer b = 100; System.out.println(a == b);输出true,而把100换成1000,输出就变成false了?
答案在于Integer内部有一个缓存机制,范围是-128到127。当你通过自动装箱(即Integer.valueOf())在这个范围内创建Integer时,它不会新建对象,而是直接返回IntegerCache.cache[]数组里预先创建好的那个对象。而超过这个范围,就老老实实new一个新对象。
java复制Integer a = 100; // 等价于 Integer.valueOf(100)
Integer b = 100; // 再次走缓存,拿同一个对象
System.out.println(a == b); // true,同一对象
Integer c = 1000;
Integer d = 1000;
System.out.println(c == d); // false,不同对象
这个设计的初衷是好的:-128到127是最高频使用的小整数,缓存它们能减少不必要的对象创建。但你如果不知道这个机制,直接用==比较两个Integer变量,就会在数据量跨过127这个边界时突然“翻车”。
绝对可靠的写法是:只要是比较包装类型的数值,一律用equals()方法,或者先把包装类型转成基本类型再比较。同时还得注意equals()方法的参数类型,Integer的equals()要求传入的是Integer对象,如果你传一个基本类型int,会自动装箱,这不影响结果,但代码阅读时要心里有数。
Long、Short、Byte也有同样的缓存机制。Character缓存的是0到127。Boolean只有两个实例TRUE和FALSE,没有缓存一说。Float和Double没有整数缓存机制,因为浮点数的离散性让“高频小值”不像整数那么明显。
3.4 包装类型判空的致命问题
自动拆箱还有一个非常隐蔽的坑:如果包装类型对象是null,一旦你把它赋值给基本类型变量,或者参与运算,就会触发NullPointerException。
java复制Integer count = null;
int value = count; // 运行时报NPE
这种错误在数据库查询对象映射、RPC接口返回值中极其常见。比如某个接口返回的Integer字段,在数据库里是NULL,映射到Java对象后就是null,你直接去跟0比较大小或者直接参与+ - * /运算,直接炸了。排查NPE的时候,翻到最后一行堆栈,往往就是这种“隐式拆箱”在捣鬼。
从工程实践角度,我建议:在实体对象或DTO中,所有可能为空的数值字段都用包装类型,否则无法区分0和null;但在局部变量、计算过程中的数值,能直接用基本类型就用基本类型,减少拆装箱的空指针风险。
3.5 包装类型的性能与内存全景对比
很多人写代码时不太关注基本类型和包装类型在内存上的差异,但这个差异在量级上来后非常恐怖。
- 基本类型
int占4字节,直接存在栈上或作为对象字段存在对象内部。 - 包装类型
Integer本身是一个对象,除了内部那个4字节的int值,还有对象头(通常8字节或12字节),可能还有对齐填充,总大小常超过16字节,是int的4倍以上。 - 如果把
int存进ArrayList<Integer>,还要算上ArrayList底层Object[]数组的引用指针(4或8字节),以及每个元素实际指向堆上的Integer对象。真实内存开销轻松超过一个裸int的5到8倍。
所以在内存敏感的长生命周期场景(比如缓存大量计数、做统计报表),优先用基本类型的数组int[],或者用专门的高性能原始类型集合库(比如fastutil、Eclipse Collections),别一股脑全用List<Integer>。
4. 面试高频题与经典陷阱:八种基本类型的变体考题
4.1 从“八种基本类型”延伸出来的必问题
接下来我们把论坛上和面试中流传最广的几个问题集中过一遍。这些问题不管你是去大厂还是中小厂,遇到概率都非常高。
第一问:Java中int和Integer有什么区别?
标准化回答包含四个要点。一是类型不同:int是基本类型,Integer是引用类型包装类。二是存储位置:int直接存值,Integer是对象,引用指向堆内存。三是默认值:int默认0,Integer默认null。四是比较方式:int用==直接比较值,Integer用==比较引用,用equals()比较值。
加分项:讲清楚自动装箱/拆箱机制,再补一句缓存机制。这样面试官会觉得你不只是背了定义,而是真的理解。
第二问:0.1 + 0.2 == 0.3输出什么?为什么?
输出false。原因是二进制浮点数无法精确表示0.1和0.2,运算结果是一个接近0.3但略有误差的数。面试时顺带提出解决方案(用BigDecimal或误差范围比较)会大大加分。
第三问:switch语句可以作用在哪些类型上?
这是八种基本类型的最经典衍生题。switch支持int、char、byte、short及其包装类型,还支持String和枚举。但注意,switch不支持long、float、double和boolean。为什么?因为switch底层实际是基于int跳转表实现的。char、byte、short会提升到int,String是通过哈希码先匹配再equals确认。long因为范围远超int,无法建跳转表,所以被排除了。
第四问:byte可以保存127,那127 + 1会怎样?
答案是编译报错。因为127 + 1的结果是int类型的128,不能自动窄化成byte。你用(byte)(127 + 1)强转,结果又变成了-128,这就是典型的溢出回绕。这个题考察的就是你对范围、默认类型和强转规则的综合理解。
第五问:为什么char和short都是16位,但取值范围不一样?
因为char是无符号的0到65535,short是有符号的-32768到32767。两者占用空间一样,但位模式解析方式不同。
4.2 代码审查中最常见的类型地雷
工作中我做代码评审时,经常在PR里见到下面这类问题,每一次都能让代码质量和线上稳定性掉一截。
地雷一:数据库字段用int存金额。 金额用基本类型int或long存分,还能控制精度;但有人用int存以“元”为单位的金额,一旦金额超过2000万,乘个汇率直接溢出。正确做法是金额用BigDecimal,或者以最小货币单位存成long。
地雷二:时间戳字段用int。 System.currentTimeMillis()返回的是long,但总有人因为偷懒把它强转成int存数据库。int能装下的秒数只到2038年,而currentTimeMillis()是毫秒,瞬间就爆。有人线上系统跑了两三年,突然发现所有新建记录的时间变成负数,排查了半天,最后发现是强转惹的祸。
地雷三:方法返回值随意用包装类型。 如果你用Integer作为方法的返回值,调用方不知道你有没有可能返回null,一旦直接拆箱就NPE。规范做法是:方法内部可能为空的用包装类型,并明确文档说明;永不为空的用基本类型,强制杜绝NPE。
地雷四:在集合排序或比较器里直接做减法。 经典错误return o1.getCount() - o2.getCount()。当第一个数接近Integer.MAX_VALUE、第二个为负数时,减法溢出导致排序结果错乱。正确写法是Integer.compare(o1.getCount(), o2.getCount())。
4.3 常用API技巧与易错点速查
用久了你会发现,基本类型相关API虽然简单,但细节不少。这里把我日常用得最多、也觉得大家最容易踩坑的几个点整理一下。
Integer.parseInt("123")返回int,而Integer.valueOf("123")返回Integer。- 解析字符串时一定要考虑数字格式异常:
Integer.parseInt("12a")会抛NumberFormatException。 Integer.toBinaryString(-1)输出的是11111111111111111111111111111111,对应补码表示。Double.parseDouble("NaN")能成功,返回NaN,这种值参与比较时永远不等于任何数,包括它自己。- 比较两个
double是否相等,别用==,因为NaN和无穷大的存在会让结果违反直觉。 Integer.MAX_VALUE + 1不会报错,直接变成Integer.MIN_VALUE,这就是整数溢出。想安全加法可以用Math.addExact(a, b),它会在溢出时抛ArithmeticException。
java复制// 安全相加示例,避免静默溢出
try {
int result = Math.addExact(Integer.MAX_VALUE, 1);
} catch (ArithmeticException e) {
// 捕获溢出异常,做兜底处理
}
从Java 8开始,Math类提供了一系列addExact、subtractExact、multiplyExact方法,在需要严格控制溢出的金融、计时、计数场景非常实用。别嫌它麻烦,它能帮你把“悄无声息的数据错误”变成“当场爆出来的异常”,后者好排查得多。
5. 从“会用”到“理解”:正确学习基本类型的姿势
5.1 为什么很多人背了表还是写不好代码
我见过不少同学能把八种基本类型的名称、字节数背得滚瓜烂熟,但一到写代码就出问题。原因是背诵只是记住了“存在性”,没有理解“为什么存在”和“怎样参与运算”。基本类型背后承载的是计算机硬件对数据的原始表示,是栈、堆、补码、IEEE 754这些底层概念的缩影。
想要真正掌握,我的建议有三个。第一,写几道位运算练习题,亲手感受补码和移位规则;第二,用javap -c反编译一段自动装箱拆箱代码,亲眼看看语法糖被还原成什么样子;第三,把Integer和int混用的代码单独拿出来做一次内存开销估算,感知数据规模和性能的关系。
5.2 实测:反编译看自动装箱背后的字节码
这里分享一个我自己实践过的验证方法。你有这样一段代码:
java复制public class BoxDemo {
public static void main(String[] args) {
Integer a = 100;
int b = a + 1;
}
}
用javac编译后,执行javap -c BoxDemo,你会看到类似这样的输出(不同JDK版本有些微差异):
java复制0: bipush 100
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: invokevirtual #3 // Method java/lang/Integer.intValue:()I
10: iconst_1
11: iadd
12: istore_2
注意看这两行关键调用:Integer.valueOf(int)就是自动装箱;Integer.intValue()就是自动拆箱。亲眼看到编译后的字节码,你才会对“语法糖”三个字有真正的体感。以后再遇到性能问题,你就会本能地想到,每一行看起来无辜的代码背后,其实都藏了方法调用和对象创建。
5.3 结合实际项目:如何选择基本类型与包装类型
结合我的实际经验,给你一套选择参考:
- DTO、实体对象、数据库映射字段:一律用包装类型,因为数据库存在
NULL的概念,只有包装类型能区分“未设置”和“数值0”。 - 局部变量、计算中间量、循环计数器:一律用基本类型,减少自动装箱和空指针风险。
- 方法参数:能不接收
null的,就用基本类型,强制调用方保证非空。 - 方法返回值:确定非空的用基本类型;可能为空的用包装类型,并加上文档或注解说明。
- 泛型集合的元素:只能用包装类型,因为泛型不接受基本类型。
- 高性能数值处理场景:能用
int[]就不用ArrayList<Integer>,能少创建对象就少创建。
5.4 关于基本类型后续学习路线的建议
八种基本类型只是Java基础的一小块。当你真正理解了它们,接下来值得深入的是这几个方向:对象的布局与内存模型、垃圾回收对对象创建的影响、String的不可变性和常量池、泛型与类型擦除。每一块都跟基本类型的“为什么”环环相扣。如果你在刷面试题,不要只背答案,把每种题背后的JVM原理和语言设计动机都弄明白,面试官问深了你也扛得住。
我自己在面试候选人时,最看重的不是对方能不能背出八种基本类型,而是能不能把Integer缓存范围、浮点精度损失、类型提升规则串起来讲清楚。这三个点能讲明白的人,说明对Java这门语言是下过功夫的。
最后再分享一个小技巧:写代码时,每当你要在int和Integer之间转换,都停下来问一句“这个变量可能是null吗?这个操作会溢出吗?这一行会创建多少对象?”这三个问题想清楚,八种基本类型相关的坑你基本就全避开了。说到底,基础不是背出来的,是踩坑踩出来的。希望这篇文章能帮你少踩几个。
