接上篇-入门Java SE-续集

本文详细介绍了Java SE中的数据类型,包括基本类型、包装类型、数组和字符串的使用。接着讨论了异常处理机制,包括异常类型、异常的创建与捕获。最后,简述了正则表达式的基本使用和匹配规则,帮助读者理解Java中如何进行数据操作和错误处理。
摘要由CSDN通过智能技术生成

😎接上篇让我们一起接着了解Java SE语言

目录

四、数据类型

基本类型

包装类型

数组

数组 类

创建数组

数组类

数组排序

数组复制

数组转化

字符串

字符串 类

创建字符串

常用方法

类型转换

StringBuilder / StringBuffer 类

创建字符串

专用方法

大数

BigInteger类

大十进制类

BigInteger 和BigDecimal类常用方法

枚举

Enum类

时间

数字类型

泛型

泛型定义

泛型使用

五、数值比较和排序的常用方法

等值判断

和 == 的区别

重写 等于 方法

数值比较

Comparator 接口和 Comparable 接口都用于比较两个元素的大小:

compareTo方法

比较 方法

数据排序

六、异常

异常类型

可投掷类

运行时异常类

已检查异常类

异常类

状态信息

构造方法

常用方法

自定义异常

异常转译

异常处理

抛出异常throw

捕获异常

注意事项

七、正则匹配

基本使用

正则表达式

表意符号

字符集

字符串匹配

捕获组

普通捕获组

命名捕获组

非捕获组


四、数据类型

基本类型

Java 语言提供了八种基本类型,用户无需定义也可以直接使用。其数据保存在相应的方法栈中。

基本数据类型字节数默认值包装数据类型备注
字节10字节
20
整型40整数
80
40.0f数字后加f
80.0
2字符必须用单引号
布尔1布尔
  • 布尔型

布尔 类型只含有两个值:true 和 false。字节存储为 11111111 和 00000000 。

boolean b = true;      
boolean b = false;   
  • 字符型

char 类型使用单引号来表示字符。因为 Java 统一采用 unicode 编码,2 字节可以表示一字符。char 类型同样可以用十六进制码保存汉字等特殊字符:'\u0000' - '\uffff'。

char ch = 'a';      
char ch = '中';
char ch = '/u3089';   
  • 整型和浮点型

Java 没有无符号类型,所有数据都有符号。

  1. 整型(byte/short/int/long) 用来表示整型数据。

  2. 浮点型(float/double) 用来表示浮点数据,实际以指数形式存储,所以和实际值之间有偏差。

    • 为 float 类型赋值必须在数字后加 f,否则数字默认被识别为 double 类型,会导致赋值出错。

    • 数字基本类型都包含最大最小值常量,如 和 .Integer.MAX_VALUEInteger.MIN_VALUE

    • 在浮点型有三个特殊数值表示溢出和出错:

      • POSITIVE_INFINITY:正无穷大,正数除以 0 可以得到。
      • NEGATIVE_INFINITY:负无穷大,负数除以 0 可以得到。
      • NaN:非数,零除以 0 可以得到。(两个 NAN 值也不相等)
int n = 0;
float f = 0.0f;
long l = Long.MAX_VALUE;               
double d = POSITIVE_INFINITY;

包装类型

均继承自 Number 抽象类,把基本类型数据封装成对象。基本类型和包装类型之间会自动进行转化。

  • 基本类型(如int),是一个值。允许不赋初值,系统自动添加默认值。

  • 包装类型(如Integer),是一个对象。实例化必须赋初值,且赋值后不能改变(final)。

包装类型主要用于集合框架中的元素。但阿里巴巴要求所有实体类(POJO) 属性、远程过程调用方法(RPC) 的返回值和参数都必须使用包装数据类型。以此来提醒使用者在需要使用时,必须显式地进行赋值。

类型转换

对于基础类型:

  • 按上图顺序可以自动进行类型转换。但整型转化为浮点型时,如果数据过大可能会导致数据丢失精度。

  • 反之则必须进行强制类型转换。但务必小心,超出范围可能会产生意想不到的错误。

int i = 'x';                  // 自动转换
char c = (char)60;            // 强制转换

但是包装类型之间的转换,需要使用特殊的方法。

Integer i = l.intValue();
Long l = i.longValue();

数组

数组 类

数据的集合。本质是一个对象,数据存储在堆区,由引用指向数组首个元素的地址。

创建数组

创建数组时,必须确定数组长度和类型。但如果储存的是基本类型,允许不赋初值(使用默认值)。

int[] arr = new int[4];                    // 方法一
int[] arr = {1,2,3,4};                     // 方法二
int[] arr = new int[]{1,2,3,4};            // 方法三

数组长度:在数组对象中,定义了 长度 属性记录了数组长度。

int len = arr.length;                      // 返回数组长度  

数组类

对数组进行操作的辅助类,实现了对数组的常用操作。

数组排序

sort 方法:可以对数组排序,默认数组数值从小到大排列,用户可以自定义排列顺序,

Arrays.sort(arr);                                     // 数组排序

数组复制

copyOf/copyOfRange 方法:复制数组。底层调用 System.arrayCopy 的本地方法实现,常用于数组扩容。

int[] arr1 = Arrays.copyOf(arr, 10);                  // 复制数组:前 10 个单位
int[] arr2 = Arrays.copyOf(arr, 0, arr.length);       // 复制数组:从 0 到 arr.length - 1

数组转化

asList 方法:将数组转化为列表(List 类),但数组数据必须是包装类型。

调用该方法将数组转换为列表后,在内存中实际还是以数组形式存储。这可能会导致以下两个问题:

  1. 调用 List 类的 add 方法向列表中插入数据,会导致异常;
  2. 对原数组进行更改,也会导致列表中的数据发生变化。
arr[] = new Integer[]{1, 2, 3, 4};                    // 数组必须是包装数据类型

List list = Arrays.asList(arr);                       // 将数组转换为集合(有问题)               
List list = new ArrayList<>(Arrays.asList(arr));      // 将数组转换为集合(推荐)  

字符串

字符串 类

保存字符串。String 类本质是一个 final 对象,由引用指向存储字符串对象的地址。引用虽然可变,但内存数据不能被更改。

创建字符串

String 对象创建后一经赋值不再改变,有以下两种创建方式:

  1. 直接赋值:如果常量池没有,则在常量池新建对象。否则直接使用常量池中已有对象,引用指向常量池。

  2. 构造方法:如果常量池没有,则在常量池新建对象。无论如何一定会在堆区创建对象,引用指向堆区。

String str1 = "string";                       // 引用指向常量池
String str2 = "str" + "ing";                  // 引用指向常量池(指向 str1 的字符串对象)

String str3 = new String("string");           // 引用指向堆区(在堆区新建字符串对象)
String str4 = str1 + str2;                    // 引用指向堆区

String newStr = new String(str.getBytes("ISO-8859-1"), "GBK");          // 获取指定类型编码对象,按指定类型编码

String 对象创建后一经赋值不再改变。对字符串数据进行改变,实际是创建新的 String 对象,并改变引用指向新的对象。

str1 = "goodbye";                             // str1 指向新的字符串对象

常用方法

int len = str.length();                   // 返回字符串长度

String[] strs = str.split(",");           // 按分隔符分解字符串

boolean c = str.contains(str2);           // 判断是否存在子字符串
int index = str.indexOf(str2);            // 查找子字符串出现的第一个位置,没有返回-1
int index = str.lastIndexOf(str2);        // 查找子字符串出现的最后一个位置,没有返回-1

String str2 = str.trim();                 // 去除字符串左右空格  
String str2 = str.substring(0,3);         // 截取指定位置(0-2)的子字符串
String str2 = str.replace("a", "b");      // 新字符 a 替换旧字符 b

类型转换

// Number > String

String s1 = data.toString();              // data 必须为包装数据类型
String s2 = Integer.toString(data);       // data 可以为基础数据类型,包括字符数组 char[]
String s3 = String.valueOf(data);         // data 可以为基础数据类型,包括字符数组 char[]

// String > char

char c = str.charAt(0);
char[] ch = str.toCharArray();

// String > int

int n1 = Integer.parseInt(str);          
int n2 = Integer.valueOf(str);

StringBuilder / StringBuffer 类

由于 String 类不可变性,对其频繁更改往往会产生较多临时变量类,占用大量内存。对此我们通常使用 StringBuilder/StringBuffer 来避免,这两个类允许在原有内存地址对字符串进行操作。其中 StringBuilder 类性能更好,StringBuffer 类线程安全。

创建字符串

必须通过构造方法创建,不可以直接赋值的形式创建:StringBuffer str = “hello”;

字符串默认长度为16,超出后会进行自动扩容。

StringBuffer str = new StringBuffer("hello");

将 StringBuilder / StringBuffer 类转化为 String 类。

String str2 = str.toString();    

专用方法

StringBuilder / StringBuffer 类可以使用 String 类的全部方法,还新增了以下方法直接对字符串进行修改。

str.append("add");                 // 末尾添加字符串,也可以是其他基础类型
str.insert(0,"insert");            // 指定位置插入字符串,也可以是其他基础类型
str.deleteCharAt(6);               // 删除指定位置(6)的字符
str.delete(6,8);                   // 删除指定位置(6和7)的字符串
str.reverse(str2);                 // 翻转字符串

大数

在 Java 程序中,我们可能会用到一些数值特别巨大、或者小数特别精确的数值,这些数值无法用基础类型表示。因此我们定义了 BigInteger/BigDecimal 类来保存这类数据,实际是以字符串形式在堆区存储。

BigInteger类

主要用来操作比 long 类型更大的整型数字。

大十进制类

基于 BigInteger 类实现。由于基本浮点数类型(float/double) 会产生精度丢失问题,因此常使用 BigDecimal 类代替。涉及金额必须使用该类。

float x = 1.0f;                           // 约等于 0.1
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a == b);               // false

BigDecimal a = new BigDecimal("1.0");     // 等于 0.1
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
System.out.println(x.equals(y));          // true

BigInteger 和BigDecimal类常用方法

BigDecimal x = a.add(b);                  // 加
BigDecimal x = a.subtract(b);             // 减
BigDecimal x = a.multiply(b);             // 乘
BigDecimal x = a.divide(b);               // 除
BigDecimal x = a.abs();                   // 绝对值
a.compareTo(b);                           // 比较大小

// BigDecimal 类专用
BigDecimal x = y.setScale(3, rules);      // 设置精度和保留规则

枚举

Enum类

(JDK 1.5 新增)比 Class 类 多了部分特殊约束的特殊类型,能更加简洁地定义常量。

使代码更具可读性,允许进行编译时检查,预先记录可接受值的列表,并避免由于传入无效值而引起的意外行为。

自定义枚举类实际是继承 Enum 类的 final 类,在类中将自定义该类型的 public static final 属性,并引入了相关方法。

// 定义枚举类
public enum Day {
    MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 使用枚举类
public class Demo {
    public boolean test(Day today){
        if(today == Day.MONDAY) return true;
        else teturn false;
    }
}

我们可以通过在枚举类型中定义属性,方法和构造函数让它变得更加强大。

实际开发中,枚举类通常的形式是有两个参数(int code,Sring msg)的构造器,可以作为状态码进行返回。

public enum StatusCodeEnum{

    SUCCESS(200,"成功"), NOTFOUND(404,"未找到"), ERROR(500,"错误");

    private int code;
    private String message;

    // 根据常量自动构造
    private StatusCodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "PinType{" +
                "code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

根据常量自动构造对象并调用方法

System.out.println(StatusCodeEnum.SUCCESS.getCode());


时间

数字类型

在日常 Java 开发中,我们最常使用 Long 类型,而不是 Date/Timestamp 类型表示时间。

我们可以通过 System.currentTimeMillis 方法获取当前系统时间,默认为 13 位的数字(精确到 ms)。

Long timestamp1 = System.currentTimeMillis();                 // 13 位 精确到 ms
Long timestamp2 = (System.currentTimeMillis()) / 1000;        // 10 位 精确到 s

泛型

泛型定义

定义类时并不固定数据类型,等到创建对象或调用方法时再明确数据类型。

编译过程中,由编译器检查类型安全,自动隐性地对类的数据类型进行强制转换(Object -> 指定数据类型)。编译后生成的 字节码文件(.class) 将不再含有泛型。

泛型使用

可使用 A-Z 之间的任何一个字母,常用:

  • T (type): 表示具体的一个 java 类型
  • K V (key value): 分别代表 java 键值中的 Key Value
  • E (element): 代表 java 集合框架元素
  • ?:表示不确定的 java 类
// 定义时使用泛型
public class Box<T> {
    private T t;
    public void set(T t) {
        this.t = t;
    }
    public T get() {
        return t;
    }
} 

// 调用时明确类型
class Test{
    static void main(String[] args){
        Box<Integer> myBox = new Box<>();
        myBox.set(3);
        System.out.print(myBox.get());
    }
}

五、数值比较和排序的常用方法


等值判断

Object 类实现了 equals 方法 ,用于比较两个数据元素是否相等。

浮点类型由于精度丢失问题,进行等值判断常出现错误。如果有需求推荐使用 BigDecimal 类

int a = 20 - 10;
int b = 10;
System.out.println(a.equals(b));         // true

double a = 20.0 - 10.0;
double b = 10.0;
System.out.println(a.equals(b));         // false

和 == 的区别

  1. 对于基本类型,两者等价:判断数据是否相等。

  2. 对于对象(如 String 类):

    • ==:比较两个元素内存地址是否相等,即是否是同一个元素。
    • equals 方法:比较两个元素内容是否一致。
System.out.println(s1 == s2);                 // 判断两个引用指向的内存地址是否相等  
System.out.println(s1.equals(s2));            // 判断两个引用指向的内存地址是否相等(s1 为空抛出空指针异常)
System.out.println(Objects.equals(s1,s2));    // 判断两个引用指向的元素是否一致(推荐)

重写 等于 方法

对于用户自定义类,正常使用等于 方法需要进行重写。重写 equals 方法必须重写哈希码 方法:以保证相同对象拥有相同的哈希地址。这样才能正常地把该类对象放入 HashSet/HashMap 等集合框架中查找。

Object 类的 hashcode 方法是本地方法(底层用 c/c++ 实现),直接返回对象的内存地址。

public class User{
    int ID;
    String name;

    ......

    @Override
    public boolean equals(Object obj) {
        if(this == obj)  return true;

        if(obj == null) return false;

        if(obj instanceof User){
            User other = (User) obj;
            if(equals(this.ID, other.ID) && equals(this.name, other.name)){
                return true;
            }
        }

        return false;
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + (ID == null ? 0 : ID.hashCode());
        result = 31 * result + (name == null ? 0 : name.hashCode());
        return result;
    }
}

数值比较

Comparator 接口和 Comparable 接口都用于比较两个元素的大小:

  1. Comparable 接口位于 java.lang 包内,定义在要比较的实体类内部:包含 compareTo 方法。

  2. Comparator 接口位于 java.util 包内,实现在类的外部:包含 compare 方法和 equals 方法。

Comparator 接口的 equals 方法和 Object 类的 equals 方法不同, Object 类的 equals 方法实现在实体类的内部。

compareTo方法

Java 自带数据类型均已实现 Comparable 接口并重写 compareTo 方法,默认情况下

  • 如果 s1 等于 s2,则返回 0;
  • 如果 s1 小于 s2,则返回小于 0 的值;
  • 如果 s1 大于 s2,则返回大于 0 的值。
Integer s1 = 100;
Integer s2 = 90;
System.out.println(s1.compareTo(s2));        

比较 方法

Arrays/Collections 类定义了 sort 方法对数组或者集合元素进行排列,数值的比较通过调用 Comparator 接口的 compare 方法实现。

执行 sort 方法时如果没有重写 compare 方法,默认调用的 compare 方法将会直接调用数据类型的 compareTo 方法,使数据从小到大排列。如果是自定义数据类型且未实现 compareTo 方法,则必须重写 compare 方法。

Arrays.sort(students);                      // 对数组排序
Collections.sort(students);                 // 对集合元素排序

开发者可以通过重写 compare 方法,实现自定义排列顺序。但要注意,如果数组中保存的是基础类型数据则无法自定义排序。

Arrays.sort(students, new Comparator<Student>() {          
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getID() - s2.getID();
    }            
});

Collections.sort(students, (s1, s2) ->  s1.getID() - s2.getID());     // 使用 Lamdba 表达式简写

数据排序

Arrays/Collections 类定义了 sort 方法对数组或者集合元素进行排列,数值的比较通过调用 Comparator 接口的 compare 方法实现。


六、异常


异常类型

可投掷类

Java 程序中的异常是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类。

java 程序中所有的异常都继承自 Throwable 类,Throwable 类有两个子类 Error 类和 Exception 类:

  • Error 类:【错误】表示 java 程序在运行时产生的无法处理的故障(如堆栈溢出),错误出现时会导致程序无法正常执行并强制退出。

  • Exception 类:【异常】表示 java 程序中产生的可以被处理的故障,异常出现时可以由程序进行处理。

运行时异常类

【运行时异常】 Exception 类的子类。

表示 java 程序运行状态中发生的异常,在编译时无法被检测。在 java 程序运行时会由系统自动抛出,允许应用程序不进行处理。

异常类型介绍
算术异常算术异常,以零做除数
ArrayIndexOutOfBoundException数组越界异常
NullPointerException空指针异常,对象不存在

已检查异常类

【可检查异常】Exception 类除 RuntimeException 以外其他子类的统称。

表示 java 程序编译时检测到的异常。出现时必须在程序中进行捕获或抛出,否则编译不会通过。

异常类型介绍
IOExceptionIO 异常
FileNotFoundException找不到文件异常,继承自 IO 异常
ClassNotFoundException找不到类异常

异常类

源码解析

状态信息

Throwable / Exception 类是有状态的(因此 Throwable 是接口而不能是类),记录了四个信息:

private transient Object backtrace;                          // 栈的回溯点
private String detailMessage;                                // 异常的信息:在创建异常时备注
private Throwable cause = this;                              // 异常的原因:导致该异常的异常,默认为自身
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;   // 异常的发生顺序:以栈的形式存储

构造方法

Throwable / Exception 类含有四个构造方法,在创建时可以记录异常信息:

throw new Exception();                           // 默认
throw new Exception("message");                  // 记录异常信息
throw new Exception(e);                          // 记录异常原因
throw new Exception("message", e);               // 记录详细信息和异常原因

常用方法

Throwable / Exception 类定义了多种常用方法用于获取异常数据,常用的有:

  • getMessage 方法:获取异常的信息。
  • getStackTrace 方法:获取的异常发生顺序。
  • printStackTrace 方法:获取异常的发生顺序并打印(开发和调试阶段用来显示异常信息,帮助开发者找出错误)。
catch(Exception e){
    System.out.println(e.getMessage());
    e.printStacTrace();                           
}

自定义异常

我们也可以通过继承并重写 Exception / RuntimeException 类的方式,自定义异常类并使用。

// 自定义异常,重写方法可任选
class MyException extends Exception {
    @Override
    public MyException() {
        super();
    }
    @Override
    public MyException(String message) {
        super(message);
    } 
    @Override
    public MyException(String message, Throwable cause){
        super(message,cause);
    }
    @Override
    public MyException(Throwable cause) {
        super(cause);
    }
}

异常转译

在项目开发过程中,当 Sevice/DAO 层出现如 SQLException 异常时,程序一般不会把底层的异常传到 controller 层。程序可以捕获原始异常,然后再抛出一个新的业务异常。

catch(SQLException e){
    throw new MyException("SQL Error", e);
}

异常处理

抛出异常throw

当方法执行出现问题时,方法就会创建异常对象并抛出。开发者可以在程序中自行抛出异常;JVM 在执行程序时发现问题也会自动抛出异常。

  • throw 语句:开发者自行创建异常对象并抛出,等待程序进行异常处理。

  • throws 语句:声明方法可能抛出某种异常且未经处理,调用该方法的上级需要进行异常处理。


class TestException{       
    // 把方法中的抛出异常交给上层处理     
    public void writeList(int size) throws IndexOutOfBoundsException, IOException{
        PrintWriter out = null;
        // 用户自定义异常并抛出
        if(size < 1) throw new IndexOutOfBoundsException("至少要输出1个字符");
        try{
            // 虚拟机自动发现异常也会抛出,必须出现在 try 代码块中
            out = new PrintWriter(new FileWriter(txt));
            for (int i = 0; i < size; i++)
                System.out.println("Value at: " + i + " = " + list.get(i));
        }finally{
            if (out != null) out.close();
        }
     }
}

捕获异常

当方法执行抛出异常时,必须由专门的代码块对异常进行处理。

  • try 语句:可能出现异常的代码块。

  • catch 语句:捕获相应异常后停止执行 try 代码,转而执行对应 catch 代码。如果没有异常 catch 代码不会执行。

  • final 语句:无论是否发生异常,最后 代码总会被执行。一般用于释放资源。

注意事项

  1. 如果 try 语句中出现的异常未被 catch,默认将异常 throw 给上层调用者处理。但必须在方法中声明 throws。

  2. try/catch 代码中的 return 语句会在执行完 最后 后再返回,但 final 中对返回变量的改变不会影响最终的返回结果。

  3. 最后 代码中应避免含有 return 语句或抛出异常,否则只会执行 最后 中的 返回 语句,且不会向上级抛出异常。

Java 7 后在 try 语句中打开 IO 流,会在跳出后自动关闭流。不必再用 最后 语句关闭。

class TestException{               
    public void writeList(int size) {
        PrintWriter out = null;
        try {
            if(size < 1) throw new IndexOutOfBoundsException("至少要输出1个字符");
            out = new PrintWriter(new FileWriter("OutFile.txt"));
            for (int i = 0; i < size; i++)
                System.out.println("Value at: " + i + " = " + list.get(i));
        } catch (IndexOutOfBoundsException e) {
            System.err.println("Caught IndexOutOfBoundsException: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("Caught IOException: " + e.getMessage());
        } finally {
            if (out != null) out.close();
        }
    }
}

七、正则匹配

基本使用

java.util.regex 包主要包括以下三个类:

  • 模式类

正则表达式的编译表示。没有公共构造方法,必须首先调用其公共静态编译方法获得 Pattern 对象。

  • 匹配器类

对输入字符串进行解释和匹配操作的引擎。没有公共构造方法,需要调用 Pattern 对象的 matcher 方法获得 Matcher 对象。

  • PatternSyntaxException 类

非强制异常类,表示正则表达式模式中的语法错误。

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexMatches {
    public static void main( String args[] ) {
        Pattern p = Pattern.compile("abc");        // 编译正则表达式
        Matcher matcher = p.matcher("abcdefg");    // 放入字符串中匹配

        System.out.println(matcher.lookingAt());   // 是否存在子串匹配 true
        System.out.println(matcher.matches());     // 是否完全匹配 false
    }
}

正则表达式

我们可以通过使用特殊符号,让一个正则表达式能够匹配多种符合要求的字符串。

表意符号

  • .表示任意字符

在 Java 中,正则表达式编译需要再经过一次转义。因此 才表示插入一个正则表达式的反斜线!\\

  • \\d表示一位数字
  • \\\\表示一个反斜杠

字符集

  • x|y匹配 x 或 y
  • [abc]匹配括号中任意单个字符
  • [^abc]匹配除括号中的任意单个字符
  • [a-zA-Z]匹配任意单个字母
  • [a-z&&[^def]]除 def 外的任意单个字母

字符串匹配

通过 、、 符号,我们可以对指定类型的字符串进行匹配。?*+

贪婪模式饥饿模式独占模式结果
X?X??X?+匹配0或1次
X*X*?X*+匹配0次或多次
X+X+?X++匹配1次或多次
X{n}X{n}?X{n}+匹配n次
X{m,n}X{m,n}?X{m,n}+匹配m-n次

在匹配字符串时,同一个正则表达式可能会在在字符串中匹配到多种结果。Java 提供了以下三种方式供开发者选择:

  • 贪婪模式 (默认)尽可能匹配长字符串。
  • 饥饿模式 (?)尽可能匹配短字符串。
  • 独占模式 (+)尽可能匹配长字符串,不成功会结束匹配而不回溯。

捕获组

普通捕获组

我们可以在正则表达式中同时捕获多个结果,最终以 group 的形式呈现。

  • matcher.group(0) 完全匹配整个正则表达式。
  • matcher.group(1-n) 从左到右分别记录正则表达式中 n 个括号内的结果。
public class RegexMatches {
    public static void main( String args[] ) {
        String regex = "(\\d{4})-((\\d{2})-(\\d{2}))"     
        Pattern p = Pattern.compile(regex);                // 编译正则表达式
        Matcher matcher = p.matcher("2020-10-25");         // 放入字符串

        matcher.find();                      // 执行匹配
        System.out.printf(matcher.group(0)); // 2020-10-25
        System.out.printf(matcher.group(1)); // 2020
        System.out.printf(matcher.group(2)); // 10-25
        System.out.printf(matcher.group(3)); // 10
        System.out.printf(matcher.group(4)); // 25
    }
}

命名捕获组

我们可以通过 对括号内容就行命名,并通过名称获取括号内的匹配结果。(?<Name>Expression)

public class RegexMatches {
    public static void main( String args[] ) {
        String regex = "(?<year>\\d{4})-(?<md>(?<month>\\d{2})-(?<date>\\d{2}))"; 
        Pattern p = Pattern.compile(regex);                 // 编译正则表达式
        Matcher matcher = p.matcher("2020-10-25");          // 放入字符串中匹配

        matcher.find();                            // 执行匹配
        System.out.printf(matcher.group("year"));  // 2020
        System.out.printf(matcher.group("md"));    // 10-25
        System.out.printf(matcher.group("month")); // 10
        System.out.printf(matcher.group("date"));  // 25
    }
}

非捕获组

我们可以通过 对组不进行捕获。(?:Expression)

  • (?=pattern)

例如,'Windows (?=95|98|NT|2000)' 匹配“Windows 2000”中的“Windows”,但不匹配“Windows 3.1”中的“Windows”。

预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

  • (?!pattern)

如 'Windows (?!95|98|NT|2000)' 匹配“Windows 3.1”中的 “Windows”,但不匹配“Windows 2000”中的“Windows”。

预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。


Java SE的基础知识就到这里了,小伙伴们掌握多少呢,让我们一起加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二两清酒.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值