1. public final class String
  2. implements java.io.Serializable, Comparable<String>, CharSequence {
  3. /** The value is used for character storage. */
  4. private final char value[];
  5. }
  1. String 类是不可变 字符序列;
  2. 判断 String 对象值是否相等使用 equals() 方法;

这里是以 jdk1.8 为例进行说明,其他版本可能有所不同。比如 1.9 String 是由 byte 数组构成,下面字符串构造中的内存示意图也会不太一样。

对于 1.8 而言,字符串池 StringTable 也从方法区移到了堆,所以不论是否位于 StringTable 中的字符串对象,都处于堆内存中;

字符串的构造

常见创建字符串对象有以下 6 种方式:下面根据例子详细介绍。

  1. 使用 char 数组;
  2. 使用 byte 数组;
  3. 使用 int 数组;
  4. 使用 String;
  5. 使用字面量;
  6. 使用 + 拼接 String 对象:String str = “a” + “bc”;

    char[] - char 数组

    1. String str = new String(new char[]{'a', 'b', 'c'});
    内存示意:
    image.png
    s 处于栈内存,字符串对象处于堆内存。一个字符串对象由一个 char 数组对象和一个 hash 码组成;char 数组位于堆的另一块内存。

    字符在内存中的存储是以 Unicode 编码存储的,其中 97 就是 ‘a’。

byte[] - byte 数组

byte 数组可能由网络传递的字节数据或 IO 操作读取的数据;

String str = new String(new byte[]{97, 98, 99});

内存示意:
image.png
byte 数组在构造过程会转为 char 数组存储。其中 byte 大小为 1 字节,char 大小为 2 字节。

另外还可能由于字符集导致结果不同,因为不同编码一个字符包含的字节数不同,比如 gbk 编码下一个汉字是两个字节,而 utf-8 编码下一个汉字是三个字节。

对于 java 中的 char 字符都是统一 Unicode 编码的,从外界不同编码的字节数组都会统一存储。

int[] - int 数组

String str = new String(new int[]{0x1F602}, 0, 1); // 笑哭表情

转换内存示意:
image.png

String - 从已有字符串构造

源码:

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}
String abc = new String(new char[]{'a', 'b', 'c'});
String str = new String(abc);

内存示意:
image.png

字面量

String str = "abc";

字符串字面量有以下特点:非对象、懒加载、不重复

  1. 非对象:即在代码运行到字面量所在语句之前,它还不是字符串对象。

从字节码的角度分析,代码编译为 class 文件后,字面量 “abc” 存储于 类文件常量池 中;class 完成类加载之后,字面量 “abc” 存储于 运行时常量池 中;只有当字节码指令调用 ldc 指令加载常量池字面量时,才会将字面量 “abc” 创建为堆内存中的字符串对象。(首先会根据字面量 “abc” 创建一个 char 数组,然后创建一个字符串对象使 value 指向该数组)
image.png

  1. 懒加载:即只有运行到字面量所在代码代表的指令时,才会创建对应字符串对象;
  2. 不重复:同一个类中,相同字面量值内存中对应的字符串对象只有一份;

    常用方法

    JDK8 API
方法 解释说明
char charAt(int index) 返回字符串中索引为index的字符,索引范围(0,lenth()-1);
boolean equals(Object anObject) 如果字符串与anObject相等,返回true;否则返回false;
boolean equalsIgnoreCase(String other) 判断字符串是否相等(忽略大小写);
int indexOf(String str) 返回从头开始查找的第一个子字符串str在字符串中的索引位置。如果未找到子字符串str,则返回-1;
int lastIndexOf(String str) 返回从末尾开始查找的第一个子字符串str在字符串中的索引位置。如果未找到子字符串str,则返回-1;
int length() 返回字符串的长度;
String replace(char oldChar, char new Char) 返回一个新串,它是通过用newChar替换此字符串中出现的所有oldChar而生成的;
boolean startsWith(String prefix) 如果字符串以prefix开始,则返回true;
boolean endsWith(String prefix) 如果字符串以prefix结尾,则返回true;
String substring(int beginIndex) 返回一个新字符串,该串包含从原始字符串beginIndex到串尾;
String substring(int beginIndex, int endIndex) 返回一个新字符串,该串包含从原始字符串beginIndex到串尾或endIndex-1的所有字符;
String toLowerCase() 返回一个新字符串,该串将原始字符串中的所有大写字母改成小写字母;
String toUpperCase() 返回一个新字符串,该串将原始字符串中的所有小写字母改成大写字母;
String trim() 返回一个新字符串,该串删除了原始字符串头部和尾部的空格

StringBuilder和StringBuffer

StringBuilder 和 StringBuffer 共同的父类 AbstractStringBuilder

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;
}
  • StringBuilder 和 StringBuffer 是可变字符序列;
  • StringBuffer JDK1.0 版本提供的类,线程安全,做线程同步检查,效率较低;
  • StringBuilder JDK1.5 版本提供的类,线程不安全,不做线程同步检查,因此效率较高,常用;

常用方法

方法 解释说明
public StringBuilder append(…) 重载的方法,可以为StringBuilder对象添加字符序列,仍然返回自身对象;
public StringBuilder delete(int start, int end) 可以删除从start到end-1为止的一段字符串序列,仍然返回自身对象;
public StringBuilder deleteCharAt(int index) 移除此序列索引为index的字符,仍然返回自身对象;
public StringBuilder insert(…) 重载的方法,可以为StringBuilder对象在指定位置插入字符序列,仍然返回自身对象;
public StringBuilder reverse() 将字符串序列逆序,仍然返回自身对象;
public String toString() 返回此序列中数据的字符串表示形式;
public void setCharAt(int index, char c) 将c替换到原字符序列索引为index的位置;
indexOf(), substring(), length(), charAt() 类似String类中的方法;

从 jdk 5 开始,Java 就对 String 字符串的 + 操作进行了优化,该操作编译成字节码文件后会被优化为 StringBuilder 的 append 操作。