[十二]基础数据类型之String

2019年11月06日 阅读数:81
这篇文章主要向大家介绍[十二]基础数据类型之String,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

在正式介绍String以前,咱们先介绍下CharSequencephp

char + sequence 就是字符的序列的意思css

Java中万事万物都是对象类型vue

而对于字符的序列,也就是多个char, 这么一种东西, 使用CharSequence这个接口来描述ios

既然是接口,天然规定了做为字符序列的基本协议c++

CharSequence简介


char charAt(int index); 返回指定索引的char
int length() 返回字符序列的长度
CharSequence subSequence(int start, int end) 返回子序列
String toString() 返回一个包含此序列中字符的字符串该字符串与此序列的顺序相同
default IntStream chars() 返回此序列的int stream,每一个char零位扩展为int
default IntStream codePoints() 返回此序列的代码点的stream
 
咱们都知道1.8的一个亮点就是stream和lambda
default方法也是1.8新增的,默认实现
既然CharSequence表示了 字符序列这么一个概念
显然,String内部是char数组,就是一个char的序列
 

String简介

String 类表明字符串
Java 程序中的全部字符串字面值(如 "abc" )都是String的实例
内部有一个char[] 

注意到 上面的final, 字符串是常量;它们的值在建立以后不能更改
String str = "abc";
等效于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
Java 语言提供对字符串串联符号("+")以及将其余对象转换为字符串的特殊支持
说白了就是+被重载过了,也提供了强大的将对象转换为字符串的能力
char是UTF-16中的代码单元,因此字符序列就是代码单元的序列
仍旧是一个char多是一个字符,也多是半个字符
String 类提供处理 Unicode 代码点(即字符)和 Unicode 代码单元(即 char 值)的方法
 

属性CASE_INSENSITIVE_ORDER

这就是一个比较器
逻辑也很简单,两个String 按照字典顺序进行比较,忽略大小写的
以二者length小的那个做为循环次数,进行循环
若是第一个相等比较第二个,依次类推,直到不同
若是全部的都相等,那么就比较长度了 return n1 - n2
 

字符与字节数组

在继续下去以前,再次简单介绍下字符与字节数组的关系
字符到字节,是一个编码的过程
字节到字符是一个解码的过程
一样的一个字符,在不一样的字符集和编码方式下,实际存储的值,将是不一样的
好比前面说的Unicode字符集,UTF8 和UTF16编码后的数据是不一样的
这个编码后的数据,也就是字节  , 他们是不同的 
一样的一个编码值,在不一样的字符集中,可能表明着不一样的字符
因此字符与字节之间,必然有编码参与其中
这个编码环节是必然存在的,不然,你就没办法把字节与字符联系起来
一个字符能够根据 字符集编码 进行多种方式的编码
一个字节数组也能够根据 字符集编码 进行多种方式的解码

对于同一个字符,无论进行何种编码,当他们按照当初编码的方式进行解码时,必然对应的仍是一样的那个字符
 

操做系统的文件都是以字节序列的形式存储的,因此任何一个文件都是有编码的
好比你在txt文件中输入了一个字符
这个字符 底层就会使用指定的编码存储到字节中
软件自己又把这个编码以字符的形式呈现出来
因此你才看获得是一个字符
好比这个文件中11111.txt中,存储了一个汉字春天的 " 春" 
编码方式是UTF8
二进制软件查看是E6 98 A5
与咱们进行UTF8 编码计算的结果是对应的
ANSI编码
不一样的国家和地区制定了不一样的标准
由此产生了 GB23十二、GBK、Big五、Shift_JIS 等各自的编码标准
这些使用 1 至 4 个字节来表明一个字符的各类汉字延伸编码方式,称为 ANSI 编码
在简体中文Windows操做系统中,ANSI 编码表明 GBK 编码;
在日文Windows操做系统中,ANSI 编码表明 Shift_JIS 编码

再看下面一个例子
使用ultraedit 新建了一个文件,里面写了一个汉字 "春",
其实这个默认格式就是操做系统的编码,也就是ANSI  也就是GBK
查看二进制编码为 B4 BA
而后咱们再去对照GBK的码表,你会发现彻底对的上
任何一个文件,他其实有自带或者说默认的一个编码
凡是呈现字符的地方,都有一个编码在默默地支撑,才可以让你看得见,看得清楚字符
这个字符的保存 , 就是字符按照编码表  编码 成字节序列的过程
这个字符的呈现 , 就是字节序列按照编码表  解码 成字符的过程
当你使用计算机,进行字符处理工做的时候,无时无刻都在进行着编码与解码

String构造方法

String是经常使用类之一,因此提供了很是丰富的方法
String是字符序列  内部是char[]   char就是一个十六进制的数 16位表示
因此char[] 能够用来构造String
char是16位数可以表示代码单元, int天然能够直接表示一个代码点了,因此也可使用int来构造String
另外再加上咱们刚才关于字节数组与字符关系的介绍,也可使用字节数组构造String
 
下面表格是几个基本的构造方法
String() 空String ,没啥必要由于String是不可变的

String(char[])
String(char[], int, int)
借助于字符数组或者字符数组的一部分建立对象
内部原本就是字符数组 char[]  因此天然可使用char[]构造
直接进行拷贝,因此对原有字符数组的修改不影响String对象

String(int[], int, int) 使用代码点构造String
public String(int[] codePoints, int offset, int count)
offset 和 count为范围限制
String(String)
String(StringBuffer)
String(StringBuilder)
 

getBytes 方法

先提一下另一个方法,getBytes
使用指定的字符集将此 String 编码为 byte 序列
个人编辑器环境是UTF8编码的      
"春" 的UTF8编码上面已经分析了
 
也就是说我这边有一个UTF8的字符"春"  源文件中保存的是 E6 98 A5
对于下面全部的getBytes来讲,"春" 这个字符形状符号是不变的
得到的字节数组就是  这个字符形状符号 根据不一样字符集编码方式, 编码而获得的字节数组
下面的各类转换换一个描述就是:UTF8的字符"春" ,在其余的字符集下面,编码都是多少啊?
为何UTF-8 是-26  -104 -91 ? 而不是e6 98 a5?进制问题
getBytes总共三种形式
指定编码或者使用默认
getBytes(String)
getBytes(Charset)
getBytes()
还有一种已经弃用 了
 

经过字节数组 byte[] 构造

String提供了6个跟byte[]  相关的构造方法
 
getBytes方法是字符是固定的, 固定的以UTF8格式存储在个人源文件中,
而后根据不一样的编码方式,转换为字节数组 byte[]
 
String的构造方法,则是将各个已经编码过的字节数组 byte[] 按照指定的编码方式解析 还原成为一个字符
而后再将这个字符以char[]  也就是UTF-16的方式进行存储的
个人源文件IDE环境是UTF8那么最终构造的String就是UTF8的,不会是其余的
 
好比下面的构造方法,使用前面示例中的 bytes数组
 
而后使用  String(byte[], String)   进行构造
看得很清楚
String字符串 s1 中存储的value 是Unicode的代码点U+6695    (0号平面,一个代码单元就是一个代码点)
也就是十进制的26149
 
使用byte[] 字节数组构造String的过程是下图这样子的
字节数组,根据指定字符编码转换为那个字符
而后在把字符按照UTF16 进行编码 存储到String中的char[]
上面的例子能够很好地印证这一点,字节数组是[-76, -70] 
也就是 :    ffffffb4   ffffffba   也就是 B4 BA 明明是GBK的"春"
根本就不是6625 对应关系就是他们表示的是同一个字符
既然字节数组与字符的转换离不开编码,因此天然经过byte[] 构造String对象时,必需要有编码
不设定并非没有,而是使用默认的
既然使用字节数组,那么有的时候可能须要指定范围,因此有两个根本的构造方法
而后还有默认字符编码的简化形式
再而后就是长度为整个字节数组的简化形式
这几个构造方法根本在于理解 字节数组与字符的转换
以及必须的byte[] 字节数组  以及  编码
 

valueOf

valueOf 系列用来包装
String中用来将基本类型 以及 Object 转换为String
char相关的都是直接构造String对象
其他(除了boolean,他是转换为字符串  true和false返回)
都是toString
 

copyValueOf

copyValueOf方法内部就是直接调用的两个构造方法
还不如直接使用new建立来的直接,只不过使用这个方法有更好的可读性
 

获取指定位置代码单元和代码点的方法

charAt(int) 
返回指定索引处的 char 值  索引范围为从 0 到 length() - 1
简单粗暴,  无论三七二十一就是代码单元  
若是是辅助平面,那就多是代理项
codePointAt(int)
返回指定索引处的代码点,  范围从 0 到 length() - 1
他跟Character中的codePointAt方法逻辑含义是同样的
若是是高代理,若是下一个也在掌控范围内,若是下一个是低代理,那么返回代码点
不然,返回代码单元 也就是一个char
codePointBefore(int)
返回指定索引以前的字符(Unicode 代码点)  其范围从 1 到 length
他跟Character中的codePointBefore方法逻辑含义是同样的
若是index-1 是低代理,若是在往前一个index-2 也是有效范围内,若是他还刚好是一个高代理,返回代码点
不然,返回代码单元,也就是一个char
codePointCount(int, int)
此 String 的指定文本范围中的 Unicode 代码点数
文本范围始于指定的 beginIndex,一直到索引 endIndex - 1 处的 char,  包含头不包含尾
该文本范围的长度(用 char 表示)是 endIndex-beginIndex
因为一个代码点的代码单元个数多是1个多是2个,因此代码点的个数须要计算,就不直观了
他跟Character中的codePointCount方法逻辑含义是同样的
offsetByCodePoints(int, int)
他跟Character中的offsetByCodePoints方法逻辑含义是同样的
返回此 String 中从给定的 index 处偏移 codePointOffset 个代码点的索引
根本缘由仍是一个代码点的代码单元个数多是1个多是2个 
因此 偏移codePointOffset个代码点的 代码单元的个数不肯定,须要调用方法计算
 

getChars(int, int, char[], int)复制

实例方法
就是一个复制方法,名字不太规范
复制String中指定索引开始的srcBegin 和 srcEnd   包含头不包含尾
到另外一个字节数组 char dst[]中, 存放的起始位置为dstBegin
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)

须要注意的是,复制的是char  代码单元
就是String 内部char[] 的下标索引
 

开始结束匹配校验

startsWith(String, int)
startsWith(String)
实例方法
测试String是否以指定的前缀开始
还能够指定起始位置处开始比较

从源代码看得出来,挨个比较内部的char
从头开始,所有一致才返回true

单参数是双参数的简化版本
endsWith(String) endwith就是从最后的指定参数长度的位置开始比较
 

indexOf 和lastIndexOf

indexOf 和XXXIndexOf系列都是获取下标索引相关
须要注意的是,他们的参数都是int或者String
也就是说这些方法都是真正的字符相关的
int indexOf(int ch)
int indexOf(int ch, int fromIndex)
返回   指定字符  在此字符串中第一次出现处的索引
返回的匹配的第一个

也能够指定检索的起始位置, 若是指定了索引
那么返回的值将  大于等于  指定的索引

换个说法:
若是是0号平面返回的是那个代码单元也就是代码点的索引
charAt(k) == ch   为 true 的最小 k 值  
若是是辅助平面返回的是高代理位的代码单元的索引  
codePointAt(k) == ch  为 true 的最小 k 值  
int indexOf(String str)
int indexOf(String str, int fromIndex)
返回  指定子字符串   在此字符串中第一次出现处的索引
返回匹配的第一个
 
也能够指定检索的起始位置,若是指定了索引
那么返回值须要大于等于 指定的索引
 
匹配的含义为startsWith(str) 为true
若是指定检索开始的位置,  那么
不只仅startsWith(str) 为true 还须要索引知足指定的下标范围
不然仍旧是返回-1
lastIndexOf(int)
lastIndexOf(int, int)
返回指定字符在此字符串中最后一次出现处的索引
返回匹配的最后一个

也能够指定检索位置,可是这个检索位置与indexOf不一样
indexOf中指定的索引,是从索引处日后
lastIndexOf指定的索引, 是反向,从索引处往前
指定了索引就要求 返回值 小于等于 指定索引

换个说法
若是是0号平面返回的是那个代码单元也就是代码点的索引
charAt(k) == ch   为 true 的最大 k 值 
若是是辅助平面返回的是高代理位的代码单元的索引 
codePointAt(k) == ch  为 true 的最大 k 值   而且  k 小于等于 指定的索引
lastIndexOf(String)
lastIndexOf(String, int)
返回指定 子字符串 在此字符串中最后一次出现处的索引
返回匹配的最后一个
 
也能够指定检索位置,检索索引的位置也是反向搜索
 
匹配的含义为startsWith(str) 为true
指定了索引就要求返回值 小于等于  指定索引
 
总共三个维度
匹配第一个或者最后一个 / 匹配字符或者字符串 / 是否指定查找范围 
8个方法
 
indexOf是从前日后匹配  匹配的是第一个 若是指定了下标索引,从索引处日后找  
返回的值要  大于等于 索引
 
lastIndexOf是从后往前匹配  匹配的是最后一个  若是指定了开始下表索引,是从索引处往前,反向查找
返回的值要  小于等于 索引
 
匹配字符若是是BMP,代码单元就是代码点,返回的就是那个代码单元也是代码点的索引
若是是辅助平面,一个代码点两个代码单元,返回的就是高代理位的索引  lastIndexOf和indexOf都是返回高代理项
 

length 

长度获取,内部char数组的长度