一篇与众不同的 String、StringBuilder 和 StringBuffer 详解(三)

简介: 这是一道老生常谈的问题了,字符串是不仅是 Java 中非常重要的一个对象,它在其他语言中也存在。比如 C++、Visual Basic、C# 等。

String

首先来看一下 String 类在继承树的什么位置、实现了什么接口、父类是谁,这是源码分析的几大重要因素。

String 没有继承任何接口,不过实现了三个接口,分别是 Serializable、Comparable、CharSequence 接口

  • Serializable :这个序列化接口没有任何方法和域,仅用于标识序列化的语意。
  • Comparable:实现了 Comparable 的接口可用于内部比较两个对象的大小
  • CharSequence:字符串序列接口,CharSequence 是一个可读的 char 值序列,提供了 length(), charAt(int index), subSequence(int start, int end) 等接口,StringBuilder 和 StringBuffer 也继承了这个接口

重要属性

字符串是什么,字!符!串!你品,你细品。你会发现它就是一连串字符组成的串。

也就是说

String str = "abc"; 
// === 
char data[] = {'a', 'b', 'c'};
String str = new String(data);

原来这么回事啊!

微信图片_20220414202130.png

所以,String 中有一个用于存储字符的 char 数组 value[],这个数组存储了每个字符。另外一个就是 hash 属性,它用于缓存字符串的哈希码。因为 String 经常被用于比较,比如在 HashMap 中。如果每次进行比较都重新计算其 hashcode 的值的话,那无疑是比较麻烦的,而保存一个 hashcode 的缓存无疑能优化这样的操作。

微信图片_20220414202414.png

String 可以通过许多途径创建,也可以根据 Stringbuffer 和 StringBuilder 进行创建。

微信图片_20220414202133.png

毕竟我们本篇文章探讨的不是源码分析的文章,所以涉及到的源码不会很多。

除此之外,String 还提供了一些其他方法

  • charAt :返回指定位置上字符的值
  • getChars: 复制 String 中的字符到指定的数组
  • equals: 用于判断 String 对象的值是否相等
  • indexOf : 用于检索字符串
  • substring: 对字符串进行截取
  • concat: 用于字符串拼接,效率高于 +
  • replace:用于字符串替换
  • match:正则表达式的字符串匹配
  • contains: 是否包含指定字符序列
  • split: 字符串分割
  • join:  字符串拼接
  • trim: 去掉多余空格
  • toCharArray: 把 String 对象转换为字符数组
  • valueOf: 把对象转换为字符串

StringBuilder

StringBuilder 类表示一个可变的字符序列,我们知道,StringBuilder 是非线程安全的容器,一般适用于单线程场景中的字符串拼接操作,下面我们就来从源码角度看一下 StringBuilder

首先我们来看一下 StringBuilder 的定义

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence {...}

StringBuilder 被 final 修饰,表示 StringBuilder 是不可被继承的,StringBuilder 类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder 类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt() 方法等。

StringBuilder 实现了 2 个接口

  • Serializable 序列化接口,表示对象可以被序列化。
  • CharSequence 字符序列接口,提供了几个对字符序列进行只读访问的方法,例如 ength()、charAt()、subSequence()、toString() 方法等。

StringBuilder 使用 AbstractStringBuilder 类中的两个变量作为元素

char[] value; // 存储字符数组
int count; // 字符串使用的计数

StringBuffer

StringBuffer 也是继承于 AbstractStringBuilder ,使用 value 和 count 分别表示存储的字符数组和字符串使用的计数,StringBuffer 与 StringBuilder 最大的区别就是 StringBuffer 可以在多线程场景下使用,StringBuffer 内部有大部分方法都加了 synchronized 锁。在单线程场景下效率比较低,因为有锁的开销。

StringBuilder 和 StringBuffer 的扩容问题

我相信这个问题很多同学都没有注意到吧,其实 StringBuilder 和 StringBuffer 存在扩容问题,先从 StringBuilder 开始看起

首先先注意一下 StringBuilder 的初始容量

public StringBuilder() {
  super(16);
}

StringBuilder 的初始容量是 16,当然也可以指定 StringBuilder 的初始容量。

在调用 append 拼接字符串,会调用 AbstractStringBuilder 中的 append 方法

public AbstractStringBuilder append(String str) {
  if (str == null)
    return appendNull();
  int len = str.length();
  ensureCapacityInternal(count + len);
  str.getChars(0, len, value, count);
  count += len;
  return this;
}

上面代码中有一个 ensureCapacityInternal 方法,这个就是扩容方法,我们跟进去看一下

private void ensureCapacityInternal(int minimumCapacity) {
  // overflow-conscious code
  if (minimumCapacity - value.length > 0) {
    value = Arrays.copyOf(value,
                          newCapacity(minimumCapacity));
  }
}

这个方法会进行判断,minimumCapacity 就是字符长度 + 要拼接的字符串长度,如果拼接后的字符串要比当前字符长度大的话,会进行数据的复制,真正扩容的方法是在 newCapacity

private int newCapacity(int minCapacity) {
  // overflow-conscious code
  int newCapacity = (value.length << 1) + 2;
  if (newCapacity - minCapacity < 0) {
    newCapacity = minCapacity;
  }
  return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
    ? hugeCapacity(minCapacity)
    : newCapacity;
}

扩容后的字符串长度会是原字符串长度增加一倍 + 2,如果扩容后的长度还比拼接后的字符串长度小的话,那就直接扩容到它需要的长度  newCapacity = minCapacity,然后再进行数组的拷贝。


4

总结



本篇文章主要描述了 String 、StringBuilder 和 StringBuffer 的主要特性,String、StringBuilder 和 StringBuffer 的底层构造是怎样的,以及 String 常量池的优化、StringBuilder 和 StringBuffer 的扩容特性等。

如果有错误的地方,还请大佬们提出宝贵意见。

相关文章
|
1月前
|
安全 Java
Java StringBuffer 和 StringBuilder 类
Java StringBuffer 和 StringBuilder 类
16 0
|
1天前
|
存储 编解码 算法
Java 的 String StringBuilder StringBuffer(上)
Java 的 String StringBuilder StringBuffer
18 0
|
16天前
|
移动开发 安全 Java
String、StringBuffer 、StringBuilder、StringJoiner
String、StringBuffer 、StringBuilder、StringJoiner
|
1月前
|
存储 算法 安全
【数据结构与算法初学者指南】【冲击蓝桥篇】String与StringBuilder的区别和用法
【数据结构与算法初学者指南】【冲击蓝桥篇】String与StringBuilder的区别和用法
|
1月前
|
存储 安全 Java
String、StringBuilder、StringBuffer的区别
String、StringBuilder、StringBuffer的区别
13 0
|
15天前
|
Java API 索引
Java基础—笔记—String篇
本文介绍了Java中的`String`类、包的管理和API文档的使用。包用于分类管理Java程序,同包下类无需导包,不同包需导入。使用API时,可按类名搜索、查看包、介绍、构造器和方法。方法命名能暗示其功能,注意参数和返回值。`String`创建有两种方式:双引号创建(常量池,共享)和构造器`new`(每次新建对象)。此外,列举了`String`的常用方法,如`length()`、`charAt()`、`equals()`、`substring()`等。
15 0
|
30天前
|
Java
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
【Java】如果一个集合中类型是String如何使用拉姆达表达式 进行Bigdecimal类型计算?
25 0
|
1月前
|
Java
Java String split()方法详细教程
Java String split()方法详细教程
23 0
|
1月前
|
存储 缓存 安全
【Java】Java中String不可变性的底层实现
【Java】Java中String不可变性的底层实现
16 0
|
1月前
|
Java 索引
Java中String方法学习总结_kaic
Java中String方法学习总结_kaic