JavaRush /Java 博客 /Random-ZH /Java中String、StringBuffer和StringBuilder简介

Java中String、StringBuffer和StringBuilder简介

已在 Random-ZH 群组中发布
Java 中有三个类用于处理文本数据:StringStringBufferStringBuilder。每个开发人员在学习语言之初都会遇到第一个问题。剩下的两个呢?它们有什么区别,什么时候使用一个或另一个类更好?一般来说,它们之间的差异很小,但最好在实践中理解一切:) Java 中的 String、StringBuffer 和 StringBuilder 简介 - 1

字符串类

这个类代表一个字符序列。程序中定义的所有字符串文字(例如“This is String”)都是 String 类的实例。 字符串有两个基本特征:
  • 这是一个不可变的类
  • 这是最后一堂课
一般来说,String 类不能有子类 ( final),并且该类的实例在创建后不能修改 ( immutable)。这给了 String 类几个重要的优点:
  1. 由于不变性,String 类实例的哈希码被缓存。不需要每次都评估,因为对象的字段值在创建后就永远不会改变。当使用此类作为 的键时,这会提供高性能HashMap

  2. String 类可以在多线程环境中使用,无需额外的同步。

  3. String类的另一个特点是它重载了+Java中的“”运算符。因此,字符串的连接(相加)非常简单:

public static void main(String[] args) {
    String command = "Follow" + " " + "the" + " " + "white" + " " + "rabbit";
    System.out.println(command); // Follow the white rabbit
}
在幕后,字符串连接是由 StringBuilder 或 StringBuffer 类(由编译器决定)和一个方法append(我们稍后将讨论这些类)执行的。如果我们将 String 类的实例与其他类的实例相加,后者将被简化为字符串表示形式:
public static void main(String[] args) {
    Boolean b = Boolean.TRUE;
    String result = "b is " + b;
    System.out.println(result); //b is true
}
这是 String 类的另一个有趣的属性:任何类的对象都可以使用toString()类中定义的方法转换为字符串表示形式Object,并由所有其他类继承。通常,对象上的 toString() 方法会被隐式调用。例如,当我们在屏幕上显示某些内容或将 String 添加到另一个类的对象时。String 类还有一项功能。Java 代码中定义的所有字符串文字(例如“asdf”)都会在编译时缓存并添加到所谓的字符串池中。如果我们运行以下代码:
String a = "Wake up, Neo";
String b = "Wake up, Neo";

System.out.println(a == b);
我们将在控制台中看到true,因为变量a实际上b引用在编译时添加到字符串池中的 String 类的同一实例。即不会创建具有相同值的类的不同实例,并且节省了内存。

缺陷:

不难猜测 String 类主要用于处理字符串。但在某些情况下,String类的上述特性可能会从优点变成缺点。一旦在 Java 代码中创建了字符串,通常会对它们执行许多操作:
  • 将字符串转换为不同的寄存器;
  • 子串提取;
  • 级联;
  • ETC。
我们看一下这段代码:
public static void main(String[] args) {

    String s = " Wake up, Neo! ";
    s = s.toUpperCase();
    s = s.trim();

    System.out.println("\"" + s + "\"");
}
乍一看,我们似乎只是翻译了“醒来,尼奥!”这句话。改为大写,从此字符串中删除多余的空格并将其用引号引起来。事实上,由于String类的不可变性,每次操作都会创建新的字符串实例并丢弃旧的字符串实例,从而产生大量垃圾。如何避免内存浪费?

字符串缓冲区类

要处理由于修改 String 对象而产生的临时垃圾,可以使用 StringBuffer 类。这是mutable一个类,即 多变。StringBuffer 类的对象可以包含一组特定的字符,可以通过调用某些方法来更改其长度和值。让我们看看这个类是如何工作的。要创建新对象,请使用其构造函数之一,例如:
  • StringBuffer() - 将创建一个空(无字符)对象
  • StringBuffer(String str) - 将基于 str 变量创建一个对象(包含同一序列中 str 的所有字符)
实践:
StringBuffer sb = new StringBuffer();
StringBuffer sb2 = new StringBuffer("Not empty");
Java 中通过 StringBuffer 进行字符串连接是使用append. 一般来说,StringBuffer 类中的方法append被重载为几乎可以接受任何数据类型:
public static void main(String[] args) {
    StringBuffer sb = new StringBuffer();

    sb.append(new Integer(2));
    sb.append("; ");
    sb.append(false);
    sb.append("; ");
    sb.append(Arrays.asList(1,2,3));
    sb.append("; ");

    System.out.println(sb); // 2; false; [1, 2, 3];
}
该方法append返回调用它的对象(像许多其他方法一样),这允许在“链”中调用它。上面的例子可以这样写:
public static void main(String[] args) {
    StringBuffer sb = new StringBuffer();

    sb.append(new Integer(2))
            .append("; ")
            .append(false)
            .append("; ")
            .append(Arrays.asList(1,2,3))
            .append("; ");

    System.out.println(sb); // 2; false; [1, 2, 3];
}
StringBuffer 类有许多处理字符串的方法。让我们列出主要的:
  • delete(int start, int end)start— 删除从位置 开始、结束的字符子串end
  • deleteCharAt(int index)— 删除位置处的字符index
  • insert(int offset, String str)str—在位置插入一行offset。该方法insert也是重载的并且可以采用不同的参数
  • replace(int start, int end, String str)- 将替换从一个位置startend另一个位置的所有字符str
  • reverse()— 反转所有字符的顺序
  • substring(int start)- 将返回从位置开始的子字符串 start
  • substring(int start, int end)start- 将返回从一个位置开始到另一个位置的子字符串end
官方文档中有完整的方法和构造函数列表。上述方法如何发挥作用?让我们在实践中看看:
public static void main(String[] args) {
     String numbers = "0123456789";

     StringBuffer sb = new StringBuffer(numbers);

     System.out.println(sb.substring(3)); // 3456789
     System.out.println(sb.substring(4, 8)); // 4567
     System.out.println(sb.replace(3, 5, "ABCDE")); // 012ABCDE56789

     sb = new StringBuffer(numbers);
     System.out.println(sb.reverse()); // 9876543210
     sb.reverse(); // Return the original order

     sb = new StringBuffer(numbers);
     System.out.println(sb.delete(5, 9)); // 012349
     System.out.println(sb.deleteCharAt(1)); // 02349
     System.out.println(sb.insert(1, "One")); // 0One2349
    }

优点:

  1. 正如已经提到的,StringBuffer 是一个可变类,因此使用它不会产生与 String 相同数量的内存垃圾。因此,如果对字符串进行大量修改,最好使用StringBuffer.

  2. StringBuffer — потокобезопасный класс. Его методы синхронизированы, а экземпляры могут быть использованы несколькими потоками одновременно.

Недостатки:

С одной стороны, потокобезопасность — преимущество класса, а другой — недостаток. Синхронизированные методы работают медленнее не сихнронизированных. И здесь в игру вступает StringBuilder. Давайте разберемся, что это за класс Java — StringBuilder, Howие методы есть и в чем его особенности.

Класс StringBuilder

StringBuilder в Java — класс, представляющий последовательность символов. Он очень похож на StringBuffer во всем, кроме потокобезопасности. StringBuilder предоставляет API, аналогичный API StringBuffer’a. Продемонстрируем это на уже знакомом примере, заменив объявление переменных со StringBufer’а на StringBuilder:
public static void main(String[] args) {
    String numbers = "0123456789";

    StringBuilder sb = new StringBuilder(numbers);

    System.out.println(sb.substring(3)); //3456789
    System.out.println(sb.substring(4, 8)); //4567
    System.out.println(sb.replace(3, 5, "ABCDE")); //012ABCDE56789

    sb = new StringBuilder(numbers);
    System.out.println(sb.reverse()); //9876543210
    sb.reverse(); // Return the original order

    sb = new StringBuilder(numbers);
    System.out.println(sb.delete(5, 9)); //012349
    System.out.println(sb.deleteCharAt(1)); //02349
    System.out.println(sb.insert(1, "One")); //0One2349
}
Difference лишь в том, что StringBuffer потокобезопасен, и все его методы синхронизированы, а StringBuilder — нет. Это единственная особенность. StringBuilder в Java работает быстрее StringBuffer’а благодаря несинхронизированности методов. Поэтому в большинстве случаев, кроме многопоточной среды, лучше использовать для программы на Java StringBuilder. Резюмируем все в сравнительной таблице трех классов:

String vs StringBuffer vs StringBuilder

String StringBuffer StringBuilder
Изменяемость Immutable (нет) mutable (да) mutable (да)
Расширяемость final (нет) final (нет) final (нет)
Потокобезопасность Да, за счет неизменяемости Да, за счет синхронизации Нет
Когда использовать При работе со строками, которые редко будут модифицироваться При работе со строками, которые часто будут модифицироваться в многопоточной среде При работе со строками, которые часто будут модифицироваться, в однопоточной среде
Изучить данную тему более подробно можно на втором уровне квеста Java Multithreading на курсе JavaRush:
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION