JavaSE5️⃣核心类 - String 相关类

1、String

String:字符串

Java 最常用的引用类型之一。

1.1、简介

底层实现不可变、私有的字符数组(Java 9+ 改成字节数组)

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

    private final char value[];
}

不可变

  1. 现象
    1. 字符串对象创建后无法改变。
    2. 任何对字符串的修改,底层都会创建新的字符串对象并返回引用。
  2. 原因
    • value 数组:final 修饰、私有、没有提供修改方法。
    • String 类:final 修饰,无法被子类继承。
  3. 特点
    1. 优点:线程安全、节省空间(字符串常量池)
    2. 缺点:修改时频繁创建新的 String 对象

1.1.1、创建方式

字面量 new
含义 双引号 "" 包围 new String(参数)
存储位置 常量池
创建过程 检查常量池中是否存在,是则返回引用,否则创建并放在常量池中 每次创建一个全新的字符串对象
说明 允许 JVM 优化内存分配,节省空间 参数可以是字符串、字符数组、字节数组

使用 String.intern() 可主动将字符串对象放入常量池。

若常量池中已有该对象,直接返回对象引用。

否则:

  • JDK 1.6-:将对象副本放入串池,返回副本对象引用。
  • JDK 1.7+:将对象放入串池,返回对象引用。

1.1.2、特殊值(空)

特殊的字符串值

  1. 空值null):任何引用对象都可指向 null,表示不存在

  2. 空串""):值为空的有效字符串对象。

  3. 空格串" "):值为若干个空格

    String str1 = "ABC";
    String str2 = null;
    String str3 = "";
    

1.1.3、转义字符

转义字符\

将字符串中的特殊符号进行转义,避免程序识别错误。

  • 常用转义字符

    特殊符号 对应转义字符
    " \"
    ' \'
    \ \\
    换行符 \n
    回车符 \r
    缩进符 \t
    Unicode 字符 \u####
  • 示例

    // 三个字符:A 换行符 好
    String str = "A\n\u597D";
    

1.1.4、多行字符串

两种书写形式

  1. 拼接:使用 + 拼接多个字符串。

  2. 文本块:(Java 13)使用一对 """ 包围字符串内容。

    String str1 = "SELECT * " +
        "FROM tb_user" +
        "WHERE id = 1;";
    
    String str2 = """
        SELECT *
        FROM tb_user
        WHERE id = 1;
    """;
    

1.2、基础操作

1.2.1、比较

字符串比较

  1. 比较地址==
  2. 比较内容equals()
  3. 忽略大小写euqalsIgnoreCase()

示例:对于内容相同的字符串,分析 == 的结果。

  1. 字面量:s1 和 s2 是对常量池中同一个字符串对象的引用。

    String s1 = "hello";
    String s2 = "hello";
    
    System.out.println(s1.equals(s2));	// true
    System.out.println(s1 == s2);	// true
    
  2. 字面量 & new:s1 和 s2 分别在常量池和堆中,是不同的对象。

    String s1 = "hello";
    String s2 = new String("hello");
    
    System.out.println(s1.equals(s2));	// true
    System.out.println(s1 == s2);	// false
    
  3. new:s1 和 s2 均创建了新的字符串对象,是不同的对象。

    String s1 = new String("hello");
    String s2 = new String("hello");
    
    System.out.println(s1.equals(s2));	// true
    System.out.println(s1 == s2);	// false
    

1.2.2、判空

  1. isEmpty():(Java 6)判断字符串长度是否为 0,即空串("")。

    "".isEmpty();		// true
    " ".isEmpty();		// false
    "\n".isEmpty();		// false
    
  2. isBlank():(Java 11)判断字符串是否空白

    1. 空串("")、空格串(" ")、缩进符 \t,回车符 \r,换行符 \n

    2. 可理解为是否包含有意义字符

      "".isBlank();		// true
      " ".isEmpty();		// true
      "\n".isBlank();		// true
      

1.2.3、分割

分割字符串

  1. 方法签名:传入正则表达式,返回分割后的字符串数组。

    public String[] split(String regex) {}
    
  2. 示例

    String s = "A,B,C";
    String[] result = s.split(","); // ["A", "B", "C"]
    

2、类型转换

2.1、其它 → String (valueOf)

String.valueOf():重载的静态方法

引用类型、基本类型、字符数组 → String

2.1.1、引用类型

实现逻辑对象.toString()

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

2.1.2、基本类型

  1. 数值:实现逻辑是 包装类型.toString(xxx)

    public static String valueOf(int i) {}
    public static String valueOf(long l) {}
    public static String valueOf(float f) {}
    public static String valueOf(double d) {}
    
  2. 布尔

    public static String valueOf(boolean b) {
        return b ? "true" : "false";
    }
    
  3. 字符:实现逻辑是 new String()

    public static String valueOf(char c) {
        char data[] = {c};
        return new String(data, true);
    }
    

2.1.3、字符数组

本质new String() 的重载方法。

// 整个数组
public static String valueOf(char data[]) {
    return new String(data);
}

// 指定起始索引和字符个数
public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}

2.2、String → 包装类 (parseXxx)

各包装类中定义了 parseXxx() 静态方法。

Character 类除外)

  1. 整数型

    1. Byte、Short、Integer、Long 类中定义。

    2. 重载方法:默认十进制、指定进制。

    3. Integer 和 Long 支持无符号转换。

      // 有符号转换:四种类型
      public static 基本类型 parseXxx(String s)
          throws NumberFormatException {}
      
      public static 基本类型 parseXxx(String s, int radix)
          throws NumberFormatException {}
      
      // 无符号转换:Integer、Long
      public static 基本类型 parseUnsignedXxx(String s)
          throws NumberFormatException {}
      
      public static 基本类型 parseUnsignedXxx(String s, int radix)
          throws NumberFormatException {}
      
  2. 浮点型:Float 和 Double 类中定义。

    public static 基本类型 parseXxx(String s) 
        throws NumberFormatException {}
    
  3. 布尔型:Boolean 类中定义。

    public static boolean parseBoolean(String s) {}
    

2.3、字符数组

① new String(char[])

char[] → String:String 的构造方法。

  1. 方法签名

    1. 说明:对 char[] 的修改不会影响 String。

    2. 原因:不会直接引用传入的 char[],而是拷贝一份相同内容的数组,使用其副本。

      public String(char value[]) {}
      public String(char value[], int offset, int count) {}
      
  2. 示例

    char[] chs = {'a', 'b', 'c', 'd'};
    
    String s1 = new String(chs);		// abcd
    String s2 = new String(chs, 0, 2);	// ab
    

② toCharArray()

String → char[]:String 定义的实例方法。

  1. 方法签名

    public char[] toCharArray() {}
    
  2. 示例

    String str = "Hello";
    
    str.toCharArray();	// ['H', 'e', 'l', 'l', 'o']
    

3、子串操作

Hint:索引从 0 开始

3.1、搜索

3.1.1、索引位置

  1. 方法签名

    // 首次出现
    public int indexOf(String str) {}
    // 最后一次出现
    public int lastIndexOf(String str) {}
    
  2. 示例

    "Hello".indexOf("l");	// 2
    "Hello".lastIndexOf("l");	// 3
    

3.1.2、判断包含

  1. 方法签名:形参是 CharSequence 接口,String 是其实现类。

    public boolean contains(CharSequence s) {}
    
  2. 示例

    "Hello".contains("lo");	// true
    "Hello".contains("ol");	// false
    

3.1.3、判断前后缀

  1. 方法签名

    // 是否以prefix开头
    public boolean startsWith(String prefix) {}
    // 是否以suffix结尾
    public boolean endsWith(String suffix) {}
    
  2. 示例

    "Hello".startsWith("He"); // true
    "Hello".endsWith("lo"); // true
    

3.2、提取

3.2.1、子串

  1. 方法签名

    // 指定索引之后
    public String substring(int beginIndex) {}
    // 指定索引范围(前闭后开)
    public String substring(int beginIndex, int endIndex){}
    
  2. 示例

    "Hello".substring(2);		// "llo"
    "Hello".substring(2, 4);	// "ll"
    

3.2.2、移除首尾空白字符

两种方式

  1. trim():包含英文空格 、缩进符 \t,回车符 \r,换行符 \n

    "  \tHello\r\n ".trim();	// "Hello"
    
  2. strip():(Java 11+)在 trim() 的基础上,移除中文空格字符 \u3000

    "\u3000Hello\u3000".strip();	// "Hello"
    " Hello ".stripLeading();		// "Hello "
    " Hello ".stripTrailing();		// " Hello"
    

3.3、替换子串

3.3.1、字符/字符串

  1. 方法签名

    // 替换掉所有oldChar
    public String replace(char oldChar, char newChar) {}
    
    // 替换掉所有target
    public String replace(CharSequence target,
                          CharSequence replacement) {}
    
  2. 示例

    String s = "hello";
    
    s.replace('l', 'x');	// hexxo
    s.replace("l", "xx");	// hexxxxo
    

3.3.2、正则表达式

  1. 方法签名

    // 替换掉首次匹配的 regex
    public String replaceFirst(String regex,
                               String replacement) {}
    // 替换掉所有匹配的 regex
    public String replaceAll(String regex,
                             String replacement) {}
    
  2. 示例

    String s2 = "A,,B;C ,D";
    s2.replaceFirst("[,;\\s]+", ",");   // "A,B;C ,D"
    s2.replaceAll("[,;\\s]+", ",");     // "A,B,C,D"
    

4、字符串拼接

4.1、运算符 +

使用 + 连接多个任意类型的变量。

连接之前,其它数据类型自动转型为 String

  1. 拼接原理

    常量拼接 变量拼接
    含义 常量(含 final 变量)之间的拼接 涉及变量的拼接
    原理 编译期优化 StringBuilder.append()
    具体过程 编译器拼接所有常量作为一个对象,检查常量池中是否已有该对象,是则返回引用,否则放入
  2. 示例

    String str = "A" + "B" +"C";	// 一个对象: ABC
    

4.2、concat()

String 实例方法

  1. 方法源码:创建一个新的 String 对象。

    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
    
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        // 将str复制到buf数组之后
        str.getChars(buf, len);
        return new String(buf, true);
    }
    
  2. 示例

    String str1 = "ABC";
    String str2 = str1.concat("DEF");
    

4.3、Buffer/Builder

4.3.1、使用场景

频繁拼接字符串的场景下,

使用 +concat() 有以下缺点。

  • 每次拼接都创建新的对象,丢弃旧的对象。
  • 大量临时对象,浪费内存,降低 GC 效率

4.3.2、API

① 共同点

StringBuffer & StringBuilder 共同点

抽象类 AbstractStringBuilder 的子类

  1. 可变对象:预分配缓存区,长度不足时自动扩容。

    // 存储元素
    char[] value;
    
    // 记录数组已使用字符个数
    int count;
    
  2. 链式编程:方法返回值是 this,可链式调用。

② 对比

String StringBuffer StringBuilder
版本 Java 1.0 Java 1.0 Java 1.5
可变性
特点 任何操作都创建新对象,效率低 线程安全,效率低
synchronized 方法
线程不安全,效率高
存储位置 字符串常量池

4.4、Joiner

4.4.1、使用场景

StringJoiner(Java 8+)

场景:使用分隔符连接数组元素。

  1. 作用:用于构造字符序列。

    • 由分隔符分割。
    • 可指定前后缀。
  2. 示例

    String[] nameList = {"Alice", "Beta", "Cindy"};
    
    StringJoiner joiner = new StringJoiner(", ", "---", "!---");
    for (String name : nameList) {
        joiner.add(name);
    }
    
    System.out.println(joiner.toString());	// ---Alice, Beta, Cindy!---
    

4.4.2、StringJoiner 源码

① 成员变量

  1. 前缀、后缀、分隔符(String)

  2. 字符串值(StringBuilder)

  3. 空值(String,默认是前后缀直接拼接)

    private final String prefix;
    private final String delimiter;
    private final String suffix;
    
    private StringBuilder value;
    
    private String emptyValue;
    

② 构造方法

  1. 指定分隔符、前后缀

    public StringJoiner(CharSequence delimiter,
                        CharSequence prefix,
                        CharSequence suffix) {
        // 判空
        Objects.requireNonNull(prefix, "The prefix must not be null");
        Objects.requireNonNull(delimiter, "The delimiter must not be null");
        Objects.requireNonNull(suffix, "The suffix must not be null");
        
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        
        this.emptyValue = this.prefix + this.suffix;
    }
    
  2. 指定分隔符:前后缀默认为空串。

    public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
    }
    

③ 拼接

prepareBuilder():add() 和 merge() 的预处理,

保证存储元素的 StringBuilder 是正确的。

  1. value 非空:直接拼接分隔符。

  2. value 空:创建 StringBuilder 对象,拼接前缀。

    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }
    

add():连接一个 CharSequence 变量。

  1. 预处理

  2. 拼接内容

    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }
    

merge():连接一个 Joiner 内容,不包含前后缀。

  1. 判空

  2. 保存 other 的长度,预处理 builder

  3. 连接 other 的内容。

    public StringJoiner merge(StringJoiner other) {
        Objects.requireNonNull(other);
        
        if (other.value != null) {
            final int length = other.value.length();
    
            StringBuilder builder = prepareBuilder();
            // 追加内容, 起始索引, 长度
            builder.append(other.value, other.prefix.length(), length);
        }
        return this;
    }
    

④ toString()

返回当前 value 值

  1. value 为空:直接返回 emptyValue。

  2. value 非空

    1. 后缀为空:直接返回 value。

    2. 后缀非空:拼接后缀,设置 value 的正确长度。

      @Override
      public String toString() {
          if (value == null) {
              return emptyValue;
          } else {
              if (suffix.equals("")) {
                  return value.toString();
              } else {
                  int initialLength = value.length();
                  String result = value.append(suffix).toString();
                  // reset value to pre-append initialLength
                  value.setLength(initialLength);
                  return result;
              }
          }
      }
      

⑤ 其它

  1. setEmptyValue():设置空值,取代默认的前后缀拼接。

    public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }
    
  2. length():返回字符串长度,等价于 toString().length()

    public int length() {
        return (value != null 
                ? value.length() + suffix.length()
                : emptyValue.length());
    }
    

4.4.3、String.join()

本质:仅指定分割符的 StringJoiner。

若无需指定前后缀,使用 String.join() 更方便。

  1. 方法源码:实例化 StringJoiner,仅指定分隔符。

    public static String join(CharSequence delimiter,
                              CharSequence... elements) {
        // 判空
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // 字符串连接对象
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }
    
  2. 示例

    String[] arr = {"A", "B", "C"};
    String s = String.join("***", arr); // "A***B***C"
    

4.5、场景小结

  1. 无需拼接,使用 String
  2. 非循环体拼接,使用 +concat()
  3. 循环体拼接/频繁拼接,使用 StringBuilder并发场景则使用 StringBuffer)。
  4. 分隔符连接,使用 StringJoiner
posted @ 2023-02-21 16:36  Jaywee  阅读(14)  评论(0编辑  收藏  举报

👇