String编码源码解析
来源:互联网 发布:linux系统磁盘分区 编辑:程序博客网 时间:2024/05/18 00:33
String编码的使用
1:将byte[]转换为String一般的操作:new String(byte[]),进行new String时需要指定编码格式,如果未指定编码格式,则使用默认编码格式。
// 获取默认编码格式,然后进行decode String csn = Charset.defaultCharset().name(); try{ return decode(csn, ba, off, len); } catch (UnsupportedEncodingException x) { warnUnsupportedCharset(csn); } //如果csn是未支持的编码格式,则使用ISO-8859-1进行编码 try { return decode("ISO-8859-1", ba, off, len); } catch (UnsupportedEncodingException x) { System.exit(1); return null; }
下面是获取默认编码Charset.defaultCharset()的实现
if (defaultCharset == null) { synchronized (Charset.class) { //下面代码是获取系统的Property的属性,相当于System.getProperty("file.encoding") String csn = AccessController.doPrivileged( new GetPropertyAction("file.encoding")); Charset cs = lookup(csn); // 如果是未支持的编码格式,则默认的编码格式为UTF-8 if (cs != null) defaultCharset = cs; else defaultCharset = forName("UTF-8"); } } return defaultCharset;
2:当new String()指定编码时,java提供了两种方式public String(byte bytes[], String charsetName)和public String(byte bytes[], Charset charset),下面将讲解这两种方式:
public String(byte bytes[], String charsetName)
// 获取上一次缓存中的编码格式 StringDecoder sd = deref(decoder); // 如果未设置编码方式,则使用ISO-8859-1,否则使用设置的编码方式 String csn = (charsetName == null) ? "ISO-8859-1" : charsetName; // 判断从缓存中获取的编码格式和设置的编码格式是否相同,不相同,则重新获取编码格式 if ((sd == null) || !(csn.equals(sd.requestedCharsetName()) || csn.equals(sd.charsetName()))) { sd = null; try { // 重新获取编码格式 Charset cs = lookupCharset(csn); if (cs != null) sd = new StringDecoder(cs, csn); } catch (IllegalCharsetNameException x) {} if (sd == null) throw new UnsupportedEncodingException(csn); // 将这次的编码格式存入到缓存中 set(decoder, sd); } // 进行decode return sd.decode(ba, off, len);
public String(byte bytes[], Charset charset)
该方法的核心代码和sd.decode()方法非常相似,唯一的区别在于sd.decode()方法将cd.reset()放置于else后面,而Charset编码则将reset()方法放置于每次编码前,这将导致每次使用Charset进行new String时,都需要reset。
CharsetDecoder cd = cs.newDecoder(); int en = scale(len, cd.maxCharsPerByte()); char[] ca = new char[en]; if (len == 0) return ca; boolean isTrusted = false; if (System.getSecurityManager() != null) { if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) { ba = Arrays.copyOfRange(ba, off, off + len); off = 0; } } // 用Chaeset进行编码,则将reset方法放到这个位置 cd.onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .reset(); if (cd instanceof ArrayDecoder) { int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); return safeTrim(ca, clen, cs, isTrusted); } else { //charsetName进行编码将reset方法放在这个位置 ByteBuffer bb = ByteBuffer.wrap(ba, off, len); CharBuffer cb = CharBuffer.wrap(ca); try { CoderResult cr = cd.decode(bb, cb, true); if (!cr.isUnderflow()) cr.throwException(); cr = cd.flush(cb); if (!cr.isUnderflow()) cr.throwException(); } catch (CharacterCodingException x) { // Substitution is always enabled, // so this shouldn't happen throw new Error(x); } return safeTrim(ca, cb.position(), cs, isTrusted); }
通过比较源码,发现上述两种编码格式有两个不同点:
第一:reset的位置不同
第二:使用charsetName的方式,先从缓存中获取编码格式,如果这次编码格式和上次编码格式相同,则直接进行decode,而无需重新获取编码格式。因此通过使用charsetName比Charset性能高,前提是缓存的命中率,如果统一编码格式的话,缓存的命中大大提高,则能有效的提高性能。
Charset获取方式
在JDK1.7 提供了StandardCharset,StandardCharset提供了常用的编码格式,如果版本低于1.7则可以使用Charset.forName(“UTF-8”)。Charset默认有二级缓存,缓存最近的两次获取的Charset,最近一次放在一级缓存中,第二次放在二级缓存中。
private static Charset lookup(String charsetName) { if (charsetName == null) throw new IllegalArgumentException("Null charset name"); Object[] a; // 从一级缓存中获取Charset,如果命中,则直接返回,否则调用二级缓存 if ((a = cache1) != null && charsetName.equals(a[0])) return (Charset)a[1]; // 调用二级缓存 return lookup2(charsetName); } private static Charset lookup2(String charsetName) { Object[] a; // 二级缓存中获取Charset if ((a = cache2) != null && charsetName.equals(a[0])) { cache2 = cache1; cache1 = a; return (Charset)a[1]; } Charset cs; // 如果缓存中没有获取相对应的Chaeset,则通过standardProvider获取Chaeset if ((cs = standardProvider.charsetForName(charsetName)) != null || (cs = lookupExtendedCharset(charsetName)) != null || (cs = lookupViaProviders(charsetName)) != null) { // 缓存Chaeset cache(charsetName, cs); return cs; }
为什么要去指定编码格式?
因为每台计算机默认编码格式可能不同,这将导致相同的代码在不同的计算机上运行时,出现乱码情况。简单的一个例子,当相同的代码运行在不同编码格式的计算机上,在其中的一台以GBK的编码格式将数据存入到数据库中,另一台通过的UTF-8的编码格式获取数据,则将出现乱码的情况,所以规避风险最好的方式是指定的特定的编码格式。
- String编码源码解析
- String源码解析
- Java String 源码解析
- java String源码解析
- Java String源码解析
- String 源码解析
- Java源码解析:String
- String源码解析
- Java String源码解析
- String类源码解析
- String源码解析
- String 源码解析
- JAVA源码解析-String源码
- Java源码解析-String详解
- String的replzce源码解析
- jdk1.8 String源码解析
- String为什么不可变,String源码解析
- String 源码解析,深入认识String
- 堆排序
- 一张图带你了解Android5.0中的colorPrimary、colorPrimaryDark、colorAccent
- 跨时钟域处理三大方法
- VMware12 和秘钥
- Spring Security#Expression-Based Access Control
- String编码源码解析
- 4-2 4-3 4-4 Gradient Descent for Multiple Variables
- Leetcode:Maximum Subarray
- 枚举工厂
- 手把手教你如何玩转SSH(Spring+Strus2+Hibernate)
- 酷比魔方IWork1X 的做系统问题
- 快速排序算法-Quick Sort
- POJ3104
- 机器学习学习笔记.day2