Integer ------ 最常用的整数包装类深度解析
适用版本: JDK 8 难度等级: 核心 核心概念: IntegerCache、自动装箱拆箱、位运算方法集、进制转换
一、Integer 的类结构
java
public final class Integer extends Number implements Comparable<Integer> {
@Native public static final int MIN_VALUE = 0x80000000; // -2^31
@Native public static final int MAX_VALUE = 0x7fffffff; // 2^31 - 1
public static final Class<Integer> TYPE
= (Class<Integer>) Class.getPrimitiveClass("int");
public static final int SIZE = 32;
public static final int BYTES = SIZE / Byte.SIZE;
private final int value;
private static final long serialVersionUID = 1360826667806852920L;
}
二、IntegerCache ------ 面试必问的缓存机制
java
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 默认缓存 -128 ~ 127
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// 防止缓存数组过大
h = Math.min(i, Integer.MAX_VALUE - (-low) - 1);
} catch (NumberFormatException nfe) {
// 配置错误则忽略
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for (int k = 0; k < cache.length; k++) {
cache[k] = new Integer(j++);
}
}
private IntegerCache() {}
}
缓存生效场景
java
public class IntegerCacheDemo {
public static void main(String[] args) {
// 自动装箱会调用 Integer.valueOf(int),走缓存
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true------都是缓存中同一对象
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false------超出缓存范围,各自 new
// 显式调用 valueOf 同样走缓存
System.out.println(Integer.valueOf(100) == Integer.valueOf(100)); // true
// new Integer 始终不走缓存
System.out.println(new Integer(100) == new Integer(100)); // false
}
}
调整缓存上限
bash
# 设置缓存上限为 1000
java -Djava.lang.Integer.IntegerCache.high=1000 MyApp
三、自动装箱与拆箱
java
public class BoxingDemo {
public static void main(String[] args) {
// 装箱:int → Integer ------ 编译器生成 Integer.valueOf(i)
Integer boxed = 42; // 等价于 Integer.valueOf(42)
// 拆箱:Integer → int ------ 编译器生成 integer.intValue()
int unboxed = boxed; // 等价于 boxed.intValue()
// 运算时自动拆箱
Integer x = 100, y = 200;
Integer z = x + y; // 拆箱 → 计算 → 装箱
// 三目运算符的隐藏陷阱
Integer n1 = null;
// 下面这行会抛出 NullPointerException(n1 拆箱为 int 时)
// int result = true ? n1 : 0;
}
// 实际编译后的字节码等价于:
// Integer boxed = Integer.valueOf(42);
// int unboxed = boxed.intValue();
}
四、进制转换方法
4.1 toString(int i, int radix)
java
public static String toString(int i, int radix) {
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10;
if (radix == 10) return toString(i);
char buf[] = new char[33];
boolean negative = (i < 0);
int charPos = 32;
if (!negative) i = -i;
while (i <= -radix) {
buf[charPos--] = digits[-(i % radix)];
i = i / radix;
}
buf[charPos] = digits[-i];
if (negative) buf[--charPos] = '-';
return new String(buf, charPos, (33 - charPos));
}
java
public class RadixDemo {
public static void main(String[] args) {
int val = 255;
System.out.println("十进制: " + Integer.toString(val, 10)); // 255
System.out.println("二进制: " + Integer.toString(val, 2)); // 11111111
System.out.println("八进制: " + Integer.toString(val, 8)); // 377
System.out.println("十六进制: " + Integer.toString(val, 16)); // ff
System.out.println("三十二进制: " + Integer.toString(val, 32)); // 7v
}
}
4.2 toHexString / toOctalString / toBinaryString
java
public static String toHexString(int i) {
return toUnsignedString0(i, 4); // 4位一组 → 十六进制
}
public static String toOctalString(int i) {
return toUnsignedString0(i, 3); // 3位一组 → 八进制
}
public static String toBinaryString(int i) {
return toUnsignedString0(i, 1); // 1位一组 → 二进制
}
五、parseInt ------ 字符串解析精要
java
public static int parseInt(String s, int radix) throws NumberFormatException {
if (s == null) throw new NumberFormatException("null");
if (radix < Character.MIN_RADIX)
throw new NumberFormatException("radix " + radix + " less...");
if (radix > Character.MAX_RADIX)
throw new NumberFormatException("radix " + radix + " greater...");
int result = 0;
boolean negative = false;
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') {
if (firstChar == '-') { negative = true; limit = Integer.MIN_VALUE; }
else if (firstChar != '+') throw NumberFormatException.forInputString(s);
if (len == 1) throw NumberFormatException.forInputString(s);
i++;
}
int multmin = limit / radix;
while (i < len) {
int digit = Character.digit(s.charAt(i++), radix);
if (digit < 0) throw NumberFormatException.forInputString(s);
if (result < multmin) throw NumberFormatException.forInputString(s);
result *= radix;
if (result < limit + digit) throw NumberFormatException.forInputString(s);
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
设计亮点 :使用负累加 而非正累加,避免 Integer.MIN_VALUE 取负时的溢出问题。
六、位运算方法
6.1 highestOneBit / lowestOneBit
java
public static int highestOneBit(int i) {
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
public static int lowestOneBit(int i) {
return i & -i; // 利用补码特性
}
java
public class BitDemo {
public static void main(String[] args) {
int val = 18; // 二进制: 00010010
System.out.println("highestOneBit(18): " + Integer.highestOneBit(val)); // 16
System.out.println("lowestOneBit(18): " + Integer.lowestOneBit(val)); // 2
// highestOneBit 的应用:快速找到 <= n 的最大2的幂
System.out.println("2^{log2(100)}: " + Integer.highestOneBit(100)); // 64
}
}
6.2 bitCount ------ 统计 1 的个数
java
public static int bitCount(int i) {
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
6.3 numberOfLeadingZeros / numberOfTrailingZeros
java
public static int numberOfLeadingZeros(int i) {
if (i == 0) return 32;
int n = 1;
if (i >>> 16 == 0) { n += 16; i <<= 16; }
if (i >>> 24 == 0) { n += 8; i <<= 8; }
if (i >>> 28 == 0) { n += 4; i <<= 4; }
if (i >>> 30 == 0) { n += 2; i <<= 2; }
n -= i >>> 31;
return n;
}
七、decode ------ 多进制解码器
java
public static Integer decode(String nm) throws NumberFormatException
解码规则:
| 前缀 | 进制 | 示例 |
|---|---|---|
0x / 0X |
十六进制 | 0xFF → 255 |
# |
十六进制 | #FF → 255 |
0 |
八进制 | 0377 → 255 |
| 无前缀 | 十进制 | 255 → 255 |
java
public class DecodeDemo {
public static void main(String[] args) {
System.out.println(Integer.decode("255")); // 255
System.out.println(Integer.decode("0xFF")); // 255
System.out.println(Integer.decode("0XFF")); // 255
System.out.println(Integer.decode("#FF")); // 255
System.out.println(Integer.decode("-0xFF")); // -255
System.out.println(Integer.decode("0377")); // 255(八进制)
}
}
八、无符号运算(JDK 8 新增)
java
// 无符号除法
public static int divideUnsigned(int dividend, int divisor)
// 无符号取余
public static int remainderUnsigned(int dividend, int divisor)
// 无符号比较
public static int compareUnsigned(int x, int y)
// 无符号字符串转换
public static String toUnsignedString(int i, int radix)
java
public class UnsignedOperationsDemo {
public static void main(String[] args) {
// -1 的 int 表示用 Unsigned 解读就是 2^32 - 1 = 4294967295
int signedNegative = -1;
System.out.println("无符号值: " + Integer.toUnsignedString(signedNegative));
System.out.println("无符号 ÷ 3: "
+ Integer.divideUnsigned(signedNegative, 3));
System.out.println("无符号 % 3: "
+ Integer.remainderUnsigned(signedNegative, 3));
}
}
九、综合实战:IP 地址与 int 互转
java
/**
* IPv4 地址(点分十进制)与 int 值的互换
* 常用在 Redis/MySQL 中存储 IP 地址
*/
public class IpConverter {
public static int ipToInt(String ip) {
String[] parts = ip.split("\\.");
if (parts.length != 4) {
throw new IllegalArgumentException("非法IP: " + ip);
}
// 将4个字节组装成一个int(大端序)
return (Integer.parseInt(parts[0]) << 24)
| (Integer.parseInt(parts[1]) << 16)
| (Integer.parseInt(parts[2]) << 8)
| Integer.parseInt(parts[3]);
}
public static String intToIp(int ip) {
return ((ip >> 24) & 0xFF) + "."
+ ((ip >> 16) & 0xFF) + "."
+ ((ip >> 8) & 0xFF) + "."
+ (ip & 0xFF);
}
public static void main(String[] args) {
String ip = "192.168.1.100";
int intVal = ipToInt(ip);
System.out.println("IP → int: " + intVal); // -1062731364
System.out.println("无符号形式: "
+ Integer.toUnsignedString(intVal)); // 3232235876
System.out.println("int → IP: " + intToIp(intVal)); // 192.168.1.100
}
}
十、综合实战:简易 BitMap 实现
java
/**
* 基于 int[] 和 Integer 位运算的轻量级 BitMap
* 适用于用户签到、去重计数等场景
*/
public class SimpleBitMap {
private final int[] bits;
private final int capacity;
public SimpleBitMap(int capacity) {
this.capacity = capacity;
// 每个 int 存 32 个 bit
this.bits = new int[(capacity + 31) / 32];
}
public void set(int index) {
if (index < 0 || index >= capacity) {
throw new IndexOutOfBoundsException();
}
int wordIndex = index >> 5; // index / 32
int bitIndex = index & 0x1F; // index % 32
bits[wordIndex] |= (1 << bitIndex);
}
public boolean get(int index) {
if (index < 0 || index >= capacity) {
throw new IndexOutOfBoundsException();
}
int wordIndex = index >> 5;
int bitIndex = index & 0x1F;
return (bits[wordIndex] & (1 << bitIndex)) != 0;
}
public void clear(int index) {
if (index < 0 || index >= capacity) {
throw new IndexOutOfBoundsException();
}
int wordIndex = index >> 5;
int bitIndex = index & 0x1F;
bits[wordIndex] &= ~(1 << bitIndex);
}
public int countSetBits() {
int total = 0;
for (int word : bits) {
total += Integer.bitCount(word);
}
return total;
}
public static void main(String[] args) {
// 模拟365天签到
SimpleBitMap signIn = new SimpleBitMap(365);
signIn.set(0); // 第1天签到
signIn.set(10); // 第11天签到
signIn.set(100); // 第101天签到
System.out.println("第1天签到: " + signIn.get(0)); // true
System.out.println("第2天签到: " + signIn.get(1)); // false
System.out.println("总签到天数: " + signIn.countSetBits()); // 3
}
}
十一、面试高频考点
| 问题 | 关键要点 |
|---|---|
| IntegerCache 的默认范围 | -128 ~ 127,上限可通过 JVM 参数调整 |
| 自动装箱用 valueOf 而非 new | valueOf 利用缓存,new 绕过缓存 |
| parseInt 为何用负累加 | 避免 Integer.MIN_VALUE 取负溢出 |
| == 与 equals 在 Integer 中的区别 | == 比较引用(缓存内相等,缓存外不等),equals 比较值 |
| bitCount 算法的原理 | 分治法,逐步合并计数值(变种的 SWAR 算法) |
| toUnsignedString 的用途 | 将有符号 int 以无符号形式展示 |
| highestOneBit 的应用场景 | HashMap 中计算 tableSizeFor(向上取2的幂) |