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 之间的代码)全都有实体名称。

基础方法

  1. // 返回Latin1-String的字节值value中包含的char的个数
  2. public static int length(byte[] value) {
  3. return value.length;
  4. }
  5. // true:表示当前序列在Latin1编码范围内,ISO-8859-1编码是一个8位的容器
  6. public static boolean canEncode(int cp) {
  7. return cp >>> 8 == 0;
  8. }
  9. // 用val在[index, index+len)范围内的byte值创建String
  10. public static String newString(byte[] val, int index, int len) {
  11. return new String(Arrays.copyOfRange(val, index, index + len), LATIN1);
  12. }
  13. // 将val[fromIndex, toIndex)的内存单元清零
  14. public static void fillNull(byte[] val, int index, int end) {
  15. Arrays.fill(val, index, end, (byte) 0);
  16. }
  17. // 哈希码
  18. public static int hashCode(byte[] value) {
  19. int h = 0;
  20. for (byte v : value) {
  21. // s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]。
  22. h = 31 * h + (v & 0xff);
  23. }
  24. return h;
  25. }

1 获取char/char[]

下面的方法主要进行byte数组转换为char或char数组。

  1. // 将LATIN1-String内部的字节转换为char后返回
  2. public static char getChar(byte[] val, int index) {
  3. return (char) (val[index] & 0xff);
  4. }
  5. // 将LATIN1-String内部的字节转换为char后返回,加入范围检查
  6. public static char charAt(byte[] value, int index) {
  7. if (index < 0 || index >= value.length) {
  8. throw new StringIndexOutOfBoundsException(index);
  9. }
  10. return (char) (value[index] & 0xff);
  11. }
  12. // 将LATIN1-String内部的字节批量转换为char后存入char数组
  13. public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
  14. inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
  15. }
  16. // 将LATIN1-String内部的字节批量转换为char后存入char数组
  17. @HotSpotIntrinsicCandidate
  18. public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
  19. for (int i = 0; i < len; i++) {
  20. dst[dstOff++] = (char) (src[srcOff++] & 0xff);
  21. }
  22. }
  23. // 将LATIN1-String内部的字节全部转换为char后返回
  24. public static char[] toChars(byte[] value) {
  25. char[] dst = new char[value.length];
  26. // 将LATIN1-String内部的字节批量转换为char后存入dst
  27. inflate(value, 0, dst, 0, value.length);
  28. return dst;
  29. }

2 获取byte/byte[]

  1. // 将c的一个低字节转换为LATIN-String内部的字节,存入val
  2. public static void putChar(byte[] val, int index, int c) {
  3. //assert (canEncode(c));
  4. val[index] = (byte) (c);
  5. }
  6. // 将char转换为LATIN1-String内部的字节,并返回
  7. public static byte[] toBytes(char c) {
  8. return new byte[]{(byte) c};
  9. }
  10. // 将val[off, off+len)中的一组Unicode值批量转换为LATIN1-String内部的字节,再返回
  11. public static byte[] toBytes(int[] val, int off, int len) {
  12. byte[] ret = new byte[len];
  13. for (int i = 0; i < len; i++) {
  14. int cp = val[off++];
  15. // 如果当前序列不在Latin1编码范围内,转码失败
  16. if (!canEncode(cp)) {
  17. return null;
  18. }
  19. ret[i] = (byte) cp;
  20. }
  21. return ret;
  22. }
  23. // 从LATIN1-String内部的字节转为UTF16-String内部的字节
  24. @HotSpotIntrinsicCandidate
  25. public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
  26. StringUTF16.inflate(src, srcOff, dst, dstOff, len);
  27. }
  28. // 从LATIN1-String内部的字节转为UTF16-String内部的字节后返回
  29. public static byte[] inflate(byte[] value, int off, int len) {
  30. // 创建长度为2*len的字节数组
  31. byte[] ret = StringUTF16.newBytesFor(len);
  32. // 从LATIN-String内部的字节转为UTF16-String内部的字节
  33. inflate(value, off, ret, 0, len);
  34. return ret;
  35. }
  36. // 将LATIN1-String内部的字节转换为LATIN1-String内部的字节,由于都是单字节,直接用了复制
  37. public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
  38. System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
  39. }

3 大小写转换