常用类(一)
Java并不是纯面向对象的语言。Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的。基本数据类型有它的优势:性能(效率高,节省空间)。
但是我们在实际使用中经常需要将基本数据类型转化成对象,便于操作。比如:(1)集合的操作,(2)使用Object类型接收任意类型的数据等,(3)泛型实参,这时,我们就需要将基本数据类型数据转化为对象。
10.1.2 包装类
包装类均位于java.lang包,包装类和基本数据类型的对应关系,如图10-1:
其中有六个都是继承自java.lang.Number类:
10.1.3自动装箱(auto_boxing)与自动拆箱(unboxing)
JDK1.5之前需要手动装箱与拆箱,JDK1.5之后支持自动装箱与自动拆箱。
1. 自动装箱
基本数据类型就自动的封装到与它相同类型的包装中,如:
Integer i = 100;
本质上是,编译器编译时为我们添加了:
Integer i = new Integer(100);
2. 自动拆箱
包装类对象自动转换成基本类型数据。如:
int a = new Integer(100);
本质上,编译器编译时为我们添加了:
int a = new Integer(100).intValue();
10.1.4 包装类的作用
1. 数据类型的范围
MIN_VALUE、MAX_VALUE
Float和Double中还有正无穷大POSITIVE_INFINITY、负无穷大NEGATIVE_INFINITY,还NaN,是Not a Number的缩写。NaN 用于处理计算中出现的错误情况,比如 0.0 除以 0.0 或者求负数的平方根。
程序员可以利用这种定制的 NaN 值中的特定位模式来表达某些诊断信息。
2. 数据类型的转换
1、字符串转成包装类对象
(1)使用包装类型的构造方法
除了Character类型,其他7中类型都有1个构造方法,其参数是字符串类型
例如:
Integer t2=new Integer("500");//参数是字符串,字符串的值是必须对应的数值
Integer t3=new Integer("abc");// java.lang.NumberFormatException: For input string: "abc"
(2)使用包装类的valueOf方法
例如:Integer i=Integer.valueOf("500");
2、字符串转成基本数据类型
通过包装类的parseXxx(String s)静态方法
例如:int i=Integer.parseInt("500");
3. 包装类的其他方法
1、Integer类型
public static String toBinaryString(int i) //把十进制转成二进制
public static String toHexString(int i) //把十进制转成十六进制
public static String toOctalString(int i) //把十进制转成八进制
2、Character类型
public static char toUpperCase(char ch) //转成大写字母
public static char toLowerCase(char ch) //转成小写字母
其他的查看相关API文档即可
3、equals
按照包装的基本数据类型的值比较
4、compareTo
按照包装的基本数据类型的值比较
10.1.5 缓存问题
我们在编程时大量需要值在-128到127范围之间的Integer对象。如果只能通过new来创建,需要在堆中开辟大量值一样的Integer对象。这是相当不划算的,IntegerCache.cache很好的起到了缓存的作用。
缓存
byte Byte -128–127
short Short -128–127
int Integer -128—127
long Long -128—127
float Float 不缓存
double Double 不缓存
char Character 0–127
boolean Boolean true,false
10.2 字符串String类
10.2.1 字符串String类的特点
字符串的学习,有的同学就看看API,记下方法,有的同学看看源代码,还有的同学画画图,自然学的深度是不一样的。
/** * The {@code String} class represents character strings. All * string literals in Java programs, such as {@code "abc"}, are * implemented as instances of this class. * <p> * Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. For example: * <blockquote><pre> * String str = "abc"; * </pre></blockquote><p> * is equivalent to: * <blockquote><pre> * char data[] = {'a', 'b', 'c'}; * String str = new String(data); * </pre></blockquote><p> * Here are some more examples of how strings can be used: * <blockquote><pre> * System.out.println("abc"); * String cde = "cde"; * System.out.println("abc" + cde); * String c = "abc".substring(2,3); * String d = cde.substring(1, 2); * </pre></blockquote> * <p> * The class {@code String} includes methods for examining * individual characters of the sequence, for comparing strings, for * searching strings, for extracting substrings, and for creating a * copy of a string with all characters translated to uppercase or to * lowercase. Case mapping is based on the Unicode Standard version * specified by the {@link java.lang.Character Character} class. * <p> * The Java language provides special support for the string * concatenation operator ( + ), and for conversion of * other objects to strings. String concatenation is implemented * through the {@code StringBuilder}(or {@code StringBuffer}) * class and its {@code append} method. * String conversions are implemented through the method * {@code toString}, defined by {@code Object} and * inherited by all classes in Java. For additional information on * string concatenation and conversion, see Gosling, Joy, and Steele, * <i>The Java Language Specification</i>. |
String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。 字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。例如: String str = "abc"; 等效于: char data[] = {'a', 'b', 'c'}; String str = new String(data); 下面给出了一些如何使用字符串的更多示例: System.out.println("abc"); String cde = "cde"; System.out.println("abc" + cde); String c = "abc".substring(2,3); String d = cde.substring(1, 2); String 类包括的方法可用于检查序列的单个字符、比较字符串、搜索字符串、提取子字符串、创建字符串副本并将所有字符全部转换为大写或小写。大小写映射基于 Character 类指定的 Unicode 标准版。
Java 语言提供对字符串串联符号("+")以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。有关字符串串联和转换的更多信息,请参阅 Gosling、Joy 和 Steele 合著的 The Java Language Specification。 |
1、String是个final类
2、String是不可变的字符序列
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];
/** Cache the hash code for the string */ private int hash; // Default to 0 |
String对象的字符内容是存储在一个字符数组中的。
private意味着外面无法直接获取字符数组,而且String没有提供value的get和set方法,
final意味着字符数组的引用不可改变,即通过让value指向新的数组对象来实现修改String对象,
而且String也没有提供方法来修改value数组某个元素值,因此字符串的字符数组内容也不可变。
疑问?那么字符串的拼接、字符串的截取、字符串的替换等操作是如何实现的呢?
每次修改都创建一个新的char数组表示修改结果。
3、String对象的创建
String str = “hello”;
String s1 = new String(); // 本质上 this.value = new char[0];
String s2 = new String(String original); //this.value = original.value;
String s3 = new String(char[] a); //this.value = Arrays.copyOf(value, value.length);
String s4 = new String(char[] a,int startIndex,int count)
.......
4、字符串对象是如何存储的
字符串常量存储在字符串常量池,目的是共享
字符串非常量对象存储在堆中。
5、String的拼接
结论:
常量与常量的拼接结果在常量池
只要其中有一个是变量,结果就在堆中
如果拼接的结果调用intern()方法,就在常量池中
6、String对象的比较
==比较的是地址。
equals比较的是字符串的内容,重写了Object的equals方法。
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } |
10.2.2 String类的常用方法
1、常用方法系列之一
l int length():返回字符串的长度: return value.length;
l boolean isEmpty():判断是否是空字符串:return value.length == 0;
l String toLowerCase():使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
l String toUpperCase():使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
l String trim():返回字符串的副本,忽略前导空白和尾部空白。
l boolean equals(Object obj):比较字符串的内容
l boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
l String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
2、String类和字符相关操作
l char charAt(int index): 返回某索引处的字符return value[index];
l char[] toCharArray():将此字符串转换为一个新的字符数组
l String(char[] value):分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。
l String(char[] value, int offset, int count):分配一个新的 String,它包含取自字符数组参数一个子数组的字符。
3、String类字节与字符串操作方法
编码:把字符-->字节
l byte[] getBytes():使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
l byte[] getBytes(Charset charset) :使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
l byte[] getBytes(String charsetName) :使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
解码:把字节-->字符
l String(byte[] bytes) :通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
l String(byte[] bytes, Charset charset):通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
l String(byte[] bytes, int offset, int length) :通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。
l String(byte[] bytes, int offset, int length, Charset charset):通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。
l String(byte[] bytes, int offset, int length, String charsetName):通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String。
l String(byte[] bytes, String charsetName):通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
public static void main(String[] args)throws Exception { String str = "中"; System.out.println(str.getBytes("ISO8859-1").length);//-128~127 System.out.println(str.getBytes("GBK").length); System.out.println(str.getBytes("UTF-8").length);
System.out.println(new String(str.getBytes("ISO8859-1"),"ISO8859-1"));//乱码,表示不了中文 System.out.println(new String(str.getBytes("GBK"),"GBK")); System.out.println(new String(str.getBytes("UTF-8"),"UTF-8")); } |
public static void main(String[] args)throws Exception { String str = "中"; byte[] bytes = str.getBytes("GBK");//2个字节 客户端编码 String string = new String(bytes,"ISO8859-1");//尝试转成2个字符,错 tomcat服务器6默认尝试用ISO8859-1解码 //在servlet中用代码处理 byte[] bytes2 = string.getBytes("ISO8859-1");//重新变回2个字节 再次用ISO8859-1编码 String string2 = new String(bytes2,"GBK");//再变成1个字 再次用GBK解码 System.out.println(string2); } |
public static void main(String[] args)throws Exception { String str = "中"; byte[] bytes = str.getBytes("GBK");//2个字节 浏览器客户端编码 String string = new String(bytes,"UTF-8");//尝试转成2个字符,错 好比tomcat服务器8默认尝试用UTF-8解码 System.out.println("string = " + string);//乱码
//在servlet中用Java代码处理 byte[] bytes2 = string.getBytes("UTF-8");//重新解码变成6个字节 再次用UTF-8编码 String string2 = new String(bytes2,"GBK");//尝试变成3个字 再次用GBK解码 System.out.println(string2);//乱码 } |
4、String类判断是否以指定内容开头或结尾
l boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束。
l boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始。
l boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
5、String类字符串查找操作
l boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true。
l int indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引。
l int indexOf(int ch, int fromIndex):返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
l int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引。
l int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
l int lastIndexOf(int ch):返回指定字符在此字符串中最后一次出现处的索引。
l int lastIndexOf(int ch, int fromIndex):返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。
l int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引。
l int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
indexOf和lastIndexOf方法如果未找到都是返回-1
6、String类字符串截取操作
l String substring(int beginIndex)
返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
l String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
7、String类是否匹配正则
l boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
public static void main(String[] args) { String str = "12345"; //判断str字符串中是否全部有数字组成,即有1-n个数字组成 boolean matches = str.matches("\\d+"); System.out.println(matches); String tel = "0571-4534289"; //判断这是否是一个杭州的固定电话 boolean result = tel.matches("0571-\\d{7,8}"); System.out.println(result); } |
8、String类替换操作
l String replace(char oldChar, char newChar):
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
l String replace(CharSequence target, CharSequence replacement):
使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
l String replaceAll(String regex, String replacement):
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
l String replaceFirst(String regex, String replacement):
使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
public static void main(String[] args) { String str = "12hello34world5java7891mysql456"; //把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉 String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", ""); System.out.println(string); } |
9、String类字符串拆分操作
l String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
l String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
public static void main(String[] args) { String str = "hello|world|java"; String[] strings = str.split("\\|"); for (String string : strings) { System.out.println(string); } String str2 = "hello.world.java"; String[] strings2 = str2.split("\\."); for (String string : strings2) { System.out.println(string); } } |
10、附录:字符编码(了解)
首先来看一下常用的编码有哪些,截图自Notepad++。其中ANSI在中国大陆即为GBK(以前是GB2312),最常用的是 GBK 和 UTF8无BOM 编码格式。后面三个都是有BOM头的文本格式,UCS-2即为人们常说的Unicode编码,又分为大端、小端。
所谓BOM头(Byte Order Mark)就是文本文件中开始的几个并不表示任何字符的字节,用二进制编辑器(如bz.exe)就能看到了。
UTF8的BOM头为 0xEF 0xBB 0xBF
Unicode大端模式为 0xFE 0xFF
Unicode小端模式为 0xFF 0xFE
ASCII码
计算机一开始发明的时候是用来解决数字计算的问题,后来人们发现,计算机还可以做更多的事,例如文本处理。但由于计算机只识“数”,因此人们必须告诉计算机哪个数字来代表哪个特定字符,例如65代表字母‘A’,66代表字母‘B’,以此类推。但是计算机之间字符-数字的对应关系必须得一致,否则就会造成同一段数字在不同计算机上显示出来的字符不一样。因此美国国家标准协会ANSI制定了一个标准,规定了常用字符的集合以及每个字符对应的编号,这就是ASCII字符集(Character Set),也称ASCII码。
那时候的字符编解码系统非常简单,就是简单的查表过程。例如将字符序列编码为二进制流写入存储设备,只需要在ASCII字符集中依次找到字符对应的字节,然后直接将该字节写入存储设备即可。解码二进制流的过程也是类似。
其中:
l 0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)
l 32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字。
l 65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
l 后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。
OEM字符集的衍生
当计算机开始发展起来的时候,人们逐渐发现,ASCII字符集里那可怜的128个字符已经不能再满足他们的需求了。人们就在想,一个字节能够表示的数字(编号)有256个,而ASCII字符只用到了0x00~0x7F,也就是占用了前128个,后面128个数字不用白不用,因此很多人打起了后面这128个数字的主意。可是问题在于,很多人同时有这样的想法,但是大家对于0x80-0xFF这后面的128个数字分别对应什么样的字符,却有各自的想法。这就导致了当时销往世界各地的机器上出现了大量各式各样的OEM字符集。
大家对于0x00~0x7F这个范围的解释基本是相同的,而对于后半部分0x80~0xFF的解释却不一定相同。甚至有时候同样的字符在不同OEM字符集中对应的字节也是不同的。
不同的OEM字符集导致人们无法跨机器交流各种文档。例如职员甲发了一封简历résumés给职员乙,结果职员乙看到的却是r?sum?s,因为é字符在职员甲机器上的OEM字符集中对应的字节是0x82,而在职员乙的机器上,由于使用的OEM字符集不同,对0x82字节解码后得到的字符却是?。
多字节字符集(MBCS)和中文字符集
上面我们提到的字符集都是基于单字节编码,也就是说,一个字节翻译成一个字符。这对于拉丁语系国家来说可能没有什么问题,因为他们通过扩展第8个比特,就可以得到256个字符了,足够用了。但是对于亚洲国家来说,256个字符是远远不够用的。因此这些国家的人为了用上电脑,又要保持和ASCII字符集的兼容,就发明了多字节编码方式,相应的字符集就称为多字节字符集(Muilti-Bytes Charecter Set)。例如中国使用的就是双字节字符集编码。
例如目前最常用的中文字符集GB2312,涵盖了所有简体字符以及一部分其他字符;GBK(K代表扩展的意思)则在GB2312的基础上加入了对繁体字符等其他非简体字符。这两个字符集的字符都是使用1-2个字节来表示。Windows系统采用936代码页来实现对GBK字符集的编解码。在解析字节流的时候,如果遇到字节的最高位是0的话,那么就使用936代码页中的第1张码表进行解码,这就和单字节字符集的编解码方式一致了。如果遇到字节的最高位是1的话,那么就表示需要两个字节值才能对应一个字符。
假如你使用GB2312写了这么一句话:
我叫ABC
它的二进制编码是这样的:
11001110 11010010 10111101 11010000 01000001 01000002 01000003
全角?
全角是一种电脑字符,且每个全角字符占用两个标准字符(或半角字符)位置。通常的英文字母、数字键、符号键都是半角的,半角的显示内码都是一个字节。为了排列整齐,英文和其它拉丁文的字符和标点也提供了全角格式。在中文输入法中,切换全角和半角格式的快捷键为SHIFT+空格。
ANSI标准、国家标准、ISO标准
不同ASCII衍生字符集的出现,让文档交流变得非常困难,因此各种组织都陆续进行了标准化流程。例如美国ANSI组织制定了ANSI标准字符编码(注意,我们现在通常说到ANSI编码,通常指的是平台的默认编码,例如英文操作系统中是ISO-8859-1,中文系统是GBK),ISO组织制定的各种ISO标准字符编码,还有各国也会制定一些国家标准字符集,例如中国的GBK,GB2312和GB18030。
操作系统在发布的时候,通常会往机器里预装这些标准的字符集还有平台专用的字符集,这样只要你的文档是使用标准字符集编写的,通用性就比较高了。例如你用GB2312字符集编写的文档,在中国大陆内的任何机器上都能正确显示。同时,我们也可以在一台机器上阅读多个国家不同语言的文档了,前提是本机必须安装该文档使用的字符集。
Unicode的出现
虽然通过使用不同字符集,我们可以在一台机器上查阅不同语言的文档,但是我们仍然无法解决一个问题:如果一份文档中含有不同国家的不同语言的字符,那么无法在一份文档中显示所有字符。为了解决这个问题,我们需要一个全人类达成共识的巨大的字符集,这就是Unicode字符集。
Unicode字符集涵盖了目前人类使用的所有字符,并为每个字符进行统一编号,分配唯一的字符码(Code Point)。Unicode字符集将所有字符按照使用上的频繁度划分为17个层面(Plane),每个层面上有216=65536个字符码空间。
其中第0个层面BMP,基本涵盖了当今世界用到的所有字符。其他的层面要么是用来表示一些远古时期的文字,要么是留作扩展。我们平常用到的Unicode字符,一般都是位于BMP层面上的。目前Unicode字符集中尚有大量字符空间未使用。
编码系统的变化
在Unicode出现之前,所有的字符集都是和具体编码方案绑定在一起的(即字符集≈编码方式),都是直接将字符和最终字节流绑定死了,例如ASCII编码系统规定使用7比特来编码ASCII字符集;GB2312以及GBK字符集,限定了使用最多2个字节来编码所有字符,并且规定了字节序。这样的编码系统通常用简单的查表,也就是通过代码页就可以直接将字符映射为存储设备上的字节流了。例如下面这个例子:
Unicode同样也不完美,这里就有三个的问题,一个是,我们已经知道,英文字母只用一个字节表示就够了,第二个问题是如何才能区别Unicode和ASCII?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢?第三个,如果和GBK等双字节编码方式一样,用最高位是1或0表示两个字节和一个字节,就少了很多值无法用于表示字符,不够表示所有字符。Unicode在很长一段时间内无法推广,直到互联网的出现,为解决Unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种Unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。
UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换(即Uncidoe字符集≠UTF-8编码方式)。
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
—————————————————————–
0000 0000-0000 007F | 0xxxxxxx(兼容原来的ASCII)
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
因此,Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的Unicode编码是UTF-16和UTF-8。
早期字符编码、字符集和代码页等概念都是表达同一个意思。例如GB2312字符集、GB2312编码,936代码页,实际上说的是同个东西。
但是对于Unicode则不同,Unicode字符集只是定义了字符的集合和唯一编号,Unicode编码,则是对UTF-8、UCS-2/UTF-16等具体编码方案的统称而已,并不是具体的编码方案。所以当需要用到字符编码的时候,你可以写gb2312,codepage936,utf-8,utf-16,但请不要写Unicode。
造成乱码的原因就是因为使用了错误的字符编码去解码字节流,因此当我们在思考任何跟文本显示有关的问题时,请时刻保持清醒:当前使用的字符编码是什么。只有这样,我们才能正确分析和处理乱码问题。
常见CharSet有:GBK、GB2312、US-ASCII、ISO-8859-1、UTF-8、UTF-16BE、UTF-16LE、UTF-16