字符串String
从概念上讲,Java字符串就是Unicode字符序列,例如,字符串“Java\u2122”由5个Unicode字符J a v a 和 ™组成。Java没有内置的字符串类型,而是在标准的Javal类库中提供了一个预定义类,很自然的叫做String。每个用双引号括起来的字符串都是String类的一个实例。
1.拼接
Java语言允许使用+号连接两个字符串
String a = "Expletive";
String b = "delete";
String message = a+b;
当一个字符串与一个非字符串的值进行拼接时,后者会变成字符串
int age = 13;
String rating = "PG"+age;
把多个字符串放在一起,用一个界定符分离,可以使用静态join方法
String all = String.join("/","S","M","L","XL");
//all is the string "S/M/L/XL"
在java11中,还提供了repeat方法
String repeated = "Java".repeat(3);
//repeated is "JavaJavaJava"
2.不可变字符串
Java中String类没有提供修改字符串中的某个字符的方法。不可变字符串的优势在于可以实现字符串的共享。各种字符串存放在公共的存储池中,字符串变量指向了存储池中相应的位置。如果复制一个字符串变量,原始字符串和复制的字符串共享相同的字符。
3.检测字符串是否相等
使用equals方法检测两个字符串是相等
s.equals(t)
要想检测两个字符是否相等,而不区分大小写,使用equalsIgnoreCase方法
s.equalsIgnoreCase(t)
一定不要使用==运算符检测 两个字符串是否相等,这个运算符只能确定两个字符是否存放在同一个位置上。如果字符串在同一个位置上,它们必然相等,但是完全有可能将内容相同的多个字符串副本符放在不同位置上。如果虚拟机始终将相同的字符串共享,皆可以使用==运算符检测是否相等。但实际上只有字符串字面量是共享的,而+ 或subString 等操作得到的字符串并不共享。
4.空串与Null串
空串“”是长度为0的字符串。空串是一个Java 对象,有自己的串长度(0)和内容(空)。不过,String变量还是可以存放一个特殊的值,名为null,表示目前没有任何对象与该变量关联。要检查一个字符串是否为null,要使用以下条件:
if(str == null)
有时候要检查一个字符串既不是null,也不是空串要使用一下条件:
if(str != null && str.length() !=0)
使用&&运算可确保str.length()不会报空指针,因为如果&& 左边为假就不会验证右边的结果。如果使用 & 运算,当左边为假,即str==null,右边的算式就会报空指针的异常。
5.码点与码点单元
Java字符串由char值序列组成。char数据类型是由一个采用UTF-16编码表示Unicode码点的代码单元。最常用的Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。
//length方法返回采用UTF-16编码表示给定字符串所需的码点单元数量
String greeting = "Hello";
int n = greeting.length();//is 5
//要想得到实际长度,即码点数量 可以调用:
int cpCount = greeting.codePointCount(0, greeting.length());
//调用s.charAt(n)将返回位置n的代码单元,n介于0~s.length()-1之间
char first = greeting.charAt(0);//first is 'H'
char last = greeting.charAt(4);//last is 'o'
//想要得到第i个码点,应该使用下列语句
int i = 4;
int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);
char a = (char) cp;
System.out.println(a);// a is 'o'
6.String 常用API
- char charAt(int index) 返回指定位置的代码单元
- int codePointAt(int index) 返回从给定位置开始码点
- int offsetByCodePoints(int startIndex,int cpCount) 返回从startIndex码点开始 cpCount个码点后的码点索引
- int comparTo(String othre) 按照字典顺序比较每一位字符,如果字符串位于other 之前则返回一个负数,如果字符串位于other之后,返回一个正数;如果两个字符串相等,返回0
- InStream codePoints() 将这个字符串的码点作为一个流返回,调用toArray将它们放在同一个数组中
- new String(int [] codePoints, int offset ,int count) 用数组中的offset 开始的count个码点构造一个字符串
- boolean isEmpty() 此方法只能判断当前字符串的长度是否为0
- boolean isBlank() 如果字符串为空或者由空格组成,返回true
- boolean equals(Object other ) 如果字符串与other相等,返回true 说明:通过查看方法源码可以知道,判断当前两个字符串是否相等,首先会判断两个字符串的引用地址是否相等,如果引用地址相等则返回true,如果当前两个字符串的引用地址不相等,则会比较两个字符串中的马点单元是否相等,如果所有的码点单元都相等则返回true。
- boolean euqalsIgnoreCase(String other) 如果字符串与other相等(忽略大小写),返回true
- boolean startsWith(String prefix) 如果字符串以prefix开头则返回true
- boolean endsWidth(String suffix) 如果字符串以suffix 结尾则返回true
- int indexOf(String str) 返回与字符串str匹配的第一个子串的开始位置,如果不存在则返回-1
- int indexOf(String str,int fromIndex) 返回从索引fromIndex开始与str匹配的第一个子串的开始位置,不存在返回 -1
- int indexOf(int cp) 返回与码点cp匹配的第一个子串的开始位置,不存在返回 -1
- int indexOf(int cp,int fromIndex) 返回从索引fromIndex开始与cp匹配的第一个子串的开始位置,不存在返回 -1
- int lastIndexOf(String str) 返回与字符串str匹配的最后一个子串的开始位置,如果不存在则返回 -1
- int lastIdnexOf(String str,int fromIndex) 返回从索引fromIndex 开始于str匹配的最后一个子串的开始位置,不存在返回-1
- int lastIndexOf(int cp) 返回与码点cp匹配的最后一个子串的位置,不存在返回-1
- int lastIndexOf(int cp,intfomIndex) 返回从索引fromIndex开始与cp匹配的最后一个子串的开始位置,不存在返回 -1
- int length() 返回字符串代码单元的个数
- int codePointCount(int startIndex ,int endIndex) 返回startIndex 和endIndex-1之间的码点个数
- String replace(CharSequence oldString ,CharSequence newString) 返回一个新字符串。这个字符串用newString 代替原始字符串中所有的oldString,可用String 或者StringBuilder ,StringBuffer 对象作为CharSequence
- String subString(int beginIndex) 返回一个新的字符串,这个字符串从原始字符串beginIndex位置开始的所有代码单元
- String subString(int beginIndex ,int endIndex) 返回一个新的字符串,这个字符串包含原始字符串从beginIndex到字符串endIndex -1的所有代码单元
- String toLowerCase() 返回一个新的字符串,这个字符串把原始的字符串中的大写字母改为小写字母
- String toUpperCase() 返回一个新的字符串,这个字符串把原始的字符串中的小写字母改为大写字母
- String trim() 返回一个新的字符串,这个字符串删除原始字符串头部和尾部小于等于U+0020的字符
- String strip() 返回一个新的字符串,这个字符串删除原始字符串中的空格 jdk11新增
- String join(CharSequence delimiter,CharSequence ...elements) 返回一个字符串用给定的定界符链接所有的元素、
- String repeat(int count ) 返回一个字符串 ,将当前字符串重复count次
7.构建字符串
有些时候,需要有较短的字符串构建字符串,例如按键或者来自文件的单词,如果使用字符串拼接的方式来达到目的,效率会比较低。因为每次拼接字符串时,都会创建一个新的String对象,即耗时又浪费空间。如果使用StringBuilder类就可以避免这个问题的发生。
通过查看源码来分析为什么SringBuilder拼接字符串能节省空间和时间
//查看append(Objec obj)方法中调用了appen(String str)方法
//String.valueOf(obj) 如果当前obj为null 则返回"null"字符串,返回obj.toString();
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();//如果str为null则拼接"null"字符串
int len = str.length();
ensureCapacityInternal(count + len);//确保当前StringBuilder字符数组能放下Str ,如果放不下则进行扩容
str.getChars(0, len, value, count);//将str中的字符数组数据复制到StringBuilder中
count += len;//改变当前StringBuilder 的长度
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
StringBuilder继承自AbstractStringBuilder类, 通过append(Object obj)方法可以看出,拼接的是append(String str)方法。 在append(String str)方法中首先会判断当前str是否为null,如果为null 则通过appendNull()把null“”字符串拼接起来。Stringbuiler每次拼接前都会通过ensureCapacityInternal方法比较当前存储字符数组的长度length 与要拼接的字符长度,如果不够则通过进行扩容。最后通过String的getChars方法把当前的要拼接的字符串复制到StringBuilder的字符数组中。所以SringBuilder 是通过复制字符数组来进行拼接的, 在调用toString()方法之前并没有频繁的创建字符串对象。
8.格式化输出
每一个以%字符开始的格式说明符都用相应的参数替换。格式说明符尾部的转换符指示要格式化的数值类型:f表示浮点数,s表示字符串,d表示十进制整数。
String name = "Tom";
int age = 10;
System.out.printf("hello ,%s Next year ,you will be %d",name,age);
//结果为:hello ,Tom Next year ,you will be 10
转换符
转换符 | 类型 | 示例 |
d | 十进制整数 | 159 |
x | 十六进制整数 | 9f |
o | 八进制整数 | 237 |
f | 定点浮点数 | 15.9 |
e | 指数浮点数 | 1.59e+01 |
g | 通用浮点数(e和f中较短得的一个) | |
a | 十六进制浮点数 | 0x1.fccdp3 |
s | 字符串 | Hello |
c | 字符 | H |
b | 布尔 | true |
h | 散列码 | 42628b2 |
tx或Tx | 日期时间(T强制大写) | 已经过时应当改为使用java,Time类 |
% | 百分号 | % |
n | 与平台有关的行分隔符 |
|
控制格式化输出外观的标志
逗号标志可以添加分组分隔符,例如:Sysytem.out.println("%,.2f",10000.0/3.0);
打印结果为:3,333.33
标志 | 目的 | 示例 |
+ | 打印正数或负数的符号 | +3333.33 |
空格 | 在正数之前添加空格 | | 3333.33| |
0 | 在数字面前补0 | 003333.333 |
- | 左对齐 | |3333.33 | |
( | 将负数括在括号内 | (3333.33) |
, | 添加分组分割符 | 3,3333.33 |
#(对于f格式) | 包含小数点 | 3,333. |
#(对于x或者0格式) | 添加前缀0x或0 | 0xcafe |
$ | 指定要格式化的参数索引。 例如:%1$d %1$x将以十进制和16进制打印第一个参数 | 159 9F |
< | 格式化前面说明的数值, 例如:%d%<x将以十进制和十六进制打印同一个数值 | 159 9 |
可以使用s转换格式化任意的对象。对于实现了Formattable接口的任意对象,将调用这个对象的FormatTo方法,否则调用toString方法将这个对象转换为字符串。
可以使用静态String.format 方法创建一个格式化的字符串,而不打印输出:
String message = String.format("Hello,%s. Next year ,you will be %d",name age);
基于完整性的考虑,下面简略的介绍printf方法中日期与时间格式化选项。对于新的代码,应当使用卷Ⅱ第6章中介绍的java.time包的方法。不过你可能会在遗留的代码中看到Date类相关的格式化选项。这个格式包括两个字母,以t开始,以以下表中任意字符结束
例如:
system.out.printf("%tc",new Date()); 这条语句将用下面的格式打印当前的日期和时间
打印结果为:周六 7月 25 18:05:51 CST 2020
Tip:经过测试部分转换符,有些会替换成中文,例如B, b/h都只会打印7月,没有缩写,缩写只有美国佬的月份
日期时间转换符
转换符 | 类型 | 示例 |
c | 完整日期和时间 | Mon Feb 09 18:05:19 PST 2015 |
F | ISO 8601 日期 | 2015-02-09 |
D | 美国格式的日期(月/日/年) | 02/09/2015 |
T | 24小时时间 | 18:05:19 |
r | 12小时时间 | 06:05:19 pm |
R | 24小时时间没有秒 | 18:05 |
Y | 四位数字的年(前面补0) | 2015 |
y | 年的后面两位数字(前面补0) | 15 |
C | 年的前面两位数字(前面补0) | 20 |
B | 月的完整拼写 | February |
b或h | 月的缩写 | Feb |
m | 两位数字的月(前面补0) | 02 |
d | 两位数字的日(前面补0) | 09 |
e | 两位数字的日(前面不补0) | 9 |
A | 星期几的完成拼写 | MonDay |
a | 星期几的缩写 | Mon |
j | 三位数的年中第几天(前面补0)在001到366之间 | 069 |
H | 两位数字的小时(前面补0),在0到23之间 | 18 |
k | 两位数字的小时(前面不补0),在0到23之间 | 18 |
Ⅰ | 两位数字的小时(前面补0)在1到12之间 | 06 |
l | 两位数字的小时(前面不补0)在1到12之间 | 6 |
M | 两位数字的分钟(前面补0) | 05 |
S | 两位数字的秒(前面补0) | 19 |
L | 三位数字的毫秒(前面补0) | 047 |
N | 九位数子的毫秒 | 047000000 |
P | 上午和下午的标志 | pm |
z | 从GMT起,RFC 822 数字位移 | -0800 |
Z | 时区 | PST |
s | 从格林尼治时间1970-01-01 00:00:00起的秒数 | 1595674319 |
Q | 从格林尼治时间1970-01-01 00:00:00起的秒数 | 1595674332216 |
某些格式只给出了指定日期的部分信息,因此,可以用一个格式字符串指示要格式化的参数。
System.out.printf("%1$s %2$tB %2$te ,%2$ty","Due date:",new Date());
打印结果为:Due date: 七月 25 ,20
本文章均摘录于Java核心技术卷Ⅰ 用作知识点记录