一、字符串
1.1 字符串的简介
字符串:是由若干个字符组成的一个有序的序列。在Java中,使用String这个类来描述字符串。
1) java.lang.String 使用final修饰,不能被继承。
2) 字符串底层封装的是字符数组及其针对数组的操作方法。
3) 字符串一旦创建,对象永远无法改变,但字符串可以重新赋值。
4) 字符串在内存中采用Unicode编码方式,任何一个字符对应两个字节的定长编码。
5) 字符串的索引值从0开始。
1.2 字符串常量池
JVM为了提升性能和减少内存开销,专门为字符串的一些操作,在内存中提供了一块区域, 用于存储字符串对象。该内存区域就是字符串常量池(字符串缓冲区,缓冲池)。
当使用字面量给变量赋值时,会先去常量池中寻找是否有该字面量对象,如果有,就将其地址直接 给变量。如果没有,就将该字面量对象创建在常量池中,然后将其地址赋值给变量。
其内存位置:
在 JDK 1.7 之前,运行时常量池(包括字符串常量池)存放在方法区。
在 JDK 1.7 时,字符串常量池从方法区被转移至 Java 堆中,
注意并不是运行时常量池,而是字符串常量池被单独转移到堆,运行时常量池剩下的东西还是方法区中。
在 JDK 1.8 时,此时字符串常量池还在堆中。
public class StringDemo01 {
public static void main(String[] args) {
String s1 = "abc";//
String s2 = "abc";//
//判断s1和s2的对象地址是不是同一个
System.out.println(s1 == s2);//true
/*
两个字面量做拼接操作时,编译器在编译期间就做了优化操作。
直接计算出结果,即s3="abcd"
*/
String s3 = "abc"+"d";
String s4 = "abcd";
System.out.println(s3 == s4);//true
String s5 = "d";
//拼接操作只要有变量参与,那么javac不会进行优化。只能在运行期间进行计算,
//计算后的结果存储在堆中,并不是常量池里。
String s6 = "abc"+s5;
System.out.println(s4 == s6);//false
String s7 = "ab"+"c"+s5;//优化成了"abc"+s5,但是依然在运行期间与s5做了运算。不会去堆里找,是新对象。
System.out.println(s7 == s6);//false
String s8 = new String("d");
String s9 = "abc"+s8;
System.out.println(s7 == s9);//false
String s10 = new String("abc");
String s11 = new String("abc");
System.out.println(s10 == s11);//false
//假如只有下面两行代码:问 内存中总共有多少个对象 至少5个
String s12 = "h";
String s13 = new String("abc"+s12+"o");
}
}
1.3 常用构造器
1)String()
无参构造器,构建的是一个空字符串,实际上底层是 char[] value = new char[0];
String str = new String();
System.out.println(str.length());//输出结果为0
2) String(String str)
String s1 = new String("michael");
System.out.println("s1 = " + s1);//输出结果为s1 = michael
3)String(byte[] bytes)
传入一个字节序列,按照默认字符集转成对应字符。
byte[] bytes = {-28,-72,-83,-27,-101,-67};
String s2 = new String(bytes);
System.out.println("s2 = " + s2);//输出结果为 s2 = 中国
4) String(byte[] bytes,String charsetName)
使用指定编码集charsetName解码byte数组,构建一个字符串对象。
byte[] bytes = {-28,-72,-83,-27,-101,-67};
String s3 = new String(bytes,"UTF-8");
System.out.println("s3 = " + s3);//输出结果为 s3 = 中国
5) String(byte[] bytes, int offset, int length)
使用默认编码集解码byte数组,从offset位置的元素开始,length个元素,构建一个字符串对象。
byte[] bytes = {-28,-72,-83,-27,-101,-67};
String s4 = new String(bytes,3,3);
System.out.println("s4 = " + s4);//输出结果为 s4 = 国
6)String(char[] value)
char[] chars = {'h','e','l','l','o'};
String s5 = new String(chars);
System.out.println("s5 = " + s5);//输出结果为 s5 = hello
1.4 常用方法
1)length方法
1. length()方法可以获取字符串对象的字符个数。
2. 一定要注意,数组的元素个数的获取方式引用的.length, 不是方法,是属性。
String obj = “你好,HelloWorld”;
System.out.println(obj.length());//13
String s1 = "welcome to china";
System.out.println(s1.length());//16
2) 使用indexOf实现检索
作用:用于检查子字符串所在的位置,从起始位置或者给定的位置开始左到右开始检索,找到第一个就停止,如果没有找到,返回-1。
int indexOf(String str)
int indexOf(String str,int fromIndex)
String s1 = "welcome to china";
int index = s1.indexOf("come");
System.out.println("index is " + index);//3
index = s1.indexOf("o");
System.out.println("index is " + index);//4
index = s1.indexOf("o",5);
System.out.println("index is " + index);//9
//找第二个o的下标,从第一个o的下一个位置开始查找o。
index = s1.indexOf("o",s1.indexOf("o")+1);//9
3)使用lastIndexOf实现检索
作用:检索子字符串最后一次出现的索引,从左到右,如果没有找到,返回-1.。
int lastIndexOf(String str)
int lastIndexOf(String str,int endIndex): 检索子字符串最后一次出现的位置。从左到右一直检索到指定索引endIndex(包含这个位置)就停止。
String s1 = "welcome to china";
index = s1.lastIndexOf("o");
System.out.println("index is " + index);//9
index = s1.lastIndexOf("o",9);
System.out.println("index is " + index);//9
4) 使用substring截取子串
作用:substring方法用于返回一个字符串的子字符串。
String substring(int beginIndex):从指定的开始索引位置一直到字符串结束。
String substring(int beginIndex,int endIndex):从指定的开始索引位置一直检索到指定的结束索引位置(不包含这个位置的元素)。
String s1 = "welcome to china";
String substring = s1.substring(11);
System.out.println(" 子字符串 " + substring);//china
String substring1 = s1.substring(8,14);
System.out.println(" 子字符串 " + substring1);//to chi
5)trim截掉空格
作用:去掉一个字符串的前与后的空字符,不会去掉中间的空字符
String trim()
String s2 = " michael ";
System.out.println(s2.length());//9
s2 = s2.trim();
System.out.println(s2.length());//7
6) charAt获取字符
获取指定下标上的字符,可用于单个字符的字符串转成字符类型。
char charAt(int index)
String s1 = "welcome to china";
char c =s1.charAt(1);
System.out.println(c);//输出结果为e
7) startsWith和endsWith
boolean startsWith(String str):判断字符串是否以指定子串开头。
boolean endsWith(String str): 判断字符串是否以指定子串结束。
String s1 = "welcome to china";
boolean we = s1.startsWith("we");
boolean na = s1.endsWith("na");
System.out.println(we+","+na);//输出结果为 true,true
8) 大小写变换
作用:对字符串中的字母进行转换成全部大写或全部小写。
String toUpperCase() 所有字母变成大写
String toLowerCase() 所有字母变成小写
String s3 = "你好welComE+";
s3=s3.toUpperCase();
System.out.println(s3.toUpperCase());//输出结果为你好WELCOME+
s3=s3.toLowerCase();
System.out.println(s3.toLowerCase());//输出结果为你好welcome+
9) toCharArray
作用: 将字符串转变成字符数组。
char[] toCharArray()
String s1 = "welcome to china";
char[] charArray = s1.toCharArray();
System.out.println(Arrays.toString(charArray));//输出结果为[w, e, l, c, o, m, e, , t, o, , c, h, i, n, a]
10) valueOf
作用:将其他类型转换成字符串类型。
static String valueOf(int value)
static String valueOf(double d)
static String valueOf(char[] ch)
static String valueOf(Object obj)
String num = String.valueOf(10001);
System.out.println("num:"+num);//输出结果为num:10001
11) equals与==
equals: 用于比较两个对象的属性是否一样,不是地址值。
== : 比较的是地址值是否相等,是否为一个对象。
String s10 = new String("hello");
String s11 = new String("hello");
System.out.println(s10 == s11);//false
System.out.println(s10.equals(s11));//true
1.5 StringBuilder和StringBuffer
因为直接操作String类型,会造成大量的对象在内存中出现,所以影响性能。
可以使用StringBuilder和StringBuffer来操作字符串的拼接,替换,截断等操作。
因为:StringBuilder和StringBuffer是一个可变字符串类型。拼接,替换,截断都不会产生新对象。
StringBuilder和StringBuffer类中的方法完全相同。
1.5.1 常用构造
1)StringBuilder()
构建一个空字符串的StringBuilder对象,注意,其内部的属性char[]的长度是16。
2)StringBuilder(String str)
构造一个字符串生成器,其初始化为指定的字符串内容。
1.5.2 常用方法
1)append方法。
作用:将指定字符串追加到字符串之后。
StringBuilder append(boolean b)
StringBuilder s1 = new StringBuilder();
int a = 10;
int b = 10;
s = s1.append(a > b);
System.out.println(s);//false
StringBuilder append(String str)
StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder("");
StringBuilder s3 = s1.append("欢迎");
System.out.println(s3);//欢迎
System.out.println(s1==s3);//true, 表示s1和s3指向的是同一个对象
s3.append("来到中国").append(",中国是美丽的").append(",地大物博的");
System.out.println(s1.toString());//欢迎来到中国,中国是美丽的,地大物博的
2) insert方法
作用:将指定字符串插入到字符串的指定索引处。
StringBuilder insert(int index,String str)
StringBuilder s3 = s1.append("欢迎");
System.out.println(s3);
System.out.println(s1==s3);//true, 表示s1和s3指向的是同一个对象
s3.append("来到中国").append(",中国是美丽的").append(",地大物博的");
System.out.println(s1.toString());
s1.insert(4,"伟大的");
System.out.println(s1);//欢迎来到伟大的中国,中国是美丽的,地大物博的
3)delete方法
作用:删除字符串中的一部分。
StringBuilder delete(int start , int end) 范围:[start,end)
StringBuilder s3 = s1.append("欢迎");
System.out.println(s3);
System.out.println(s1==s3);//true, 表示s1和s3指向的是同一个对象
s3.append("来到中国").append(",中国是美丽的").append(",地大物博的");
System.out.println(s1.toString());
s1.insert(4,"伟大的");
System.out.println(s1);
s1.delete(13,17);
System.out.println(s1);//欢迎来到伟大的中国,中国是地大物博的
4)replace方法
作用:将指定位置的元素替换。
StringBuilder replace(int start, int end, String str) 范围: [start , end)
StringBuilder s3 = s1.append("欢迎");
System.out.println(s3);
System.out.println(s1==s3);//true, 表示s1和s3指向的是同一个对象
s3.append("来到中国").append(",中国是美丽的").append(",地大物博的");
System.out.println(s1.toString());
s1.insert(4,"伟大的");
System.out.println(s1);
s1.delete(13,17);
System.out.println(s1);
s1.replace(4,6,"美丽");
System.out.println(s1);//欢迎来到美丽的中国,中国是地大物博的
5) reverse方法
作用:将字符序列进行反转(左右颠倒)。
StringBuilder reverse()
StringBuilder s3 = s1.append("欢迎");
System.out.println(s3);
System.out.println(s1==s3);//true, 表示s1和s3指向的是同一个对象
s3.append("来到中国").append(",中国是美丽的").append(",地大物博的");
System.out.println(s1.toString());
s1.insert(4,"伟大的");
System.out.println(s1);
s1.delete(13,17);
System.out.println(s1);
s1.replace(4,6,"美丽");
System.out.println(s1);
s1.reverse();
System.out.println(s1);//的博物大地是国中,国中的丽美到来迎欢
6)toString方法
作用:将StringBuilder对象转变成String对象。
String toString()
1.5.3 三者的区别
1.StringBuilder是可变字符串,如果进行字符串的内容计算,建议选择StringBuilder,这样性能更好一些。
2.java的字符串连接的过程是利用StringBuilder实现的。
3.StringBuilder 也是final修饰的类型,不能被继承。
4. StringBuilder没有重写equals方法和toString()。
5. StringBuilder是非线程安全的,并发处理,性能稍快。
6. StringBuffer是线程安全的,同步处理,性能稍慢。
二、正则表达式
2.1 简介
正则表达式(Regular Expressions),是一个特殊的字符串,可以对普通的字符串进行校验检测等工作。正则表达式不是Java特有的,它是一套独立的语法,可以在java,c++,python等语言中使用。
正则表达式,最基本的使用场景是用来做校验,校验一个字符串是否满足预设的规则。在校验的基础上,又添加了若干个其他的引用场景,例如: 批量的查找、替换、切割...
2.2 基本语法
2.2.1 字符集合
[] : 表示匹配括号里的任意一个字符。
[abc] :匹配a 或者 b 或者 c
[^abc] : 匹配任意一个字符,只要不是a,或b,或c 就表示匹配成功
[a-z] : 表示匹配所有的小写字母的任意一个。
[A-Za-z] :表示匹配所有的小写字母和大写字母的任意一个。
[a-zA-Z0-9]:表示匹配所有的小写字母和大写字母和数字的任意一个。
[a-z&&[^bc]] :表示匹配所有的小写字母除了b和c, 只要匹配上就是true.
2.2.2 预定义字符集
\d: 用于匹配数字字符中的任意一个 相当于[0-9]
\w: 匹配单词字符中的任意一个 单词字符就是[a-zA-Z0-9_]
\D: 用于匹配非数字字符中的任意一个 相当于[^0-9]
\W: 用于匹配非单词字符中的任意一个 相当于[^a-zA-Z0-9_]
\s: 用于匹配空格,制表符,退格符,换行符等中的任意一个
\S: 用于匹配非空格,制表符,退格符,换行符等中的任意一个
. : 用于匹配任意一个字符
2.2.3 数量词
X? :匹配0个或1个
X* :匹配0个或1个以上
x+ :匹配1个以上
X{n} :匹配n个
X{m,}:匹配m个以上
X{m,n}:匹配m~n个
2.2.4 分组:()
在正则表达式上可以使用()来进行对一些字符分组,并可以使用逻辑运算符|来进行选择匹配。
String regex = "(135|137)[\\d]{8}" //匹配手机号是135或者是137开头的
2.2.5 ^和$
^:表示严格从头匹配
$: 表示匹配到结尾
2.3 常用方法
2.3.1 boolean matches(String regex)
作用:用于检验普通字符串是否符合一个正则表达式。 符合返回true,不符合返回false。
System.out.println("a".matches("[abcdefg]"));//true
String regex = "[^hg]";
String str = "o";
System.out.println(str.matches(regex));//true
System.out.println("+".matches("."));//匹配一下+是不是一个字符串。true
System.out.println(".".matches("."));//true
System.out.println("-".matches("\\."));//false \\. 通配符点变成了普通的点符号。
System.out.println("c".matches("\\w"));//不添加[]也可以 true
System.out.println("c".matches("[\\w]"));//添加[] true
//匹配密码:密码必须是8位的数字或字母组合。
System.out.println("123abc45".matches("[a-zA-Z0-9]{8}"));//true
System.out.println("123abc4".matches("[a-zA-Z0-9]{8}"));//false 少一位不可以
System.out.println("123abc456".matches("[a-zA-Z0-9]{8}"));//false 多一位也不可以
//匹配用户名:用户名是由字母数字和下划线组成的5~8位
System.out.println("_abc123".matches("\\w{5,8}"));//true
System.out.println("_abc123+".matches("\\w{5,8}"));//false
System.out.println("1".matches("[a-z]?"));//false 1不是0个或者1个字母
System.out.println("".matches("[a-z]?"));//true
System.out.println("n".matches("[a-z]?"));//true
String regex1 = "(13|18|15)(7|8|9)[\\d]{8}";
System.out.println("13811110000".matches(regex1));//true
System.out.println("13311110000".matches(regex1));//false
2.3.2 String[] split(String regex)
作用:将一个普通字符串,按照正则表达式切分成一个字符串数组。
提示:符合正则表达式的子串,相当于刀,隔板。 相当于隔板的子串就没有了。
如果开头有满足正则表达式的字符串,默认添加一个空字符串。
String str = "hello111world222welcome333";
//请使用数字将其切分成字符串数组。
String regex1 = "\\d+";
String[] arr = str.split(regex1);
System.out.println(Arrays.toString(arr));
System.out.println("数组的长度:"+arr.length);
str = "888aaa9bbb10ccc";
arr = str.split(regex1);
System.out.println(Arrays.toString(arr));
System.out.println("数组的长度:"+arr.length);
str = "123abc234def235hhh";
arr = str.split("3");
System.out.println(Arrays.toString(arr));
System.out.println("数组的长度:"+arr.length);
运行结果:
[hello, world, welcome]
数组的长度:3
[, aaa, bbb, ccc]
数组的长度:4
[12, abc2, 4def2, 5hhh]
数组的长度:4
2.3.3 replaceAll()
String replaceAll(String regex,String replacement)
作用:使用replacement这个串替换掉所有的符合正则表达式的子字符串。
String info = "no zuo no die";
info = info.replaceAll("no","yes");
System.out.println("info:"+info);
info = "http://www.baidu123.com.cn.com456";
//将所有的字母替换成#
regex = "[a-zA-Z]+";
info = info.replaceAll(regex,"#");
System.out.println("info:"+info);
运行结果:
info:yes zuo yes die
info:#://#.#123.#.#.#456
三、Pattern类和Matcher类
Pattern对象 ---> 模式对象
Matcher对象 --->匹配结果对象
3.1.Pattern类
3.1.1 简介
字符串在真正校验时,底层实际上维护了一个Pattern对象,使用其相关方法进行的校验。
因此:我们可以直接使用Pattern进行校验。
Pattern类的构造器私有化了,因此不能调用构造器来创建对象,但是其提供了一个工厂方法来获
取对象 static compile(String regex)
public static void main(String[] args) {
String regex = "[a-zA-Z][_$a-zA-Z0-9]{7,9}";
//获取一个Pattern对象
Pattern p1 = Pattern.compile (regex);
//和普通字符串进行校验,会得到一个Matcher类的对象,该对象里封装的是匹配的结果,并且提供了各种操作。
Matcher matcher = p1.matcher("michael12_");
// 想要得到是否匹配成功,需要调用Matcher里的matches()。 该方法的作用是从头到尾进行严格匹配。
//匹配成功,返回true 匹配失败,返回false。
boolean matches = matcher.matches();
System.out.println("是否匹配成功:"+matches);
}
3.1.2 常用方法
1)pattern()方法
作用:返回Pattern的正则表达式,即特殊的字符串。返回值类型是String。
String regex = "\\d+";
Pattern p1 = Pattern.compile(regex);
String str2 = p1.pattern();
System.out.println("str2: " + str2);//str2: \d+
2)spilt(String str)方法
作用:对字符串进行切分。使用符合正则表达式的子串进行切分,返回字符串数组。
符合正则表达式的子串,就相当于隔板,不要了。
返回值类型是 String[] 。
String regex = "\\d+";
String str = "aaa111bbb222ccc";
Pattern p1 = Pattern.compile(regex);
String[] arr = p1.split(str);
System.out.println(Arrays.toString(arr));//[aaa, bbb, ccc]
3.2 Matcher类
3.2.1 简介
1. 该类的构造器私有化了,因此不能直接new,而是通过Pattern的matcher()方法获取该类的对象。
2. 该类的对象里面,封装的就是正则表达式和普通字符串匹配的结果。
3. 该类里提供了对各种方法,对封装的结果进行操作。
比如: matches()、lookingAt()、find()、start()、end()、group()、groupCount()reset()等
3.2.2 常用方法
1)lookingAt()方法 返回值类型是boolean
1. 用于匹配普通字符串的开头部分是否符合正则表达式。符合返回true,不符合返回false。
2. 内部含有一个类似于指针的操作,当匹配成功后,指针指向了匹配成功的子串。
匹配前位于普通字符串的前面。
String regex = "//d+";
String info = "abc123";
Pattern pattern = Pattern.compile(regex);//获取模式
Matcher matcher = pattern.matcher(info);//获取匹配结果对象
//调用了lookingAt()方法
boolean result = matcher.lookingAt();
System.out.println("result: " + result);//false
2)find()方法 返回值类型是boolean
1. 用于匹配普通字符串里是否有符合正则表达式的子串,检索到字符串的最后。
如果有,返回true;如果没有,返回false。
2. 该方法也有一个指针操作,当匹配成功后,指针就向后移动。
注意:匹配前,指针还是在字符串前。
String regex = "\\d+";
String info = "abc123eee444ff555ggg";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(info);
//循环的次数,表示了find()找到的符合要求的个数。
int count = 0;
while (matcher.find()) {
count++;
}
System.out.println(count);//3
3)group()、groupCount()、reset()、start()、end()
这些方法必须基于matches()、lookingAt()、find()方法,才能使用。
即:matches()、lookingAt()、find()方法用于匹配工作。剩下的方法用于匹配成功后进行的操作。
1. group() 返回值类型是 String
作用:用于返回指针指向的符合正则表达式的那一部分。
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("abc123def456xxx");
while(matcher.find()){
System.out.println(matcher.group());
}
输出结果:123
456
2. reset()
作用:重置指针。
例如:当使用find()后指针已经移动到了符合正则表达式的最后一个部分。 再次调用find()时,会继续向后检索,因此就会检索失败,没有任何符合要求的group() 。如果想要从头检索,就需要重置指针。
3. start()和end() 返回值类型都是int
start():用于返回符合正则表达式的子串的开始位置的索引。
end(): 用于返回符合正则表达式的子串的最后一个字符位置的索引+1。
public class PatternDemo05Matcher03 {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("abc123def456xxx");
int count = 1;
while (matcher.find()) {
int start =matcher.start();
int end =matcher.end();
System.out.println("第"+count+"部分的开始和结束位置:"+start+","+end);
count++;
}
}
}
输出结果:
第1部分的开始和结束位置:3,6
第2部分的开始和结束位置:9,12
4. groupCount() 返回值类型是int
作用:用于获取正则表达式中小括号的个数,()用于分组。
Pattern pattern = Pattern.compile("(ca)(t)");
Matcher matcher = pattern.matcher("one cat,two cats in the yard");
System.out.println(matcher.groupCount());//2
while(matcher.find()) {
//打印符合正则表达式的子串,因此循环次数是2次。
System.out.println(matcher.group());//两遍cat
}
/**
* 使用小括号的个数来循环,可以使用group的重载方法:
* String group(int group)
* 注意:想要使用()的个数进行遍历,需要将指针移动到字符串之前,然后还需要调用一下find()方法。
* group(0): 表示的是对组进行拆分括号的整体的样子。
* group(1): 表示第一个括号的样子。
* group(2): 表示第二个括号的样子。
*/
matcher.reset();
matcher.find();
for(int i = 0; i <= matcher.groupCount(); i++) {
System.out.println(matcher.group(i));
}
输出结果:
2
cat
cat
cat
ca
t
5. lookingAt() 和 find()
两者在一起使用时,可能会影响指针的操作。因为在使用lookingAt()方法时,指针会向后移动一次。所以在使用lookingAt()方法后,再使用find()方法,指针就不是从字符串前开始移动的,而是紧随着lookingAt()方法结束的位置移动。
public static void main(String[] args) {
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("123aaa444bbb555");
System.out.println("字符串是否以数字开头:"+matcher.lookingAt());//true 此时指针移动到123处
matcher.reset();//重置指针,让其位于字符串的开头
while (matcher.find()) {
System.out.println(matcher.group());
}
}
输出结果:
字符串是否以数字开头:true
123
444
555