包装类型缓存池源码剖析

简介: 编译器会在自动装箱过程调用 valueOf() 方法,因此多个值相同且值在缓存池范围内的 Integer 实例使用自动装箱来创建,那么就会引用相同的对象。new Integer(123) 与 Integer.valueOf(123) 的区别在于:基本类型对应的缓冲池范围如下:1. Byte.valueOf(“A”);byte即字节的意思,由8位组成,即其可以表示的最大值为

缓存池源码解读

编译器会在自动装箱过程调用 valueOf() 方法,因此多个值相同且值在缓存池范围内的 Integer 实例使用自动装箱来创建,那么就会引用相同的对象。

new Integer(123) 与 Integer.valueOf(123) 的区别在于:

  • new Integer(123) 每次都会新建一个对象;
  • Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。

基本类型对应的缓冲池范围如下:

  • boolean values true and false【true,false】
  • all byte values【-128~127】
  • short values between -128 and 127【-128~127】
  • int values between -128 and 127【-128~127】
  • long values between -128 and 127【-128~127】
  • char in the range \u0000 to \u007F【0~127】

下面我们对包装类型缓存池源码进行一一解读

public static void main(String[] args) throws CloneNotSupportedException {
  Byte a = Byte.valueOf("12");
  Character character = Character.valueOf('1');
  Boolean aBoolean = Boolean.valueOf("true");
  Short aShort = Short.valueOf("123");
  Integer integer = Integer.valueOf("123");
  Long aLong = Long.valueOf("123");
  Float aFloat = Float.valueOf("123");
  Double aDouble = Double.valueOf("123");
}
1. Byte.valueOf("A");

byte即字节的意思,由8位组成,即其可以表示的最大值为请添加图片描述

我们通过下面分析可知,Byte的缓存池大小为-128到127,即Byte的缓存池为all byte values。

我们点进valueOf方法

public static Byte valueOf(String s) throws NumberFormatException {
  return valueOf(s, 10);
}

valueOf方法调用重载的valueOf,参数10指10进制转换后面用。

public static Byte valueOf(String s, int radix)
  throws NumberFormatException {
  return valueOf(parseByte(s, radix));
}

重载valueOf再调用一个重载方法,传入一个parseByte的结果

public static byte parseByte(String s, int radix)
  throws NumberFormatException {
  int i = Integer.parseInt(s, radix);
  if (i < MIN_VALUE || i > MAX_VALUE)
    throw new NumberFormatException(
    "Value out of range. Value:\"" + s + "\" Radix:" + radix);
  return (byte)i;
}

Integer.parseInt方法将字符串转换为数字,radix我们知道前面传入的是10,因此s传入的值只能是十进制数字表示的字符串,否则会报NumberFormatException。

下面也对i的值进行了限制,本类的静态变量MAX_VALUE = 127,MIN_VALUE = -128。

最后将i强转为一个byte。

public static Byte valueOf(byte b) {
  final int offset = 128;
  return ByteCache.cache[(int)b + offset];
}

这里定义了一个偏移量128,因为负数在二进制中不方便表示,这里对实际值+128存储,即-128实际存储为0000 0000。

然后返回ByteCache.cache数组中对应下标的byte。

private static class ByteCache {
  private ByteCache(){}

  static final Byte cache[] = new Byte[-(-128) + 127 + 1];

  static {
    for(int i = 0; i < cache.length; i++)
      cache[i] = new Byte((byte)(i - 128));
  }
}

这个类就是缓存池本池了,一个静态内部类里的静态不可变变量cache数组即是Byte的缓存池。

该数组的初始化容量为256,且通过静态代码块开始就将所有数据进行了初始化。

2. Character.valueOf('1');
public static Character valueOf(char c) {
  if (c <= 127) { // must cache
    return CharacterCache.cache[(int)c];
  }
  return new Character(c);
}

此处char小于等于127获取缓存池中的值,否则会new一个Character对象。

这里求一个java中不走缓存值的示例😂

private static class CharacterCache {
  private CharacterCache(){}

  static final Character cache[] = new Character[127 + 1];

  static {
    for (int i = 0; i < cache.length; i++)
      cache[i] = new Character((char)i);
  }
}

Character的缓存池本池,一个静态内部类里的静态不可变变量cache数组。

该数组的初始化大小为128,且通过静态代码块进行了初始化,且与ASCII码表对应

ASCII码对照表

3. Boolean.valueOf("true");
public static Boolean valueOf(String s) {
  return parseBoolean(s) ? TRUE : FALSE;
}
public static boolean parseBoolean(String s) {
  return ((s != null) && s.equalsIgnoreCase("true"));
}

判断s是不是字符串TRUE,是则返回TRUE,否则返回FALSE。

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

即Boolean的缓存池就是true和false两个静态不可变的包装对象。

4. Integer.valueOf("123")
public static Integer valueOf(String s) throws NumberFormatException {
  return Integer.valueOf(parseInt(s, 10));
}

调用重载方法,调用本类parseInt方法,转换进制为10进制,因此同byte只能传入数字表示的字符串。

public static Integer valueOf(int i) {
  if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)];
  return new Integer(i);
}

IntegerCache.lowIntegerCache.high之间的值从缓存池中获取,否自new一个新的Integer对象。

偏移量是(-IntegerCache.low)即128。

private static class IntegerCache {
  static final int low = -128;
  static final int high;
  static final Integer cache[];

  static {
    // high value may be configured by property
    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);
        // Maximum array size is Integer.MAX_VALUE
        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
      } catch( NumberFormatException nfe) {
        // If the property cannot be parsed into an int, ignore it.
      }
    }
    high = h;

    cache = new Integer[(high - low) + 1];
    int j = low;
    for(int k = 0; k < cache.length; k++)
      cache[k] = new Integer(j++);

    // range [-128, 127] must be interned (JLS7 5.1.7)
    assert IntegerCache.high >= 127;
  }

  private IntegerCache() {}
}

IntegerCache缓存池本池:一个静态内部类里的静态不可变变量cache数组。

low = -128:缓存池最小值-128,heigh:取一个环境变量的配置,如果存在使用该值,不存在使用默认值127。

同样通过静态代码块进行了初始化,偏移量是(-IntegerCache.low)即128。

5. Short.valueOf("123");
public static Short valueOf(String s) throws NumberFormatException {
    return valueOf(s, 10);
}

调用重载方法,传入radix,10进制转换。

public static Short valueOf(String s, int radix)
    throws NumberFormatException {
    return valueOf(parseShort(s, radix));
}

调用重载方法,传入parseShort的返回值。

public static Short valueOf(short s) {
    final int offset = 128;
    int sAsInt = s;
    if (sAsInt >= -128 && sAsInt <= 127) { // must cache
        return ShortCache.cache[sAsInt + offset];
    }
    return new Short(s);
}

偏移量128,-128到127之间的数从缓存池获取,其他new Short(s);

private static class ShortCache {
    private ShortCache(){}

    static final Short cache[] = new Short[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Short((short)(i - 128));
    }
}

ShortCache缓存池本池:一个静态内部类里的静态不可变变量cache数组。静态代码块中进行初始化数据。

6. Long.valueOf("123");
public static Long valueOf(String s) throws NumberFormatException
{
    return Long.valueOf(parseLong(s, 10));
}

调用重载构造方法,传入parseLong的返回值

public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
}

偏移量128,-128到127之间的数从缓存池获取,其他new Long(s);

private static class LongCache {
    private LongCache(){}

    static final Long cache[] = new Long[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Long(i - 128);
    }
}

LongCache缓存池本池:一个静态内部类里的静态不可变变量cache数组。静态代码块中进行初始化数据。

7. Float.valueOf("123");
public static Float valueOf(String s) throws NumberFormatException {
    return new Float(parseFloat(s));
}

直接返回new Float无缓存池。

8. Double.valueOf("123");
public static Double valueOf(String s) throws NumberFormatException {
    return new Double(parseDouble(s));
}

直接返回new Double无返回值。

目录
相关文章
|
6月前
|
存储 缓存 Java
干翻Mybatis源码系列之第八篇:Mybatis二级缓存的创建和存储
干翻Mybatis源码系列之第八篇:Mybatis二级缓存的创建和存储
|
2月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
172 24
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
30天前
|
缓存 NoSQL Ubuntu
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
51 3
|
6月前
|
缓存 中间件 数据库
中间件缓存策略类型
【5月更文挑战第6天】中间件缓存策略类型
51 2
中间件缓存策略类型
|
6月前
|
缓存 Java 关系型数据库
Spring Boot与Spring中的数据缓存Cache支持与实战(附源码)
Spring Boot与Spring中的数据缓存Cache支持与实战(附源码)
79 0
Spring Boot与Spring中的数据缓存Cache支持与实战(附源码)
|
6月前
|
缓存 Java 测试技术
Spring5源码(19)-Spring从缓存中获取单例bean
Spring5源码(19)-Spring从缓存中获取单例bean
37 0
|
6月前
|
缓存 Java 数据库连接
|
6月前
|
存储 设计模式 Java
Mybatis源码细节探究:二级缓存Cache对象是在什么时候创建的?
Mybatis源码细节探究:二级缓存Cache对象是在什么时候创建的?
|
6月前
|
缓存 Java 数据库连接
干翻Mybatis源码系列之第八篇:Mybatis提供的缓存方案细节注意
干翻Mybatis源码系列之第八篇:Mybatis提供的缓存方案细节注意
|
6月前
|
设计模式 缓存 Java
干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案
干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案