理解String的特性以及与StringBuilder StringBuffer的区别

一、String类型

String类型在Java中是十分常用的类型,指向一个字符串,如:“ABC”、“123”等。String类是定义于java.lang.string包中的,该包提供了切割、复制、查找等函数。String是一个用 final 声明的常量类,不能被任何类所继承,且无法被修改。

(一)String不变性

关于String类,一个重要的特性是其不可变性,即一个字符串一旦被创建,就不会再被修改。但是显然我们在写代码时,以下句子是可以被通过的:

String s = "abcd";
s = "123";

这是因为,这种不可变性指的是其内存中的实现。当我们创建一个字符串时,内存中会创建这样的一个字符串对象的引用。而当我们尝试修改s的内容时,我们并不是修改字符串对象,而是在内存中重新创建了一个字符串对象,并且将指针指向了这个新创建的字符串对象。
在这里插入图片描述

(二)String在内存中的两种存储方法

1.存储位置

String实际上是一个指针(原型为一个char*类型的指针,后来改进为byte[]数组指针,学过C/C++应该明白),而这个指针存储的位置可以是栈或者堆,这主要是取决于这个字符串被创建的方法,自动创建的变量是存在栈中的,而通过new生成的变量是存储在堆中的。

创建方法一:

 String str1 = "abc"; 

步骤:

  1. 栈中开辟一块空间存放引用str1,
  2. String池中开辟一块空间,存放String常量"abc",
  3. 引用str1指向池中String常量"abc",
  4. str1所指代的地址即常量"abc"所在地址,输出为true

创建方法二:

String str2 = new String("abc"); 

步骤:

  1. 栈中开辟一块空间存放引用str2,
  2. 堆中开辟一块空间存放一个新建的String对象"abc",
  3. 引用str2指向堆中的新建的String对象"abc",
  4. str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false

注意:第一种创建方法得到的字符串常量存储在常量池中,而且具有不变性,而第二种创建方法则存储在堆中,不具有不变性(相同字符串也是存储在不同地址的)。

2. 存储方式——字符串常量池

在Java的内存分配中,总共3种常量池,分别是Class常量池、运行时常量池、字符串常量池,String类型的对象自然是存在字符串常量池中。

字符串的分配需要消耗高昂的时间和空间的,而且字符串使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性,常量池中一定不存在两个相同的字符串。

所以说,如果两个字符串指针指向了同一个字符串,如:

String s = "abcd";
String s2 = "abcd";

则会指向字符串常量池中的同一个字符串对象。
在这里插入图片描述

当String类对字符串进行连接操作时,同样的,也是在字符串常量池中创建了一个新的字符串,并将字符串指针指向了该字符串。

s = s.concat("ef");

在这里插入图片描述

(三)java.lang.String提供的函数

1 char charAt(int index)
返回指定索引处的 char 值。
2 int compareTo(Object o)
把这个字符串和另一个对象比较。
3 int compareTo(String anotherString)
按字典顺序比较两个字符串。
4 int compareToIgnoreCase(String str)
按字典顺序比较两个字符串,不考虑大小写。
5 String concat(String str)
将指定字符串连接到此字符串的结尾。
6 boolean contentEquals(StringBuffer sb)
当且仅当字符串与指定的StringBuffer有相同顺序的字符时候返回真。
7 static String copyValueOf(char[] data)
返回指定数组中表示该字符序列的 String。
8 static String copyValueOf(char[] data, int offset, int count)
返回指定数组中表示该字符序列的 String。
9 boolean endsWith(String suffix)
测试此字符串是否以指定的后缀结束。
10 boolean equals(Object anObject)
将此字符串与指定的对象比较。
11 boolean equalsIgnoreCase(String anotherString)
将此 String 与另一个 String 比较,不考虑大小写。
12 byte[] getBytes()
使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
13 byte[] getBytes(String charsetName)
使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
14 void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
将字符从此字符串复制到目标字符数组。
15 int hashCode()
返回此字符串的哈希码。
16 int indexOf(int ch)
返回指定字符在此字符串中第一次出现处的索引。
17 int indexOf(int ch, int fromIndex)
返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
18 int indexOf(String str)
返回指定子字符串在此字符串中第一次出现处的索引。
19 int indexOf(String str, int fromIndex)
返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
20 String intern()
返回字符串对象的规范化表示形式。
21 int lastIndexOf(int ch)
返回指定字符在此字符串中最后一次出现处的索引。
22 int lastIndexOf(int ch, int fromIndex)
返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。
23 int lastIndexOf(String str)
返回指定子字符串在此字符串中最右边出现处的索引。
24 int lastIndexOf(String str, int fromIndex)
返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
25 int length()
返回此字符串的长度。
26 boolean matches(String regex)
告知此字符串是否匹配给定的正则表达式。
27 boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
测试两个字符串区域是否相等。
28 boolean regionMatches(int toffset, String other, int ooffset, int len)
测试两个字符串区域是否相等。
29 String replace(char oldChar, char newChar)
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
30 String replaceAll(String regex, String replacement)
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
31 String replaceFirst(String regex, String replacement)
使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
32 String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。
33 String[] split(String regex, int limit)
根据匹配给定的正则表达式来拆分此字符串。
34 boolean startsWith(String prefix)
测试此字符串是否以指定的前缀开始。
35 boolean startsWith(String prefix, int toffset)
测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
36 CharSequence subSequence(int beginIndex, int endIndex)
返回一个新的字符序列,它是此序列的一个子序列。
37 String substring(int beginIndex)
返回一个新的字符串,它是此字符串的一个子字符串。
38 String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。
39 char[] toCharArray()
将此字符串转换为一个新的字符数组。
40 String toLowerCase()
使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
41 String toLowerCase(Locale locale)
使用给定 Locale 的规则将此 String 中的所有字符都转换为小写。
42 String toString()
返回此对象本身(它已经是一个字符串!)。
43 String toUpperCase()
使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
44 String toUpperCase(Locale locale)
使用给定 Locale 的规则将此 String 中的所有字符都转换为大写。
45 String trim()
返回字符串的副本,忽略前导空白和尾部空白。
46 static String valueOf(primitive data type x)
返回给定data type类型x参数的字符串表示形式。
47 contains(CharSequence chars)
判断是否包含指定的字符系列。
48 isEmpty()
判断字符串是否为空。

二、StringBuilder

StringBuffer类也代表字符串, 内部实现跟String不一样。

(一)StringBuilder的存储方法

StringBuilder的继承结构如下:
在这里插入图片描述
StringBuilder对象则代表一个字符序列可变的字符串,StringBuilder内部维护了一个char[]类型的value,当我们要修改指向的字符串时可通过StringBuilder提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列,而非创建一个新的String对象。一旦通过StringBuilder生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。这种存储方式使得存储的字符串更少,因此拥有更高的效率和更少的存储空间。当我们想要连接两个字符串时进行的操作如下图:
在这里插入图片描述
在StringBuilder中如果数组的剩余容量,无法添加全部内容,则通过expandCapacity(int minimumCapacity)方法对value进行扩容,其中minimumCapacity = 原value长度 + append添加的内容长度。
1、扩大容量为原来的两倍 + 2,为什么要 + 2,而不是刚好两倍?
2、如果扩容之后,还是无法添加全部内容,则将 minimumCapacity 作为最终的容量大小;
3、利用 System.arraycopy 方法对原value数据进行复制;
在使用StringBuilder时,如果给定一个合适的初始值,可以避免由于char[]数组多次复制而导致的性能问题。

(二)java.lag.StrigBuilder提供的函数

一、创建Stringbuilder对象
StringBuilder strB = new StringBuilder();

1 append(String str)/append(Char c)
字符串连接
2 toString()
返回一个与构建起或缓冲器内容相同的字符串
3 appendcodePoint(int cp)
追加一个代码点,并将其转换为一个或两个代码单元并返回this
4 setCharAt(int i, char c)
将第 i 个代码单元设置为 c(可以理解为替换)char字符使用 ‘ ’
5 insert(int offset, String str)/insert(int offset, Char c)
在指定位置之前插入字符(串)
6 delete(int startIndex,int endIndex)
删除起始位置(含)到结尾位置(不含)之间的字符串

三、StringBuffer

StringBuffer和StringBuilder类似,提供了几乎同样的函数,不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。

线程安全的实现是通过synchronized实现的,StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。
在这里插入图片描述
但是线程安全的实现必然会带来时间性能上的减弱。

总结

Sting、StringBuilder、StringBuffer三者均用于实现字符串,但由于其字符串存储方法的不同,三者时间性能上从优到差为StringBuilder>StringBuffer>String。所以:
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值