10 Integer —— 最常用的整数包装类深度解析

28 阅读6分钟

Integer —— 最常用的整数包装类深度解析

适用版本: JDK 8 难度等级: 核心 核心概念: IntegerCache、自动装箱拆箱、位运算方法集、进制转换


一、Integer 的类结构

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 —— 面试必问的缓存机制

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() {}
}

缓存生效场景

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
    }
}

调整缓存上限

# 设置缓存上限为 1000
java -Djava.lang.Integer.IntegerCache.high=1000 MyApp

三、自动装箱与拆箱

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)

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));
}
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

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 —— 字符串解析精要

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

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;  // 利用补码特性
}
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 的个数

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

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 —— 多进制解码器

public static Integer decode(String nm) throws NumberFormatException

解码规则:

前缀进制示例
0x / 0X十六进制0xFF → 255
#十六进制#FF → 255
0八进制0377 → 255
无前缀十进制255 → 255
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 新增)

// 无符号除法
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)
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 互转

/**
 * 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 实现

/**
 * 基于 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 而非 newvalueOf 利用缓存,new 绕过缓存
parseInt 为何用负累加避免 Integer.MIN_VALUE 取负溢出
== 与 equals 在 Integer 中的区别== 比较引用(缓存内相等,缓存外不等),equals 比较值
bitCount 算法的原理分治法,逐步合并计数值(变种的 SWAR 算法)
toUnsignedString 的用途将有符号 int 以无符号形式展示
highestOneBit 的应用场景HashMap 中计算 tableSizeFor(向上取2的幂)