Java-第十五部分-源码解读-String和Integer

344 阅读3分钟

源码解读全文

String

压缩算法

  • jdk9及之后,Stirng底层使用byte数组存储,那么当面对中文时,如果将原先的char数组转化为byte数组
  • 以下以String s = new String(char数组为例)
  1. String底层有个压缩算法策略,由COMPACT_STRINGS控制,默认开启
static final boolean COMPACT_STRINGS;

static {
    COMPACT_STRINGS = true;
}
  1. 源构造器
String(char[] value, int off, int len, Void sig) {
    if (len == 0) {
        this.value = "".value;
        this.coder = "".coder;
        return;
    }
    if (COMPACT_STRINGS) { //先尝试压缩
        byte[] val = StringUTF16.compress(value, off, len);
        //val==null 压缩失败
        if (val != null) {
            this.value = val;
            this.coder = LATIN1; //压缩成功,则记录为LATIN1的编码
            return;
        }
    }
    this.coder = UTF16; //压缩失败,则记录为UTF16的编码
    //并将char数组,直接转换成byte数组
    this.value = StringUTF16.toBytes(value, off, len);
}
  1. 压缩
public static byte[] compress(char[] val, int off, int len) {
    byte[] ret = new byte[len];
    //调用下面的函数,返回len==0,表明压缩失败直接弹出
    if (compress(val, off, ret, 0, len) == len) {
        return ret;
    }
    return null;
}

//如果char数组中的16进制值超过一个字节,表示压缩失败,返回len=0
public static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
    for (int i = 0; i < len; i++) {
        char c = src[srcOff];
        if (c > 0xFF) {
            len = 0;
            break;
        }
        dst[dstOff] = (byte)c;
        srcOff++;
        dstOff++;
    }
    return len;
}
  1. char数组转换为byte数组
public static byte[] toBytes(char[] value, int off, int len) {
    //具体处理len,
    byte[] val = newBytesFor(len);
    for (int i = 0; i < len; i++) {
        //传入字节数组 每一位 char字符
        putChar(val, i, value[off]);
        off++;
    }
    return val;
}

//处理len
public static byte[] newBytesFor(int len) {
    //负数
    if (len < 0) {
        throw new NegativeArraySizeException();
    }
    //超出 Integer.MAX_VALUE >> 1; *0.5
    if (len > MAX_LENGTH) {
        throw new OutOfMemoryError("UTF16 String size is " + len +
                                   ", should be less than " + MAX_LENGTH);
    }
    // *2
    return new byte[len << 1];
}

//放字符
static void putChar(byte[] val, int index, int c) {
    //边界检查,下标小于长度
    assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
    //因为要将char的每一位,两个字节,拆成两位一个字节的byte
    //传入的位要*2,举例 i = 0 1 2 那么对应到byte的index为 0 1 2 3 4 5
    index <<= 1;
    //存高位
    val[index++] = (byte)(c >> HI_BYTE_SHIFT);
    //存低位
    val[index]   = (byte)(c >> LO_BYTE_SHIFT);
}

拼接字符串

  • 对于下面这类拼接操作,调用stringBuilder进行拼接 JDK1.8的处理
String str = new String("hello") + new String("java");

0 new #2 <java/lang/StringBuilder>
3 dup
4 invokespecial #3 <java/lang/StringBuilder.<init> : ()V>
7 new #4 <java/lang/String>
10 dup
11 ldc #5 <hello>
13 invokespecial #6 <java/lang/String.<init> : (Ljava/lang/String;)V>
16 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
19 new #4 <java/lang/String>
22 dup
23 ldc #8 <java>
25 invokespecial #6 <java/lang/String.<init> : (Ljava/lang/String;)V>
28 invokevirtual #7 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
31 invokevirtual #9 <java/lang/StringBuilder.toString : ()Ljava/lang/String;>
34 astore_1
  1. StringBuilder调用了AbstractStringBuilder的方法
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
  1. AbstractStringBuilder
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    //扩大容量
    ensureCapacityInternal(count + len);
    //jkd9之后有变化
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
  1. ensureCapacityInternal
private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}
  1. getChars
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    //异常判断
    if (srcBegin < 0) {
        throw new StringIndexOutOfBoundsException(srcBegin);
    }
    if (srcEnd > value.length) {
        throw new StringIndexOutOfBoundsException(srcEnd);
    }
    if (srcBegin > srcEnd) {
        throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
    }
    //复制
    System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
  1. jdk9之后处理有了变化,putStringAt(count, str);调用这个来处理新的字符串
private void putStringAt(int index, String str) {
    putStringAt(index, str, 0, str.length());
}
//底层调用了getBytes
private void putStringAt(int index, String str, int off, int end) {
    if (getCoder() != str.coder()) {
        inflate();
    }
    // 目标数组,str的偏移,在这个数组的起点,编码方式 8/16,长度
    str.getBytes(value, off, index, coder, end - off);
}

void getBytes(byte[] dst, int srcPos, int dstBegin, byte coder, int length) {
    if (coder() == coder) { //同一个字符编码,直接复制
        System.arraycopy(value, srcPos << coder, dst, dstBegin << coder, length << coder);
    } else {    // this.coder == LATIN && coder == UTF16 不同,需要将新加字符串另外处理
        StringLatin1.inflate(value, srcPos, dst, dstBegin, length);
    }
}

Integer

装箱

  • 针对下面两句简单代码,底层同样进行了不同的处理
Integer x = 5;
Integer i3 = 128;
  1. 装箱时调用了valueOf
Integer x = 5;

0 iconst_5
1 invokestatic #2 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
4 astore_1
  1. valueOf方法,中间有一个IntegerCache.cache[]数组,并且范围是low~high
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
  1. IntegerCacheInteger的静态内部类,Integer常量池,并进行了初始化
private static class IntegerCache {
    //low
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        //如果修改了 integerCacheHighPropValue,另外处理
        String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                //high取大的那个
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                // 数组的最大范围不能超过 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]; // 1 0 -1 三个数
        int j = low;
        //从-128开始赋值
        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() {}
}
  1. 设置integerCacheHighPropValuejava -Djava.lang.Integer.IntegerCache.high = xxxx Aclass.class
  2. 那么可以看出,如果值在-128和127直接,直接返回静态常量池中的值,如果不在这个范围之内需要new对象
public Integer(int value) {
    this.value = value;
}

拆箱

int m = x + 1;

调用了intValue

26 aload_1
27 invokevirtual #4 <java/lang/Integer.intValue : ()I>
30 iconst_1
31 iadd
32 istore_3

intvalue

public int intValue() {
    return value;
}