目录

  • 字符集与字符编码、码点
  • ASCII
  • ISO-8859-1(Latin 1)
  • GB2312、GBK、BIG5
  • GB2312
  • BIG5
  • GBK
  • 乱码
  • Unicode字符集
  • 代码单元
  • UTF-32
  • UTF-8
  • UTF-16
  • 高位代理项与低位代理项
  • 自同步的字符编码
  • Java String
  • Java 8及以前
  • Java 9及以后
  • 关于String对象创建个数问题
  • String常用API
  • String的数据类型转换
  • StringBuffer和StringBuilder
  • 构造器和常用方法
  • StringBuffer与StringBuilder、String的区别


字符集与字符编码、码点

  • 字符集:存放了字符与数字的映射关系
  • 码点(code point):不同的字符对应不同的数字,这个数字就是码点
  • 字符编码:通过字符集可以知道一个字符对应的数字,所以计算机需要先通过一定的规则将这个二进制的bit数转为十进制整数,才能从对应的字符集中找到这个二进制bit数对应的具体字符。而这个规则就是字符编码,对于除了Unicode字符集以外的其它字符集都是简单的将二进制转为十进制数,所以对于这些字符集而言,字符集和字符编码并无什么不同

ASCII

  • 单字节的字符集,此时字符集就等同于字符编码,因为码点和二进制bit数之间只有简单的二进制转成十进制
  • 总共占用7bit,对于计算机中包含8bit的一个字节的基本单位而言,最高位没有被用到,一般用来做奇偶校验位或直接补零
  • 优点是简单,只包含可输出字符(如数字、大小写字母、英文符号等)和控制字符;没有特殊的字符编码规则,字符集等同于字符编码
  • 缺点是表示的字符太少,对于非英文字符无法表示

ISO-8859-1(Latin 1)

  • 对ASCII的改进,新增了其它可输出字符和控制字符,使得一个字节8bit能刚好被用完
  • 优点是保留有ASCII原有有点的基础上一定程度上扩充了ASCII字符集的表示字符范围
  • 缺点依然是表示的范围太小

GB2312、GBK、BIG5

recordset 字符集 string字符集_recordset 字符集


recordset 字符集 string字符集_字符编码_02

  • GB2312主要针对简体中文,BIG5主要针对繁体中文

GB2312

  • GB2312兼容ASCII,因为GB2312不管是高位字节还是低位字节都大于128

BIG5

  • “訏功蓋”问题: BIG5字符集的低位字节与ASCII有冲突,虽然遇到问题的概率较低,但是还是会出现问题
  • “\”在很多编程语言中都是作为转义字符

GBK

recordset 字符集 string字符集_recordset 字符集_03

乱码

  • 当输入文本使用的字符集与读取文本使用的字符集不一致且两种字符集不兼容时就会导致乱码

Unicode字符集

recordset 字符集 string字符集_字符串_04


recordset 字符集 string字符集_字符编码_05

代码单元

recordset 字符集 string字符集_java_06

recordset 字符集 string字符集_字符编码_07

UTF-32

  • 是定长的字符编码,将每一个字符的码点固定的与一个四字节长度的二进制数相对应,所以UTF-32的代码单元(code unite)是4字节
  • 优点是能够表示目前常用的所有字符,缺点是浪费空间,需要补充太多的0,导致固定长度的字节流能够表达的字符数字减少

UTF-8

  • UTF-8是一种可变长度的字符编码,有特定的规则将二进制数转换为码点,针对不同范围的码点使用不同长度的二进制数表示,最低是1个字节,所以 UTF-8的代码单元是1字节

  • UTF-8的优点是能够表达几乎所有常用的字符,且完美兼容ASCII字符集,变长的特性使得固定长度的字节流能够表达更多的字符

UTF-16

  • UTF-16字符编码是可变长的字符编码,对于字符对应的码点和二进制数之间采用特定的规则进行相互对应,一般采用2个或4个字节的二进制数对应一个码点,所以UTF-16的代码单元是2个字节

高位代理项与低位代理项

recordset 字符集 string字符集_字符串常量池_08


recordset 字符集 string字符集_字符编码_09


recordset 字符集 string字符集_字符编码_10

  • UTF-16缺点时对于单字节表示的字符会浪费一个字节的空间,全部补0

自同步的字符编码

  • 自同步是指,对于根据不同字符编码在二进制字节流中划分出来的代码单元,对于单独的每个代码单元在不追溯前面代码单元的情况下能识别出来其是否为某个字符的起始代码单元

Java String

Java 8及以前

  • 对于Java 8及以前,String使用UTF-16作为字符编码,并使用char数组来存储数据
  • char数组中的每一个char对应的不是字符而是UTF-16中的代码单元,根据代码单元编码完以后才是对应的字符,比如下图的表情字符其实对应两个char

  • 在Java 8中计算字符长度,不应该使用String.length()而是应该使用String.codePointCount()
  • 在Java 8中获取字符串中某个位置上的字符时,不应该使用String.charAt(),而是应该使用String.codePointAt()

Java 9及以后

  • 由于UTF-16的缺点,Java 9以后对于String的字符编码使用了两种字符编码分别是Latin 1和 UTF-16,存储数据也从原来的char数组改为byte数组


关于String对象创建个数问题

  • string对象的创建可以有两种方式,方式不同,创建的对象不同
// s 指向字符串常量池中的 hello ,所以只创建了一个对象
String s = "hello";
// s1 指向堆中的 String 对象,而对象中的数字符数组索引指向字符串常量池中的字符串常量 world
String s1 = new String("world");
  • 常量与常量的拼接结果在字符串常量池,且字符串常量池中不会存在相同内容的常量
// 字符串常量池此时有三个字符串常量,hello、world、helloworld 以字符数组的形式存在,索引s直接指向常量池helloworld 
String s = "hello" + "world";
  • 字符串拼接时只要其中有一个是变量,结果就在堆中
String s = "hello"; 
// s1 指向堆中的String对象 helloworld,字符串常量池中有字符串常量 hello、world
String s1 = s + "world";
  • 如果拼接的结果调用intern()方法,返回值就在常量池中
String s = "hello"; 
// 如果字符串常量池中已经有 helloworld,则 s1 指向字符串常量池中的字符串常量 helloworld,字符串常量池中有字符串常量 hello、world、helloworld
// 如果字符串常量池没有 helloworld ,则 会先在字符串常量池中创建字符串常量 helloworld ,然后 s1 指向字符串常量池中的字符串常量 helloworld
String s1 = (s + "world").intern();

String常用API

  • int length():返回字符串的长度: return value.length
  • char charAt(int index): 返回某索引处的字符 return value[index]
  • boolean isEmpty():判断是否是空字符串:return value.length == 0
  • String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
  • String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
  • String trim():返回字符串的副本,忽略前导空白和尾部空白
  • boolean equals(Object obj):比较字符串的内容是否相同
  • boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
  • String concat(String str):将指定字符串连接到此字符串的结尾,等价于用“+”
  • int compareTo(String anotherString):比较两个字符串的大小
  • String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串
  • String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串
  • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
  • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
  • boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
  • boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
  • int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
  • int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
  • int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
  • int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索

注:indexOf和lastIndexOf方法如果未找到都是返回-1

  • String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
  • String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
  • String replaceAll(String regex, String replacement) :使用给定的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
  • String replaceFirst(String regex, String replacement) :使用给定的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
  • boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
  • String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
  • String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中

String的数据类型转换

  • String数据类型与基本数据类型间的转换
  • recordset 字符集 string字符集_字符编码_11

  • String数据类型与字符数组转换
  • recordset 字符集 string字符集_java_12

  • String数据类型与字节数组转换
  • recordset 字符集 string字符集_java_13

StringBuffer和StringBuilder

recordset 字符集 string字符集_字符编码_14


recordset 字符集 string字符集_java_15

  • StringBuffer和StringBuilder代表可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象
  • StringBuffer和StringBuilder很多方法与String相同

构造器和常用方法

  • 以StringBuffer为例,StringBuilder和StringBuffer相同

扩容是自动进行的

recordset 字符集 string字符集_recordset 字符集_16

StringBuffer与StringBuilder、String的区别

recordset 字符集 string字符集_java_17