JDK源码分析系列---String,StringBuilder,StringBuffer
1.String
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
//存储字符,final修饰
private final char value[];
//缓存hash code,默认0
private int hash;
//序列号
private static final long serialVersionUID = -6849794470754667710L;
//声明可序列化字段
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
}
1.1 基本属性
- char value[],用来存储字符串对象的字符数组
- int hash,用来缓存字符串的hash code,默认值为0
- long serialVersionUID,用来序列化的序列版本号
- ObjectStreamField[],可序列化类的字段说明
1.2 常用构造器
public String() {
this.value = "".value;
}
初始化新创建的对象,表示空字符串""。请注意,此构造函数是不需要使用的,因为字符串是不可变的.
String str = new String(); 本质上是创建了一个空的字符数组,str的长度为0
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
初始化新创建的对象,表示和参数一样的字符串,换句话说是创建了和参数一样的对象副本,除非需要显示的声明副本,否则该构造函数是不需要的,因为字符串是不可变的
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
把StringBuffer的内容复制到String对象中,随后修改StringBuffer对象的值,并不会影响String对象
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
把StringBuilder的内容复制到String对象中,随后修改StringBuilder的值,并不会影响String对象;
此构造函数是为了把StringBuilder转移到String对象,但是推荐使用StringBuilder的toString()方法,因为运行更快
1.3 常用方法
//返回字符串的长度
public int length() {
return value.length;
} //比较两个String值是否相等
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
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;
} //生成hash code
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
equals方法的判断流程:
- 首先判断两个对象是否相同,若相同返回true;若不同,下一步
- 判断参数是否为String对象,若不是,返回false;若是,下一步
- 判断两个String的长度是否相等,若不是,返回false;若是,下一步
- 按字符数组索引依次比较字符,如果有任一不相同,返回false,否则返回true
这里可以看到equals方法的实现也是调用了==来比较对象是否相同,因此在讨论equals和==区别的时候 要全面分析.
1.2 为什么说String是不可变对象?
存储字符的数组value[]是final修饰的,值不可更改.
2. AbstractStringBuilder
可变的字符序列,StringBuilder和StringBuffer都继承了该类,要了解StringBuilder和StringBuffer首先先了解AbstractStringBuilder.
2.1 基本属性
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
}
char[] value: 存储字符的数组
int count: 存储的字符的数量
2.2 构造器
/**
* 无参构造器,用于子类序列化
*/
AbstractStringBuilder() {
} /**
* 指定字符数组容量
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
2.3 常用方法
/**
* 返回字符的数量
*/
@Override
public int length() {
return count;
} /**
* 返回当前可存储字符的最大数量,即容量
*/
public int capacity() {
return value.length;
} /**
* 保证当前容量大于等于指定的最小数量minimumCapacity,会调用扩容方法
*/
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
} /**
* 扩容,只有minimumCapacity大于当前容量,才会copy数组扩容
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
/**
* 当前对象拼接字符串str
* 如果参数为null,那么最终字符串为"null",如果参数类型为boolean,那么返回的是"true"或"false"
* 例1: "abc".append(null) = "abcnull"
* 例2: "abc".append(false) = "abcfalse"
*/
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;
}
3. StringBuilder
可变的字符序列,非线程安全,StringBuilder和StringBuffer的实现方法很相似,区别在于是否线程安全,在单线程的情况下可使用StringBuilder,因为它比StringBuffer运行更快.StringBuilder继承了AbstractStringBuilder类.
3.1 基本属性
继承父类
3.2 构造器
/**
* 默认容量16
*/
public StringBuilder() {
super(16);
} /**
* 指定初始容量
*/
public StringBuilder(int capacity) {
super(capacity);
} /**
* 把String字符串初始化到对象中,容量变为str的长度+16
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
} /**
* 把字符初始化到对象中,容量变为字符的长度+16
*/
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
3.3 常用方法
同父类
4. StringBuffer
可变的字符序列,线程安全,StringBuffer继承了AbstractStringBuilder类.
4.1 基本属性
继承父类,同时还有属性toStringCache
这里涉及到一个关键字transient,其作用是让某些被修饰的成员属性变量不被序列化,为什么不需要序列化呢?主要是因为这个变量可能为null,也可能非空,不能准确的代表StringBuffer的属性.所以没有必要序列化,也节省了空间.
public final class StringBuffer extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{ /**
* 最后一次调用toString返回值的缓存
* 当StringBuffer被修改时该缓存被清除
*/
private transient char[] toStringCache; /** 序列号 */
static final long serialVersionUID = 3388685877147921107L;
}
4.2 构造器
同StringBuilder
4.3 常用方法
与StringBuilder方法基本相同,区别在于在StringBuilder的方法上加了synchronized锁. 不同的地方还包括每个修改对象的方法,比如append方法都会清除toStringCache缓存.
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
- 在调用toString方法的时候,会先判断缓存toStringCache是否存在,如果不存在,那么把当前对象赋值给toStringCache,然后得到toStringCache的字符串;如果toStringCache已经存在,那么直接读取缓存的字符串.
- toStringCache是否存在依赖于StringBuffer对象是否被修改,如果被修改了,那么缓存被清空.
5.总结
- String,StringBuilder,StringBuffer这三个类都可以创建和操作字符串;
- 不同点在于String是不可变的,不存在线程安全问题;StringBuilder和StringBuffer字符串是可变的,StringBuilder线程不安全,StringBuffer线程安全;
- 对于简单的字符串赋值和拼接操作,可使用String
- 对于字符串频繁修改和拼接操作,不建议使用String,因为每次操作都需要创建一个String对象,单线程推荐使用StringBuilder,多线程推荐使用StringBuffer.
JDK源码分析系列---String,StringBuilder,StringBuffer的更多相关文章
- JDK源码学习系列03----StringBuffer+StringBuilder
JDK源码学习系列03----StringBuffer+StringBuilder 由于前面学习了StringBuffer和StringBuilder的父类A ...
- HashMap源码分析(一):JDK源码分析系列
正文开始 注:JDK版本为1.8 HashMap1.8和1.8之前的源码差别很大 目录 简介 数据结构 类结构 属性 构造方法 增加 删除 修改 总结 1.HashMap简介 HashMap基于哈希表 ...
- 【JDK源码分析】String的存储区与不可变性
// ... literals are interned by the compiler // and thus refer to the same object String s1 = " ...
- 【JDK源码分析】String的存储区与不可变性(转)
// ... literals are interned by the compiler // and thus refer to the same object String s1 = " ...
- 【JDK源码分析】String的存储区与不可变 专题
<Think in Java>中说:“关系操作符生成的是一个boolean结果,它们计算的是操作数的值之间的关系”. "=="判断的是两个对象的内存地址是否一样,适用于 ...
- JDK源码分析系列02---ArrayList和LinkList
ArrayList和LinkList的源码分析 概要 ArrayList和LinkList是常用的存储结构,不看源码先分析字面意思,Array意思是数组,可知其底层是用数组实现的,Link意思是链接, ...
- JDK源码分析(8) StringBuffer & StringBuilder
简介 StringBuffer与StringBuilder是两个常用的操作字符串的类.大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的.前者是JDK1.5加 ...
- LinkedList源码分析:JDK源码分析系列
如果本文中有不正确的地方请指出由于没有留言可以在公众号添加我的好友共同讨论. 1.介绍 LinkedList 是线程不安全的,允许元素为null的双向链表. 2.继承结构 我们来看一下LinkedLi ...
- JDK源码分析(一)—— String
dir 参考文档 JDK源码分析(1)之 String 相关
随机推荐
- 傅里叶分析(matlab)
一维信号的傅里叶变换:fft(t) 二维图像的傅里叶变换:fft2(t) fft2(x) ⇒ fft(fft(x)')' 0. 基础 f(t)=∑k=−∞∞αkeikt 1. frequency sp ...
- You don't have permission to access / on this server问题的解决.
vhosts.conf配置文件中虚拟主机的配置如下,Options Indexes FollowSymLinks 后面添加 ExecCGI <VirtualHost 192.168.10.82: ...
- OA 框架
@{ Layout = null;}<!DOCTYPE html><html><head> <meta name="viewport&q ...
- IdentityServer流程图与相关术语
概念图 apparch 最常见的交互是:浏览器与web应用程序通信web应用程序与web APIs进行通信基于浏览器的应用程序与web APIs通信原生应用与web APIs通信基于服务的应用程序 ...
- 消息队列中点对点与发布订阅区别(good)
背景知识 JMS一个在 Java标准化组织(JCP)内开发的标准(代号JSR 914).2001年6月25日,Java消息服务发布JMS 1.0.2b,2002年3月18日Java消息服务发布 1.1 ...
- CoolFormat(Qt Creator也可管理VC的Project)
http://download.csdn.net/download/akof1314/8457593 https://github.com/akof1314/CoolFormat http://dow ...
- Raknet是一个基于UDP网络传输协议的C++网络库(还有一些其它库,比如nanomsg,fastsocket等等)
Raknet是一个基于UDP网络传输协议的C++网络库,允许程序员在他们自己的程序中实现高效的网络传输服务.通常情况下用于游戏,但也可以用于其它项目. Raknet有以下好处: 高性能 在同一台计算机 ...
- 微软Skype实时口译增加中文
直击现场 在机器翻译技术上,微软的 Skype 业务也算是行业内名列前茅.日前其实时口语翻译技术再次跃升一个台阶,新增了对中文(普通话)的翻译支持. 据美国科技新闻网站 TheVerge 报道,此前, ...
- Aria2在Windows上如何安装配置使用
一.下载所需的软件 二.安装与使用 三.Aria2的额外内容 四.Aria2的使用 五.Aria2与其它插件配合使用 一.下载所需的软件 可以从一下地址获取最新版本 GitHub: https://g ...
- qt中用tcp传输xml消息 good
本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:WIN7 开发环境:Qt5 3.1.2 说明: 在tcp上传输xml消息. 协议格式如 ...