Loading...
# 第十三章 字符串
**可以证明,字符串操作是计算机程序设计中最常见的行为。**
-----
### 1. 不可变 String
* String 对象是不可变的。String 类中的每一个看起来会修改 String 值得方法,实际上都是创建了一个全新的 String 对象,以包含修改后的字符串内容。而最初的 String 对象则丝毫未动。
```java
package Thirteen;
public class OneTest {
public static String upcase(String s){
return s.toUpperCase() ;
}
public static void main(String[] args) {
String q = "thirteen" ;
System.out.println(q);
String qq = upcase(q) ;
System.out.println(qq);
System.out.println(q);
}
}
output
thirteen
THIRTEEN
thirteen
```
* 当把 q 传给 upcase() 方法的时候,实际传递的是引用的一个拷贝。其实每当把 String 对象作为方法的参数的时候,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。
* 传入 upcase() 的引用有了名字 s ,只有 upcase() 运行的时候,局部引用 s 才存在,一旦 upcase() 运行结束, s 就消失了。 upcase() 的返回值,也只是最终结果的引用。这些都足以说明, upcase() 返回的引用已经指向了一个新的对象,而原本的 q 其实还在原地。
* 对一个方法而言,参数是为该方法提供信息的,而不是想让该方法改变自己的。
---
### 2. 重载 “ + ” 与 SringBuilder
* String 对象是不可变的 ,你可以给一个 String 对象加任意多的别名。 因为 String 对象具有只读特性,所以指向它的任何引用都不可能改变它的值,因此,也就不会对其他的引用有什么影响,
* 重载的意思是:一个操作符在应用于特定的类时,被赋予了特殊的意义。(用于 String 的 “ + ” 与 “ += ” 是 java 中仅有的两个重载过的操作符,而 java 并不允许程序员重载任何操作符 )
* 通过 “ + ” 操作符 连接 String:
```java
package Thirteen;
public class TwoTest {
public static void main(String[] args) {
String mango = "mango" ;
String s = "abc" + mango + "def" + 47 ;
System.out.println(s) ;
}
}
///output
abcmangodef47
```
* 可以使用 JDK 自带的工具 javap 来反编译以上代码: javap -c TwoTest , -c 表示将生成 JVM 字节码。
* 当使用 “ + ” 的时候,会自动使用 StringBuilder 类。为每个字符串调用一次 StringBuilder 的 append() 方法。最后调用 toString() 生成结果。
* 使用 StringBuilder 还允许你预先为其指定大小,如果想在 toString() 方法中使用循环,那么最好自己创建一个 StringBuilder 对象,用它来构造最终的结果
```java
package Thirteen;
import java.util.Random;
public class ThreeTest {
public static Random rand = new Random(47) ;
public String toString(){
StringBuilder result = new StringBuilder("[") ;
for(int i = 0 ; i< 25 ; i++){
result.append(rand.nextInt(100));
result.append(",") ;
}
result.delete(result.length()-1 , result.length()) ;
result.append("]") ;
return result.toString() ;
}
public static void main(String[] args) {
ThreeTest tts = new ThreeTest() ;
System.out.println(tts);
}
}
/// output
[58,55,93,61,61,29,68,0,22,7,88,28,51,89,9,78,98,61,20,58,16,40,11,22,4]
```
* StringBuffer 提供了丰富全面的方法。包括: insert() , repleace() , substring() , 甚至 reverse() , 但是最常用的还是 append() , toString() ,还有 delete() 方法。
----
### 3. 无意识的递归
* java 中的每个类从根本上都是继承自 Object , 标准类容器也不例外,因此容器类都有 toString() 方法,并且覆写了这个方法,使得生成的 String 结果能够表达容器本身,以及容器所包含的对象。
```java
......
return "xxxx " + this ;
.....
```
* 并不会返回想要的结果,因为编译器看到前面是一个 String 类型,后面跟一个 “ + ” ,会尝试把 this 换成 String 类型。想正确调用 this 需要调用 super.toString() 方法。
---
### 4. String 上的操作
以下是 String 对象的一些基本方法。
|方法 | 应用|
|:--|:--|
|构造器|创建 String 对象|
|length()|String 中字符的个数|
|charAt()|取得 String 中该索引位置上的 char|
|getChars(),getBytes()|复制 char 或者 byte 到一个目标数组中|
|toCharArray()|生成一个 char[] , 包含 String 的所有字符|
|equals(),equalsIgnoreCase()|比较两个 String 内容 是否相同|
|compareTo()|按词典顺序比较 String 的内容,比较结果为负数、零或正数。注意,大小写并不等价|
|contains()|如果该 String 对象包含参数的内容,则返回 true|
|contentEquals()|如果该 String 与参数内容完全一致,则返回 true|
|equalsIgnoreCase()|忽略大小写,如果两个 String 内容相同,则返回 true|
|regionMatcher()|返回 boolean 结果,已表明所比较区域是否相等|
|startsWith()|返回 boolean 结果,以表明该 String 是否 以此参数开始|
|endsWith()|返回 boolean 类型结果,以表明此参数是否该字符串的后缀|
|indexOf(),lastIndexOf()|如果该 String 并不包含此参数,就返回 -1 ;否则返回此参数在 String 中的起始索引。lastIndexOf() 是从后向前搜索|
|substring(subSequence())|返回一个新的 String ,以包含参数指定的子字符串|
|concat()|返回一个新的 String 对象,内容为原始 String 连接上参数 String|
|replace()|返回替换字符后的新 String 对象。如果替换没有发生,则返回原始的 String 对象|
|toLowerCasetoUpperCase()|将字符的大小写改变以后,返回一个新的 String 对象,如果没有改变发生,则返回原始的 String 对象|
|trim() | 将 String 两端的空白字符删除后,返回一个新的 String对象, 如果改变没有发生,则返回原始的 String 对象 |
|vaueOf()|返回一个表示参数内容的 String |
|intern| 为每个唯一的字符序列生成一个且仅生成一个 String 引用|
* 可以看出,当 需要改变字符串的内容时,String 类的方法都会返回一个新的 String 对象。如果没有发生改变, String 的方法只是返回指向原对象的引用而已,这可以节约存储空间以及避免额外的开销。
---
murong mengjie, [27.06.18 09:36]
### 5. 格式化输出
#### 5.1. printf()
* printf() 虽然不使用重载的 “ + ” 操作符,但是可以使用特殊的 占位符来表示数据将来的位置,还可以将插入格式化字符串的参数。这些占位符叫做 *格式修饰符*
printf("Row 1: [%d %f\n]" , x,y ) ;
---
#### 5.2. System.out.format()
* format 方法可用于 PrintStream 或者 PrintWriter 对象,其中也包括 System.out 对象。 format() 方法模仿自 C 的 printf()
* format() 和 printf() 是等价的,他们只需要一个简单的格式化字符串,加上一串参数即可,每个参数对应一个格式修饰符。
---
#### 5.3. Formatter 类
* 所有的格式化功能都由 java.util.Formatter 处理,可以将 Formatter 看做一个翻译器,他将你的格式化字符串与数据翻译成需要的结果。
---
#### 5.4. 格式化说明符
* 如果想要控制空格与对齐等等,需要更加精细复杂的格式修饰符,同样是使用的 Formatter
```java
//语法:
%[argument_index$][flags][width][.precision]conversion
```
---
####5. 5. Formatter 转换
* 下面的表格包含了最常用的类型转换:
|类型转换字符||
|--|--|
|d|整数型(十进制)|
|c|Unicode字符|
|b|boolean 值|
|s|String|
|f|浮点数(十进制)|
|e|浮点数(科学计数)|
|x|正数(十六进制)|
|h|散列码(十六进制)|
|%|字符“ % ”|
* 对于 b 转换,基本类型是 boolean 基本类型或者 Boolean 对象,其转换结果是对应的 true 或者 false ,但是对于其他类型的参数,只要不为 null ,全部都是 true ,即便是0 。
----
####5. 6. String.format
* String.format() 是一个 static 方法,接受和 Formatter。format() 方法一样的参数,但是返回一个 String 对象。
* 相当于 C 中的 sprintf() 方法。
* 一个十六进制转储( dump ) 工具:
```java
package Thirteen;
import java.io.File;
public class HexTest {
public static String format(byte[] date){
StringBuilder result = new StringBuilder() ;
int n = 0 ;
for(byte b : date){
if(n % 16 == 0){
result.append(String.format("%05X: ",n)) ;
}else{
result.append(String.format("%02X : " ,b)) ;
}
n++ ;
if(n % 16 == 0){
result.append("\n") ;
}
}
result.append("\n");
return result.toString() ;
}
public static void main(String[] args) throws Exception{
if(args.length == 0){
System.out.println(format(BinaryFile.read("xxx.class")));
}else{
System.out.println(format(BinaryFile.read(new File(args[0]))));
}
}
}
```
----
### 6.正则表达式
* 是用正则表达式可以使我们以编程的方式,构造复杂的文本模式,并对输入的字符串进行搜索。
---
#### 6.1 基础
* 一般来说,正则表达式就是以某种方式来描述字符串。也就是:如果一个字符串包含有这些东西,那么就正是我在找的东西。
* 找一个负号在前面的数字 : -?
* 用 \d 表示 一位数字
* 在 其他语言中 , \\\\ 表示我想在正则表达式中插入一个普通的 反斜线,请不要给它任何特殊的意义。在 java 中 \\\\ 的意思是我要插入一个正则表达式的反斜线,所以其后面的字符具有特殊的意义。比如像表示一位数字,那么正则表达式应该是:**\\\\d** ,如果想插入一个普通的反斜线,那么应该写成:**\\\\\\\\** 不过换行和制表符之类的东西只需使用单反斜线: \n\t 。
* 可能有一个负号,后面跟着一位或多位数字: -?\\\\d+
* 应用正则表达式的最简单的途径,就是使用 String 类内建 的 功能。
```java
package Thirteen;
public class IntegerMatch {
public static void main(String[] args) {
System.out.println("-1234".matches("-?\\d+"));
System.out.println("5678".matches("-?\\d+"));
System.out.println("+564".matches("-?\\d+"));
System.out.println("+911".matches("(-|\\+)?\\d+"));
}
}
// output
true
true
false
true
```
* 可能以一个加号或者减号开头,在正则表达式中,括号有着将表达式分组的效果,而竖直线 | 则表示或操作: ( -|\\\\+ )? ,表示字符串的起始字符可能是一个 - 或者 + 或者两个都没有,因为跟着 ? 修饰符 , 因为 + 在正则表达式中有着特殊的意义,所以必须使用转义字符 \\\\ 将其转义为普通字符
* split() 方法 ,功能是将字符串从正则表达式匹配的地方切开
* replaceFirst() 方法,只替换正则表达式第一个匹配的字串
* replaseAll() ,替换所有匹配的地方。
* \W 意思是非单词字符
* \w 意思是一个单词字符。
* f\\\\w+ 以字母 f 开头,后面跟 一个或多个字母。
---
#### 6.2 创建正则表达式
|字符||
|:-- |:-- |
|B|指定字符 B|
|\xhh|十六进制值为 oxhh 的字符|
|\uhhhh|十六进制表示为 oxhhhh 的 Unicode 字符|
|\t|制表符 Tab|
|\n|换行符|
|\r|回车|
|\f|换页|
|\e|转义(Escape)|
|字符类||
|:--|:--|
|.|任意字符|
|[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)|
|边界匹配符||
|:--|:--|
|^|一行的起始|
|$|一行的结束|
|\b|词的边界|
|\B|非词的边界|
|\G|前一个匹配的结束|
---
####6.3. 量词
量词描述了一个模式吸收输入文本的方式
* 贪婪型:量词总是贪婪的。贪婪表达式会为所有可能的模式发现尽可能多的匹配。
* 勉强型:用问号来指定,这个量词匹配满足模式所需的最少字符数,因此也被称作懒惰的、最少匹配的、不贪婪的。
* 占有型:防止正则表达式失控。
* 表达式应该用圆括号括起来,以便他能够按照我们期望的效果去执行。例如: abc+ ,看起来是:匹配一个或多个 abc 序列,但实际上表示的是:匹配 ab ,后面跟随一个或者多个 c。正确的应该这样写:( abc ) +
* CharSequence ,接口 CharSequence 从 CharBuffer 、String 、 StringBuffer 、 StringBuilder 类之中抽象出了字符序列的一般化定义:
```java
interface CharSeuence{
charAt(int i) ;
length();
subSequence(int start , int end );
toString() ;
}
```
----
#### 6.4 Pattern 和 Matcher
* 根据传入的 String 类型 生成一个 Pattern 对象。将要检索的字符串传入 Pattern 对象的 ,matcher() 方法。 matcher() 方法会生成一个 Matcher 对象。
java
Pattern p = Pattern.compile(arg) ;
Matcher m = p.matcher(args[0]) ;
* Matcher 对象上的方法:
1. boolean matchers() ; //判断整个输入字符串是否匹配正则表达式
2. boolean lookingAt() ; //判断字符串的始部分是否能够匹配模式
3. boolean find() ;
4. boolean find(int start) ;
* Matcher.find() 方法可以用来在 CharSequence 中查找多个匹配。
* \\\\w+ 可以将字符串划分为单词。
```java
package Thirteen;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class IntegerMatch {
public static void main(String[] args) {
Matcher m =Pattern.compile("\\w+").matcher("Evening is full of the linnet");
while(m.find()){
// 像迭代器那样前向遍历输入字符串
System.out.println(m.group()+"");
}
System.out.println();
int i = 0 ;
while(m.find(i)){
//接受一个整数作为参数,整数表示字符串中字符的位置,以此作为搜索的起点
System.out.println(m.group()+"");
i++ ;
}
}
}
```
* 组(Group) 是用括号划分的正则表达式,可以根据组的编号来引用某个组。组号为 0 表示整个表达式,组号 1 表示第一队括号括起来的组。以此类推。
A ( B ( C ) ) D
三个组 ,组0 是 ABCD , 组1 是 BC ,组 2是 C
* 在匹配操作成功后, strat() 返回先前匹配的起始位置的索引, end () 返回尝试所匹配的最后字符的索引加一的值。
---
#### 6.5 split()
* split() 方法将输入字符串断开成字符串对象数组,断开边界由正则表达式确立。
---
#### 6.6 替换操作
* 正则表达式特别便于替换文本,提供了很多的方法。
---
### 7. 扫描输入
* Scanner类的构造器可以接受任何类型的输入对象。
* 默认情况下,Scanner 根据空白字符对输入的内容进行分词,但是你可以用正则表达式指定自己所需的定界符。
```java
Scanner scanner = new Scanner("12,13,14");
scanner.useDelimit("\\s*,\\s*") ;
* 使用正则表达式:
java
Scanner scanner = new Scanner("xxx");
String pattern = "(\\d+[.]\\d+[.])" ;
while(scanner.hasNext(pattern)){
/// Code
}
```
---
### 8. String Tokenizer
* 分隔字符串,不过有了 Scanner 之后已经废弃掉了。
-------
**由于时间原因,这本书之后就没再做什么笔记了**
-------
由本人从 Thinking in java ( java 编程思想 ) 整理而来
最后修改:2018 年 07 月 15 日 02 : 45 PM
© 允许规范转载
赞赏
打赏?不存在的= =
×Close
赞赏作者
扫一扫支付
支付宝支付
微信支付