Java作为一门强类型语言,数据类型系统是其核心基础。很多初学者在面试和实际开发中,经常在数据类型转换、精度丢失、字符编码等基础问题上栽跟头。这些问题看似简单,但涉及计算机底层原理,如果不彻底搞懂,很容易写出隐藏bug的代码。
我在技术面试中经常用这类问题考察候选人的基本功,发现即使是工作3-5年的开发者,对浮点数精度、字符编码等细节也常常一知半解。本文将系统梳理Java数据类型的扩展知识,结合高频面试题,帮你建立完整的知识体系。
Java支持四种进制表示法:
int a = 100;0b,如int b = 0b1100100;(Java7+支持)0,如int c = 0144;0x,如int d = 0x64;进制转换常见面试题:
java复制System.out.println(0b10); // 输出多少?
System.out.println(010); // 输出多少?
System.out.println(0x10); // 输出多少?
注意:在实际开发中,除非处理硬件或加密算法,否则不建议使用八进制表示法,容易与十进制混淆。
位运算在性能敏感场景(如Redis位图、Bloom过滤器)中有重要应用:
java复制// 常规写法
if (n % 2 == 1) { /* 奇数 */ }
// 位运算写法(效率更高)
if ((n & 1) == 1) { /* 奇数 */ }
java复制int a = 5, b = 10;
a ^= b;
b ^= a;
a ^= b;
// 现在a=10, b=5
java复制int i = -5;
int abs = (i ^ (i >> 31)) - (i >> 31);
实操心得:位运算虽然高效,但会降低代码可读性。建议添加详细注释,或封装成工具方法。
Java的float和double采用IEEE 754标准,这种表示法导致经典问题:
java复制System.out.println(0.1 + 0.2); // 输出0.30000000000000004
根本原因:
java复制// 错误用法:仍然有精度问题
BigDecimal d1 = new BigDecimal(0.1);
// 正确用法:使用String构造
BigDecimal d2 = new BigDecimal("0.1");
BigDecimal并设置RoundingModelong(避免小数运算)java复制double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 1e-10;
if (Math.abs(a - b) < epsilon) {
System.out.println("视为相等");
}
问题:以下代码输出什么?
java复制float f1 = 20_000_000f;
float f2 = f1 + 1;
System.out.println(f1 == f2); // true
答案与分析:
Java内部使用UTF-16编码,但开发者常混淆的概念:
关键区别:
| 特性 | UTF-8 | UTF-16 |
|---|---|---|
| 最小单位 | 8位(1字节) | 16位(2字节) |
| 扩展机制 | 可变长度(1-4字节) | 固定2字节(代理对扩展) |
| 英文效率 | 高(1字节/字符) | 低(2字节/字符) |
| 中文效率 | 通常3字节 | 通常2字节 |
String的不可变性设计:
java复制String s1 = "hello";
String s2 = new String("hello");
// s1在常量池,s2在堆内存
JVM字符串优化:
java复制String s3 = "he" + "llo"; // 编译后等价于"hello"
String s4 = s1 + "world"; // 运行时创建新对象
常见乱码问题解决方案:
java复制// 错误示例:依赖平台默认编码
byte[] bytes = "中文".getBytes();
// 正确做法:明确指定编码
byte[] utf8Bytes = "中文".getBytes(StandardCharsets.UTF_8);
String recovered = new String(utf8Bytes, StandardCharsets.UTF_8);
避坑指南:涉及IO操作时(如文件读写、网络传输),必须显式指定字符编码,否则可能因运行环境不同导致乱码。
问题:以下代码输出什么?
java复制Integer a = 100, b = 100;
System.out.println(a == b); // true
Integer c = 200, d = 200;
System.out.println(c == d); // false
解析:
问题:以下代码能否编译?输出什么?
java复制short s1 = 1;
s1 = s1 + 1; // 编译错误
short s2 = 1;
s2 += 1; // 正常编译
解析:
s1 + 1自动提升为int,不能直接赋给short+=运算符包含隐式类型转换题目:实现一个方法,输入字符串形式的数字(如"123.45"),返回精确到分的金额(以long类型表示,单位:分)
参考答案:
java复制public static long parseMoney(String amount) {
BigDecimal decimal = new BigDecimal(amount);
// 四舍五入到小数点后两位
decimal = decimal.setScale(2, RoundingMode.HALF_UP);
// 转换为分(乘以100)
return decimal.multiply(new BigDecimal(100)).longValue();
}
测试用例:
java复制System.out.println(parseMoney("3.1415")); // 输出314
System.out.println(parseMoney("10.999")); // 输出1100
数值类型选择原则:
字符串处理建议:
类型转换规范:
java复制int big = 200;
if (big > Byte.MAX_VALUE) {
throw new ArithmeticException("值超出byte范围");
}
byte small = (byte) big;
编码统一约定:
现象:计算结果出现微小误差
排查步骤:
现象:中文显示为问号或乱码
排查流程:
现象:ClassCastException或精度丢失
解决方案:
java复制// 低效
Integer sum = 0;
for (Integer i : list) { sum += i; }
// 高效
int sum = 0;
for (Integer i : list) { sum += i; }
java复制int a = 10;
a <<= 1; // 等同于a *= 2
a >>= 2; // 等同于a /= 4
java复制// 每次调用都重新编译
String.matches("[0-9]+");
// 预编译后复用
private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+");
boolean matches = NUMBER_PATTERN.matcher(input).matches();
java复制StringJoiner sj = new StringJoiner(", ", "[", "]");
sj.add("Java").add("Python").add("C++");
System.out.println(sj); // 输出[Java, Python, C++]
Java 14引入的instanceof模式匹配:
java复制// 传统写法
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// Java14+写法
if (obj instanceof String s) {
System.out.println(s.length());
}
Java 16正式引入的记录类(Record):
java复制record Point(int x, int y) {}
Point p = new Point(10, 20);
System.out.println(p.x()); // 自动生成访问方法