字符串
1、不可变的String
每一个String对象都是不可变的,他们在创建的时候已经在内存中固定了。String对象的改变本质是拷贝一个当前的String的值然后完成修改后,生成新的String对象。这个新的对象和原来对象在内存中没有一丁点关系。是两个独立的对象
2、重载“+” 与StringBuilder
Java 不允许程序员重载任何操作符。"Hello" + "World"
本质是在编译器编译之后,生成一个StringBuilder
调用两次append()
将Hello
和World
添加。最后使用toString()
返回整合之后的字符串。如果使用+
太多的话就会产生很多的String中间量。上述的Hello和World产生两个String的中间量。
使用“+”编译器会自动将代码转换成StringBuilder对象。自动转换在循环内是会出现问题的,例如
1
2
3
4
5
6String[] strs = {"Hello","World","Java","Good"};
String result = "";
for(int i=0;i<strs.length;i++){
result += strs[i];
}
return result;
上述的代码会对循环内的每一个+=
操作创建新的StringBuilder对象完成字符串的拼接。使用如下的代码会更加高效
1
2
3
4
5
6String[] strs = {"Hello","World","Java","Good"};
StringBuilder result = new StringBuilder();
for(int i=0;i<strs.length;i++){
result.append(strs[i]);
}
return result.toString();
编译器自动编译有时候可能会愚蠢的优化代码。需要自己知道如何编写更加高效的代码。避开编译器的优化陷阱。
StringBuilder为什么效率更高?
StringBuilder 会在内存中申请一个空间。这个空间可以自己指定也可以使用默认的。使用append()操作的时候就是在自己的空间内完成操作,一次申请,随意改变。String 对象是只读的。内存的申请和释放都是有代价的!!
愚蠢的便捷
自己创建了一个StringBuilder然后在使用如下形式的函数
append(a + ":"+ b)
;
这个有什么问题。编译器会帮你将a+ ":" + b
部分新建一个StringBuilder对象来处理。这样就会得到两个字符串了。
小结
StringBuilder常用的操作有
append()
和toString()
.其它的还有insert()、replace()、substring()、reverse()、delete()
StringBuilder和StringBuffer。前者线程不安全、后者线程安全。
3、无意识的递归
一个类A中的
toString()
的方法如下。为了输出该类当前实例对象的地址。1
2
3public String toString(){
return "Class A address:" + this+ "\n";
}会产生异常,自动转换类型。将A类转换成String类。
+ this
会将this转换成String类。this指向A类,然后A又调用toString()方法来转换成String.递归的无出口。this -> this.toString() -> this -> ….
想要打印当前对象的地址使用Object.toString()
.应该使用父类的toString()super.toString()
。这样就不会出现死递归。4、String上的操作
String常用的操作在自己的编程至今。最多的就是字符串的更改和比较。更改使用StringBuilder更加好。String比较最多的便是相等比较。
equals(),equalsIgnoreCase()
相等比较。
使用"String".equals(a)
替换a.equals("String")
。如果a
是个null。调用equals()
方法会出现问题。length()
字符串的长度trim()
去掉自字符串两端的空白字符其它的操作有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17charAt()
getChars()
getBytes()
toCharArrary()
compareTo()
contains()
contentEquals()
regionMatcher()
startWith()
endsWith()
indexOf(),lastIndexOf()
substring()
concat()
replace()
toLowerCase(),toUpperCase()
valueOf()
intern()用的时候再查帮助手册
5、格式化输出
5.1 printf()
使用System.out.printf()
可以输出和C语言中的占位符。格式化输出.使用System.out.format()来替代。
5.2 System.out.format()
Sytem.out.format()
可以使用%d,%s 等占位符
5.3 Formatter类
在Java中,Formatter类是一个翻译器。翻译格式化字符和数据,构造Formatter类时需要传递一个输出流,说明要向什么地方输出。
向控制台输出
1
2Formatter f = new Formatter(System.out);
f.format("")
还可以使用OutputStream
和File
输出
5.4 格式化说明符
格式化的语法
%[argument_index$][flag][width][.precision]conversion
- flag 是左右对齐标志,默认为右对齐。如果flag = ‘-’ ;变成左对齐。
- width 宽度,整数
- .precision 精度。保留小数点后几位。如果String类型,输出字符的最大数量。浮点数,默认打印6位,但是可以自己指定。小数位多则舍入,少就补零。整数没有精度,在整数中使用精度会触发异常。
- conversion 需要转换成的类型
5.5 Formatter转换
字符 | 说明 |
---|---|
d | 整数型(十进制) |
c | Unicode字符 |
b | Boolean值 |
s | String |
f | 浮点数(十进制) |
e | 浮点数(科学计数法) |
x | 整数(十进制) |
h | 散列码(16进制) |
% | 字符‘%’ |
各种类型的适用字符
类型 | 可用转换字符 |
---|---|
字符 | c、b、h |
整数 | d、c、b、s、x、h |
浮点数 | b、s、f、e、h |
对象 | b、s、h |
布尔类型 | b、s、h |
b转换对于Boolean和boolean会对应true和false,但是对于其他的数据类型。只要不是null。对应转换的都是true,只要是null输出的是false
5.6 String.format()
想要获得一个格式化的字符串,可以使用”+”和StringBuilder中的append()来构建。但是还有一个更加方便的方法。使用String.format()方法获得一个格式化的字符串。
6、正则表达式
6.1 基础
字符表
字符 | 意义 |
---|---|
B | 指定字符B,可以是其他指定字符 |
\xhh | 十六进制为oxhh的字符串 |
\uhhh | 十六进制表示为oxhhhh的Unicode编码 |
\t | 标识符Tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\e | 转义 |
字符类,字符类是最需要掌握的。掌握字符类之后便可以显示正则表达式的威力
字符类 | 意义 |
---|---|
. | 任意字符 |
[abc] | 包含a、b和c的任何字符。等价于a|b|c |
[^abc] | 除了a、b和c之外的任何字符 |
[a-zA-Z] | 从a到z或A到Z的任何字符(范围) |
[abc[hij]] | 任意a、b、c、h、i和j的字符。等价于a|b|c|h|i|j展开合并 |
[a-z&&[hij]] | 任意h,i或j(交) |
\s | 空白符(空格、tab、换行、换页和回车) |
\S | 非空白字符,等价于[^\s] |
\d | 数字[0-9] |
\D | 非数字[^0-9] |
\w | 词字符[a-zA-Z0-9] |
\W | 非词字符[^\w] |
逻辑操作表
字符 | 意义 |
---|---|
XY | Y跟在X后面 |
X|Y | X或Y |
(X) | 捕获组 (capturing group).可以在表达式中用\i引用第i个捕获组 |
边界匹配表
字符 | 意义 |
---|---|
^ | 一行的起始 |
$ | 一行的结束 |
\b | 词的边界 |
\B | 非词的边界 |
\G | 前一个匹配的结束 |
6.2 创建正则表达式
6.3 量词
量词的主要作用的确定要怎么匹配,有贪婪型、勉强型、占有型。
贪婪型|勉强型|占有型|如何匹配
:-:|:-:|:-:|:-:|:-:
X?|X??|X?+|一个或零个X
X*|X*?|X?+|零个或多个X
X+|X+?|X++| 一个或多个X
X{n}|X{n}?|X{n}+| 恰好n次X
X{n,}|X{n,}?|X{n,}+| 至少n次X
X{n,m}|X{n,m}?|X{n,m}| X至少n次,且不超过m次
6.4 Pattern和Matcher
字符串匹配正则表达式,可以使用
String.matches(pattern)
.例如"Rudolph".matches(pattern)
其中pattern为String pattern = "[rR]udolph"
使用正则表达式的对象处理
1
2
3
4
5
6
7
8// 原始字符
String str = "afafafafagavawe";
// 正则表达式
String pattern = "[af]ag":
// 正则表达式对象
Pattern p = Pattern.complile(str);
// 匹配对象
Matcher m = p.matcher(pattern);
matcher常用方法
方法名 | 解释 |
---|---|
boolean matches() | 整个输入字符串是否匹配正则表达式模式 |
boolean lookAt() | 判断该字符串的始部分是否能够匹配模式 |
boolean find() | 查找多个匹配,每调用一次就会自动向前迭代。 |
boolean find(i) | 查找特定索引的匹配,指定开始搜索的位置 |
int groupCount() | 该匹配器中的分组数目 |
String group(int i) | 返回前一次匹配的指定的组号,没有返回null。没有group(0)等价于group() |
int start(int group) | 返回在前一次匹配操作中寻找到的组的起始索引 |
int end(int group) | 返回在前一次匹配操作中寻找到的组的最后一个字符串索引加一的值。注意是加一!! |
pattern标记
Pattern Pattern.compile(String regex, int flag);
标记的作用的是设定特定模式的匹配规则,.是否匹配行终结符,是否考虑字符串的大小写等。
6.5 split()
和String.split()分隔字符串有相同的作用
1
2
3String input = "This is unusual use!!of exclamtion!!points":
Pattern.compile("!!").split(input);
Pattern.complie("!!").split(input,3);
String[] split(CharSequence input);
// limit 分成几个部分
String[] split(CharSequence input,int limit);
6.6 替换操作
- replaceFirst(String replacement)
- replaceAll(String replacement)
- appendReplacement(StringBuffer sbuf,String replacement) 渐进制替换
- appendTail(StringBuffer sbuf);
说明StringBuffer sbuf是存最后的结果的。这种编程模式将输入参数作为输出参数。6.7 reset()
通过reset() 方法,可以将现有的Matcher对象应用于一个新的字符串序列。简单来说就是替换掉找到的matcher对象的值。6.8 正则表达式与Java I/O
核心思想,读取文件中的文件,按照行数生成一个字符串数组。然后再对这字符串数组进行正则表达式处理。先读取完成再处理会比边读取边处理效率更高。7、扫描输入
Scanner 的构造器可以接收File对象,InputStream,String或者Readable对象。除了char类型外,其它的基本数据类型都有相应的next方法。hasNext方法判断下一个输入分词是否有相应的数据类型。7.1 Scanner定界符
默认情况下,Scanner的是使用空白字符作为定界符的。也可以使用正则表达式指定自己的所需的定界符。需要使用useDelimiter(),例如:scanner.useDelimiter("\\s*,\\s*");
使用逗号作为分界符。7.2 用正则表达式扫描
使用scanner的next()方法可以使用正则表达式来获得相应的匹配,使用scanner.match()
获得匹配的数据。配合使用正则表达式扫描时,仅仅针对下一个输入分词进行匹配。如果正则表达式中含有定界符,那永远都不可能匹配成功。//因为扫描分词的时候已经将定界符处理了,已经按照定界符处理过了,得到的数据里面一定没有定界符。在得到的数据里面使用定界符进行匹配一定找不到数据。1
2
3
4
5
6scanner.next(pattern);
MatcherResult match = scanner.match();
// 获得匹配的第一个数据
String data1 = match.group(1);
// 获得匹配的第二个数据
String data2 = match.group(2);8、StringTokenizer
这是是没有引入Scanner类的时候对字符串的处理,但是现在已经有Scanner类了,StringTokenizer可以认为废弃了。功能什么的都不比Scanner+正则表达式强大。