前言
本文主要介绍了关于jdk源码分析之string、stringbuilder和stringbuffer的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧
string类的申明
|
1
2
|
public final class string
implements java.io.serializable, comparable<string>, charsequence {…}
|
string类用了final修饰符,表示它不可以被继承,同时还实现了三个接口, 实现serializable接口表示string类可被序列化;实现comparable<t> 接口主要是提供一个compareto 方法用于比较string字符串;还实现了charsequence 接口,这个接口代表的是char值得一个可读序列(charbuffer, segment, string, stringbuffer, stringbuilder也都实现了charsequence接口)
string主要字段、属性说明
|
1
2
3
4
5
6
7
8
|
/*字符数组value,存储string中实际字符 */
private final char value[];
/*字符串的哈希值 默认值0*/
private int hash;
/*字符串的哈希值 默认值0*/
/*一个比较器,用来排序string对象, comparetoignorecase方法中有使用 */
public static final comparator<string> case_insensitive_order
= new caseinsensitivecomparator();
|
string 部分方法分析
string类提供了系列的构造函数,其中有几个都已经不推荐使用了,如下图:
构造函数
以下是两个常用的构造函数的实现:
|
1
2
3
4
5
6
7
8
9
10
11
|
//string str = new string(“123”)
public string(string original) {
this.value = original.value;
this.hash = original.hash;
}
//string str3 = new string(new char[] {'1','2','3'});
public string(char value[]) {
//将字符数组值copy至value
this.value = arrays.copyof(value, value.length);
}
|
boolean equals(object anobject)
string 类重写了 equals 方法,将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 string 对象时,结果才为 true。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public boolean equals(object anobject) {
//直接将对象引用相比较,相同返回true
if (this == anobject) {
return true;
}
//比较当前对象与anobject的字符序列value
if (anobject instanceof string) {
string anotherstring = (string)anobject;
int n = value.length;
if (n == anotherstring.value.length) {
char v1[] = value;
char v2[] = anotherstring.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
|
int compareto(string anotherstring)
逐位比较两个字符串的字符序列,如果某一位字符不相同,则返回该位的两个字符的unicode 值的差,所有位都相同,则计算两个字符串长度之差,两个字符串相同则返回0
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public int compareto(string anotherstring) {
int len1 = value.length;
int len2 = anotherstring.value.length;
//取长度较小的字符串的长度
int lim = math.min(len1, len2);
char v1[] = value;
char v2[] = anotherstring.value;
int k = 0;
while (k < lim) {
//将两个字符串的字符序列value逐个比较,如果不等,则返回该位置两个字符的unicode 之差
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2; //返回unicode 之差
}
k++;
}
//长度较小的字符串所有位都比较完,则返回两个字符串长度之差
//如果两个字符串相同,那么长度之差为0,即相同字符串返回0
return len1 - len2;
}
|
comparetoignorecase(string str)方法实现于此类似,比较时忽略字符的大小写,实现方式如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public int compare(string s1, string s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charat(i);
char c2 = s2.charat(i);
if (c1 != c2) {
c1 = character.touppercase(c1);
c2 = character.touppercase(c2);
if (c1 != c2) {
c1 = character.tolowercase(c1);
c2 = character.tolowercase(c2);
if (c1 != c2) {
// no overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
|
native string intern()
当调用 intern 方法时,如果池已经包含一个等于此 string 对象的字符串(用 equals(object) 方法确定),则返回池中的字符串。否则,将此 string 对象添加到池中,并返回此 string 对象的引用。
所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作,例如:string str1 = "123";
string内存位置:常量池or堆
string对象可以直接通过字面量创建,也可以通过构造函数创建,有什么区别呢?
1.通过字面量或者字面量字符串通过”+”拼接的方式创建的string对象存储在常量池中,实际创建时如果常量池中存在,则直接返回引用,如果不存在则创建该字符串对象
2.使用构造函数创建字符串对象,则直接在堆中创建一个string对象
3.调用intern方法,返回则会将该对象放入常量池(不存在则放入常量池,存在则返回引用)
下面举例说明string对象内存分配情况:
|
1
2
3
4
5
6
7
|
string str1 = new string("123");
string str2 = "123";
string str3 = "123";
string str4 = str1.intern();
system.out.println(str1==str2); // false str1在堆中创建对象,str2在常量池中创建对象
system.out.println(str2==str3); // true str2在常量池中创建对象,str3直接返回的str2创建的对象的引用 所以str2和str3指向常量池中同一个对象
system.out.println(str4==str3); // true str4返回常量池中值为"123"的对象,因此str4和str2、str3都相等
|
关于字符串拼接示例:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class stringtest {
public static final string x = "abc"; // 常量x
@test
public void test() {
string str5 = new string("abc");
string str6 = str5+"def"; //堆中创建
string str7 = "abc"+"def"; //常量池
string str8 = x+"def"; //x为常量,值是固定的,因此x+"def"值已经定下来为abcdef,实际上编译后得代码相当于string str8 = "abcdef"
string str9 = "abc";
string str10 = str9+"def"; //堆中
system.out.println(str6==str7); //false
system.out.println(str8==str7); //true
system.out.println(str10==str7); //false
system.out.println(x==str9); //true
} }
|
反编译后的代码看一下便一目了然:
内存分配如下:
string、stringbuffer、stringbuilder
由于string类型内部维护的用于存储字符串的属性value[]字符数组是用final来修饰的:
|
1
2
|
/** the value is used for character storage. */
private final char value[];
|
表明在赋值后可以再修改,因此我们认为string对象一经创建后不可变,在开发过程中如果碰到频繁的拼接字符串操作,如果使用string提供的contact或者直接使用”+”拼接字符串会频繁的生成新的字符串,这样使用显得低效。java提供了另外两个类:stringbuffer和stringbuilder,用于解决这个问题:
看一下下面的代码:
|
1
2
3
4
5
6
7
8
9
|
string str1="123";
string str2="456";
string str3="789";
string str4 = "123" + "456" + "789"; //常量相加,编译器自动识别 string str4=“123456789”
string str5 = str1 + str2 + str3; //字符串变量拼接,推荐使用stringbuilder
stringbuilder sb = new stringbuilder();
sb.append(str1);
sb.append(str2);
sb.append(str3);
|
下面是stringbuilder类的实现,只截取了分析的部分代码:
|
1
2
3
4
5
6
7
8
9
|
public final class stringbuilder
extends abstractstringbuilder
implements java.io.serializable, charsequence
{
//拼接字符串
@override
public stringbuilder append(string str) {
//调用父类abstractstringbuilder.append super.append(str); return this; } }
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
abstract class abstractstringbuilder implements appendable, charsequence {
/**
* 存储字符串的字符数组,非final类型,区别于string类
*/
char[] value;
/**
* the count is the number of characters used.
*/
int count;
public abstractstringbuilder append(string str) {
if (str == null)
return appendnull();
int len = str.length();
//检查是否需要扩容
ensurecapacityinternal(count + len);
//字符串str拷贝至value
str.getchars(0, len, value, count);
count += len;
return this;
}
private void ensurecapacityinternal(int minimumcapacity) {
// overflow-conscious code
// minimumcapacity=count+str.length
//拼接上str后的容量 如果 大于value容量,则扩容
if (minimumcapacity - value.length > 0) {
//扩容,并将当前value值拷贝至扩容后的字符数组,返回新数组引用
value = arrays.copyof(value,
newcapacity(minimumcapacity));
}
}
//stringbuilder扩容
private int newcapacity(int mincapacity) {
// overflow-conscious code
// 计算扩容容量
// 默认扩容后的数组长度是按原数(value[])组长度的2倍再加上2的规则来扩展,为什么加2?
int newcapacity = (value.length << 1) + 2;
if (newcapacity - mincapacity < 0) {
newcapacity = mincapacity;
}
return (newcapacity <= 0 || max_array_size - newcapacity < 0)
? hugecapacity(mincapacity)
: newcapacity;
}
}
|
stringbuffer和stringbuilder用一样,内部维护的value[]字符数组都是可变的,区别只是stringbuffer是线程安全的,它对所有方法都做了同步,stringbuilder是线程非安全的,因此在多线程操作共享字符串变量的情况下字符串拼接处理首选用stringbuffer, 否则可以使用stringbuilder,毕竟线程同步也会带来一定的消耗。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对快网idc的支持。
原文链接:https://www.cnblogs.com/ashleyboy/p/9063153.html
相关文章
- ASP.NET本地开发时常见的配置错误及解决方法? 2025-06-10
- ASP.NET自助建站系统的数据库备份与恢复操作指南 2025-06-10
- 个人网站服务器域名解析设置指南:从购买到绑定全流程 2025-06-10
- 个人网站搭建:如何挑选具有弹性扩展能力的服务器? 2025-06-10
- 个人服务器网站搭建:如何选择适合自己的建站程序或框架? 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-29 94
-
2025-05-29 97
-
2025-05-29 16
-
2025-05-25 104
-
2025-05-25 79





