3.正则表达式
3.1正则表达式的使用
实现相同的功能,用String、StringBuffer、StringBuilder可以实现,用正则表达式也可以实现。
但是在实现过程中, 复杂程度是完全不一样的。
3.2基本的元字符
3.2.1正则表达式的匹配规则
逐个字符进行匹配, 判断是否和正则表达式中定义的规则一致。
以下借助 String 类中的 matches 方法进行正则基础的语法讲解。
boolean matches(String regex); 是String类中的非静态方法, 使用字符串对象调用这个方法, 参数是一个正则表达式。 使用指定的正则表达式, 和当前的字符串进行匹配。 验证当前的字符串是否和指定的规 则是相匹配的。
3.2.2元字符
元字符 | 意义 |
---|---|
^ | 匹配一个字符串的开头。在java的正则匹配中,可以省略不写 |
$ | 匹配一个字符串的结尾。在java的正则匹配中,可以省略不写 |
[] | 匹配一位字符。 [abc]:表示这一位的字符,可以是a、也可以是b、也可以是c [abc]:表示这一位的字符,可以是a、也可以是b、也可以是c [a-z]:表示这一位的字符,可以是[a,z]范围内的任意的字符 [a-zABC]:表示这一位的字符,可以是[a,z]范围内的任意字符,或者A、 或者B、或者C [a-zA-Z]:表示这一位的字符,可以是任意的字母,包括大写字母和小写字母 [ ^abc]:表示这一位的字符,可以是除了a、b、c之外的任意字符 [ ^a-z[hk]]:表示这一位的字符,不能是任意的小写字母,但是h、k除外 |
\ | 转义字符。由于正则表达式在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 |
() | 分组,把某些连续的字符试为一个整体对待 |
3.3 String类中的常用的方法
3.3.1字符串的匹配
// QQ号的规则: // 1. 纯数字组成的 // 2. 不能以0作为开头 // 3. 长度在 [6,11] private static boolean chechQQ(String qqNumber) { return qqNumber.matches("[1-9]\\d{5,10}"); } // 验证邮箱的合法性 // 1. 126 // 2. 前半部分可以是任意的单词字符, 长度限制在 [4,12] // 3. 以.com作为结尾 private static boolean checkEmail(String email) { return email.matches("\\w{4,12}@126\\.com"); }
3.3.2字符串的切割
private static void split() { // 需求: 将一个存储有所有的姓名的字符串,切割出每一个名字。 String names = "lily lucy polly Jim LiLei HanMeimei"; // 实现: 借助字符串的一个方法 split(String regex) // 将字符串中,满足正则规则的子部分,切割掉 String[] nameArr = names.split(" +"); System.out.println(Arrays.toString(nameArr)); }
3.3.3字符串的替换
private static void replace() { // 需求: 将这个存储有所有的名字的字符串,名字之间的分隔用, 来替代 String names = "lily lucy polly Jim LiLei HanMeimei"; // 实现: 借助字符串中的一个方法 replaceAll(String regex, String replacement) // 将字符串中,满足指定正则的部分,替换成 replacement // String result = names.replaceAll(" +", ", "); String result = names.replaceFirst(" +", ", "); System.out.println(result); } private static void example() { // 需求: 将一个手机号的中间4位替换成 **** // 17788889999 => 177****9999 String phoneNumber = "17788889999"; // 在replace方法中,使用$1获取第一个分组的值 String result = phoneNumber.replaceAll("(1\\d{2})(\\d{4})(\\d{4})", "$1****$3"); System.out.println(result); }
3.4Pattern和Matcher类
3.4.1. 简介
在 java.util.regex 包中
Pattern类: 在Java中,正则表达式的载体。使用正则表达式进行字符串的校验、切割、替 换,都需要使用到这个类。
在Java中,使用字符串的形式来写正则表达式。此时,如果需要这个字符串被当做是一 个正则表达式使用, 必须先由这样的字符串编译为 Pattern 类的对象。然后才可以使用 这个对象的某些方法, 进行常见的操作(校验、切割、替换)。
Matcher类: 在Java中, 对一个正则校验的结果描述。
3.4.2常用方法
Pattern
修饰符&返回值 | 方法 | 描述 |
---|---|---|
static boolean | match(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
操作修饰符&返回值 | 方法 | 描述 |
---|---|---|
boolean | matches() | 得到本次整体匹配的结果,一个字符串是否和 一个正则表达式是整体匹配的 |
boolean | find() | 查找字符串中,是否有满足指定规则的子部分 |
boolean | find(int start) | 查找字符串中,从指定下标开始往后,有没有 满足指定规则的子部分 |
String | group() | 获取当前匹配到的字符串 |
int | start() | 获取当前比配到的部分首字符的下标 |
int | end() | 获取当前匹配到的部分尾字符的下标+1 |
boolean | lookingAt() | 查找字符串,是否有满足指定规则的子部分, 每次查询都从第0位开始。 |
find()
从字符串的第0位开始查询, 查询是否有满足指定规则的子部分。 当再次调用find()的时候, 从上次查询到的位置开始, 继续向后查询。
关于查询
无论是 matches, 还是find, 还是lookingAt, 在字符串中进行校验、匹配的时候, 是有一 个浮标存在的。
4.Lambda表达式
4.1 Lambda表达式的使⽤场景
通常来讲,使⽤lambda表达式,是为了简化接⼝实现的。
关于接⼝实现,可以有很多种⽅式来实现。例如: 设计接⼝的实现类、使⽤匿名内 部类。但是lambda表达式,⽐这两种⽅式都简单
4.2 Lambda表达式对接⼝的要求
虽然说, lambda表达式可以在⼀定程度上简化接⼝的实现。 但是, 并不是所有的 接⼝都可以使⽤lambda表达式来简洁实现的。
lambda表达式毕竟只是⼀个匿名⽅法。 当实现的接⼝中的⽅法过多或者多少的时 候, lambda表达式都是不适⽤的。
lambda表达式,只能实现函数式接⼝。
4.3函数式接口
-
基础概念
如果说, ⼀个接⼝中, 要求实现类必须实现的抽象⽅法, 有且只有⼀个! 这样的接 ⼝, 就是函数式接⼝。
-
@FunctionalInterface
是⼀个注解, ⽤在接⼝之前, 判断这个接⼝是否是⼀个函数式接⼝。 如果是函数式 接⼝, 没有任何问题。 如果不是函数式接⼝, 则会报错。 功能类似于 @Override
@FunctionalInterface interface FunctionalInterfaceTest { void test(); }
-
系统内置的若⼲函数式接⼝(不常用,但是要注意)
4.4 Lambda表达式的语法
4.4.1. Lambda表达式的基础语法
lambda表达式,其实本质来讲,就是⼀个匿名函数。 因此在写lambda表达式的时 候,不需要关⼼⽅法名是什么。
实际上,我们在写lambda表达式的时候, 也不需要关⼼返回值类型。
我们在写lambda表达式的时候,只需要关注两部分内容即可:参数列表和⽅法体
lambda表达式的基础语法:
(参数) -> { ⽅法体 }
参数部分 : ⽅法的参数列表, 要求和实现的接⼝中的⽅法参数部分⼀致, 包括参数 的数量和类型。
方法体部分 : ⽅法的实现部分, 如果接⼝中定义的⽅法有返回值, 则在实现的时 候, 注意返回值的返回。
-> : 分隔参数部分和⽅法体部分。
4.5 Lambda表达式的语法进阶
在一些代码中,的确可以使⽤lambda表达式实现接⼝,但是依然不够简洁,有简化的空间。
4.5.1参数部分的精简
-
参数的类型
-
由于在接⼝的⽅法中,已经定义了每⼀个参数的类型是什么。 ⽽且在使⽤ lambda表达式实现接⼝的时候, 必须要保证参数的数量和类型需要和接⼝ 中的⽅法保持⼀致。 因此, 此时lambda表达式中的参数的类型可以省略不 写。
-
注意事项:
-
如果需要省略参数的类型,要保证:要省略,每⼀个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参数类型没有省略
-
-
// 多个参数、⽆返回值的⽅法实现 NoneReturnMutipleParameter lambda1 = (a, b) -> { System.out.println("多个参数、⽆返回值⽅法的实现: 参数a是 " + a + ", 参数b是 " + b); };
-
参数的小括号
-
如果⽅法的参数列表中的参数数量 有且只有⼀个 ,此时,参数列表的⼩括 号是可以省略不写的。
-
注意事项:
-
只有当参数的数量是⼀个的时候, 多了、少了都不能省略。
-
省略掉⼩括号的同时, 必须要省略参数的类型
-
-
// 有参、⽆返回值的⽅法实现 NoneReturnSingleParameter lambda2 = a -> { System.out.println("⼀个参数、⽆返回值⽅法的实现: 参数是 "+ a); };
4.5.2方法体部分的精简
-
方法体⼤括号的精简
-
当⼀个⽅法体中的逻辑, 有且只有⼀句的情况下, ⼤括号可以省略。
-
// 有参、⽆返回值的⽅法实现 NoneReturnSingleParameter lambda2 = a -> System.out.println("⼀个参数、⽆返回值⽅法的实现: 参数是 " + a);
-
return的精简
-
如果⼀个⽅法中唯⼀的⼀条语句是⼀个返回语句, 此时在省略掉⼤括号的同 时, 也必须省略掉return。
-
SingleReturnMutipleParameter lambda3 = (a, b) -> a + b;
4.5.3静态方法的引用
-
语法:
-
类:静态⽅法
-
-
注意事项:
-
在引⽤的⽅法后⾯,不要添加⼩括号。
-
引⽤的这个⽅法,参数(数量、类型)和 返回值,必须要跟接⼝中定义的 ⼀致。
4.5.4非静态方法的引用
-
语法:
-
对象::⾮静态⽅法
-
-
注意事项:
-
在引⽤的⽅法后⾯,不要添加⼩括号。
-
引⽤的这个⽅法,参数(数量、类型)和 返回值, 必须要跟接⼝中定义的 ⼀致。
-
4.5.6构造方法的引用
-
使⽤场景
-
如果某⼀个函数式接⼝中定义的⽅法, 仅仅是为了得到⼀个类的对象。 此 时我们就可以使⽤构造⽅法的引⽤, 简化这个⽅法的实现。
-
-
语法
-
类名::new
-
-
注意事项
-
可以通过接⼝中的⽅法的参数, 区分引⽤不同的构造⽅法。
-
4.5.7 对象方法的特殊引用
如果在使⽤lambda表达式,实现某些接⼝的时候。lambda表达式中包含了某⼀个 对象,此时⽅法体中,直接使⽤这个对象调⽤它的某⼀个⽅法就可以完成整体的逻辑。其他的参数,可以作为调⽤⽅法的参数。 此时,可以对这种实现进⾏简化。
4.5.8 Lambda表达式需要注意的问题
这⾥类似于局部内部类、匿名内部类,依然存在闭包的问题。
如果在lambda表达式中,使⽤到了局部变量,那么这个局部变量会被隐式的声明为 final。 是⼀个常量, 不能修改值。