介绍String的本质,相关API,以及StringBuffer/StringBuilder
<!--more-->
字符串
字符串是最重要的引用类型,互联网基本上只做一件事情,那就是处理字符串
所以能处理好字符串是Web
服务器的基本要求,比如(PHP
,Python
,Ruby
,Perl
,Java
),
而C/C++
对于字符串处理能力不强,所以一般不使用其做Web
服务器
字符串的不可变性
Java是如何保证字符串不可变的
从代码实现角度分析,首先String
类是final
的,且其内部有一个char[] value
的指针指向堆中用来存储字符的一个数组,其也是final
如果不使用反射等操作的话,是无法将其改变指向的,且暴露出的方法没有可以进行修改的API
,保证了其的不可变性
String类内部存储Hash值
String
类的内部实现中有一个private int hash
,默认为0
,将当前字符串的hash
值缓存了起来
因为字符串不可变,所以调用hashCode()
时,如果内部的这个hash
值不为0
的话就不需要再次重复进行计算
不可变的原因及缺点
字符串是不可变的原因:要做到线程安全及存储安全
线程安全很好理解,至于存储安全指的是在HashMap
中使用String
作为key
,没有办法在String
可变的情况,还继续遵守hashCode
的约定
不可变的对象天生就是线程安全
不可变的缺点:每当修改时需要创建新的对象
String
/StringBuilder
/StringBuffer
的相关使用
StringBuilder
- 优先使用,线程不安全,速度快
StringBuffer
- 线程安全,速度相对较慢
StringBuilder/StringBuffer
类型的值不可以作为HashMap
的Key
字符串与编码
为了要把人类世界中的字符转化为计算机世界中的字节,发明了一种映射关系,这种映射即被称为字符集
- 将人类能看懂的字符变为字节,称为编码
- 将字节转化为人类能看懂的字符,称为解码
问题的根源就在于,人类发明了很多种字符集,不同的字符集在进行相互的操作的时候,就会发生问题
Unicode
Unicode
作为一种最常用的字符集,因为要包括全世界各种语言的字符,所以其内部使用int
值进行存储(int
可以存储42亿
个不同字符,可以满足需求)
然后其通过不同的int
值对应不同的字符,这个不同的字符对应的int
值被称为码点(code point
)
举个例子,假设现在的Unicode
的对应关系为,值为1
的int
值对应字符1
,值为2
的int
值对应字符2
等等:
1 -> '1'
2 -> '2'
而由于其是int
值,所以字符1
对应的其实是00000000 00000000 00000000 00000001
四个字节
在这个例子中,前三个字节是用不到的,所以出现了浪费了一些字节的情况
在此基础上,出现了最常用的2个编码方案:
- UTF-16 (
java
程序内部使用的存储方法) - UTF-8
Mac/Linux
的默认编码是UTF-8
,windows
默认的中文编码为GBK
,这里建议都使用UTF-8
,因为其对多语言支持最好
UTF-16
基本的编码思路是,对于较小的码点,比如1234 -> 字符1
,
那么字符1的编码就直接是1234
的十六进制04d2
,所以直接使用2个字节04 d2
就可以完成存储
对于较大的码点,就需要使用更多的字节,比如123456
经过一系列复杂的计算算出一个值,使用更多的字节进行存储
另外多个字节在传输过程中,会有顺序的不同,比如
04 d2
也会被传输成d2 04
,分为big ending
和little ending
针对这种情况,BOM(Byte Order Mark)
,字节顺序标记,就会出现在文本文件头部,不可见,但是可以用来标识顺序
而UTF-8
的实现思路还有不同,它将前127
个字符(最常用)存储为1
个字节,然后按照常用级别,将剩下的字符们分为2
个字节存储,
更加不常用的就用3
个字节存储(绝大多数中文就是3
个字节),然后最多到6
个字节存储的字符
GBK
前身为GB2313
,非常简单粗暴,指定2
个字节为一个对应的中文,完全没管Unicode
,是完全不同的2张表
当GBK
编码的文件被使用Unicode
之类的字符集解码时,就会出现乱码