`

String.getBytes("XXX")时发生了什么

    博客分类:
  • java
阅读更多
首先要了解,java里边,String是以char数组的形式存储的,而char字符是16 bits的Unicode字符。
1、当执行new String("i am a 字符ゅ").getBytes()的时候,是把内存中的char数组,转化成Charset.defaultCharset()这种字符集的字符数组,而这种字符集是提取的系统属性里边"file.encoding"属性的值,参见Charset的源代码:
public static Charset defaultCharset() {
        if (defaultCharset == null) {
	    synchronized (Charset.class) {
		java.security.PrivilegedAction pa =
		    new GetPropertyAction("file.encoding");
		String csn = (String)AccessController.doPrivileged(pa);
		Charset cs = lookup(csn);
		if (cs != null)
		    defaultCharset = cs;
                   else 
		    defaultCharset = forName("UTF-8");
            }
	}
	return defaultCharset;
}

(我本机就是utf-8)然后会创建一个StringEncoder而StringEncoder会创建一个CharsetEncoder:UTF-8$Encoder,创建UTF-8$Encoder时在调到CharsetEncoder的构造函数时会传入一个new byte[]{(byte)'?'}用来在解析到不能解析的字符的时候,把不能解析的字符替换成'?'(63,0x3F)(当然utf-8编码格式是可以表示java的char可以表示的所有字符的,这个在后边讲到iso-8859-1的时候会用到)。接下来就用StringEncoder#encode(),最终还是调用CharsetEncoder-UTF_8$Encoder来encode。而真正一个一个char解析的地方是UTF_8$Encoder#encodeArrayLoop(CharBuffer src, ByteBuffer dst)方法中:
	if (c < 0x80) {
		// Have at most seven bits
		if (dp >= dl)
			return overflow(src, sp, dst, dp);
		da[dp++] = (byte) c;
	} else if (c < 0x800) {
		// 2 bytes, 11 bits
		if (dl - dp < 2)
			return overflow(src, sp, dst, dp);
		da[dp++] = (byte) (0xc0 | ((c >> 06)));
		da[dp++] = (byte) (0x80 | (c & 0x3f));
	} else if (Surrogate.is(c)) {
		// Have a surrogate pair
		if (sgp == null)
			sgp = new Surrogate.Parser();
		int uc = sgp.parse((char) c, sa, sp, sl);
		if (uc < 0) {
			updatePositions(src, sp, dst, dp);
			return sgp.error();
		}
		if (dl - dp < 4)
			return overflow(src, sp, dst, dp);
		da[dp++] = (byte) (0xf0 | ((uc >> 18)));
		da[dp++] = (byte) (0x80 | ((uc >> 12) & 0x3f));
		da[dp++] = (byte) (0x80 | ((uc >> 06) & 0x3f));
		da[dp++] = (byte) (0x80 | (uc & 0x3f));
		sp++; // 2 chars
	} else {
		// 3 bytes, 16 bits
		if (dl - dp < 3)
			return overflow(src, sp, dst, dp);
		da[dp++] = (byte) (0xe0 | ((c >> 12)));
		da[dp++] = (byte) (0x80 | ((c >> 06) & 0x3f));
		da[dp++] = (byte) (0x80 | (c & 0x3f));
	}
	sp++; 

以上代码是判断当前char字符的int值的范围,然后确定是需要一个、两个或是三个字节,这个可以根据utf-8编码规则来确定:
UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
即小于1000 0000(0x80)需要一个字节(0xxxxxxx),小于0000 1000 0000 0000(0x200)需要两个字节(110xxxxx 10yyyyyy),其余最多占满16位的需要三个字节(1110xxxx 10yyyyyy 10zzzzzz)。以上代码根据这些规则进行了移位和按位操作,把两个字节的unicode字符编码成了utf-8格式的字节数组,这个时候,占用两个字节的字符不会丢失信息。
2、当执行new String("i am a 字符ゅ").getBytes("iso-8859-1")的时候,StringEncoder也会创建一个CharsetEncoder:ISO_8859_1$Encoder,创建的时候已然会传入一个new byte[]{(byte)'?'}用来在解析到不能解析的字符的时候,把不能解析的字符替换成'?'(63,0x3F),来看看ISO_8859_1$Enoder的encodeArrayLoop方法:
	while (sp < sl) {
		char c = sa[sp];
		if (c <= '\u00FF') {
			if (dp >= dl)
				return CoderResult.OVERFLOW;
			da[dp++] = (byte) c;
			sp++;
			continue;
		}
		if (sgp.parse(c, sa, sp, sl) < 0)
			return sgp.error();
		return sgp.unmappableResult();
	}

这里可以看到如果遇到unicode值大于'\u00FF'(1111 1111, 0xFF,即一个字节)的字符,全部当做不可映射字符(unmappable-input),在CharsetEncoder#encode()调用过encodeLoop()后替换不可映射的字符为replacement,即我们传入的new byte[]{(byte)'?'}
	if (action == CodingErrorAction.REPLACE) {
		if (out.remaining() < replacement.length)
			return CoderResult.OVERFLOW;
		out.put(replacement);
	}

这样就是为什么我们在运行如下代码的时候
new String("This is a 中文的 String!".getBytes("iso-8859-1"),"utf-8").toString();

结果是:This is a ??? String!
当然这也就是涉及到了把byte[]按照某种编码格式解析成String(java的unicode字符集合char[])的问题,本质跟上边原理是相反的。
3、new String("i am a 字符ゅ").getBytes("XXX")按照如上的过程,再根据XXX编码规则(不知为何参数类型叫做Charset),就可以解析出来了。
0
1
分享到:
评论

相关推荐

    Javamail开发实例

    ehlo xxx auth login 用户名 密码 mail from:&lt;aaa@sohu.com&gt; rcpt to:&lt;bbb@sina.com&gt; data from:&lt;aaa@sohu.com&gt; to:&lt;bbb@sina.com&gt; subject:test xxxxxx . quit ``` #### POP3接收协议 POP3协议主要用于从邮件...

    通过HttpURLConnection获取SESSIONID

    private static final String twoUrlString = "http://xxx.action"; public String getSessionId() { String sessionId = ""; try { URL url = new URL(oneUrlString); hc = (HttpURLConnection) ...

    FileOutputStream向文件进行输出.txt

    Class c = XXX.class; InputStream is = c.getResourceAsStream("aa.txt"); try { int len = is.available(); byte[] bs = new byte[len]; // ... } catch (IOException e) { e.printStackTrace(); } } }...

    Scala的String类方法整合

    `getBytes(String charsetName)` **功能描述**:按照指定的字符集获取当前字符串的字节表示。 **示例**: ```scala val str = "Hello" println(str.getBytes("UTF-8")) // 输出字节数组 ``` #### 14. `getChars...

    JSP对URL链接中的中文乱码处理方法总结.docx

    xxx.dtoptname=中文参数 String strPtname = request.getParameter("ptname"); strPtname = new String(strPtname.getBytes("ISO-8859-1"), "UTF-8"); 方法三:使用 JSP页的 charset 属性 这种方法是通过在 JSP 页...

    C#三种模拟自动登录和提交POST信息的实现方法

    if (e.Url.ToString().ToLower().IndexOf("xxx.htm") &gt; 0) //判断是否为登录页面 { HtmlDocument doc = webBrowser1.Document; for (int i = 0; i &lt; doc.All.Count; i++) { if (doc.All[i].TagName.ToUpper() ...

    android getpost url

    String str = "name=xxx&age=12"; OutputStream os = conn.getOutputStream(); os.write(str.getBytes("UTF-8")); os.flush(); // 获取响应码 if (conn.getResponseCode() == 200) { // 读取输入流 ...

    java经典面试题(附答案)

    4. **静态包含与动态包含**:`&lt;%@include file="xxx.jsp"%&gt;`是静态包含,编译时合并到当前JSP页面;`&lt;jsp:include&gt;`是动态包含,运行时根据请求动态加载页面。 5. **List与Map的区别**:List是有序集合,元素可以...

    Java程序设计(第二版)大纲、考试模拟试题及答案

    File file1=new File("d:\\xxx\\yyy\\zzz"); file1.mkdirs(); File file2=new File(file1,"1.txt"); file2.createNewFile(); String s="I like java."; byte[] b=s.getBytes(); FileOutputStream out=new...

    阿里云-消息队列TCP接入手册.pdf

    public static void main(String[] args) { Properties properties = new Properties(); properties.put(PropertyKeyConst.ProducerId, "XXX"); // Producer ID properties.put(PropertyKeyConst.AccessKey, ...

    阿里云-消息队列TCP接入手册-D.docx

    Message msg = new Message("TopicTestMQ", "TagA", "Hello MQ".getBytes()); producer.send(msg); } producer.shutdown(); } } ``` 在实际应用中,可以根据业务需求选择合适的发送方式。同步发送保证消息发送...

    struts2乱码处理

    - 使用`new String("XXX".getBytes("ISO-8859-1"), "UTF-8")`解码。其中,“XXX”是前台传递过来的含有中文的参数值,“UTF-8”为指定的字符集编码,可根据实际需求进行调整。 **注意事项**: - 该方案中,后端...

    《JAVA程序设计》期末考试试题及答案.doc

    byte b[] = s.getBytes(); FileOutputStream file = new FileOutputStream("test.txt", true); file.write(b); file.close(); ``` - **结果分析**:每次运行上述代码都会将字符串“ABCDE”追加到文件`test.txt`的...

    关于jsp字符乱码的处理

    str = new String(str.getBytes("ISO-8859-1"), "UTF-8"); ``` #### 二、数据库中的乱码处理 **1. 数据库字符集配置** - 如果数据库中存储的中文字符出现乱码,首先需要检查数据库的字符集设置是否正确。 - ...

    Java考试题、各种考试题参考.pdf

    题目问的是:当某一线程正处于休眠状态,而另一个线程用`Thread`类中的`interrupt()`方法中断它时,抛出的异常类型是什么? - 正确答案是:**C) InterruptedException**。 ### 2. 文件操作与路径创建 **知识点概述...

    ssh(structs,spring,hibernate)框架中的上传下载

     使用BlobByteArrayType字段类型后,为什么我们就可以象一般的字段类型一样操作Blob字段呢?可以确定的一点是:BlobByteArrayType不可能逾越Blob天生的操作方式,原来是BlobByteArrayType数据类型本身具体数据访问...

    jsp传中文值

    &lt;%=new String(request.getParameter("chineseVar").getBytes("iso-8859-1"), "UTF-8")%&gt; ``` 这段代码中,首先使用`request.getParameter()`方法获取名为`chineseVar`的URL参数。然后,使用`getBytes("iso-8859-1...

    HTTP POST 提交方式

    3. **状态码(Status Code)**:服务器返回的状态码,用于指示请求是否成功或者出现了什么问题。 #### 三、实现HTTP POST请求的方法 在Java中,可以通过多种方式实现HTTP POST请求,包括使用`HttpURLConnection`和`...

    Ajax FireFox IE 乱码兼容问题

    String key = new String(request.getParameter("key").getBytes("ISO-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ``` ### 进一步优化 除了以上提到的解决方案外...

    JavaEE中关于ServletConfig的小结

    String value = config.getInitParameter("xxx"); // 输出到响应流 response.getOutputStream().write(value.getBytes()); // 获取所有初始化参数的名字 Enumeration&lt;String&gt; e = config....

Global site tag (gtag.js) - Google Analytics