字符串的不可变
String这个类是final修饰的, 继承Object, String对象时不变的, 不论任何修改String对象的方法,其实都是创建一个新的String, 原始的String对象纹丝不动
// strings/Immutable.java
public class Immutable {
public static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[] args) {
String q = "howdy";
System.out.println(q); // howdy
String qq = upcase(q);
System.out.println(qq); // HOWDY
System.out.println(q); // howdy
}
}
/* Output:
howdy
HOWDY
howdy
当我们把p作为参数传给upcase()方法时, 实际上传的是一个引用的拷贝,而该引用指向的对象其实一直待在单一的物理位置没变过, 像赋值 也是传引用
回到这个demo中, p的引用拷贝一份 为s 传给upcase(), 运行结束后 s消失, 新产生的返回值返回一个新的对象的引用
+
的重载与 StringBuilder
String对象时不可改变的, 你可以给一个String对象添加任意多的别名, 因为String是只读的, 所以指向他的任何引用都不能改变其值, 因此也不会改变其他引用读取它的值
“+”这个操作符为String对象重载过, 重载的意义也就是说一个操作符在不同的类中被赋予了不同的作用, 在java中只有+ 跟 += 是被重载过的操作符, 其它的操作符是不允许重载的
// strings/Concatenation.java
public class Concatenation {
public static void main(String[] args) {
String mango = "mango";
String s = "abc" + mango + "def" + 47;
System.out.println(s);
}
}
/* Output:
abcmangodef47
*/
通过javap反编译 javap -c Concatenation 可以看出
javac,java,javap
javac - javac.exe(java编译器)
.java结尾的代码是java源码, 源码会经过jvm编译器编译成.class(字节码 - 特殊的二进制文件,因为java底层是c跟c++写的)文件, 而我们的javac这个指令的作用就是生成对应的字节码文件, 字节码才能在java虚拟机上运行
java -java.exe解释器
经过javac编译后的.class文件传到java解释器中, 这个指令是运行字节码文件, 把字节码一行一行的翻译(翻译顾名思义,将字节码转成运行机器能识别的机器码), 怎么理解这个一行一行的翻译呢, 也就是说翻译一行, 然后去机器里执行, 执行完了再翻译,然后再去机器里执行,这样执行的速度很慢的
JIT即时翻译技术
为了解决这个慢的问题 ,就出现了即时翻译技术(JIT)
这种技术在运行的时候直接将全部或者部分字节码转换成机器码, 而不用jvm去翻译字节码, 这样来提升速度.
它会在运行时将字节码装入内存中,然后有jit来进行翻译,将字节码翻译成机器码,但是如果全部代码都用jit的话就会产生两个影响,第一是所有代码都编译肯定浪费时间, 第二个就是这样的话会增加代码的长度,因为机器码是比字节码长的,这样会导致页面调度,也会降低速度
为了解决这个问题, 新版的jdk采用了hotSpot技术(用惰性评估策略-需要运行的代码才会被翻译, 不需要的不会)
代码第一次运行的时候 编译->翻译->运行. 第二次的时候 编译->运行 , 这样的话代码反复运行,也不会再多浪费时间去翻译
这里粗略介绍jit https://developer.ibm.com/zh/articles/j-lo-just-in-time/ 这个更具体介绍
javap - java字节码分解器/反编译器
这个指令运行字节码,类似于java的功能
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=3, Args_size=1
0: ldc #2; //String mango
2: astore_1
3: new #3; //class StringBuilder
6: dup
7: invokespecial #4; //StringBuilder."<init>":()
10: ldc #5; //String abc
12: invokevirtual #6; //StringBuilder.append:(String)
15: aload_1
16: invokevirtual #6; //StringBuilder.append:(String)
19: ldc #7; //String def
21: invokevirtual #6; //StringBuilder.append:(String)
24: bipush 47
26: invokevirtual #8; //StringBuilder.append:(I)
29: invokevirtual #9; //StringBuilder.toString:()
32: astore_2
33: getstatic #10; //Field System.out:PrintStream;
36: aload_2
37: invokevirtual #11; //PrintStream.println:(String)
40: return
编译器自动引入了 java.lang.StringBuilder
类。虽然源代码中并没有使用 StringBuilder
类,但是编译器却自作主张地使用了它,就因为它更高效。
一共调用了4次的append
结论就是如果使用单纯的string , 它会经常创建对象, 而直接用StringBuilder的话,是只创建了一个, 而且使代码更加简洁, StringBuffer相比StringBuilder来说 是线程安全的, 因此也就相对的会有一些效率上的开销
具体要用String, 还是StringBuffer,或是StringBuilder 可以通过javap反编译来看
意外递归
因为所有的类其实都是集成自Object类, 他们都有toString方法, 基本上都重写了toString方法
如果你希望 toString()
打印出类的内存地址,也许你会考虑使用 this
关键字:
// strings/InfiniteRecursion.java
// Accidental recursion
// {ThrowsException}
// {VisuallyInspectOutput} Throws very long exception
import java.util.*;
import java.util.stream.*;
public class InfiniteRecursion {
@Override
public String toString() {
return " InfiniteRecursion address: " + this + "\n"
}
public static void main(String[] args) {
Stream.generate(InfiniteRecursion::new)
.limit(10)
.forEach(System.out::println);
}
}
上面这个demo就会抛异常, 因为在toString的时候 返回的是字符串拼接, this是代表这个类, 当编译执行”+”操作时,就需要找到this对应的toString,来输出对应的类信息, 它就会继续调用这个toString方法 造成递归
所以说最好用的get到内存地址的方法还是调用Object.toString,也就是不重写toString, 直接打印就好了
字符串操作
方法 | 参数,重载版本 | 作用 |
---|---|---|
构造方法 | 默认版本,String ,StringBuilder ,StringBuffer ,char 数组,byte 数组 |
创建String 对象 |
length() | String 中字符的个数 |
|
charAt() | int 索引 |
获取String 中索引位置上的char |
getChars() ,getBytes() |
待复制部分的开始和结束索引,复制的目标数组,目标数组的开始索引 | 复制char 或byte 到一个目标数组中 |
toCharArray() | 生成一个char[] ,包含String 中的所有字符 |
|
equals() ,equalsIgnoreCase() |
与之进行比较的String |
比较两个String 的内容是否相同。如果相同,结果为true |
compareTo() ,compareToIgnoreCase() |
与之进行比较的String |
按词典顺序比较String 的内容,比较结果为负数、零或正数。注意,大小写不等价 |
contains() |
要搜索的CharSequence |
如果该String 对象包含参数的内容,则返回true |
isEmpty() | 返回boolean 结果,以表明String 对象的长度是否为0 |
|
regionMatches() | 该 String 的索引偏移量,另一个String 及其索引偏移量,要比较的长度。重载版本增加了“忽略大小写”功能 |
返回boolean 结果,以表明所比较区域是否相等 |
startsWith() | 可能的起始String 。重载版本在参数中增加了偏移量 |
返回boolean 结果,以表明该String 是否以传入参数开始 |
endsWith() | 该String 可能的后缀String |
返回boolean 结果,以表明此参数是否是该字符串的后缀 |
indexOf() ,lastIndexOf() |
重载版本包括:char ,char 与起始索引,String ,String 与起始索引 |
如果该String 并不包含此参数,就返回-1;否则返回此参数在String 中的起始索引。lastIndexOf ()是从后往前搜索 |
matches() | 一个正则表达式 | 返回boolean 结果,以表明该String 和给出的正则表达式是否匹配 |
split() | 一个正则表达式。可选参数为需要拆分的最大数量 | 按照正则表达式拆分 String ,返回一个结果数组 |
join() (Java8引入的) |
分隔符,待拼字符序列。用分隔符将字符序列拼接成一个新的String |
用分隔符拼接字符片段,产生一个新的String |
substring() (即subSequence() ) |
重载版本:起始索引;起始索引+终止索引 | 返回一个新的 String 对象,以包含参数指定的子串 |
concat() | 要连接的String |
返回一个新的String 对象,内容为原始String 连接上参数String |
replaceFirst() | 要替换的正则表达式,用来进行替换的String |
返回替换首个目标字符串后的String 对象 |
replaceAll() | 要替换的正则表达式,用来进行替换的String |
返回替换所有目标字符串后的String 对象 |
toLowerCase() ,toUpperCase() |
将字符的大小写改变后,返回一个新的String 对象。如果没有任何改变,则返回原始的String 对象 |
|
trim() | 将String 两端的空白符删除后,返回一个新的String 对象。如果没有任何改变,则返回原始的String 对象 |
|
valueOf() (static ) |
重载版本:Object ;char[] ;char[] ,偏移量,与字符个数;boolean ;char ;int ;long ;float ;double |
返回一个表示参数内容的String |
intern() |
为每个唯一的字符序列生成一个且仅生成一个 String 引用 |
|
format() |
要格式化的字符串,要替换到格式化字符串的参数 |
返回格式化结果String |
从这个表中也可以看的出,凡是需要改变String内容的 都是直接返回一个新的String,而不改变内容的则直接返回该对象的引用
格式化输出
JavaSE5引用了printf()—>System.out.printf(“Row 1: [%d %f]%n”, x, y);
%d代表整数. %f代表浮点小数
这一行代码在运行的时候,首先将 x
的值插入到 %d_
的位置,然后将 y
的值插入到 %f
的位置。这些占位符叫做格式修饰符,它们不仅指明了插入数据的位置,同时还指明了将会插入什么类型的变量,以及如何格式化。在这个例子中 %d
表示 x
是一个整数,%f
表示 y
是一个浮点数(float
或者 double
)。
format()跟printf()的作用一致
// strings/SimpleFormat.java
public class SimpleFormat {
public static void main(String[] args) {
int x = 5;
double y = 5.332542;
// The old way:
System.out.println("Row 1: [" + x + " " + y + "]");
// The new way:
System.out.format("Row 1: [%d %f]%n", x, y);
// or
System.out.printf("Row 1: [%d %f]%n", x, y);
}
}
/* Output:
Row 1: [5 5.332542]
Row 1: [5 5.332542]
Row 1: [5 5.332542]
*/
Formatter
类
java中所有的格式化动作都是由java.uitl.formatter类处理的,formatter可以看成是一个翻译器,它将你的格式化字符串与数据翻译成你需要的格式, 当你创建一个对象的时候需要给它的构造器传入一些信息,告诉它结果将输出向哪里
import java.io.*;
import java.util.*;
public class Turtle {
private String name;
private Formatter f;
public Turtle(String name, Formatter f) {
this.name = name;
this.f = f;
}
public void move(int x, int y) {
f.format("%s The Turtle is at (%d,%d)%n",
name, x, y);
}
public static void main(String[] args) {
PrintStream outAlias = System.out;
Turtle tommy = new Turtle("Tommy",
new Formatter(System.out));
Turtle terry = new Turtle("Terry",
new Formatter(outAlias));
tommy.move(0,0);
terry.move(4,8);
tommy.move(3,4);
terry.move(2,5);
tommy.move(3,3);
terry.move(3,3);
}
}
/* Output:
Tommy The Turtle is at (0,0)
Terry The Turtle is at (4,8)
Tommy The Turtle is at (3,4)
Terry The Turtle is at (2,5)
Tommy The Turtle is at (3,3)
Terry The Turtle is at (3,3)
*/
格式化修饰符 %s
表明这里需要 String
参数。
注意这个demo, 可以看到new Formatter()的传入构造参数, 作为输出的路径
所有的 tommy
都将输出到 System.out
,而所有的 terry
则都输出到 System.out
的一个别名中。Formatter
的重载构造器支持输出到多个路径,不过最常用的还是 PrintStream()
(如上例)、OutputStream
和 File
。你可以在 附录:流式 I/O 中了解更多信息。
格式化修饰符
在插入数据时,如果你想要优化空格与对齐,你需要复杂的格式化修饰符,一下是其通用无法
%[argument_index$][flags][width][.precision]conversion
其中width是控制字段的最小长度, formatter对象会通过添加空格来控制长度,默认情况下数据是右对齐的,可以通过”-“来控制对齐方向
与width相对的是precision.用于指定最大长度,但是区别是,width可以应用于各种类型的数据转换,而precision应用于String时它标识String的最大长度,但是用于浮点数时,用于表示小数部分要显示出的位数(默认是6位),如果小数位数过多则舍入,如果位数少则补零,由于整数没有小数部分,所以precision无法应用于整数,如果你对整数应用precision则会抛异常
// strings/ReceiptBuilder.java
import java.util.*;
public class ReceiptBuilder {
private double total = 0;
private Formatter f =
new Formatter(new StringBuilder());
public ReceiptBuilder() {
f.format(
"%-15s %5s %10s%n", "Item", "Qty", "Price");
f.format(
"%-15s %5s %10s%n", "----", "---", "-----");
}
public void add(String name, int qty, double price) {
f.format("%-15.15s %5d %10.2f%n", name, qty, price);
total += price * qty;
}
public String build() {
f.format("%-15s %5s %10.2f%n", "Tax", "",
total * 0.06);
f.format("%-15s %5s %10s%n", "", "", "-----");
f.format("%-15s %5s %10.2f%n", "Total", "",
total * 1.06);
return f.toString();
}
public static void main(String[] args) {
ReceiptBuilder receiptBuilder =
new ReceiptBuilder();
receiptBuilder.add("Jack's Magic Beans", 4, 4.25);
receiptBuilder.add("Princess Peas", 3, 5.1);
receiptBuilder.add(
"Three Bears Porridge", 1, 14.29);
System.out.println(receiptBuilder.build());
}
}
/* Output:
Item Qty Price
---- --- -----
Jack's Magic Be 4 4.25
Princess Peas 3 5.10
Three Bears Por 1 14.29
Tax 2.80
-----
Total 49.39
*/
“%-15.15s : -代表右对齐, 15代表最小长度, “.15”代表最大长度, s代表字符串
%10.2f%n : 左对齐, 10代表最小长度,”.2f”代表浮点型两位小数,%n代表换行
“%-15.15s %5d %10.2f%n” 他们之间的空格就会展示成空格
正如你所见,通过相当简洁的语法,Formatter
提供了对空格与对齐的强大控制能力。在该程序中,为了恰当地控制间隔,格式化字符串被重复利用了多遍。
下面的表格展示了最常用的类型转换:
类型 | 含义 |
---|---|
d | 整型,十进制 |
c | Unicode字符 |
b |
Boolean值 |
s | String |
f | 浮点型(十进制) |
e |
浮点数(科学计数) |
x |
整型(十六进制) |
h | 散列码(十六进制) |
% | 字面值“%” |
// strings/Conversion.java
import java.math.*;
import java.util.*;
public class Conversion {
public static void main(String[] args) {
Formatter f = new Formatter(System.out);
char u = 'a';
System.out.println("u = 'a'");
f.format("s: %s%n", u);
// f.format("d: %d%n", u);
f.format("c: %c%n", u);
f.format("b: %b%n", u);
// f.format("f: %f%n", u);
// f.format("e: %e%n", u);
// f.format("x: %x%n", u);
f.format("h: %h%n", u);
int v = 121;
System.out.println("v = 121");
f.format("d: %d%n", v);
f.format("c: %c%n", v);
f.format("b: %b%n", v);
f.format("s: %s%n", v);
// f.format("f: %f%n", v);
// f.format("e: %e%n", v);
f.format("x: %x%n", v);
f.format("h: %h%n", v);
BigInteger w = new BigInteger("50000000000000");
System.out.println(
"w = new BigInteger(\"50000000000000\")");
f.format("d: %d%n", w);
// f.format("c: %c%n", w);
f.format("b: %b%n", w);
f.format("s: %s%n", w);
// f.format("f: %f%n", w);
// f.format("e: %e%n", w);
f.format("x: %x%n", w);
f.format("h: %h%n", w);
double x = 179.543;
System.out.println("x = 179.543");
// f.format("d: %d%n", x);
// f.format("c: %c%n", x);
f.format("b: %b%n", x);
f.format("s: %s%n", x);
f.format("f: %f%n", x);
f.format("e: %e%n", x);
// f.format("x: %x%n", x);
f.format("h: %h%n", x);
Conversion y = new Conversion();
System.out.println("y = new Conversion()");
// f.format("d: %d%n", y);
// f.format("c: %c%n", y);
f.format("b: %b%n", y);
f.format("s: %s%n", y);
// f.format("f: %f%n", y);
// f.format("e: %e%n", y);
// f.format("x: %x%n", y);
f.format("h: %h%n", y);
boolean z = false;
System.out.println("z = false");
// f.format("d: %d%n", z);
// f.format("c: %c%n", z);
f.format("b: %b%n", z);
f.format("s: %s%n", z);
// f.format("f: %f%n", z);
// f.format("e: %e%n", z);
// f.format("x: %x%n", z);
f.format("h: %h%n", z);
}
}
/* Output:
u = 'a'
s: a
c: a
b: true
h: 61
v = 121
d: 121
c: y
b: true
s: 121
x: 79
h: 79
w = new BigInteger("50000000000000")
d: 50000000000000
b: true
s: 50000000000000
x: 2d79883d2000
h: 8842a1a7
x = 179.543
b: true
s: 179.543
f: 179.543000
e: 1.795430e+02
h: 1ef462c
y = new Conversion()
b: true
s: Conversion@15db9742
h: 15db9742
z = false
b: false
s: false
h: 4d5
*/
这里值得注意的是 b 针对布尔值是 分true,或false 针对别的,只要不为空皆为true
还有许多其他的修饰符,具体看java.util.formatter
String.format()
它内部其实就是创建了一个formatter对象来实现的, 它只返回string对象,它能接收跟Formatter.format()一样的入参,String.format()是static方法
// strings/DatabaseException.java
public class DatabaseException extends Exception {
public DatabaseException(int transactionID,
int queryID, String message) {
super(String.format("(t%d, q%d) %s", transactionID,
queryID, message));
}
public static void main(String[] args) {
try {
throw new DatabaseException(3, 7, "Write failed");
} catch(Exception e) {
System.out.println(e);
}
}
}
/*
Output:
DatabaseException: (t3, q7) Write failed
*/
正则表达式
基础
一般来说正则表达式就是以某种方式来描述字符串,”如果一个字符串含有某些东西, 那么这个字符串就是我们想找的东西”
-?
如果你要找一个负数字, 就”-“ 加 “?”
正则里/d代表一位数字, 但是java中的//代表/ 所以在java中一位数用//d表示, 但是换行还是用/n
要表示一个或者多个之前的表达式就用”+”
-?\\d+
上面这个demo就代表有可能带符号,一位或者多位的数字
应用正则表达式最简单的途径就是用于String类的内建功能
// strings/IntegerMatch.java
public class IntegerMatch {
public static void main(String[] args) {
System.out.println("-1234".matches("-?\\d+"));
System.out.println("5678".matches("-?\\d+"));
System.out.println("+911".matches("-?\\d+"));
System.out.println("+911".matches("(-|\\+)?\\d+"));
}
}
/* Output:
true
true
false
true
*/
从上面这个demo可以看出+911 不满足-?\d+
可能以一个+或-开头,用|链接
(-|\\+)?
因为+在java中有特殊的含义,所以必须通过\\进行转译
String
类还自带了一个非常有用的正则表达式工具——split()
方法,其功能是“将字符串从正则表达式匹配的地方切开。”
// strings/Splitting.java import java.util.*;
public class Splitting {
public static String knights =
"Then, when you have found the shrubbery, " +
"you must cut down the mightiest tree in the " +
"forest...with... a herring!";
public static void split(String regex) {
System.out.println(
Arrays.toString(knights.split(regex)));
}
public static void main(String[] args) {
split(" "); // Doesn't have to contain regex chars
split("\\W+"); // Non-word characters
split("n\\W+"); // 'n' followed by non-words
}
}
/* Output:
[Then,, when, you, have, found, the, shrubbery,, you,
must, cut, down, the, mightiest, tree, in, the,
forest...with..., a, herring!]
[Then, when, you, have, found, the, shrubbery, you,
must, cut, down, the, mightiest, tree, in, the, forest,
with, a, herring]
[The, whe, you have found the shrubbery, you must cut
dow, the mightiest tree i, the forest...with... a
herring!]
*/
\W 代表非单词字符,注意是大写的W, 小写的w就代表单词字符
创建正则表达式
表达式 | 含义 |
---|---|
B | 指定字符B |
\xhh | 十六进制值为0xhh 的字符 |
\uhhhh | 十六进制表现为0xhhhh 的Unicode字符 |
\t | 制表符Tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\e | 转义(Escape) |
. | 任意字符 |
[abc] |
包含a或b或c的任意字符 |
[^abc] |
不包含a且不包含b且不包含c的任意字符 |
[a-zA-Z] |
a到z或A到Z的任意字符(范围) |
[abc[hij]] |
a,b,c,h,i,j中的任意字符 |
[a-z&&[hij]] |
h,i,j中的任意字符 |
\s |
空白符(空格、tab、换行、换页、回车) |
\S |
非空白符 |
\d |
数字[0,9] |
\D |
非数字[^0,9] |
\w |
词字符([a-zA-Z_0-9] ) |
\W |
非词字符([^\w] ) |
这里只列出了部分常用的表达式,具体在JDK文档中 java.util.regex.Pattern
逻辑操作符 | 含义 |
---|---|
XY | Y跟在X后面 |
X|Y | X或Y |
(x) | 捕获组,可以在表达式中用\i 引用第i个捕获组 |
边界匹配符
边界匹配符 | 含义 |
---|---|
^ | 一行的开始 |
$ | 一行的结束 |
\b | 词的边界 |
\B |
非词的边界 |
\G | 前一个匹配的结束 |
量词
量词是用来描述捕获输入文本的方式
- 贪婪型: 量词总是贪婪的,除非有其他的选项被设置。贪婪表达式会为所有可能的模式发现尽可能多的匹配。导致此问题的一个典型理由就是假定我们的模式仅能匹配第一个可能的字符组,如果它是贪婪的,那么它就会继续往下匹配。
- 勉强型: 用问号来指定,这个量词匹配满足模式所需的最少字符数。因此也被称作懒惰的、最少匹配的、非贪婪的或不贪婪的。
- 占有型: 目前,这种类型的量词只有在 Java 语言中才可用(在其他语言中不可用),并且也更高级,因此我们大概不会立刻用到它。当正则表达式被应用于
String
时,它会产生相当多的状态,以便在匹配失败时可以回溯。而“占有的”量词并不保存这些中间状态,因此它们可以防止回溯。它们常常用于防止正则表达式失控,因此可以使正则表达式执行起来更高效。
| 贪婪型 | 勉强型 | 占有型 | 如何匹配 | | —- | —- | —- | —- | |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}+
| 至少n次,至多m次X |
应该非常清楚地意识到,表达式 X
通常必须要用圆括号括起来,以便它能够按照我们期望的效果去执行。例如:
abc+
看起来它似乎应该匹配1个或多个abc
序列,如果我们把它应用于输入字符串abcabcabc
,则实际上会获得3个匹配。然而,这个表达式实际上表示的是:匹配ab
,后面跟随1个或多个c
。要表明匹配1个或多个完整的字符串abc
,我们必须这样表示:
正确的写法是
(abc)+
Pattern
和 Matcher
static方法Pattern.compile(正则表达式(String类型)), 这个方法会根据你的正则表达式产生一个pattern对象,调用pattern.matcher(被检验的对象)会生成一个Matcher对象,它有很多功能,比如matcher.replaceAll(),可以把被匹配字符串中匹配正则表达式的字串进行替换,换成你传入的参数
// strings/Finding.java
import java.util.regex.*;
public class Finding {
public static void main(String[] args) {
Matcher m = Pattern.compile("\\w+")
.matcher(
"Evening is full of the linnet's wings");
while(m.find())
System.out.print(m.group() + " ");
System.out.println();
int i = 0;
while(m.find(i)) {
System.out.print(m.group() + " ");
i++;
}
}
}
/* Output:
Evening is full of the linnet s wings
Evening vening ening ning ing ng g is is s full full
ull ll l of of f the the he e linnet linnet innet nnet
net et t s s wings wings ings ngs gs s
*/
“\w+” 贪婪型的满足一个或多个字符, 什么意思呢 传进来的是”Evening is full of the linnet’s wings”这个字符串对吧,他会进行匹配,匹配到E,如果是勉强型就直接返回E,但是贪婪型它不给你返回,它还要继续匹配,直到下一个字符不满足才将结果返回,也就是返回Evening, 而m.find() 这个就类似iterater的next,也就是是否有匹配的子串,返回true或false
而find(i)就是从下标为i开始是否有子字串
组(Groups)
组是用括号划分的正则表达式,可以根据组的编号来引用某个组。组号为 0 表示整个表达式,组号 1 表示被第一对括号括起来的组,以此类推。因此,下面这个表达式
A(B(C))D
中有三个组:组 0 是 ABCD
,组 1 是 BC
,组 2 是 C
。
(A) (B(CD))
上面这个有四组 : 组0是ABCD,组1是A,组2是BCD,组3是CD
Matcher
对象提供了一系列方法,用以获取与组相关的信息:
public int groupCount()
返回该匹配器的模式中的分组数目,组 0 不包括在内。public String group()
返回前一次匹配操作(例如find()
)的第 0 组(整个匹配)。public String group(int i)
返回前一次匹配操作期间指定的组号,如果匹配成功,但是指定的组没有匹配输入字符串的任何部分,则将返回null
。public int start(int group)
返回在前一次匹配操作中寻找到的组的起始索引。public int end(int group)
返回在前一次匹配操作中寻找到的组的最后一个字符索引加一的值。
注意,find()
可以在输入的任意位置定位正则表达式,而 lookingAt()
和 matches()
只有在正则表达式与输入的最开始处就开始匹配时才会成功。matches()
只有在整个输入都匹配正则表达式时才会成功,而 lookingAt()
^5 只要输入的第一部分匹配就会成功。
Pattern
标记 - 略
split()
split()
方法将输入 String
断开成 String
对象数组,断开边界由正则表达式确定:
String[] split(CharSequence input)
String[] split(CharSequence input, int limit)
// strings/SplitDemo.java
import java.util.regex.*;
import java.util.*;
public class SplitDemo {
public static void main(String[] args) {
String input =
"This!!unusual use!!of exclamation!!points";
System.out.println(Arrays.toString(
Pattern.compile("!!").split(input)));
// Only do the first three:
System.out.println(Arrays.toString(
Pattern.compile("!!").split(input, 3)));
}
}
/* Output:
[This, unusual use, of exclamation, points]
[This, unusual use, of exclamation!!points]
*/
替换操作
正则表达式在进行文本替换时特别方便,它提供了许多方法:
replaceFirst(String replacement)
以参数字符串replacement
替换掉第一个匹配成功的部分。replaceAll(String replacement)
以参数字符串replacement
替换所有匹配成功的部分。appendReplacement(StringBuffer sbuf, String replacement)
执行渐进式的替换,而不是像replaceFirst()
和replaceAll()
那样只替换第一个匹配或全部匹配。这是一个非常重要的方法。它允许你调用其他方法来生成或处理replacement
(replaceFirst()
和replaceAll()
则只能使用一个固定的字符串),使你能够以编程的方式将目标分割成组,从而具备更强大的替换功能。appendTail(StringBuffer sbuf)
在执行了一次或多次appendReplacement()
之后,调用此方法可以将输入字符串余下的部分复制到sbuf
中。
通过reset()
reset()
方法,可以将现有的Matcher
对象应用于一个新的字符序列,也就是重新给matcher()对象一个新的被校验字串扫描输入