Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
沉默王二
Jmx Java
提交
5eff31fa
J
Jmx Java
项目概览
沉默王二
/
Jmx Java
10 个月 前同步成功
通知
160
Star
18
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
Jmx Java
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
5eff31fa
编写于
2月 24, 2023
作者:
沉默王二
💬
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
聊聊String、StringBuilder、StringBuffer 三兄弟
上级
37538c05
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
514 addition
and
1 deletion
+514
-1
README.md
README.md
+1
-0
docs/.vuepress/sidebar.ts
docs/.vuepress/sidebar.ts
+1
-0
docs/home.md
docs/home.md
+1
-0
docs/overview/what-is-java.md
docs/overview/what-is-java.md
+29
-0
docs/string/builder-buffer.md
docs/string/builder-buffer.md
+232
-0
docs/string/intern.md
docs/string/intern.md
+1
-1
二哥的 Java 进阶之路.md
二哥的 Java 进阶之路.md
+249
-0
未找到文件。
README.md
浏览文件 @
5eff31fa
...
...
@@ -119,6 +119,7 @@
-
[
聊聊Java字符串,以及为什么String是不可变的?
](
docs/string/immutable.md
)
-
[
深入理解Java字符串常量池
](
docs/string/constant-pool.md
)
-
[
深入解析String.intern()方法
](
docs/string/intern.md
)
-
[
聊聊String、StringBuilder、StringBuffer 三兄弟
](
docs/string/builder-buffer.md
)
-
[
Java如何判断两个字符串是否相等?
](
docs/string/equals.md
)
-
[
最优雅的Java字符串拼接是哪种方式?
](
docs/string/join.md
)
-
[
如何在Java中优雅地分割String字符串?
](
docs/string/split.md
)
...
...
docs/.vuepress/sidebar.ts
浏览文件 @
5eff31fa
...
...
@@ -126,6 +126,7 @@ export const sidebarConfig = sidebar({
"
string/immutable
"
,
"
string/constant-pool
"
,
"
string/intern
"
,
"
string/builder-buffer
"
,
"
string/equals
"
,
"
string/join
"
,
"
string/split
"
,
...
...
docs/home.md
浏览文件 @
5eff31fa
...
...
@@ -128,6 +128,7 @@ head:
-
[
聊聊Java字符串,以及为什么String是不可变的?
](
string/immutable.md
)
-
[
深入理解Java字符串常量池
](
string/constant-pool.md
)
-
[
深入解析String.intern()方法
](
string/intern.md
)
-
[
聊聊String、StringBuilder、StringBuffer 三兄弟
](
docs/string/builder-buffer.md
)
-
[
Java如何判断两个字符串是否相等?
](
string/equals.md
)
-
[
最优雅的Java字符串拼接是哪种方式?
](
string/join.md
)
-
[
如何在Java中优雅地分割String字符串?
](
string/split.md
)
...
...
docs/overview/what-is-java.md
浏览文件 @
5eff31fa
...
...
@@ -28,6 +28,8 @@ head:
“三妹啊,听我慢慢来给你解释。”
### Java 的由来
Java 是一门计算机编程语言,高级、健壮、面向对象,并且非常安全。它由 Sun 公司在 1995 年开发,主力开发叫 James Gosling,被称为 Java 之父,就是下图这位,头秃的厉害。
![](
https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/overview/one-01.png
)
...
...
@@ -70,6 +72,33 @@ public class HelloWorld {
“好吧。”
### Java 是编译型语言还是解释型语言
“二哥,你之前给我看了 .class 文件和 .java 源代码,它们之间的关系是什么样的呢?”三妹还是挺喜欢学习的嘛,发现的问题都很关键。
“不错不错,都能挖掘到这个点了。”
.java 是源代码,也就是我们开发人员可以看懂的,可以编写的;.class 是字节码文件,是经过 javac 编译后的文件,是交给
[
JVM
](
https://tobebetterjavaer.com/jvm/what-is-jvm.html
)
执行的文件。
[
JVM到底是如何运行Java代码的?
](
https://tobebetterjavaer.com/jvm/how-run-java-code.html
)
上面👆🏻这篇文章会详细讲解运行的过程。
“三妹,这里再顺带给你讲一下,Java 是编译型语言还是解释型语言。”
“好啊,我正要问这个‘编译’到底是怎么回事呢?”
Java的第一道工序是通过javac命令把Java源码编译成字节码,之后,我们可以通过java命令运行字节码,此时就有2种处理方式了。
-
1、字节码由JVM逐条解释执行。
-
2、部分字节码可能由
[
JIT,即时编译,戳链接了解
](
https://tobebetterjavaer.com/jvm/jit.html
)
编译为机器指令直接执行。
也就是说,为了跨平台,Java源代码首先会编译成字节码,字节码不是机器语言,需要JVM来解释。有了JVM这个中间层,Java的运行效率就没有直接把源代码编译为机器码来得效率更高,这个应该能理解吗,多了中间商嘛。所以为了提高效率,JVM引入了 JIT 编译器,把一些经常执行的字节码直接搞成机器码。
所以,Java是解释和编译并存。但通常来说,我们会说“Java 是编译型语言”,尽管这样并不准确,主要是 JIT 是后面才出现的,“先入为主嘛”。
### 学 Java 有钱途吗?
“二哥,学 Java 到底有没有前途啊?我毕业以后能不能找到工作啊?”
“三妹啊,就目前来说,Java 不仅仅是一门编程语言,它还是一个由一系列计算机软件和规范组成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境,并广泛应用于以下这些场合。”
...
...
docs/string/builder-buffer.md
0 → 100644
浏览文件 @
5eff31fa
---
title
:
聊聊String、StringBuilder、StringBuffer 三兄弟
shortTitle
:
String与StringBuilder和StringBuffer
category
:
-
Java核心
tag
:
-
数组&字符串
description
:
Java程序员进阶之路,小白的零基础Java教程,从入门到进阶,聊聊String与StringBuilder和StringBuffer 三兄弟
head
:
-
-
meta
-
name
:
keywords
content
:
Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,StringBuilder,StringBuffer,string stringbuilder,string stringbuffer,string stringbuilder stringbuffer
---
## 聊聊String、StringBuilder、StringBuffer 三兄弟
“哥,
[
上一篇深入理解 String.intern()
](
https://tobebetterjavaer.com/string/intern.html
)
讲到了 StringBuilder,这一节我们就来聊聊吧!”三妹很期待。
“好啊,它们之间的关系还真的是挺和谐的。”看着三妹好奇的样子,我感到学技术就应该是这个样子才对。
由于字符串是不可变的,所以当遇到
[
字符串拼接
](
https://tobebetterjavaer.com/string/join.html
)
(尤其是使用
`+`
号操作符)的时候,就需要考量性能的问题,你不能毫无顾虑地生产太多 String 对象,对珍贵的内存造成不必要的压力。
于是 Java 就设计了一个专门用来解决此问题的 StringBuffer 类。
```
java
public
final
class
StringBuffer
extends
AbstractStringBuilder
implements
Serializable
,
CharSequence
{
public
StringBuffer
()
{
super
(
16
);
}
public
synchronized
StringBuffer
append
(
String
str
)
{
super
.
append
(
str
);
return
this
;
}
public
synchronized
String
toString
()
{
return
new
String
(
value
,
0
,
count
);
}
// 其他方法
}
```
不过,由于 StringBuffer 操作字符串的方法加了
[
`synchronized` 关键字
](
https://tobebetterjavaer.com/thread/synchronized-1.html
)
进行了同步,主要是考虑到多线程环境下的安全问题,所以执行效率会比较低。
于是 Java 就给 StringBuffer “生了个兄弟”,名叫 StringBuilder,说,“孩子,你别管线程安全了,你就在单线程环境下使用,这样效率会高得多,如果要在多线程环境下修改字符串,你到时候可以使用
`ThreadLocal`
来避免多线程冲突。”
```
java
public
final
class
StringBuilder
extends
AbstractStringBuilder
implements
java
.
io
.
Serializable
,
CharSequence
{
// ...
public
StringBuilder
append
(
String
str
)
{
super
.
append
(
str
);
return
this
;
}
public
String
toString
()
{
// Create a copy, don't share the array
return
new
String
(
value
,
0
,
count
);
}
// ...
}
```
除了类名不同,方法没有加 synchronized,基本上完全一样。
实际开发中,StringBuilder 的使用频率也是远高于 StringBuffer,甚至可以这么说,StringBuilder 完全取代了 StringBuffer。
[
之前我们也曾聊过
](
https://tobebetterjavaer.com/overview/what-is-java.html
)
,Java 是一门解释型的编程语言,所以当编译器遇到
`+`
号这个操作符的时候,会将
`new String("二哥") + new String("三妹")`
这行代码编译为以下代码:
```
java
new
StringBuilder
().
append
(
"二哥"
).
append
(
"三妹"
).
toString
();
```
这个过程是我们看不见的,但这正是 Java 的“智能”之处,它可以在编译的时候偷偷地帮我们做很多优化,这样既可以提高我们的开发效率(
`+`
号写起来比创建 StringBuilder 对象便捷得多),也不会影响 JVM 的执行效率。
当然了,如果我们使用
[
javap
](
https://tobebetterjavaer.com/jvm/bytecode.html
)
反编译
`new String("二哥") + new String("三妹")`
的字节码的时候,也是能看出 StringBuilder 的影子的。
```
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: new #4 // class java/lang/String
10: dup
11: ldc #5 // String 二哥
13: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V
16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: new #4 // class java/lang/String
22: dup
23: ldc #8 // String 三妹
25: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V
28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: areturn
```
可以看到 Java 编译器将字符串拼接操作(
`+`
)转换为了 StringBuilder 对象的 append 方法,然后再调用 StringBuilder 对象的 toString 方法返回拼接后的字符串。
来看一下 StringBuilder 的 toString 方法:
```
java
public
String
toString
()
{
return
new
String
(
value
,
0
,
count
);
}
```
value 是一个 char 类型的数组:
```
java
/**
* The value is used for character storage.
*/
char
[]
value
;
```
在 StringBuilder 对象创建时,会为 value 分配一定的内存空间(初始容量 16),用于存储字符串。
```
java
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public
StringBuilder
()
{
super
(
16
);
}
```
随着字符串的拼接,value 数组的长度会不断增加,因此在 StringBuilder 对象的实现中,value 数组的长度是可以
[
动态扩展的,就像ArrayList那样
](
https://tobebetterjavaer.com/collection/arraylist.html
)
。
继续来看 StringBuilder 的 toString 方法:
```
java
public
String
toString
()
{
return
new
String
(
value
,
0
,
count
);
}
```
value 用于存储 StringBuilder 对象中包含的字符序列。count 是一个 int 类型的变量,表示字符序列的长度。toString() 方法会调用
`new String(value, 0, count)`
,使用 value 数组中从 0 开始的前 count 个元素创建一个新的字符串对象,并将其返回。
再来看一下 append 方法:
```
java
public
StringBuilder
append
(
String
str
)
{
super
.
append
(
str
);
return
this
;
}
```
实际上是调用了 AbstractStringBuilder 中的 append(String str) 方法。在 AbstractStringBuilder 中,append(String str) 方法会检查当前字符序列中的字符是否够用,如果不够用则会进行扩容,并将指定字符串追加到字符序列的末尾。
```
java
/**
* Appends the specified string to this character sequence.
* <p>
* The characters of the {@code String} argument are appended, in order,
* increasing the length of this sequence by the length of the argument.
* If {@code str} is {@code null}, then the four characters {@code "null"}
* are appended.
* <p>
* Let <i>n</i> be the length of this character sequence just prior to
* execution of the {@code append} method. Then the character at index
* <i>k</i> in this character sequence is equal to the character at index
* <i>k</i> in the argument {@code str}, if <i>k</i> is less than
* <i>n</i>; otherwise, it is equal to the character at index
* <i>k-n</i> in the argument {@code str}.
*
* @param str a string.
* @return a reference to this object.
*/
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
;
}
```
append(String str) 方法将指定字符串追加到当前字符序列中。如果指定字符串为 null,则追加字符串 "null";否则会检查指定字符串的长度,然后根据当前字符序列中的字符数和指定字符串的长度来判断是否需要扩容。
如果需要扩容,则会调用 ensureCapacityInternal(int minimumCapacity) 方法进行扩容。扩容之后,将指定字符串的字符拷贝到字符序列中。
来看一下 ensureCapacityInternal 方法:
```
java
private
void
ensureCapacityInternal
(
int
minimumCapacity
)
{
// overflow-conscious code
if
(
minimumCapacity
-
value
.
length
>
0
)
expandCapacity
(
minimumCapacity
);
}
void
expandCapacity
(
int
minimumCapacity
)
{
int
newCapacity
=
value
.
length
*
2
+
2
;
if
(
newCapacity
-
minimumCapacity
<
0
)
newCapacity
=
minimumCapacity
;
if
(
newCapacity
<
0
)
{
if
(
minimumCapacity
<
0
)
// overflow
throw
new
OutOfMemoryError
();
newCapacity
=
Integer
.
MAX_VALUE
;
}
value
=
Arrays
.
copyOf
(
value
,
newCapacity
);
}
```
`ensureCapacityInternal(int minimumCapacity)`
方法用于确保当前字符序列的容量至少等于指定的最小容量 minimumCapacity。如果当前容量小于指定的容量,就会为字符序列分配一个新的内部数组。新容量的计算方式如下:
-
如果指定的最小容量大于当前容量,则新容量为两倍的旧容量加上 2;
-
如果指定的最小容量小于等于当前容量,则不会进行扩容,直接返回当前对象。
在进行扩容之前,
`ensureCapacityInternal(int minimumCapacity)`
方法会先检查当前字符序列的容量是否足够,如果不足就会调用
`expandCapacity(int minimumCapacity)`
方法进行扩容。
`expandCapacity(int minimumCapacity)`
方法首先计算出新容量,然后使用
`Arrays.copyOf(char[] original, int newLength)`
方法将原字符数组扩容到新容量的大小。
关于扩容,后面在讲
[
ArrayList
](
https://tobebetterjavaer.com/collection/arraylist.html
)
的时候会再次说明,今天就先聊到这吧。
“我想,关于 String、StringBuilder、StringBuilder 之间的差别,你都搞清楚了吧?”我问。
“哥,你真棒!区别我是搞清楚了,你后面讲的源码扩容还没消化,我一会去加个餐,再细看一下。”三妹说。
“可以的,实际上,你现在只需要知道 StringBuilder 的用法就可以了。”喝了一口右手边的可口可乐(无糖)后,我感觉好爽快啊。
---
最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了
**数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关**
等等等等……详情戳:
[
可以说是2022年全网最全的学习和找工作的PDF资源了
](
https://tobebetterjavaer.com/pdf/programmer-111.html
)
微信搜
**沉默王二**
或扫描下方二维码关注二哥的原创公众号沉默王二,回复
**111**
即可免费领取。
![](
https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png
)
\ No newline at end of file
docs/string/intern.md
浏览文件 @
5eff31fa
...
...
@@ -131,7 +131,7 @@ new StringBuilder().append("二哥").append("三妹").toString();
-
在 StringBuilder 对象上调用 append("三妹"),将 "三妹" 追加到 StringBuilder 中。
-
在 StringBuilder 对象上调用 toString() 方法,将 StringBuilder 转换为一个新的字符串对象,内容为 "二哥三妹"。
关于
StringBuilder
,我们随后会详细地讲到。今天先了解到这。
关于
[
StringBuilder
](
https://tobebetterjavaer.com/string/builder-buffer.html
)
,我们随后会详细地讲到。今天先了解到这。
不过需要注意的是,尽管 intern 可以确保所有具有相同内容的字符串共享相同的内存空间,但也不要烂用 intern,因为任何的缓存池都是有大小限制的,不能无缘无故就占用了相对稀缺的缓存空间,导致其他字符串没有坑位可占。
...
...
二哥的 Java 进阶之路.md
浏览文件 @
5eff31fa
...
...
@@ -111,6 +111,8 @@
“三妹啊,听我慢慢来给你解释。”
### Java 的由来
Java 是一门计算机编程语言,高级、健壮、面向对象,并且非常安全。它由 Sun 公司在 1995 年开发,主力开发叫 James Gosling,被称为 Java 之父,就是下图这位,头秃的厉害。
![](
https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/overview/one-01.png
)
...
...
@@ -153,6 +155,33 @@ public class HelloWorld {
“好吧。”
### Java 是编译型语言还是解释型语言
“二哥,你之前给我看了 .class 文件和 .java 源代码,它们之间的关系是什么样的呢?”三妹还是挺喜欢学习的嘛,发现的问题都很关键。
“不错不错,都能挖掘到这个点了。”
.java 是源代码,也就是我们开发人员可以看懂的,可以编写的;.class 是字节码文件,是经过 javac 编译后的文件,是交给
[
JVM
](
https://tobebetterjavaer.com/jvm/what-is-jvm.html
)
执行的文件。
[
JVM到底是如何运行Java代码的?
](
https://tobebetterjavaer.com/jvm/how-run-java-code.html
)
上面👆🏻这篇文章会详细讲解运行的过程。
“三妹,这里再顺带给你讲一下,Java 是编译型语言还是解释型语言。”
“好啊,我正要问这个‘编译’到底是怎么回事呢?”
Java的第一道工序是通过javac命令把Java源码编译成字节码,之后,我们可以通过java命令运行字节码,此时就有2种处理方式了。
-
1、字节码由JVM逐条解释执行。
-
2、部分字节码可能由
[
JIT,即时编译,戳链接了解
](
https://tobebetterjavaer.com/jvm/jit.html
)
编译为机器指令直接执行。
也就是说,为了跨平台,Java源代码首先会编译成字节码,字节码不是机器语言,需要JVM来解释。有了JVM这个中间层,Java的运行效率就没有直接把源代码编译为机器码来得效率更高,这个应该能理解吗,多了中间商嘛。所以为了提高效率,JVM引入了 JIT 编译器,把一些经常执行的字节码直接搞成机器码。
所以,Java是解释和编译并存。但通常来说,我们会说“Java 是编译型语言”,尽管这样并不准确,主要是 JIT 是后面才出现的,“先入为主嘛”。
### 学 Java 有钱途吗?
“二哥,学 Java 到底有没有前途啊?我毕业以后能不能找到工作啊?”
“三妹啊,就目前来说,Java 不仅仅是一门编程语言,它还是一个由一系列计算机软件和规范组成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境,并广泛应用于以下这些场合。”
...
...
@@ -3930,4 +3959,223 @@ new StringBuilder().append("二哥").append("三妹").toString();
微信搜
**沉默王二**
或扫描下方二维码关注二哥的原创公众号沉默王二,回复
**111**
即可免费领取。
![](
https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png
)
## 聊聊String、StringBuilder、StringBuffer 三兄弟
“哥,
[
上一篇深入理解 String.intern()
](
https://tobebetterjavaer.com/string/intern.html
)
讲到了 StringBuilder,这一节我们就来聊聊吧!”三妹很期待。
“好啊,它们之间的关系还真的是挺和谐的。”看着三妹好奇的样子,我感到学技术就应该是这个样子才对。
由于字符串是不可变的,所以当遇到
[
字符串拼接
](
https://tobebetterjavaer.com/string/join.html
)
(尤其是使用
`+`
号操作符)的时候,就需要考量性能的问题,你不能毫无顾虑地生产太多 String 对象,对珍贵的内存造成不必要的压力。
于是 Java 就设计了一个专门用来解决此问题的 StringBuffer 类。
```
java
public
final
class
StringBuffer
extends
AbstractStringBuilder
implements
Serializable
,
CharSequence
{
public
StringBuffer
()
{
super
(
16
);
}
public
synchronized
StringBuffer
append
(
String
str
)
{
super
.
append
(
str
);
return
this
;
}
public
synchronized
String
toString
()
{
return
new
String
(
value
,
0
,
count
);
}
// 其他方法
}
```
不过,由于 StringBuffer 操作字符串的方法加了
[
`synchronized` 关键字
](
https://tobebetterjavaer.com/thread/synchronized-1.html
)
进行了同步,主要是考虑到多线程环境下的安全问题,所以执行效率会比较低。
于是 Java 就给 StringBuffer “生了个兄弟”,名叫 StringBuilder,说,“孩子,你别管线程安全了,你就在单线程环境下使用,这样效率会高得多,如果要在多线程环境下修改字符串,你到时候可以使用
`ThreadLocal`
来避免多线程冲突。”
```
java
public
final
class
StringBuilder
extends
AbstractStringBuilder
implements
java
.
io
.
Serializable
,
CharSequence
{
// ...
public
StringBuilder
append
(
String
str
)
{
super
.
append
(
str
);
return
this
;
}
public
String
toString
()
{
// Create a copy, don't share the array
return
new
String
(
value
,
0
,
count
);
}
// ...
}
```
除了类名不同,方法没有加 synchronized,基本上完全一样。
实际开发中,StringBuilder 的使用频率也是远高于 StringBuffer,甚至可以这么说,StringBuilder 完全取代了 StringBuffer。
[
之前我们也曾聊过
](
https://tobebetterjavaer.com/overview/what-is-java.html
)
,Java 是一门解释型的编程语言,所以当编译器遇到
`+`
号这个操作符的时候,会将
`new String("二哥") + new String("三妹")`
这行代码编译为以下代码:
```
java
new
StringBuilder
().
append
(
"二哥"
).
append
(
"三妹"
).
toString
();
```
这个过程是我们看不见的,但这正是 Java 的“智能”之处,它可以在编译的时候偷偷地帮我们做很多优化,这样既可以提高我们的开发效率(
`+`
号写起来比创建 StringBuilder 对象便捷得多),也不会影响 JVM 的执行效率。
当然了,如果我们使用
[
javap
](
https://tobebetterjavaer.com/jvm/bytecode.html
)
反编译
`new String("二哥") + new String("三妹")`
的字节码的时候,也是能看出 StringBuilder 的影子的。
```
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: new #4 // class java/lang/String
10: dup
11: ldc #5 // String 二哥
13: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V
16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: new #4 // class java/lang/String
22: dup
23: ldc #8 // String 三妹
25: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V
28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: areturn
```
可以看到 Java 编译器将字符串拼接操作(
`+`
)转换为了 StringBuilder 对象的 append 方法,然后再调用 StringBuilder 对象的 toString 方法返回拼接后的字符串。
来看一下 StringBuilder 的 toString 方法:
```
java
public
String
toString
()
{
return
new
String
(
value
,
0
,
count
);
}
```
value 是一个 char 类型的数组:
```
java
/**
* The value is used for character storage.
*/
char
[]
value
;
```
在 StringBuilder 对象创建时,会为 value 分配一定的内存空间(初始容量 16),用于存储字符串。
```
java
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public
StringBuilder
()
{
super
(
16
);
}
```
随着字符串的拼接,value 数组的长度会不断增加,因此在 StringBuilder 对象的实现中,value 数组的长度是可以
[
动态扩展的,就像ArrayList那样
](
https://tobebetterjavaer.com/collection/arraylist.html
)
。
继续来看 StringBuilder 的 toString 方法:
```
java
public
String
toString
()
{
return
new
String
(
value
,
0
,
count
);
}
```
value 用于存储 StringBuilder 对象中包含的字符序列。count 是一个 int 类型的变量,表示字符序列的长度。toString() 方法会调用
`new String(value, 0, count)`
,使用 value 数组中从 0 开始的前 count 个元素创建一个新的字符串对象,并将其返回。
再来看一下 append 方法:
```
java
public
StringBuilder
append
(
String
str
)
{
super
.
append
(
str
);
return
this
;
}
```
实际上是调用了 AbstractStringBuilder 中的 append(String str) 方法。在 AbstractStringBuilder 中,append(String str) 方法会检查当前字符序列中的字符是否够用,如果不够用则会进行扩容,并将指定字符串追加到字符序列的末尾。
```
java
/**
* Appends the specified string to this character sequence.
* <p>
* The characters of the {@code String} argument are appended, in order,
* increasing the length of this sequence by the length of the argument.
* If {@code str} is {@code null}, then the four characters {@code "null"}
* are appended.
* <p>
* Let <i>n</i> be the length of this character sequence just prior to
* execution of the {@code append} method. Then the character at index
* <i>k</i> in this character sequence is equal to the character at index
* <i>k</i> in the argument {@code str}, if <i>k</i> is less than
* <i>n</i>; otherwise, it is equal to the character at index
* <i>k-n</i> in the argument {@code str}.
*
* @param str a string.
* @return a reference to this object.
*/
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
;
}
```
append(String str) 方法将指定字符串追加到当前字符序列中。如果指定字符串为 null,则追加字符串 "null";否则会检查指定字符串的长度,然后根据当前字符序列中的字符数和指定字符串的长度来判断是否需要扩容。
如果需要扩容,则会调用 ensureCapacityInternal(int minimumCapacity) 方法进行扩容。扩容之后,将指定字符串的字符拷贝到字符序列中。
来看一下 ensureCapacityInternal 方法:
```
java
private
void
ensureCapacityInternal
(
int
minimumCapacity
)
{
// overflow-conscious code
if
(
minimumCapacity
-
value
.
length
>
0
)
expandCapacity
(
minimumCapacity
);
}
void
expandCapacity
(
int
minimumCapacity
)
{
int
newCapacity
=
value
.
length
*
2
+
2
;
if
(
newCapacity
-
minimumCapacity
<
0
)
newCapacity
=
minimumCapacity
;
if
(
newCapacity
<
0
)
{
if
(
minimumCapacity
<
0
)
// overflow
throw
new
OutOfMemoryError
();
newCapacity
=
Integer
.
MAX_VALUE
;
}
value
=
Arrays
.
copyOf
(
value
,
newCapacity
);
}
```
`ensureCapacityInternal(int minimumCapacity)`
方法用于确保当前字符序列的容量至少等于指定的最小容量 minimumCapacity。如果当前容量小于指定的容量,就会为字符序列分配一个新的内部数组。新容量的计算方式如下:
-
如果指定的最小容量大于当前容量,则新容量为两倍的旧容量加上 2;
-
如果指定的最小容量小于等于当前容量,则不会进行扩容,直接返回当前对象。
在进行扩容之前,
`ensureCapacityInternal(int minimumCapacity)`
方法会先检查当前字符序列的容量是否足够,如果不足就会调用
`expandCapacity(int minimumCapacity)`
方法进行扩容。
`expandCapacity(int minimumCapacity)`
方法首先计算出新容量,然后使用
`Arrays.copyOf(char[] original, int newLength)`
方法将原字符数组扩容到新容量的大小。
关于扩容,后面在讲
[
ArrayList
](
https://tobebetterjavaer.com/collection/arraylist.html
)
的时候会再次说明,今天就先聊到这吧。
“我想,关于 String、StringBuilder、StringBuilder 之间的差别,你都搞清楚了吧?”我问。
“哥,你真棒!区别我是搞清楚了,你后面讲的源码扩容还没消化,我一会去加个餐,再细看一下。”三妹说。
“可以的,实际上,你现在只需要知道 StringBuilder 的用法就可以了。”喝了一口右手边的可口可乐(无糖)后,我感觉好爽快啊。
---
最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了
**数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关**
等等等等……详情戳:
[
可以说是2022年全网最全的学习和找工作的PDF资源了
](
https://tobebetterjavaer.com/pdf/programmer-111.html
)
微信搜
**沉默王二**
或扫描下方二维码关注二哥的原创公众号沉默王二,回复
**111**
即可免费领取。
![](
https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png
)
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录