Java 的 String StringBuilder StringBuffer(上):https://developer.aliyun.com/article/1491125
7、系列7:正则匹配(不讲)
(28)boolean matches(正则表达式):判断当前字符串是否匹配某个正则表达式
常用正则表达式:(暂)
字符类
[abc]
:a
、b
或 c
(简单类)
[^abc]
:任何字符,除了 a
、b
或 c
(否定)
[a-zA-Z]
:a
到 z
或 A
到 Z
,两头的字母包括在内(范围)
预定义字符类
.
:任何字符(与行结束符可能匹配也可能不匹配)
\d
:数字:[0-9]
\D
:非数字: [^0-9]
\s
:空白字符:[ \t\n\x0B\f\r]
\S
:非空白字符:[^\s]
\w
:单词字符:[a-zA-Z_0-9]
\W
:非单词字符:[^\w]
POSIX 字符类(仅 US-ASCII)
\p{Lower}
小写字母字符:[a-z]
\p{Upper}
大写字母字符:[A-Z]
\p{ASCII}
所有 ASCII:[\x00-\x7F]
\p{Alpha}
字母字符:[\p{Lower}\p{Upper}]
\p{Digit}
十进制数字:[0-9]
\p{Alnum}
字母数字字符:[\p{Alpha}\p{Digit}]
\p{Punct}
标点符号:!"#$%&'()*+,-./:;<=>?@[]^_`{|}~
\p{Blank}
空格或制表符:[ \t]
边界匹配器
^
:行的开头
$
:行的结尾
Greedy 数量词
X?
:X,一次或一次也没有
X*
:X,零次或多次
X+
:X,一次或多次
X{
n}
:X,恰好 n 次
X{
n,}
:X,至少 n 次
X{
n,
m}
:X,至少 n 次,但是不超过 m 次
Logical 运算符
XY:X 后跟 Y
X|
Y:X 或 Y
(
X)
:X,作为捕获组
特殊构造(非捕获)
(?:X) X,作为非捕获组
(?=X) X,通过零宽度的正 lookahead
(?!X) X,通过零宽度的负 lookahead
(?<=X) X,通过零宽度的正 lookbehind
(?<!X) X,通过零宽度的负 lookbehind
(?>X) X,作为独立的非捕获组
@Test public void test1(){ //简单判断是否全部是数字,这个数字可以是1~n位 String str = "12a345"; //正则不是Java的语法,它是独立与Java的规则 //在正则中\是表示转义, //同时在Java中\也是转义 boolean flag = str.matches("\\d+"); System.out.println(flag); } @Test public void test2(){ String str = "123456789"; //判断它是否全部由数字组成,并且第1位不能是0,长度为9位 //第一位不能是0,那么数字[1-9] //接下来8位的数字,那么[0-9]{8}+ boolean flag = str.matches("[1-9][0-9]{8}+"); System.out.println(flag); } @Test public void test03(){ //密码要求:必须有大写字母,小写字母,数字组成,6位 System.out.println("Cly892".matches("^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])[A-Za-z0-9]{6}$"));//true System.out.println("1A2c45".matches("^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])[A-Za-z0-9]{6}$"));//true System.out.println("Clyyyy".matches("^(?=.*[A-Z])(?=.*[0-9])[A-Za-z0-9]{6}$"));//false }
1.验证用户名和密码,要求第一个字必须为字母,一共6~16位字母数字下划线组成:(1\w{5,15}$)
2.验证电话号码:xxx/xxxx-xxxxxxx/xxxxxxxx:(^(\d{3,4}-)\d{7,8}$)
3.验证手机号码:( ^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ )
4.验证身份证号: (\d{15}$)|(^\d{18}$)|(\d{17}(\d|X|x)$)
5.验证Email地址:(^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$)
6.只能输入由数字和26个英文字母组成的字符串:(2+$)
7.整数或者小数:(3+(.[0-9]+){0,1}$)
8.中文字符的正则表达式:([\u4e00-\u9fa5])
9.金额校验(非零开头的最多带两位小数的数字):(^([1-9][0-9]*)+(.[0-9]{1,2})?$)
10.IPV4地址:(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))
8、系列8:替换
(29)String replace(xx,xx):不支持正则
(30)String replaceFirst(正则,value):替换第一个匹配部分
(31)String repalceAll(正则, value):替换所有匹配部分
@Test public void test4(){ String str = "hello244world.java;887"; //把其中的非字母去掉 str = str.replaceAll("[^a-zA-Z]", ""); System.out.println(str); }
9、系列9:拆分
(32)String[] split(正则):按照某种规则进行拆分
@Test public void test4(){ String str = "张三.23|李四.24|王五.25"; //|在正则中是有特殊意义,我这里要把它当做普通的| String[] all = str.split("\\|"); //转成一个一个学生对象 Student[] students = new Student[all.length]; for (int i = 0; i < students.length; i++) { //.在正则中是特殊意义,我这里想要表示普通的. String[] strings = all[i].split("\\.");//张三, 23 String name = strings[0]; int age = Integer.parseInt(strings[1]); students[i] = new Student(name,age); } for (int i = 0; i < students.length; i++) { System.out.println(students[i]); } } @Test public void test3(){ String str = "1Hello2World3java4atguigu5"; str = str.replaceAll("^\\d|\\d$", ""); String[] all = str.split("\\d"); for (int i = 0; i < all.length; i++) { System.out.println(all[i]); } } @Test public void test2(){ String str = "1Hello2World3java4atguigu"; str = str.replaceFirst("\\d", ""); System.out.println(str); String[] all = str.split("\\d"); for (int i = 0; i < all.length; i++) { System.out.println(all[i]); } } @Test public void test1(){ String str = "Hello World java atguigu"; String[] all = str.split(" "); for (int i = 0; i < all.length; i++) { System.out.println(all[i]); } }
9.1.4 字符串对象的内存分析
@Test public void test1(){ String s1 = "hello"; String s2 = "hello"; String s3 = new String("hello"); } @Test public void test02(){ String s1 = "hello"; String s2 = "world"; String s3 = "helloworld"; String s4 = s1 + "world";//s4字符串内容也helloworld,s1是变量,"world"常量,变量 + 常量的结果在堆中 String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是变量,变量 + 变量的结果在堆中 String s6 = "hello" + "world";//常量+常量,编译期经过优化,跟s3完全相同的情况。 System.out.println(s3 == s4);//false System.out.println(s3 == s5);//false System.out.println(s3 == s6);//true } @Test public void test03(){ final String s1 = "hello"; final String s2 = "world"; String s3 = "helloworld"; String s4 = s1 + "world";//s4字符串内容也helloworld,s1是常量,"world"常量,常量+ 常量 结果在常量池中 String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是常量,常量+ 常量 结果在常量池中 String s6 = "hello" + "world";//常量+ 常量 结果在常量池中,因为编译期间就可以确定结果 System.out.println(s3 == s4);//true System.out.println(s3 == s5);//true System.out.println(s3 == s6);//true } @Test public void test04(){ String s1 = "hello"; String s2 = "world"; String s3 = "helloworld"; String s4 = (s1 + "world").intern();//如果常量池已经有“helloworld”,直接返回,否则把拼接结果的引用放到常量池中 String s5 = (s1 + s2).intern(); System.out.println(s3 == s4);//true System.out.println(s3 == s5);//true }
结论:
- 常量+常量:结果是常量池
- 常量与变量 或 变量与变量:结果是堆
- intern方法有jvm版本的区别,这里不再深入分析,jdk8中执行原理是如果字符串常量池有内容相同的字符串则直接返回,否则把堆中创建的字符串引用放入字符串常量池,返回此引用,总之所有版本都是通过字符串常量池返回的内容。
9.1.5 面试题
class TEXT{ public int num; public String str; public TEXT(int num, String str){ this.num = num; this.str = str; } } public class Class4 { //tIn是传对象的地址,修改形参的属性,会影响实参 //intIn是传数据,基本数据类型的形参修改和实参无关 //Integer和String对象不可变 public static void f1(TEXT tIn, int intIn, Integer integerIn, String strIn){ tIn.num =200; tIn.str = "bcd";//形参和实参指向的是同一个TEXT的对象,修改了属性,就相当于修改实参对象的属性 intIn = 200;//基本数据类型的形参是实参的“副本”,无论怎么修改和实参都没关系 integerIn = 200;//Integer对象和String对象一样都是不可变,一旦修改都是新对象,和实参无关 strIn = "bcd"; } public static void main(String[] args) { TEXT tIn = new TEXT(100, "abc");//tIn.num = 100, tIn.str="abc" int intIn = 100; Integer integerIn = 100; String strIn = "abc"; f1(tIn,intIn,integerIn,strIn); System.out.println(tIn.num + tIn.str + intIn + integerIn + strIn); //200 + bcd + 100 + 100 + abc } }
9.2 StringBuilder&StringBuffer
9.2.1 与String区别
因为String对象是不可变对象,虽然可以共享常量对象,但是对于频繁字符串的修改和拼接操作,效率极低。因此,JDK又在java.lang包提供了可变字符序列StringBuilder和StringBuffer类型。
StringBuffer: 老的,线程安全的(因为它的方法有synchronized修饰),效率低
StringBuilder: 线程不安全的,效率高
9.2.2 常用API
常用的API,StringBuilder、StringBuffer的API是完全一致的
(1)StringBuffer append(xx):拼接,追加
(2)StringBuffer insert(int index, xx):在[index]位置插入xx
(3)StringBuffer delete(int start, int end):删除[start,end)之间字符
StringBuffer deleteCharAt(int index):删除[index]位置字符
(4)void setCharAt(int index, xx):替换[index]位置字符
(5)StringBuffer reverse():反转
(6)void setLength(int newLength) :设置当前字符序列长度为newLength
(7)StringBuffer replace(int start, int end, String str):替换[start,end)范围的字符序列为str
(8)int indexOf(String str):在当前字符序列中查询str的第一次出现下标
int indexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询str的第一次出现下标
int lastIndexOf(String str):在当前字符序列中查询str的最后一次出现下标
int lastIndexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询str的最后一次出现下标
(9)String substring(int start):截取当前字符序列[start,最后]
(10)String substring(int start, int end):截取当前字符序列[start,end)
(11)String toString():返回此序列中数据的字符串表示形式
@Test public void test6(){ StringBuilder s = new StringBuilder("helloworld"); s.setLength(30); System.out.println(s); } @Test public void test5(){ StringBuilder s = new StringBuilder("helloworld"); s.setCharAt(2, 'a'); System.out.println(s); } @Test public void test4(){ StringBuilder s = new StringBuilder("helloworld"); s.reverse(); System.out.println(s); } @Test public void test3(){ StringBuilder s = new StringBuilder("helloworld"); s.delete(1, 3); s.deleteCharAt(4); System.out.println(s); } @Test public void test2(){ StringBuilder s = new StringBuilder("helloworld"); s.insert(5, "java"); s.insert(5, "chailinyan"); System.out.println(s); } @Test public void test1(){ StringBuilder s = new StringBuilder(); s.append("hello").append(true).append('a').append(12).append("atguigu"); System.out.println(s); System.out.println(s.length()); }
9.2.3 效率测试
/* * Runtime:JVM运行时环境 * Runtime是一个单例的实现 */ public class TestTime { public static void main(String[] args) { // testStringBuilder(); testStringBuffer(); // testString(); } public static void testString(){ long start = System.currentTimeMillis(); String s = new String("0"); for(int i=1;i<=10000;i++){ s += i; } long end = System.currentTimeMillis(); System.out.println("String拼接+用时:"+(end-start));//444 } public static void testStringBuilder(){ long start = System.currentTimeMillis(); StringBuilder s = new StringBuilder("0"); for(int i=1;i<=10000;i++){ s.append(i); } long end = System.currentTimeMillis(); System.out.println("StringBuilder拼接+用时:"+(end-start));//4 } public static void testStringBuffer(){ long start = System.currentTimeMillis(); StringBuffer s = new StringBuffer("0"); for(int i=1;i<=10000;i++){ s.append(i); } long end = System.currentTimeMillis(); System.out.println("StringBuffer拼接+用时:"+(end-start));//7 } }