String源码分析
前言:String类在日常开发过程中使用频率非常高,平时大家可能看过String的源码,但是真的认真了解过它么,笔者在一次笔试过程中要求写出String的equals方法,瞬间有点懵逼,凭着大致的理解,算是写出来了,可是下来一翻String的源码顿悟,原来自己写得是多么的low,所以有必要把这些基础知识点记录下来,加深印象。
注:本文jdk源码版本为jdk1.8.0_172
1.String类的基本概念
首先String是不可变对象,其体现主要在String类是被final关键字修饰,因此该对象不能被继承,不能被修改。那你可能要问了为什么我们在日常操作过程中不是可以很方便的修改String的内容吗?其实我们修改其内容是new了一个对象,原来的内容并没有改变。
String内部是通过char数组来存储的内容,从以下源码中可以发现:

注:这里的char数组也是被final修饰。
2.String的构造函数
不知道你注意没,String的构造函数非常的多:

这里挑选几个笔者认为有特点的构造函数进行分析,其它构造函数请查看相应源码。
#1.默认构造函数:
public String() {
this.value = "".value;
}
注:默认构造函数的实现非常简单,就是返回空字符串的数组形式。
#2.入参为char数组:
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
分析:如果传入char数组,是通过Arrays#copyOf方法进行拷贝的。
#3.入参为String对象:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
分析:如果入参为String对象,则直接进行相应的赋值即可(value和hash值)。
#4.入参为StringBuffer:
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
分析:这里对StringBuffer进行了加锁,然后再进行拷贝操作。为什么要对StringBuffer加锁呢?StringBuffer不是线程安全的吗?其实这里对其进行加锁正是为了保证在多线程环境下只能有一个线程去操作StringBuffer对象,这里需要注意一下。
#5.入参为StringBuilder:
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
分析:对比StringBuffer入参,如果用StringBuilder作为入参是不加锁操作的,因为StringBuilder本身线程不安全,但会提升性能,并且其源码上也做出了相应注释。

注意:以StringBuffer和StringBuilder为入参的构造函数这里要特别关注一下:StringBuffer线程安全,为了保证在String中也线程安全所以需要加锁,而StringBuilder非线程安全,因此不需要加锁操作,直接进行拷贝即可。
3.hashCode方法
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;
}
分析:String的hashCode方法还是比较简单的,它是遍历char数组以31为基数做一个累加操作。注意这里是以31来作为的基数,为什么取31作为基数可参考:String hashCode 方法为什么选择数字31作为乘子
4.equals方法
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;
}
分析:首先会判断是否是同一个对象,如果是,则直接返回true;其次判断传入对象是否为String对象,如果对象不匹配直接返回false,否则依次比较两个对象的char,只要发现一个不相等,则直接返回false,停止循环。这里的写法还是比较简洁的,在日常开发中可以利用起来。
5.关于不可变对象
关于不可变对象,这里看一段源码就清楚了
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
截取函数,直接看第9行代码处,如果beginIndex==0,返回的是当前对象,否则这里是new的一个新对象,其实String中的很多函数都是这样的操作,具体可翻看源码阅读一下。
总结
#1.String类在日常开发中经常使用,但可能对其源码并不是十分了解,通过分析其源码,了解更多看似简单的知识点。
#2.String类的构造函数非常多,需要注意一下,特别是StringBuffer和StringBuilder为入参的构造函数。
#3.String是不可变对象。
#4.String#hashCode的计算方式,以31为计算基数。
by Shawn Chen,2019.08.20日,上午。
String源码分析的更多相关文章
- (转)Java中的String为什么是不可变的? -- String源码分析
背景:被问到很基础的知识点 string 自己答的很模糊 Java中的String为什么是不可变的? -- String源码分析 ps:最好去阅读原文 Java中的String为什么是不可变的 什 ...
- string源码分析 ——转载 http://blogs.360.cn/360cloud/2012/11/26/linux-gcc-stl-string-in-depth/
1. 问题提出 最近在我们的项目当中,出现了两次与使用string相关的问题. 1.1. 问题1:新代码引入的Bug 前一段时间有一个老项目来一个新需求,我们新增了一些代码逻辑来处理这个新需求.测试阶 ...
- Java中的String为什么是不可变的? — String源码分析
原文地址:http://www.importnew.com/16817.html 什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为 ...
- String源码分析(1)--哈希篇
本文基于JDK1.8,首发于公众号:Plus技术栈 让我们从一段代码开始 System.out.println("a" + "b" == "ab&qu ...
- 【转】Java中的String为什么是不可变的? -- String源码分析
什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不 ...
- String 源码分析
Java 源码阅读 - String String 类型看起来简单,实际上背后的复杂性基本可以涵盖了整个 Java 设计,涉及到设计模式(不可变对象).缓存(String Pool 的理念).JVM( ...
- Java中的String为什么是不可变的? -- String源码分析
众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不能改变状态的意思是, ...
- String 类源码分析
String 源码分析 String 类代表字符序列,Java 中所有的字符串字面量都作为此类的实例. String 对象是不可变的,它们的值在创建之后就不能改变,因此 String 是线程安全的. ...
- Java-Integer源码分析
除了两种浮点型,剩下的几种基本数据类型的包装类几乎都实现了常量池,有好处用数据的时候直接去拿,没有再去创建,坏处是在程序编译的时候就存入大量数据不管用不用到.下面是一篇很好的文章,很详细,转自:htt ...
随机推荐
- 外汇MT4编程手册
1.为了最大的方便用户,交易中断的一些变量可以从智能系统输入. AccountNumber-账号(同义词:AccNum) Ask –卖价(买方出价) Balance – 交易账户的余额值 Bars – ...
- CSS ,flex: 1的用处
flex: 1:的妙用 首先 flex 是 flex-grow.flex-shrink.flex-basis的缩写. 当 flex 取值为一个非负数字,则该数字为 flex-grow 值,flex- ...
- ORA-03113:通信通道的文件结尾 解决办法
登录Oracle时出现错误:“ORA-03113:通信通道的文件结尾” 错误排查方法 Oracle出现错误,查看trace日志寻找问题根源:D:\oracle\diag\rdbms\orcl\orcl ...
- git使用——准备工作
参考:如何在 Git 中重置.恢复,返回到以前的状态 初次运行 Git 前的配置 安装好Git后首先要做的便是配置Git环境.每台计算机上只需要配置一次,程序升级时会保留配置信息. 你可以在任何时候再 ...
- Kubernetes 1.14发布:对Windows节点的生产级支持、Kubectl更新与持久本地卷通用版本已全面到来
今天,我们高兴地宣布Kubernetes 1.14版本的正式亮相,这亦是我们在2019年当中进行的首次发布!Kubernetes 1.14版本由31项增强功能组成,具体包括:10项稳定版功能,12项b ...
- PAT基础级-钻石段位样卷2-7-1 心理阴影面积 (5 分)
这是一幅心理阴影面积图.我们都以为自己可以匀速前进(图中蓝色直线),而拖延症晚期的我们往往执行的是最后时刻的疯狂赶工(图中的红色折线).由红.蓝线围出的面积,就是我们在做作业时的心理阴影面积. 现给出 ...
- 利用socketserver模块的简单功能来完成一个多线程消息传递
客户端:客户端的代码无需改动 import socket client = socket.socket() client.connect(("127.0.0.1",8777)) w ...
- jwt的原理以及使用
jwt原理(json web token) 我们之前是使用session实现登录,通过实际密码+盐组成字符串进行md5存入redis或者数据库中,输入的密码与实际校验通过,发送给客户端一个有效时间的t ...
- 前端学习笔记--CSS样式--文本
1.文本与文字样式主要的属性: 子标签可以继承父标签的样式: 关于颜色: 文本属性: letter-spacing: line-height: text-align: 字体:
- centos安装nodejs和配置npm
1.下载安装nodejs 1 wget http://nodejs.org/dist/v7.4.0/node-v7.4.0.tar.gz 2 yum install gcc openssl-devel ...