提交 fc1bc09b 编写于 作者: 沉默王二's avatar 沉默王二 💬

字符串

上级 aff1615b
......@@ -104,12 +104,13 @@
## Java基础语法
- [Java中常用的48个关键字 & 2个保留字](docs/basic-extra-meal/48-keywords.md)
- [简单过一下Java中常用的48个关键字和2个保留字](docs/basic-extra-meal/48-keywords.md)
- [Java注释](docs/basic-grammar/javadoc.md)
- [Java中的数据类型](docs/basic-grammar/basic-data-type.md)
- [Java 数据类型转换](docs/basic-grammar/type-cast.md)
- [Java流程控制语句](docs/basic-grammar/flow-control.md)
- [Java中的数据类型(8种基本数据类型和引用数据类型)](docs/basic-grammar/basic-data-type.md)
- [Java数据类型转换(强制类型转换+自动类型转换)](docs/basic-grammar/type-cast.md)
- [聊聊Java基本数据类型缓存池](docs/basic-extra-meal/int-cache.md)
- [Java运算符](docs/basic-grammar/operator.md)
- [Java流程控制语句](docs/basic-grammar/flow-control.md)
## 数组&字符串
......@@ -123,6 +124,7 @@
- [Java如何判断两个字符串是否相等?](docs/string/equals.md)
- [最优雅的Java字符串拼接是哪种方式?](docs/string/join.md)
- [如何在Java中优雅地分割String字符串?](docs/string/split.md)
- [Java 9为什么要将String的底层实现由char数组改成了byte数组?](docs/basic-extra-meal/jdk9-char-byte-string.md)
## Java面向对象编程
......@@ -201,7 +203,6 @@
- [Java命名规范](docs/basic-extra-meal/java-naming.md)
- [彻底弄懂Java中的Unicode和UTF-8编码](docs/basic-extra-meal/java-unicode.md)
- [Java中new Integer与Integer.valueOf的区别](docs/basic-extra-meal/int-cache.md)
- [深入剖析Java中的拆箱和装箱](docs/basic-extra-meal/box.md)
- [一文彻底讲明白的Java中的浅拷贝与深拷贝](docs/basic-extra-meal/deep-copy.md)
- [深入理解Java中的hashCode方法](docs/basic-extra-meal/hashcode.md)
......@@ -209,7 +210,6 @@
- [Java重写(Overriding)时应当遵守的11条规则](docs/basic-extra-meal/Overriding.md)
- [Java到底是值传递还是引用传递?](docs/basic-extra-meal/pass-by-value.md)
- [详解Java中Comparable和Comparator接口的区别](docs/basic-extra-meal/comparable-omparator.md)
- [jdk9为什么要将String的底层实现由char数组改成了byte数组?](docs/basic-extra-meal/jdk9-char-byte-string.md)
- [为什么JDK源码中,无限循环大多使用for(;;)而不是while(true)?](docs/basic-extra-meal/jdk-while-for-wuxian-xunhuan.md)
- [Java 中,先有Class还是先有Object?](docs/basic-extra-meal/class-object.md)
- [详解Java中instanceof关键字的用法](docs/basic-extra-meal/instanceof.md)
......
......@@ -115,6 +115,7 @@ export const sidebarConfig = sidebar({
"basic-grammar/javadoc",
"basic-grammar/basic-data-type",
"basic-grammar/type-cast",
"basic-extra-meal/int-cache",
"basic-grammar/operator",
"basic-grammar/flow-control",
],
......@@ -133,6 +134,7 @@ export const sidebarConfig = sidebar({
"string/equals",
"string/join",
"string/split",
"basic-extra-meal/jdk9-char-byte-string",
],
},
{
......@@ -235,9 +237,8 @@ export const sidebarConfig = sidebar({
prefix:"basic-extra-meal/",
collapsible: true,
children: [
"basic-extra-meal/java-naming",
"java-naming",
"java-unicode",
"int-cache",
"box",
"deep-copy",
"hashcode",
......@@ -245,7 +246,6 @@ export const sidebarConfig = sidebar({
"Overriding",
"pass-by-value",
"comparable-omparator",
"jdk9-char-byte-string",
"jdk-while-for-wuxian-xunhuan",
"class-object",
"instanceof",
......
......@@ -12,7 +12,7 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java二维数组,数组
---
# 4.2 聊聊Java的二维数组
# 4.2 二维数组
“二哥,今天我们简单过一下二维数组吧,挺简单的。”三妹放下手机对我说。
......
---
title: Java中常用的48个关键字和2个保留字
title: 简单过一下Java中常用的48个关键字和2个保留字
shortTitle: 48个关键字和2个保留字
category:
- Java核心
......@@ -12,7 +12,7 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Java保留字,Java关键字,关键字,保留字
---
# 3.1 Java中常用的48个关键字和2个保留字
# 3.1 48个关键字和2个保留字
“二哥,就之前你给我展示的 Java 代码中,有 public、static、void、main 等等,它们应该都是关键字吧?”三妹的脸上泛着甜甜的笑容,我想她在学习 Java 方面已经变得越来越自信了。
......
---
title: Java中new Integer与Integer.valueOf的区别
shortTitle: new Integer与Integer.valueOf的区别
title: 聊聊Java基本数据类型缓存池
shortTitle: Java基本数据类型缓存池
category:
- Java核心
tag:
- Java重要知识点
description: Java程序员进阶之路,小白的零基础Java教程,从入门到进阶,Java中new Integer与Integer.valueOf的区别
description: Java程序员进阶之路,小白的零基础Java教程,从入门到进阶,Java基本数据类型缓存池
head:
- - meta
- name: keywords
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Integer
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Integer,java数据类型缓存池,java IntegerCache,Java 基本数据类型缓存池
---
“三妹,今天我们来补一个小的知识点:Java 数据类型缓存池。”我喝了一口枸杞泡的茶后对三妹说,“考你一个问题哈:`new Integer(18) 与 Integer.valueOf(18)` 的区别是什么?”
# 3.5 基本数据类型缓存池
“三妹,今天我们来补一个小的知识点:Java 基本数据类型缓存池。”我喝了一口枸杞泡的茶后对三妹说,“考你一个问题哈:`new Integer(18) 与 Integer.valueOf(18)` 的区别是什么?”
“难道不一样吗?”三妹有点诧异。
......@@ -111,9 +113,11 @@ private static class IntegerCache {
}
```
详细解释下:当我们通过 Integer.valueOf() 方法获取整数对象时,会先检查该整数是否在 IntegerCache 中,如果在,则返回缓存中的对象,否则创建一个新的对象并缓存起来。
需要注意的是,如果使用 new Integer() 创建对象,即使值在 -128 到 127 范围内,也不会被缓存,每次都会创建新的对象。因此,推荐使用 Integer.valueOf() 方法获取整数对象。
之前我们在[学习 static 关键字](https://tobebetterjavaer.com/oo/static.html)的时候,提到过静态代码块,还记得吧?三妹。静态代码块通常用来初始化一些静态变量,它会优先于 main() 方法执行。
[学习 static 关键字](https://tobebetterjavaer.com/oo/static.html)的时候,会详细解释静态代码块,你暂时先记住,三妹,静态代码块通常用来初始化一些静态变量,它会优先于 main() 方法执行。
在静态代码块中,low 为 -128,也就是缓存池的最小值;high 默认为 127,也就是缓存池的最大值,共计 256 个。
......@@ -169,9 +173,9 @@ Exception in thread "main" java.lang.AssertionError
“原来 assert 是这样用的啊,我明白了。”三妹表示学会了。
“那,缓存池之所以存在的原因也是因为这样做可以提高程序的整体性能,因为相对来说,比如说 Integer,-128~127 这个范围内的 256 个数字使用的频率会高一点。”我总结道
在 Java 中,针对一些基本数据类型(如 Integer、Long、Boolean 等),Java 会在程序启动时创建一些常用的对象并缓存在内存中,以提高程序的性能和节省内存开销。这些常用对象被缓存在一个固定的范围内,超出这个范围的值会被重新创建新的对象
“get 了!二哥你真棒,又学到了。”三妹很开心~
使用数据类型缓存池可以有效提高程序的性能和节省内存开销,但需要注意的是,在特定的业务场景下,缓存池可能会带来一些问题,例如缓存池中的对象被不同的线程同时修改,导致数据错误等问题。因此,在实际开发中,需要根据具体的业务需求来决定是否使用数据类型缓存池。
----
......
---
title: jdk9为什么要将String的底层实现由char数组改成了byte数组?
title: Java 9为什么要将String的底层实现由char数组改成了byte数组?
shortTitle: String的底层实现由char改成了byte?
category:
- Java核心
......@@ -9,18 +9,20 @@ description: Java程序员进阶之路,小白的零基础Java教程,从入
head:
- - meta
- name: keywords
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java,string,char,byte
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java,string,char,byte,java string 底层实现,java字符串源码,java string char,java string byte,java string char byte,java
---
大家好,我是二哥呀!如果你不是 Java8 的钉子户,你应该早就发现了:String 类的源码已经由 `char[]` 优化为了 `byte[]` 来存储字符串内容,为什么要这样做呢?
# 4.11 Java 9为什么要将String的底层实现由char数组改成了byte数组?
开门见山地说,从 `char[]``byte[]`,最主要的目的是**为了节省字符串占用的内存**。内存占用减少带来的另外一个好处,就是 GC 次数也会减少。
“二哥,最近在我阅读 Java 11 的字符串源码,发现和 Java 8 的有很大不同。”三妹的脸上洋溢着青春的微笑😊,甜美地说道:“String 类的源码已经由 `char[]` 优化为了 `byte[]` 来存储字符串内容,为什么要这样做呢?”
## 一、为什么要优化 String 节省内存空间
“开门见山地说,从 `char[]``byte[]`,最主要的目的是**节省字符串占用的内存空间**。内存占用减少带来的另外一个好处,就是 GC 次数也会减少。”我用右手的大拇指凑了一下眼镜解释道。
### 为什么要优化?
我们使用 `jmap -histo:live pid | head -n 10` 命令就可以查看到堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。
以我正在运行着的编程喵喵项目实例(基于 Java 8)来说,结果是这样的。
以我正在运行着的[编程喵](https://github.com/itwanger/coding-more)项目实例(基于 Java 8)来说,结果是这样的。
![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/basic-extra-meal/jdk9-char-byte-string-d826ce88-bbbe-47a3-a1a9-4dd86dd3632f.png)
......@@ -30,17 +32,15 @@ head:
`char[]` 对象有 17673 个,占用了 1621352 个字节的内存,排在第一位。
那也就是说优化 String 节省内存空间是非常有必要的,如果是去优化一个使用频率没有 String 这么高的类库,就显得非常的鸡肋。
## 二、`byte[]` 为什么就能节省内存空间呢?
众所周知,char 类型的数据在 JVM 中是占用两个字节的,并且使用的是 UTF-8 编码,其值范围在 '\u0000'(0)和 '\uffff'(65,535)(包含)之间。
那也就是说优化 String 节省内存空间是非常有必要的,如果是去优化一个使用频率没有 String 这么高的类,就没什么必要,对吧?
### 为什么能节省内存空间?
众所周知,char 类型的数据在 JVM 中是占用两个字节的,并且使用的是 UTF-8 [编码](https://tobebetterjavaer.com/basic-extra-meal/java-unicode.html),其值范围在 '\u0000'(0)和 '\uffff'(65,535)(包含)之间。
也就是说,使用 `char[]` 来表示 String 就导致了即使 String 中的字符只用一个字节就能表示,也得占用两个字节。
也就是说,使用 `char[]` 来表示 String 就会导致,即使 String 中的字符只用一个字节就能表示,也得占用两个字节。
而实际开发中,单字节的字符使用频率仍然要高于双字节的
>PS:在计算机中,单字节字符通常指的是一个字节(8位)可以表示的字符,而双字节字符则指需要两个字节(16位)才能表示的字符。单字节字符和双字节字符的定义是相对的,不同的编码方式对应的单字节和双字节字符集也不同。常见的单字节字符集有ASCII(美国信息交换标准代码)、ISO-8859(国际标准化组织标准编号8859)、GBK(汉字内码扩展规范)、GB2312(中国国家标准,现在已经被GBK取代),像拉丁字母、数字、标点符号、控制字符都是单字节字符。双字节字符集包括 Unicode、UTF-8、GB18030(中国国家标准),中文、日文、韩文、拉丁文扩展字符属于双字节字符
当然了,仅仅将 `char[]` 优化为 `byte[]` 是不够的,还要配合 Latin-1 的编码方式,该编码方式是用单个字节来表示字符的,这样就比 UTF-8 编码节省了更多的空间。
......@@ -81,7 +81,7 @@ Java 会根据字符串的内容自动设置为相应的编码,要么 Latin-1
也就是说,从 `char[]``byte[]`**中文是两个字节,纯英文是一个字节,在此之前呢,中文是两个字节,英文也是两个字节**
## 三、为什么用UTF-16而不用UTF-8呢?
### 为什么用UTF-16而不用UTF-8呢?
在 UTF-8 中,0-127 号的字符用 1 个字节来表示,使用和 ASCII 相同的编码。只有 128 号及以上的字符才用 2 个、3 个或者 4 个字节来表示。
......@@ -95,13 +95,9 @@ Java 会根据字符串的内容自动设置为相应的编码,要么 Latin-1
- 1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式(开始三个 1);
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式(开始四个 1)。
关于字符编码,我在《Java程序员进阶之路》里曾讲到过,想要深入了解的小伙伴查看下面的链接🔗:
>https://tobebetterjavaer.com/basic-extra-meal/java-unicode.html
也就是说,UTF-8 是变长的,那对于 String 这种有随机访问方法的类来说,就很不方便。所谓的随机访问,就是charAt、subString这种方法,随便指定一个数字,String要能给出结果。如果字符串中的每个字符占用的内存是不定长的,那么进行随机访问的时候,就需要从头开始数每个字符的长度,才能找到你想要的字符。
有小伙伴可能会问,UTF-16也是变长的呢?一个字符还可能占用 4 个字节呢?
三妹可能会问,UTF-16也是变长的呢?一个字符还可能占用 4 个字节呢?
的确,UTF-16 使用 2 个或者 4 个字节来存储字符。
......@@ -114,6 +110,14 @@ Java 会根据字符串的内容自动设置为相应的编码,要么 Latin-1
>参考链接:[https://www.zhihu.com/question/447224628](https://www.zhihu.com/question/447224628)
“好了,三妹,那关于Java 9为什么要将String的底层实现由char数组改成了byte数组就聊到这里吧。”我对三妹说,“有时候,读一读源码确实能成长很多,多问问为什么,挺好!”
“是啊,任何知识想要深入去学习,就能挖掘出来很多。”三妹说,“比如说今天聊到的UTF-16和UTF-8。”
“好了,我要去回答星球球友的提问了,你可以休息会,比如说听听 H.O.T 的歌,真不错。”
“哈哈,没想到,哥你也是个 H.O.T 的粉丝啊!”
----
最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html)
......
......@@ -12,7 +12,7 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Java流程控制语句,Java判断语句,Java循环语句,if,switch,while,do-while,for
---
# 3.5 Java流程控制语句
# 3.7 Java流程控制语句
“二哥,流程控制语句都有哪些呢?”三妹的脸上泛着甜甜的笑容,她开始对接下来要学习的内容充满期待了,这正是我感到欣慰的地方。
......
......@@ -12,7 +12,11 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Java 简介,数据类型转换,强制数据类型转换,自动类型转换,java类型转换
---
# 3.4 Java数据类型转换(强制类型转换+自动类型转换)
# 3.4 Java数据类型转换
“三妹,今天我们来聊聊 Java 中的数据类型转换。”我开门见山地对三妹说。
“好啊。”三妹愉快地说。
数据类型转换发生在所赋值的数值类型和接收数据的变量类型不一致的时候,它需要从一种数据类型转换成另一种数据类型。数据类型的转换可以分为隐式转换(自动类型转换)和显式转换(强制类型转换)两种。
......@@ -36,9 +40,9 @@ head:
```java
public static void main(String[] args) {
float price1 = 10.9f; // 定义牙膏的价格
double price2 = 5.8; // 定义面巾纸的价格
int num1 = 2; // 定义牙膏的数量
float price1 = 10.9f; // 定义牙膏的价格,单精度浮点型float
double price2 = 5.8; // 定义面巾纸的价格,双精度浮点型double
int num1 = 2; // 定义牙膏的数量,整型 int
int num2 = 4; // 定义面巾纸的数量
double res = price1 * num1 + price2 * num2; // 计算总价
System.out.println("一共付给收银员" + res + "元"); // 输出总价
......@@ -63,7 +67,6 @@ b = b * 2; // Type mismatch: cannot convert from int to byte
如上所示,第二行会报“类型不匹配:无法从int转换为byte”错误。
该程序试图将一个完全合法的 byte 型的值 `50*2` 存储给一个 byte 型的变量。但是当表达式求值的时候,操作数被自动的提升为 int 型,计算结果也被提升为 int 型。这样表达式的结果现在是 int 型,不强制转换它就不能被赋为 byte 型。
所以应该使用一个显示的强制类型转换,例如:
......@@ -80,11 +83,13 @@ b = (byte)(b*2);
### 强制类型转换
尽管自动类型转换是很有帮助的,但并不能满足所有的编程需要。例如,如果你需要将 double 型的值赋给一个 int 型的变量,你将怎么办?
尽管自动类型转换是很有帮助的,但并不能满足所有的编程需要。
“例如,三妹,注意听哦,如果你需要将 double 型的值赋给一个 int 型的变量,你将怎么办?”我瞅准时机,对三妹提出了这个问题,“这种转换不会自动进行,因为 double 型的变化范围比 int 型的要小。这种转换有使成为“缩小转换”,因为你肯定要将源数据类型的值变小才能适合目标数据类型。”
这种转换不会自动进行,因为 double 型的变化范围比 int 型的要小。这种转换有使成为“缩小转换”,因为你肯定要将源数据类型的值变小才能适合目标数据类型
“我不会啊!”三妹有点难为情地说
所以当两种数据类型不兼容,或目标类型的取值范围小于源类型时,自动转换将无法进行,这时就需要进行强制类型转换。其语法格式如下:
“当两种数据类型不兼容,或目标类型的取值范围小于源类型时,自动转换将无法进行,这时就需要进行强制类型转换。其语法格式如下”,我喝了一口拿铁说到。
```java
(type)variableName
......
......@@ -114,12 +114,14 @@ head:
### Java基础语法
- [Java中常用的48个关键字 & 2个保留字](basic-extra-meal/48-keywords.md)
- [简单过一下Java中常用的48个关键字和2个保留字](basic-extra-meal/48-keywords.md)
- [Java注释](basic-grammar/javadoc.md)
- [Java 中的数据类型](basic-grammar/basic-data-type.md)
- [Java 数据类型转换](basic-grammar/type-cast.md)
- [Java流程控制语句](basic-grammar/flow-control.md)
- [Java中的数据类型(8种基本数据类型和引用数据类型)](basic-grammar/basic-data-type.md)
- [Java数据类型转换(强制类型转换+自动类型转换)](basic-grammar/type-cast.md)
- [聊聊Java基本数据类型缓存池](basic-extra-meal/int-cache.md)
- [Java运算符](basic-grammar/operator.md)
- [Java流程控制语句](basic-grammar/flow-control.md)
### 数组&字符串
......@@ -133,6 +135,7 @@ head:
- [Java如何判断两个字符串是否相等?](string/equals.md)
- [最优雅的Java字符串拼接是哪种方式?](string/join.md)
- [如何在Java中优雅地分割String字符串?](string/split.md)
- [Java 9为什么要将String的底层实现由char数组改成了byte数组?](basic-extra-meal/jdk9-char-byte-string.md)
### 面向对象编程
......@@ -211,7 +214,6 @@ head:
- [Java命名规范](basic-extra-meal/java-naming.md)
- [彻底弄懂Java中的Unicode和UTF-8编码](basic-extra-meal/java-unicode.md)
- [Java中new Integer与Integer.valueOf的区别](basic-extra-meal/int-cache.md)
- [深入剖析Java中的拆箱和装箱](basic-extra-meal/box.md)
- [一文彻底讲明白的Java中的浅拷贝与深拷贝](basic-extra-meal/deep-copy.md)
- [深入理解Java中的hashCode方法](basic-extra-meal/hashcode.md)
......@@ -219,7 +221,6 @@ head:
- [Java重写(Overriding)时应当遵守的11条规则](basic-extra-meal/Overriding.md)
- [Java到底是值传递还是引用传递?](basic-extra-meal/pass-by-value.md)
- [详解Java中Comparable和Comparator接口的区别](basic-extra-meal/comparable-omparator.md)
- [jdk9为什么要将String的底层实现由char数组改成了byte数组?](basic-extra-meal/jdk9-char-byte-string.md)
- [为什么JDK源码中,无限循环大多使用for(;;)而不是while(true)?](basic-extra-meal/jdk-while-for-wuxian-xunhuan.md)
- [Java 中,先有Class还是先有Object?](basic-extra-meal/class-object.md)
- [详解Java中instanceof关键字的用法](basic-extra-meal/instanceof.md)
......
......@@ -12,7 +12,7 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,Java简介
---
# 2.1 Java简介,什么是 Java?
# 2.1 Java简介
“二哥,到底什么是 Java?给我说说呗。”
......
......@@ -12,7 +12,7 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,StringBuilder,StringBuffer,string stringbuilder,string stringbuffer,string stringbuilder stringbuffer
---
# 4.7 聊聊String、StringBuilder、StringBuffer 三兄弟
# 4.7 String、StringBuilder、StringBuffer
“哥,[上一篇深入理解 String.intern()](https://tobebetterjavaer.com/string/intern.html) 讲到了 StringBuilder,这一节我们就来聊聊吧!”三妹很期待。
......
......@@ -12,7 +12,7 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,equals,java equals,java string 比较,java字符串比较
---
# 4.8 Java如何判断两个字符串是否相等?
# 4.8 如何判断两个字符串是否相等?
“二哥,如何比较两个字符串相等啊?”三妹问。
......
......@@ -12,7 +12,7 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,不可变
---
# 4.4 聊聊Java字符串,以及为什么String是不可变的?
# 4.4 Java字符串及不可变性
我正坐在沙发上津津有味地读刘欣大佬的《码农翻身》——Java 帝国这一章,门铃响了。起身打开门一看,是三妹,她从学校回来了。
......
......@@ -9,15 +9,18 @@ description: Java程序员进阶之路,小白的零基础Java教程,从入
head:
- - meta
- name: keywords
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,字符串拼接
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,字符串拼接,java字符串拼接,java string拼接
---
# 4.9 字符串拼接
“哥,你让我看的《[Java 开发手册](https://tobebetterjavaer.com/nice-article/weixin/sulwgalcpdssbjavakfsc.html)》上有这么一段内容:循环体内,拼接字符串最好使用 StringBuilder 的 `append()` 方法,而不是 + 号操作符。这是为什么呀?”三妹疑惑地问。
“哥,你让我看的《[Java 开发手册](https://tobebetterjavaer.com/pdf/ali-java-shouce.html)》上有这么一段内容:循环体内,拼接字符串最好使用 StringBuilder 的 `append()` 方法,而不是 + 号操作符。这是为什么呀?”三妹疑惑地问。
好的,三妹,哥来慢慢给你讲。”我回答。
其实这个问题,我们之前已经[聊过](https://tobebetterjavaer.com/string/builder-buffer.html)。”我慢吞吞地回答道,“不过,三妹,哥今天来给你深入地讲讲。”
三妹能在学习的过程中不断地发现问题,让我感到非常的开心。其实很多时候,我们不应该只是把知识点记在心里,还应该问一问自己,到底是为什么,只有迈出去这一步,才能真正的成长起来。
PS:三妹能在学习的过程中不断地发现问题,让我感到非常的开心。其实很多时候,我们不应该只是把知识点记在心里,还应该问一问自己,到底是为什么,只有迈出去这一步,才能真正的成长起来。
### javap 探究+号操作符拼接字符串的本质
“+ 号操作符其实被 Java 在编译的时候重新解释了,换一种说法就是,+ 号操作符是一种语法糖,让字符串的拼接变得更简便了。”一边给三妹解释,我一边在 Intellij IDEA 中敲出了下面这段代码。
......@@ -66,9 +69,9 @@ class Demo {
“然后看标号为 17 的这行,是一个 invokevirtual 指令,用于调用对象的方法,也就是 StringBuilder 对象的 `append()` 方法。”
“也就意味着把 chenmo 这个字符串添加到 StringBuilder 对象中了。”
“也就意味着把 chenmo("沉默")这个字符串添加到 StringBuilder 对象中了。”
“再往下看,标号为 21 的这行,又调用了一次 `append()` 方法,意味着把 wanger 这个字符串添加到 StringBuilder 对象中了。”
“再往下看,标号为 21 的这行,又调用了一次 `append()` 方法,意味着把 wanger("王二")这个字符串添加到 StringBuilder 对象中了。”
换成 Java 代码来表示的话,大概是这个样子:
......@@ -77,14 +80,14 @@ class Demo {
public static void main(String[] args) {
String chenmo = "沉默";
String wanger = "王二";
System.out.println((new StringBuilder(String.valueOf(chenmo))).append(wanger).toString());
System.out.println((new StringBuilder(chenmo)).append(wanger).toString());
}
}
```
“哦,原来编译的时候把“+”号操作符替换成了 StringBuilder 的 `append()` 方法啊。”三妹恍然大悟。
“是的,不过到了 Java 9,情况发生了一些改变,同样的代码,字节码指令完全不同了。”我说。
“是的,不过到了 Java 9(不是长期支持版本,所以我会拿 Java 11 来演示),情况发生了一些改变,同样的代码,字节码指令完全不同了。”我说。
同样的代码,在 Java 11 的环境下,字节码指令是这样的:
......@@ -130,6 +133,8 @@ public class com.itwanger.thirtyseven.Demo {
“好吧,总之就是 Java 9 以后,JDK 用了另外一种方法来动态解释 + 号操作符,具体的实现方式在字节码指令层面已经看不到了,所以我就以 Java 8 来继续讲解吧。”
### 为什么要编译为 StringBuilder.append
“再回到《Java 开发手册》上的那段内容:循环体内,拼接字符串最好使用 StringBuilder 的 `append()` 方法,而不是 + 号操作符。原因就在于循环体内如果用 + 号操作符的话,就会产生大量的 StringBuilder 对象,不仅占用了更多的内存空间,还会让 Java 虚拟机不停的进行垃圾回收,从而降低了程序的性能。”
更好的写法就是在循环的外部新建一个 StringBuilder 对象,然后使用 `append()` 方法将循环体内的字符串添加进来:
......@@ -177,6 +182,8 @@ for (int i = 0; i < 100000; i++) {
“是的,哥,原来如此。”
### append方法源码解析
“好了,三妹,来看一下 StringBuilder 类的 `append()` 方法的源码吧!”
```java
......@@ -240,17 +247,9 @@ str.getChars(0, len, value, count)
5)更新数组的长度 count。
“说到 StringBuilder 就必须得提一嘴 StringBuffer,两者就像是孪生双胞胎,该有的都有,只不过大哥 StringBuffer 因为多呼吸两口新鲜空气,所以是线程安全的。”我说,“它里面的方法基本上都加了 synchronized 关键字来做同步。”
### String.concat 拼接字符串
```java
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
```
“除了可以使用 + 号操作符,StringBuilder 和 StringBuilder 的 `append()` 方法,还有其他的字符串拼接方法吗?”三妹问。
“除了可以使用 + 号操作符,StringBuilder 的 `append()` 方法,还有其他的字符串拼接方法吗?”三妹问。
“有啊,比如说 String 类的 `concat()` 方法,有点像 StringBuilder 类的 `append()` 方法。”
......@@ -285,7 +284,7 @@ public String concat(String str) {
“和 `+` 号操作符相比,`concat()` 方法在遇到字符串为 null 的时候,会抛出 NullPointerException,而“+”号操作符会把 null 当做是“null”字符串来处理。”
如果拼接的字符串是一个空字符串(""),那么 concat 的效率要更高一点,毕竟不需要 `new StringBuilder` 对象。
如果拼接的字符串是一个空字符串(""),那么 concat 的效率要更高一点,毕竟不需要 `new StringBuilder` 对象。
如果拼接的字符串非常多,`concat()` 的效率就会下降,因为创建的字符串对象越来越多。
......@@ -293,6 +292,8 @@ public String concat(String str) {
“有,当然有。”
### String.join 拼接字符串
String 类有一个静态方法 `join()`,可以这样来使用。
```java
......@@ -327,6 +328,8 @@ public static String join(CharSequence delimiter, CharSequence... elements) {
里面新建了一个叫 StringJoiner 的对象,然后通过 for-each 循环把可变参数添加了进来,最后调用 `toString()` 方法返回 String。
### StringUtils.join 拼接字符串
“实际的工作中,`org.apache.commons.lang3.StringUtils``join()` 方法也经常用来进行字符串拼接。”
```java
......@@ -372,7 +375,7 @@ public static String join(final Object[] array, String separator, final int star
内部使用的仍然是 StringBuilder。
“好了,三妹,关于字符串拼接的知识点我们就讲到这吧。注意 Java 9 以后,对 + 号操作符的解释和之前发生了变化,字节码指令已经不同了,等后面你学了字节码指令后我们再详细地讲一次。”我说。
“好了,三妹,关于字符串拼接的知识点我们就讲到这吧。注意 Java 9 以后,对 + 号操作符的解释和之前发生了变化,字节码指令已经不同了,等后面你学了[字节码指令](https://tobebetterjavaer.com/jvm/zijiema-zhiling.html)后我们再详细地讲一次。”我说。
“嗯,哥,你休息吧,我把这些例子再重新跑一遍。”三妹说。
......
......@@ -12,6 +12,8 @@ head:
content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,java字符串,String,拆分字符串
---
# 4.10 字符串分割
“哥,我感觉字符串拆分没什么可讲的呀,直接上 String 类的 `split()` 方法不就可以了!”三妹毫不客气地说。
“假如你真的这么觉得,那可要注意了,事情远没这么简单。”我微笑着说。
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册