Java基础(五)Lambda表达式、Stream流、正则表达式

  本系列文章:
    Java基础(一)基本数据类型、变量类型、修饰符、表达式、数组、分支循环、关键字
    Java基础(二)字符串、四种引用、内存泄漏、克隆、语法糖、IO
    Java基础(三)面向对象、封装继承多态、重写和重载、内部类、包装类
    Java基础(四)异常、枚举、日期、反射、泛型
    Java基础(五)Lambda表达式、Stream流、正则表达式

一、Lambda表达式

1.1 Lambda语法

 Lambda表达式是 Java8 中最重要的新功能之一。使用 Lambda 表达式可以替代只有一个抽象函数的接口实现,可以替代匿名内部类,代码看起来更简洁易懂。Lambda表达式同时还提升了对集合的迭代、遍历、过滤数据的操作。
 Lambda 表达式的语法:

(parameters) -> expression
或
(parameters) ->{
    
     statements; }

 Lambda表达式的重要特征:

1、可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
2、可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
3、可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
4、可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

 看一些简单的例子:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

 Lambda表达式应用场景:任何有函数式接口的地方。只有一个抽象方法(Object 类中的方法除外)的接口是函数式接口。就像Runnable接口中,只有一个run方法。

1.2 Lambda使用

1.2.1 Runnable

 最常见的匿名内部类之一应该就是Runnable,在JDK1.8之前要这样写:

        new Thread(new Runnable() {
    
    
            public void run() {
    
    
                System.out.println("The runable  is using!");
            }
        }).start();

 在JDK1.8之后可以使用lambda表达式:

	//It's a lambda function
	new Thread(() -> System.out.println("It's a lambda function")).start();

1.2.2 排序

 再看个例子,对集合元素进行排序:

        List<String> list = Arrays.asList("java","javascript","scala","python");
        //before java8
        Collections.sort(list, new Comparator<String>() {
    
    
            @Override
            public int compare(String o1, String o2) {
    
    
                return o1.length()-o2.length();
            }
        });
        for(String str:list){
    
    
            System.out.println(str);
        }
        //after java8
        Collections.sort(list,(a,b)->a.length()-b.length());

1.2.3 遍历

 遍历集合中的元素也是 lambda表达式的常见场景之一,示例:

        List<String> languages = Arrays.asList("Java","Python","C++");
        //before java8
        for(String language:languages) {
    
    
            System.out.println(language);
        }
        //after java8
        languages.forEach(x -> System.out.println(x));

1.2.4 过滤

 用 lambda表达式充当过滤条件,筛选出一部分元素,也是常见的操作。示例:

/*实体类*/
public class Student {
    
    
    private String name;
    private int age;
    private int score;

    public Student() {
    
    
    }

    public Student(String name, int age, int score) {
    
    
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public String getName() {
    
      return name; }

    public void setName(String name) {
    
      this.name = name; }

    public int getAge() {
    
     return age; }

    public void setAge(int age) {
    
       this.age = age; }

    public int getScore() {
    
      return score; }

    public void setScore(int score) {
    
       this.score = score;  }

    @Override
    public String toString() {
    
    
        return "student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

/*过滤接口*/
public interface StudentFilter {
    
    
    boolean compare(Student student);
}

/*客户端*/
public class Test {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<Student> list = new ArrayList<Student>();
        list.add(new Student("zhangsan",14,67));
        list.add(new Student("lisi",13,89));
        list.add(new Student("wangwu",15,97));
        list.add(new Student("maliu",12,63));
        list.add(new Student("zhaoqi",17,75));

        getByFilter(list,(e)->e.getAge()>14 );
        getByFilter(list, (e)->e.getScore()>75);
        System.out.println("-------------------");
        getByFilter(list, (e)->e.getName().length()>5);
    }

    public static void getByFilter(ArrayList<Student> students, StudentFilter filter){
    
    
        ArrayList<Student> list = new ArrayList<>();
        for(Student student:students){
    
    
            if(filter.compare(student)){
    
    
                list.add(student);
            }
        }
        printStudent(list);
    }

    public static void printStudent(ArrayList<Student> students){
    
    
        for(Student student:students){
    
    
            System.out.println(student);
        }
    }
}

1.3 Lambda表达式的多种写法

public class Java8Tester {
    
    
   public static void main(String args[]){
    
    
      Java8Tester tester = new Java8Tester();
        
      // 类型声明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用类型声明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括号中的返回语句
      MathOperation multiplication = (int a, int b) -> {
    
     return a * b; };
        
      // 没有大括号及返回语句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括号
      GreetingService greetService1 = message ->
      	  System.out.println("Hello " + message);
      // 用括号
      GreetingService greetService2 = (message) ->
      	  System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   interface MathOperation {
    
    
      int operation(int a, int b);
   }
    
   interface GreetingService {
    
    
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
    
    
      return mathOperation.operation(a, b);
   }
}

1.4 一些代表输入输出的函数式接口

Supplier:代表一个输出
Consumer: 代表一个输入
 BiConsumer :代表两个输入
Function :代表一个输入,一个输出(一般输入和输出是不同类型的)
 UnaryOperator :代表一个输入,一个输出(输入和输出是相同类型的)
 BiFunction :代表两个输入,一个输出(一般输入和输出是不同类型的)
 BinaryOperator :代表两个输入,一个输出(输入和输出是相同类型的)

  1. 消费(Consumer)型接口:
public void test01(){
    
    
    //消费型接口100
    Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
    consumer.accept(100);
}
  1. 提供(Supplier)型接口
	    List<Integer> list = new ArrayList<>();
	    Supplier<Integer> supplier = () -> (int)(Math.random() * 10);
	    list.add(supplier.get());
	    System.out.println(supplier); //com.test.JavaTest$$Lambda$1/471910020@816f27d
	    for (Integer integer : list) {
    
    
	        System.out.println(integer);  //9
	    }
  1. 函数(Function)型接口:
    String oldStr = "abc123456xyz";
    Function<String, String> function = (s) -> s.substring(1, s.length()-1);
    System.out.println(function.apply(oldStr));  //bc123456xy

1.5 Lambda表达式的方法引用

 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用。
  lambda表达式方法引用的分类:
在这里插入图片描述

  • 1、静态方法引用
     如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用。
     静态方法引用的lambda写法:
	    Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
	    System.out.println(com1.compare(1, 2));  //-1

	    Comparator<Integer> com2 = Integer::compare;
	    System.out.println(com2.compare(2, 1));   //1
  • 2、实例方法引用
     如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用。
     实例方法引用的lambda写法:
    //Lambda表达式的形式
    PrintStream ps = System.out;
    Consumer<String> con1 = (s) -> ps.println(s);
    con1.accept("aaa");  //aaa

    //方法引用的形式
    Consumer<String> con2 = ps::println;
    con2.accept("bbb");  //bbb
  • 3、对象方法引用
     抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用。
  • 4、构造方法引用
     如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用。
     构造器方法引用:
	//lambda表达式形式
    Supplier<List> sup1 = () -> new ArrayList();
    
	//方法引用形式
    Supplier<List> sup2 = ArrayList::new;

   数组构造器方法引用:

interface test {
    
    
    public String[] run(int length);
}
 
public class blog {
    
    
    public static void main(String[] args) {
    
    
   //实质代码:     test t1 = (length) -> new String[length];
        test t2 = String[]::new;
        String[] arr = t2.run(5);
    }
}

   多种构造引用的例子:

public class Test5 {
    
    
    public static void main(String[] args) {
    
    
        Supplier<Person> s1 = ()->new Person();
        s1.get();   //调用get后才会真正创建对象
        Supplier<Person> s2 = Person::new;
        s2.get();

        Supplier<List> s3 = ArrayList::new;
        Supplier<Set> s4 = HashSet::new;
        Supplier<Thread> s5 = Thread::new;
        Supplier<String> s6 = String::new;

        Consumer<Integer> c1 = (age)->new Account(age);
        Consumer<Integer> c2 = Account::new;
        c1.accept(123);
        c2.accept(456);

        Function<String,Account> f1 = (str)->new Account(str);
        Function<String,Account> f2 = Account::new;
        f1.apply("abc");
        f2.apply("def");

    }
}

class Account{
    
    
    public Account(){
    
    
        System.out.println("调用无参构造方法");
    }

    public Account(int age){
    
    
        System.out.println("age 参数构造" +age);
    }

    public Account(String str){
    
    
        System.out.println("str 参数构造" +str);
    }
}

class Person{
    
    
    public Person(){
    
    
        System.out.println("调用无参的构造方法");
    }
}

二、Stream

2.1 Stream简介

 Stream是一组用来处理数组、集合的API。
  Java 8引入函数式编程,原因有二:

  1. 代码简洁,函数式编程写出的代码简洁且意图明确,使用 stream 接口让你从此告别 for 循环
  2. 多核友好,Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下parallel()方法。

  Stream特性:

  1. 不是数据结构,没有内部存储
  2. 不支持索引访问;
  3. 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算
  4. 支持并行
  5. 很容易生成数组或集合(List,Set);
  6. 支持过滤,查找,转换,汇总,聚合等操作;
  7. 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中(保留意见:毕竟peek方法可以修改流中元素);

  Stream运行机制:
  1)Stream分为 源source,中间操作,终止操作
  2)流的源可以是一个数组、一个集合、一个生成器方法,一个I/O通道等等;
  3)一个流可以有零个和或者多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用。一个流只会有一个终止操作
  4)Stream只有遇到终止操作,它的源才开始执行遍历操作

2.2 Stream的创建

  获取流有以下几种方式。

  • 1、所有的 Collection 集合都可以通过 stream 默认方法获取流
      根据Collection获取流, java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流。示例:
//List集合获取流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//Set集合获取流
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
//Vecter获取流
Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream(); 

  Map 接口不是 Collection 的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value或entry等情况。示例:

Map<String, String> map = new HashMap<>();

Stream<String> keyStream = map.keySet().stream();

Stream<String> valueStream = map.values().stream();

Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
  • 2、Stream 接口的静态方法 of 可以获取数组对应的流
      示例:
String[] array = {
    
     "张三", "李四", "王五", "赵六" };
Stream<String> stream = Stream.of(array);
//of 方法的参数其实是一个可变参数,所以支持数组
Stream<String> stream1 = Stream.of(1, 2, 3, 4, 5);

2.3 Stream的中间操作

  Stream的中间操作有如下几种:

1.过滤 filter
2.去重 distinct
3.排序 sorted
4.截取 limit
5.跳跃 skip
6.转换map/flatMap
7.其他 peek

2.3.1 filter

  过滤一些元素,示例:

Arrays.asList(1, 2, 3, 4, 5)
	.stream()
	.filter(x->x%2==0)
	.forEach(System.out::println);

  上述代码的功能是:获取1~5之间的偶数,并输出。结果:

2
4

2.3.2 distinct

  根据元素的 hashCode() 和 equals() 去除重复元素,示例:

Arrays.asList(1,2,3,4,5,6,7,7,7,7)
	.stream()
	.distinct()
	.forEach(System.out::println);

  上述代码的功能是:去除List中的重复数,并输出。结果:

1
2
3
4
5
6
7

2.3.3 sorted

  对集合中的元素排序,顺序是倒序,示例:

Arrays.asList(1,2,3,4,5,6)
	.stream()
	.sorted((a,b)->b-a)
	.forEach(System.out::println);

  结果:

6
5
4
3
2
1

2.3.4 limit

  截断流,使其元素不超过给定数量,示例:

Arrays.asList(1,2,3,4,5,6,7)
	.stream()
	.limit(1)
	.forEach(System.out::println);   //1

  上述代码的功能是:取集合中的第一条数据。

2.3.5 skip

  跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流,示例:

Arrays.asList(1,2,3,3,4,5)
	.stream()
	.skip(1)
	.forEach(System.out::println);

  上述代码的功能是:跳过集合中的第一条数据。结果:

2
3
3
4
5

2.3.6 map

  接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。示例:

	Arrays.asList("1","2","3","5")
		 .stream()
		 .map(x->Integer.valueOf(x))
		 .forEach(System.out::println);

  上述代码的功能是:String转Integer。结果:

1
2
3
5

2.3.7 peek

  类似于打印日志的功能在进行操作时查看当前值,示例:

		Arrays.asList("1","2","3","5")
			.stream()
			.peek(System.out::println)
			.forEach(System.out::println);

  上述代码的功能是:在遍历集合元素时,打印当前值。结果:

1
1
2
2
3
3
5
5

2.4 Stream的终止操作

  终止操作有如下几种:

1、循环 forEach
2、计算 min、max、count、 average
3、匹配 anyMatch、 allMatch、 noneMatch、findFirst、 findAny
4、汇聚 reduce
5、收集器 toArray collect

2.4.1 forEach

  循环,之前已有示例,不再赘述。

2.4.2 min

  最小值,示例:

		Integer min = Arrays.asList(1, 3, 4, 5, 2)
			.stream()
			.min((a, b) -> a - b)
			.get();
		System.out.println(min);  //1

2.4.3 max

  最大值,示例:

		Integer max = Arrays.asList(1, 3, 4, 61, 1)
			.stream()
			.max((a, b) -> a - b)
			.get();
		System.out.println(max);  //61

2.4.4 count

  统计集合中元素的数量,示例:

		long count = Arrays.asList(1, 2,3, 4, 5, 6)
			.stream()
			.count();
		System.out.print(count);  //6

2.4.5 average

  求平均值,示例:

		double avg = Arrays.asList(1, 3, 4, 61, 1)
				.stream()
				.mapToLong(x -> Long.valueOf(x + ""))
				.average()
				.getAsDouble();
		System.out.print(avg);   //14

2.4.6 findFirst

  返回第一个匹配到的元素,示例:

		Optional<Integer> first= Arrays.asList(1, 2, 3, 4, 5)
			.stream()
			.filter(x -> x % 2 == 0)
			.findFirst();
		System.out.print(first.get());  //2

2.4.7 reduce

  根据一定的规则将Stream中的元素进行计算后返回一个唯一的值,示例:

		Integer reduce = Arrays.asList(1, 2, 3, 4, 5)
			.stream()
			.reduce((a, b) -> a + b)
			.get();
		System.out.print(reduce);   //15

2.4.8 collect

  将Stream中的元素处理后,存储到固定的集合中。Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

  示例:

		List<Integer> collect = Arrays.asList(1, 2, 3, 4, 5)
			.stream()
			.filter(x -> x % 2 == 0)
			.collect(Collectors.toList());
		System.out.print(collect.toString()); //[2, 4]

  上述代码的作用是:将过滤后的元素存储到List中。

三、正则表达式

  正则表达式定义了字符串的模式。正则表达式可以用来搜索、编辑或处理文本。
  一个字符串其实就是一个简单的正则表达式,例如 Hello World 正则表达式匹配 “Hello World” 字符串。.(点号)也是一个正则表达式,它匹配任何一个字符如:“a” 或 “1”。
  在使用正则表达式时,主要用到了java.util.regex 包中的几个类:
1. Pattern 类
  pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
2. Matcher 类
  Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
3. PatternSyntaxException
  PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

3.1 正则表达式语法

  在其他语言中,\\ 表示:在正则表达式中插入一个普通的反斜杠。
  在 Java 中,\\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。
  所以,在其他的语言中,一个反斜杠 \ 就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其他语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \\ 代表其他语言中的一个\,这也就是为什么表示一位数字的正则表达式是 \\d,而表示一个普通的反斜杠是 \\。看个例子:

		System.out.println("\\");    //  \
		System.out.println("\\\\");  //  \\

  正则表达式规则:

字符 说明
\ 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如, n匹配字符 n。\n 匹配换行符。序列 \\ 匹配 \ ,\( 匹配 (。
^ 匹配输入字符串开始的位置。
$ 匹配输入字符串结尾的位置。
* 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。
+ 一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。
? 零次或一次匹配前面的字符或子表达式。例如,"do(es)?“匹配"do"或"does"中的"do”。? 等效于 {0,1}。
{n} n 是非负整数。正好匹配 n 次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。
{n,} n 是非负整数。至少匹配 n 次。例如,"o{2,}“不匹配"Bob"中的"o”,而匹配"foooood"中的所有 o。"o{1,}“等效于"o+”。"o{0,}“等效于"o*”。
{n,m} m 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。‘o{0,1}’ 等效于 ‘o?’。注意:您不能将空格插入逗号和数字之间。
? 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如,在字符串"oooo"中,"o+?“只匹配单个"o”,而"o+“匹配所有"o”。
. 匹配除"\r\n"之外的任何单个字符。若要匹配包括"\r\n"在内的任意字符,请使用诸如"[\s\S]"之类的模式。
(pattern) 匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果"匹配"集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用"(“或者”)"。
(?:pattern) 匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"or"字符 (
(?=pattern) 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95
(?!pattern) 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95
x|y 匹配 x 或 y。
[xyz] 字符集。匹配包含的任一字符。
[^xyz] 反向字符集。匹配未包含的任何字符。
[a-z] 字符范围。匹配指定范围内的任何字符。
[^a-z] 反向范围字符。匹配不在指定的范围内的任何字符。
\b 匹配一个字边界,即字与空格间的位置。例如,“er\b"匹配"never"中的"er”,但不匹配"verb"中的"er"。
\B 非字边界匹配。“er\B"匹配"verb"中的"er”,但不匹配"never"中的"er"。
\cx 匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是"c"字符本身。
\d 数字字符匹配。等效于 [0-9]。
\D 非数字字符匹配。等效于 [^0-9]。
\f 换页符匹配。等效于 \x0c 和 \cL。
\n 换行符匹配。等效于 \x0a 和 \cJ。
\r 匹配一个回车符。等效于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。
\S 匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。
\t 制表符匹配。与 \x09 和 \cI 等效。
\v 垂直制表符匹配。与 \x0b 和 \cK 等效。
\w 匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。
\W 与任何非单词字符匹配。与"[^A-Za-z0-9_]"等效。
\xn 匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,"\x41"匹配"A"。"\x041"与"\x04"&"1"等效。允许在正则表达式中使用 ASCII 代码。
\num 匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,"(.)\1"匹配两个连续的相同字符。
\n 标识一个八进制转义码或反向引用。如果 \n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 n 是八进制转义码。
\nm 标识一个八进制转义码或反向引用。如果 \nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 \nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 \nm 匹配八进制值 nm,其中 n 和 m 是八进制数字 (0-7)。
\nml 当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml。
\un 匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (©)。

  关于常用的正则表达式,可参考:正则表达式语法及例子

3.2 Pattern类

  和Matcher 类通常配合用的是Pattern类,Pattern类用于创建一个正则表达式,也可以说是创建一个匹配模式,可以通过两个静态方法创建:compile(String regex)和compile(String regex,int flags),其中regex是正则表达式,flags为可选模式(如:Pattern.CASE_INSENSITIVE 忽略大小写)。示例:

		Pattern pattern = Pattern.compile("Java");
		System.out.println(pattern.pattern());//ava

  Pattern类还有两个根据匹配模式拆分输入序列的方法:split(CharSequence input) 和split(CharSequence input, int limit),其中limit为返回元素的个数。示例:

		Pattern pattern = Pattern.compile("Java");
		String test="123Java456Java789Java";
		String[] result = pattern.split(test);
		for(String s : result) {
    
    
		    System.out.println(s);
		}

  结果:

123
456
789

  关于split(CharSequence input, int limit)方法,当limit值大于所能返回的字符串的最多个数或者为负数,返回的字符串个数将不受限制,但结尾可能包含空串,而当limit=0时与split(CharSequence input)等价,但结尾的空串会被丢弃。示例:

		Pattern pattern = Pattern.compile("Java");
		String test = "123Java456Java789Java";

		String[] result = pattern.split(test,2);
		for(String s : result)
		            System.out.println(s);  //123    456Java789Java

		result = pattern.split(test,10);
		System.out.println(result.length);  //4

		result = pattern.split(test,-2);
		System.out.println(result.length);  //4

		result = pattern.split(test,0);
		System.out.println(result.length);  //3

  Pattern类也自带一个静态匹配方法matches(String regex, CharSequence input),但只能进行全字符串匹配并且只能返回是否匹配上的boolean值。示例:

		String test1 = "Java";
		String test2 = "Java123456";

		System.out.println(Pattern.matches("Java",test1));//true
		System.out.println(Pattern.matches("Java",test2));//false

3.3 Matcher 类

  Pattern类中的matcher(CharSequence input)会返回一个Matcher对象。Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持,要想得到更丰富的正则匹配操作,那就需要将Pattern与Matcher联合使用。
  Matcher类提供了三个返回boolean值得匹配方法:matches(),lookingAt(),find(),find(int start),其中matches()用于全字符串匹配,lookingAt从字符串最开头开始匹配满足的子串,find可以对任意位置字符串匹配,其中start为起始查找索引值。示例:

		Pattern pattern = Pattern.compile("Java");
		String test1 = "Java";
		String test2 = "Java1234";
		String test3 = "1234Java";
		Matcher matcher = pattern.matcher(test1);
		System.out.println(matcher.matches());//true
		matcher = pattern.matcher(test2);
		System.out.println(matcher.matches());//false

		matcher = pattern.matcher(test2);
		System.out.println(matcher.lookingAt());//true
		matcher = pattern.matcher(test3);
		System.out.println(matcher.lookingAt());//false

		matcher = pattern.matcher(test1);
		System.out.println(matcher.find());//true
		matcher = pattern.matcher(test2);
		System.out.println(matcher.find());//true
		matcher = pattern.matcher(test3);
		System.out.println(matcher.find(2));//true
		matcher = pattern.matcher(test3);
		System.out.println(matcher.find(5));//false

  正则表达式里有“组”的概念,组是用括号划分的正则表达式,可以根据组的编号来引用这个组。组号为0表示整个表达式,组号为1表示被第一对括号括起的组,依次类推,例如A(B©)D,组0是ABCD,组1是BC,组2是C。Matcher类提供了start(),end(),group()分别用于返回字符串的起始索引,结束索引,以及匹配到到的字符串。示例:

		Pattern pattern = Pattern.compile("Java");
		String test = "123Java456";

		Matcher matcher = pattern.matcher(test);
		matcher.find();
		System.out.println(matcher.start());//3
		System.out.println(matcher.end());//7
		System.out.println(matcher.group());//Java

  Matcher类提供了start(int gropu),end(int group),group(int i),groupCount()用于分组操作,示例:

		Pattern pattern = Pattern.compile("(Java)(Python)");
		String test = "123JavaPython456";
		Matcher matcher = pattern.matcher(test);
		matcher.find();
		System.out.println(matcher.groupCount());//2

		System.out.println(matcher.group(1));//Java
		System.out.println(matcher.start(1));//3
		System.out.println(matcher.end(1));//7

		System.out.println(matcher.group(2));//Python
		System.out.println(matcher.start(2));//7
		System.out.println(matcher.end(2));//13

  Matcher类还提供region(int start, int end)(不包括end)方法用于设定查找范围,并提供regionStrat()和regionEnd()用于返回起始和结束查找的索引。示例:

		Pattern pattern = Pattern.compile("Java");
        String test = "123JavaJava";
        Matcher matcher = pattern.matcher(test);
        matcher.region(7, 11);
        System.out.println(matcher.regionStart());//7
        System.out.println(matcher.regionEnd());//11
        matcher.find();
        System.out.println(matcher.group());//Java

  Matcher类提供了两种用于重置当前匹配器的方法:reset()和reset(CharSequence input),示例:

		Pattern pattern = Pattern.compile("Java");
        String test = "Java";
        Matcher matcher = pattern.matcher(test);

        matcher.find();
        System.out.println(matcher.group());//Java

        matcher.reset();//从起始位置重新匹配

        matcher.find();
        System.out.println(matcher.group());//Java

        matcher.reset("Python");
        System.out.println(matcher.find());//false

  Matcher类的匹配方法:replaceAll(String replacement) 和 replaceFirst(String replacement),其中replaceAll是替换全部匹配到的字符串,而replaceFirst仅仅是替换第一个匹配到的字符串。示例:

		Pattern pattern = Pattern.compile("Java");
        String test = "JavaJava";
        Matcher matcher = pattern.matcher(test);
        System.out.println(matcher.replaceAll("Python"));//PythonPython
        System.out.println(matcher.replaceFirst("python"));//PythonJava

3.4 PatternSyntaxException类

  PatternSyntaxException 是一个非强制异常类,它指示一个正则表达式模式中的语法错误。PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。

  • 1、public String getDescription()
      获取错误的描述。
  • 2、public int getIndex()
      获取错误的索引。
  • 3、public String getPattern()
      获取错误的正则表达式模式。
  • 4、 public String getMessage()
      返回多行字符串,包含语法错误及其索引的描述、错误的正则表达式模式和模式中错误索引的可视化指示。

猜你喜欢

转载自blog.csdn.net/m0_37741420/article/details/119981971