Java学习—day11_字符串及正则表达式的介绍
字符串 【会】
字符串,是由若干个字符组成的一个有序序列。用String来表示一个字符串。
字符串中的内容,用双引号括起来。在双引号中,字符的数量不限制,可以是0个,可以是1
个,也可以是多个。
String类:java将与字符串相关的功能面向对象了,形成了对应的类--字符串类.
例如大家最常见的:
String str1 = "hello world";
字符串的分类
不可变字符串:
- 对应的类:String.
- 特点:字符串本身不能发生改变,与指向字符串的引用无关.
- 直接使用"",创建的是不可变字符串
可变字符串:
- 对应的类:
StringBuilder/StringBuffer
. - 特点:字符串本身可以发生变化,与指向可变字符串的引用无关
创建可变字符串 StringBuffer stringBuffer = new StringBuffer("hello world");
字符串的内存分析:
字符串, 是一个引用数据类型。 但是字符串的引用, 和之前在面向对象部分的引用有一点差
别。
差别: 类的对象, 是直接在堆上开辟的空间。 字符串, 是在 常量池 中开辟的空间。 (常量
池, 是在方法区中的一个子空间)
String str = new String("hello world");
- String是一个Java中用来描述字符串的类, 里面是有构造方法的。
- 通过String类提供的构造方法, 实例化的字符串对象, 在堆上开辟的空间。 在堆空间中, 有一个内部维护的属性, 指向了常量池中的某一块空间。
// 在堆上开辟了一个String对象的空间, 把这个堆上空间的地址给了str1
// 在堆空间中, 有一个内部的属性, 指向了常量池中的 "hello world"
String str1 = new String("hello world");
// 在堆上开辟了一个String对象的空间, 把这个堆上空间的地址给了str2
// 在堆空间中, 有一个内部的属性, 指向了常量池中的 "hello world"
String str2 = new String("hello world");
System.out.println(str1 == str2); // false: 因为此时 str1和str2 里面存储的是两块堆空间的地址。
System.out.println(str1.equals(str2)); // true: 因为在String类中,已经重写过equals方法了, 重写的实现为比较实际指向的常量池中的字符串。
ps:
以后尽量使用equals进行String的比较
字符串拼接的内存分析
-
直接使用两个字符串字面量进行拼接
- 其实,就是直接将两个由双引号直接括起来的字符串进行拼接 。类似于
String str
= "hello" + "world"
。 - 这里,直接在常量池中进行空间操作。将常量池中拼接之后的结果, 地址给
str
进行
赋值。
- 其实,就是直接将两个由双引号直接括起来的字符串进行拼接 。类似于
-
使用一个字符串变量和其他的进行拼接
String s = new String(“hello”);
String
s1
= s + “world”;- 这里的拼接,不是在常量池中直接完成的。
- 在这个拼接的过程中, 隐式的实例化了一个String类的对象, 在堆上开辟了空间。
堆上空间内部维护了一个指向了常量池中拼接结果的一个属性。 这个堆上的空间地
址给左侧的引用进行了赋值。
字符串的常用方法
字符串的构造方法
- 字符串构造方法列举
构造方法 | 方法描述 |
---|---|
String() | 无参构造, 实例化一个空的字符串对象。 所谓的空字符 串,其实是 “”, 并不是null。 |
String(String str ) | 通过一个字符串, 实例化另外一个字符串。 |
String(char[] arr) | 通过一个字符数组, 实例化一个字符串。 将字符数组中的 所有的字符拼接到一起。 |
String(char[] arr, int offset, int count) | 通过一个字符数组, 实例化一个字符串。 将字符数组中的 指定范围的字符拼接到一起。 |
String(byte[] arr) | 通过一个字节数组, 实例化一个字符串。 将字节数组中的 所有的字节拼接成字符串。 |
String(byte[] arr, int offset, int count) | 通过一个字节数组, 实例化一个字符串。 将字节数组中的 指定范围的字节拼接成字符串。 |
public class StringMethod1 {
public static void main(String[] args) {
// 1. 无参构造, 实例化一个空的字符串对象。 所谓的空字符串,其实是 "", 并不是null。
String s1 = new String(); // String s1 = "";
System.out.println(s1);
// 2. 通过一个字符串, 实例化另外一个字符串。
String s2 = new String("hello");
System.out.println(s2);
char[] arr1 = { 'h', 'e', 'l', 'l', 'o' };
// 3. 通过一个字符数组, 实例化一个字符串。将字符数组中的所有的字符拼接到一起。
String s3 = new String(arr1);
System.out.println(s3);
// 4. 通过一个字符数组, 实例化一个字符串。 将字符数组中的指定范围的字符拼接到一起。
String s4 = new String(arr1, 2, 3);
System.out.println(s4);
byte[] arr2 = { 97, 98, 99, 100, 101, 102, 103, 104 };
// 5. 将一个字节数组中的每一个字节,转成对应的字符,再拼接到一起,组成一个字符串。
String s5 = new String(arr2);
System.out.println(s5);
// 6. 将一个字节数组中的offset位开始,取length个字节,将每一个字节,转成对应的字符,再拼接到一起,组成一个字符串。
String s6 = new String(arr2, 2, 4);
System.out.println(s6);
}
}
字符串的非静态方法
因为字符串, 是常量。 任何的修改字符串的操作, 都不会对所修改的字符串造成任何的影
响。 所有的对字符串的修改操作, 其实都是实例化了新的字符串对象。 在这个新的字符串
中, 存储了修改之后的结果。 并将这个新的字符串以返回值的形式返回。 所以, 如果需要
得到对一个字符串修改之后的结果, 需要接收方法的返回值。
返回值 | 方法 | 方法描述 |
---|---|---|
String | concat(String str) | 字符串拼接.将一个字符串与另一个字符串进行拼接并返回拼接之后的结果 |
String | substring(int beginIndex) | 字符串截取。 从beginIndex 开始, 一直截取到字符串的结尾。 |
String | substring(int beginIndex, int endIndex) | 字符串截取。 截取字符串中 [beginIndex, endIndex) 范围内的子字符串。 |
String | replace(char oldChar, char newChar) | 字符串替换。 用新的字符替换原字符串中所有的旧的字符。 |
String | replace(CharSequence old, harSequence newC) | 字符串替换。 用新的字符序列替换原字符串中所有的旧的字符序列。 |
char | charAt(int index) | 字符获取。 获取指定位的字符。 |
char[] | toCharArray() | 将字符串转成字符数组。 |
byte[] | getBytes() | 将字符串转成字节数组。 |
int | indexOf(char c) | 获取某一个字符在一个字符串中第一次出现的下标。 如果没有出现, 返回 -1 |
int | indexOf(char c, int fromIndex) | 获取某一个字符在一个字符串中从fromIndex 位开始往后第一次出的下标。 |
int | lastIndexOf(char c) | 获取某一个字符在一个字符串中最后一次出现的下标。 |
int | lastIndexOf(char c, int fromIndex) | 获取某一个字符在一个字符串中,从fromIndex 位开始往前最后一次出现的下标。 |
String | toUppeerCase() | 将字符串中的所有的小写字母转成大写字母。 |
String | toLowerCase() | 将字符串中的所有的大写字母转成小写字母 |
boolean | isEmpty() | 判断一个字符串是否是空字符串。 |
int | length() | 获取一个字符串的长度。 |
boolean | contains(String str) | 判断一个字符串中, 是否包含另外一个字符串。 |
boolean | startsWith(String prefix) | 判断一个字符串, 是否是以指定的字符串作为开头。 |
boolean | endsWith(String shuffix) | 判断一个字符串, 是否是以指定的字符串作为结尾。 |
String | trim() | 去除一个字符串首尾的空格。 |
boolean | equals(Object obj) | 判断两个字符串的内容是否相同。 |
boolean | equalsIgnoreCase(String str) | 判断两个字符串的内容是否相同, 忽略大小写。 |
int | compareTo(String other) | 对两个字符串进行大小比较。 |
int | compareToIgnoreCase(String other) | 对两个字符串进行大小比较, 忽略大小写。 |
public class StringMethod2 {
public static void main(String[] args) {
// 1. 字符串的拼接,效率比加号拼接高
String ret1 = "hello".concat("world");
System.out.println(ret1); // helloworld
// 2. 字符串截取
String ret2 = "hello world".substring(3);
System.out.println(ret2); // lo world
String ret3 = "hello world".substring(3, 8);
System.out.println(ret3); // lo wo
// *. 字符序列截取,与字符串截取没有太大区别
CharSequence charSequence = "hello world".subSequence(3, 8);
System.out.println(charSequence);
// 3. 用新的字符替换原字符串中所有的旧的字符
String ret4 = "hello world".replace('l', 'L');
System.out.println(ret4); // heLLo worLd
// 4. 用新的字符序列替换原字符串中所有的旧的字符序列
String ret5 = "hello world".replace("ll", "~");
System.out.println(ret5); // he~o world
// 5. 转成字符数组
char[] ret6 = "hello world".toCharArray();
System.out.println(Arrays.toString(ret6)); // [h, e, l, l, o, , w, o, r, l, d]
// 6. 转成字节数组
byte[] ret7 = "hello world".getBytes();
byte[] ret7 = "hello world".getBytes("utf8");
System.out.println(Arrays.toString(ret7)); // [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
// 7. 获取某一个字符在一个字符串中第一次出现的下标。(从左到右)
//c:/a/b/c/bing.png
int ret8 = "hello world".indexOf('L');
System.out.println(ret8); // 2
// 8. 获取某一个字符在一个字符串中从fromIndex位开始往后,第一次出现的下标。
int ret9 = "hello world".indexOf('l', 4);
System.out.println(ret9); // 9
// 9. 获取某一个字符在一个字符串中最后一次出现的下标。(从右到左)
int ret10 = "hello world".lastIndexOf('o');
System.out.println(ret10); // 7
// 10. 获取某一个字符在一个字符串中,从fromIndex位开始往前,最后一次出现的下标
int ret11 = "hello world".lastIndexOf('o', 5);
System.out.println(ret11); // 4
// 11. 字符串大小写字母转变
System.out.println("hello WORLD".toUpperCase());
System.out.println("hello WORLD".toLowerCase());
// 12. 判断一个字符串中, 是否包含另外一个字符串。
System.out.println("hello world".contains("loo"));
// 需求:判断一个字符串中是否包含某一个字符
// 答案:或者这个字符在字符串中出现的下标,如果不是-1,说明包含。
// 13. 判断一个字符串, 是否是以指定的字符串作为开头。
//举例:http://www.baidu.com:8080/a/g/d?name=zhangsan
//网址结构:协议 :// 域名(ip地址) : 端口号 / 资源路径 ? 查询条件(结构: key1 = value1 & key2=value2)
/*
协议:同一个的规范说明,应用层用户通信时使用的统一协议时http/https
https:安全的http协议
域名:是给主机起的一个名字,域名会与主机IP做映射关系.当我们访问域名时,系
统会自动到DNS服务器查找是否有于当前的域名对应的ip映射.找到,直接使用映射的ip访问
服务器,找不到,显示无法访问.
一个主机有一个唯一的一个ip地址,域名与ip是一一对应的.我们可以根据域名找
到指定的主机
DNS服务器:内部放的是所有注册过的ip 与域名的映射关系.
端口号:确定一台主机上的某一个服务器(app)
端口号的取值范围(0,65535)
资源路径:资源在服务器上的路径
查询条件:就是客户端的需求说明.
注意:可以省略的组件:
协议 默认http
端口
资源路径
查询条件
*/
System.out.println("哈利波特与魔法石.mp4".startsWith("哈利波特"));
System.out.println("哈利波特与魔法石.mp4".endsWith(".mp4"));
// 14. 去除一个字符串首尾的空格
System.out.println(" hello world ".trim());
// 15. 判断两个字符串的内容是否相同
System.out.println("hello world".equals("HELLO WORLD"));
System.out.println("hello world".equalsIgnoreCase("HELLO WORLD")); // true
// 16. 比较两个字符串的大小
// 大体的逻辑:
// > 0: 前面的字符串 > 参数字符串
// == 0: 两个字符串大小相等
// < 0: 前面的字符串 < 参数字符串
/*
* 字典顺序:按照ASCII表比较当前的两个字符,ASCII码大的认为是大的字符
* 规则:从左边第一个字符开始比较
* 如果当前的字符不相同,直接认为ASCII大的字符串是大字符串,后面的字符停
止比较
* 当前字符比较的具体规则:使用前面的字符-后面的字符,返回差值.如果是负
数,说明前面的字符串小于后面的.反之前面的大.
* 如果当前的字符相同,再去比较第二个字符,依次往后推,如果比到最后都相同,
则认为两个字符串相等,差值返回0.
* 如果两个字符串中一个是另一个从开始算的子串.比较的结果就是字符个数的差
值,如果后面的是子串,差值为正数,
* 前面的是子串,差值为负数
*/
int result = "hello world".compareTo("hh");
System.out.println(result);
//17.切割: String[] split(String)
String s6 = "h.e.l.l.o";
//当我们将字符串中的某个字符作为刀,它就不再是内容
String[] strings1 = s6.split("\\.");//.是一个特殊字符,表示任意
for (String ss:strings1) {
System.out.println(ss);
}
思考:有一个字符串 "hello" 使用空字符串切,结果是什么?
}
}
字符串的静态方法
-
常用的静态方法
-
返回值 方法 描述 String join(CharSequencedelimiter, CharSequenceelements)
将若干个字符串拼接到一起,在拼接的时候,元素与元素之间以指定的分隔符进行分隔。 String format(String format, Object... args)
以指定的格式, 进行字符串的格式化 public class StringMethod3 { public static void main(String[] args) { // 将若干个字符串拼接到一起,在拼接的时候,元素与元素之间以指定的分隔符进行分隔 String str1 = String.join(", ", "lily", "lucy", "uncle wang", "polly"); System.out.println(str1); float score = 100; String name ="xiaoming"; int age = 19; // 大家好,我叫xiaoming,今年19岁了,本次考试考了100分。 /** * 常见占位符: * %s : 替代字符串 -> %ns: 凑够n位字符串,如果不够,补空格 * %d : 整型数字占位符 -> %nd: 凑够n位,如果不够补空格。 * %f : 浮点型数字占位符 -> %.nf: 保留小数点后面指定位的数字 * %c : 字符型占位符 */ String str2 = String.format("大家好,我叫%11s,今年%03d岁了,本次考试考了%.6f分。", name, age, score); System.out.println(str2); } }
StringBuffer和StringBuilder类
都是用来操作字符串的类,我们称为可变字符串
字符串都是常量, 所有的操作字符串的方法, 都不能直接修改字符串本身。 如果我们需要得
到修改之后的结果, 需要接收返回值。
StringBuffer
和StringBuilder
不是字符串类, 是用来操作字符串的类。 在类中维护了一个字
符串的属性, 这些字符串操作类中的方法, 可以直接修改这个属性的值。 对于使用方来说,
可以不去通过返回值获取操作的结果。
在StringBuffer
或者StringBuilder
类中, 维护了一个字符数组。 这些类中所有的操作方法,
都是对这个字符数组进行的操作。返回值 常用方法 方法描述 构造方法() 实例化一个字符串操作类对象, 操作的是一个空字符串。 构造方法 (String str)
实例化一个字符串操作类对象, 操作的是一个指定的字符串。 StringBuffer/StringBuilder
append(…) 将一个数据拼接到现有的字符串的结尾 StringBuffer/StringBuilder
insert(int offset, …) 将一个数据插入到字符串的指定位。 StringBuffer/StringBuilder
delete(int start, int end) 删除一个字符串中 [start, end) 范围内的数据。如果start越界了, 会出现下标越界异常。 如果end越界了, 没影响, 会将字符串后面的所有的内容都删除。 StringBuffer/StringBuilder
deleteCharAt(int index)
删除指定下标位的字符。 StringBuffer/StringBuilder
replace(int start, int end, String str)
替换,将字符串中 [start, end) 范围内的数据替换成指定的字符串。 void setChatAt(int index, char c)
将指定下标位的字符, 替换成新的字符。 StringBuffer/StringBuilder
reverse() 将一个字符串前后倒置、翻转。 String toString()
返回一个正在操作的字符串。 public class Test { public static void main(String[] args) { // 1. 构造方法 StringBuilder sb = new StringBuilder("hello world"); // 2. 增: 在一个字符串后面拼接其他的字符串 sb.append('!'); // 3. 增: 在指定的下标位插入一条数据 sb.insert(3, "AAAAA"); // 4. 删: 删除字符串中的 [start, end) 范围内的数据 sb.delete(3, 5); // 5. 删: 删除指定位的字符 sb.deleteCharAt(6); // 6. 截取一部分的字符串,这个操作不会修改到自己,如果希望得到截取的部分,需要接收返回值。 String sub = sb.substring(4, 6); // 7. 替换,将字符串中 [start, end) 范围内的数据替换成指定的字符串 sb.replace(3, 6, "l"); // 8. 修改指定下标位的字符 sb.setCharAt(0, 'H'); // 9. 将字符串前后翻转 sb.reverse(); System.out.println(sb); } }
区别
StringBuffer
和StringBuilder
从功能上来讲, 是一模一样的。 但是他们两者还是有区别的:StringBuffer
是线程安全的StringBuilder
是线程不安全的。
使用场景
- 当处于多线程的环境中, 多个线程同时操作这个对象, 此时使用
StringBuffer。
- 当没有处于多线程环境中, 只有一个线程来操作这个对象, 此时使用
StringBuilder。
ps:
但凡是涉及到字符串操作的使用场景, 特别是在循环中对字符串进行的操作。 一定不要使用
字符串的方法, 用StringBuffer
或者StringBuilder
的方法来做。
-
- 由于字符串本身是不可变的, 所以String类所有的修改操作, 其实都是在方法内实例化了一
个新的字符串对象, 存储拼接之后的新的字符串的地址, 返回这个新的字符串。 如果操作比
较频繁, 就意味着有大量的临时字符串被实例化、被销毁, 效率极低。 StringBuffer、
StringBuilder
不同, 在内部维护了一个字符数组, 所有的操作都是围绕这个字符数组进行的
操作。 当需要转成字符串的时候, 才会调用toString()
方法进行转换。 当频繁用到字符串操
作的时候, 没有中间的临时的字符串出现, 效率较高。
- 由于字符串本身是不可变的, 所以String类所有的修改操作, 其实都是在方法内实例化了一
正则表达式 [了解]
正则表达式, 不是Java特有的。 是一套独立的, 自成体系的知识点。 在很多语言中, 都
有对正则的使用。
正则表达式, 使用来做字符串的校验、匹配的, 其实正则只有一个作用: 验证一个字符串是
否与指定的规则匹配。
但是, 在很多的语言中, 都在匹配的基础上, 添加了其他的功能。 例如Java: 在匹配的基
础上, 还添加了 删除、替换... 功能。
正则表达式的使用(会)
实现相同的功能, 用String、StringBuffer、StringBuilder
可以实现, 用正则表达式也可以
实现。
正则表达式的匹配规则
逐个字符进行匹配, 判断是否和正则表达式中定义的规则一致。
元字符
元字符 | 意义 |
---|---|
^ | 匹配一个字符串的开头。 在Java的正则匹配中,可以省略不写。 |
$ | 匹配一个字符串的结尾。 在Java的正则匹配中,可以省略不写 |
[] | 匹配一位字符。 |
[abc]: 表示这一位的字符, 可以是a、也可以是b、也可以是c 。 | |
[a-z]: 表示这一位的字符, 可以是 [a, z] 范围内的任意的字符。 | |
[a-zABC]: 表示这一位的字符, 可以是 [a,z ] 范围内的任意字符, 或者A、或者B、或者C 。 | |
[a-zA-Z]: 表示这一位的字符, 可以是任意的字母, 包括大写字母和小写字母。 | |
[^a-z[hk]]: 表示这一位的字符, 不能是任意的小写字母, 但是 h 、 k 除外。 | |
[^abc]: 表示这一位的字符, 可以是除了 a、b、c 之外的任意字符。 | |
\ | 转义字符。 由于正则表达式在Java中是需要写在一个字符串中。 而字符串中的 |
\也是一个转义字符。 因此Java中写正则表达式的时候, 转义字符都是 \ \ | |
使得某些特殊字符成为普通字符, 可以进行规则的指定。 | |
使得某些普通字符变得具有特殊含义。 | |
\d | 匹配所有的数字, 等同于 [0-9] 。 |
\D | 匹配所有的非数字, 等同于 [^0-9] 。 |
\w | 匹配所有的单词字符, 等同于 [a-zA-Z0-9_] 。 |
\W | 匹配所有的非单词字符, 等同于 [^a-zA-Z0-9_] 。 |
. | 通配符, 可以匹配一个任意的字符。 |
+ | 前面的一位或者一组字符, 连续出现了一次或多次。 |
? | 前面的一位或者一组字符, 连续出现了一次或零次。 |
* | 前面的一位或者一组字符, 连续出现了零次、一次或多次。 |
{} | 对前面的一位或者一组字符出现次数的精准匹配。 |
{m} : 表示前面的一位或者一组字符连续出现了m次。 | |
{m,} : 表示前面的一位或者一组字符连续出现了至少m次。 | |
{m,n} : 表示前面的一位或者一组字符连续出现了至少m次,最多n次。 | |
作用于整体或者是一个分组, 表示匹配的内容, 可以是任意的一个部分。 | |
| | abc|123|opq : 表示整体的部分, 可以是 abc , 也可以是 123, 也可以是 opq |
() | 分组。 把某些连续的字符视为一个整体对待。 |
Pattern和Matcher类
Pattern类: 在Java中,正则表达式的载体。使用正则表达式进行字符串的校验、切割、替
换,都需要使用到这个类。
修饰符&返回值 | 方法 | 描述 |
---|---|---|
static boolean | matches(String regex, CharSequence sequence) | 静态的规则校验, 直接校验某一个字符串是否符合某一个规则 |
static Pattern | compile(String regex) | 将一个字符串, 编译为Pattern对象, 从而可以当做是一个正则表达式使用。 |
String[] | split(CharSequence sequence) | 将一个字符序列, 按照指定的规则进行切割, 得到每一个切割部分。 |
String[] | split(CharSequence sequence, int limit) | 将一个字符序列, 按照指定的规则进行切割, 切割成指定的段数, 得到每一个切割部分。 |
Matcher | matcher(CharSequence sequence) | 将一个正则表达式和一个字符串进行校验。 |
Matcher类: 在Java中, 对一个正则校验的结果描述。