StringLatin1是什么?
从JDK9开始,String对象不再以char[]形式存储,而是以名为value的byte[]形式存储。value有一个名为coder的编码标记,该标记有两种取值:LATIN1和UTF-16(UTF-16使用大端法还是小端法取决于系统)。Java中存储String的byte数组的默认编码是LATIN1(即ISO-8859-1)和UTF16。
因此,JDK9就将String分为两套编码格式,由JVM识别系统的编码,而后选择调用的编码。
ISO8859-1(Latin-1)
ISO8859-1,通常叫做Latin-1。Latin-1包括了书写所有西方欧洲语言不可缺少的附加字符。
ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
ISO-8859-1收录的字符除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号出现的比较晚,没有被收录在ISO-8859-1当中。
因为ISO-8859-1编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。这是个很重要的特性,MySQL数据库默认编码是Latin1就是利用了这个特性。ASCII编码是一个7位的容器,ISO-8859-1编码是一个8位的容器。
ISO-8859-1 的较低部分(从 1 到 127 之间的代码)是最初的 7 比特 ASCII。
ISO-8859-1 的较高部分(从 160 到 255 之间的代码)全都有实体名称。
基础方法
// 返回Latin1-String的字节值value中包含的char的个数
public static int length(byte[] value) {
return value.length;
}
// true:表示当前序列在Latin1编码范围内,ISO-8859-1编码是一个8位的容器
public static boolean canEncode(int cp) {
return cp >>> 8 == 0;
}
// 用val在[index, index+len)范围内的byte值创建String
public static String newString(byte[] val, int index, int len) {
return new String(Arrays.copyOfRange(val, index, index + len), LATIN1);
}
// 将val[fromIndex, toIndex)的内存单元清零
public static void fillNull(byte[] val, int index, int end) {
Arrays.fill(val, index, end, (byte) 0);
}
// 哈希码
public static int hashCode(byte[] value) {
int h = 0;
for (byte v : value) {
// s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]。
h = 31 * h + (v & 0xff);
}
return h;
}
1 获取char/char[]
下面的方法主要进行byte数组转换为char或char数组。
// 将LATIN1-String内部的字节转换为char后返回
public static char getChar(byte[] val, int index) {
return (char) (val[index] & 0xff);
}
// 将LATIN1-String内部的字节转换为char后返回,加入范围检查
public static char charAt(byte[] value, int index) {
if (index < 0 || index >= value.length) {
throw new StringIndexOutOfBoundsException(index);
}
return (char) (value[index] & 0xff);
}
// 将LATIN1-String内部的字节批量转换为char后存入char数组
public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
// 将LATIN1-String内部的字节批量转换为char后存入char数组
@HotSpotIntrinsicCandidate
public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
for (int i = 0; i < len; i++) {
dst[dstOff++] = (char) (src[srcOff++] & 0xff);
}
}
// 将LATIN1-String内部的字节全部转换为char后返回
public static char[] toChars(byte[] value) {
char[] dst = new char[value.length];
// 将LATIN1-String内部的字节批量转换为char后存入dst
inflate(value, 0, dst, 0, value.length);
return dst;
}
2 获取byte/byte[]
// 将c的一个低字节转换为LATIN-String内部的字节,存入val
public static void putChar(byte[] val, int index, int c) {
//assert (canEncode(c));
val[index] = (byte) (c);
}
// 将char转换为LATIN1-String内部的字节,并返回
public static byte[] toBytes(char c) {
return new byte[]{(byte) c};
}
// 将val[off, off+len)中的一组Unicode值批量转换为LATIN1-String内部的字节,再返回
public static byte[] toBytes(int[] val, int off, int len) {
byte[] ret = new byte[len];
for (int i = 0; i < len; i++) {
int cp = val[off++];
// 如果当前序列不在Latin1编码范围内,转码失败
if (!canEncode(cp)) {
return null;
}
ret[i] = (byte) cp;
}
return ret;
}
// 从LATIN1-String内部的字节转为UTF16-String内部的字节
@HotSpotIntrinsicCandidate
public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
StringUTF16.inflate(src, srcOff, dst, dstOff, len);
}
// 从LATIN1-String内部的字节转为UTF16-String内部的字节后返回
public static byte[] inflate(byte[] value, int off, int len) {
// 创建长度为2*len的字节数组
byte[] ret = StringUTF16.newBytesFor(len);
// 从LATIN-String内部的字节转为UTF16-String内部的字节
inflate(value, off, ret, 0, len);
return ret;
}
// 将LATIN1-String内部的字节转换为LATIN1-String内部的字节,由于都是单字节,直接用了复制
public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}