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 ...
随机推荐
- 【转载】Response对象的作用以及常用方法属性
Response对象是Asp.Net应用程序中非常重要的一个内置对象,其作用为负责将服务器执行好的信息输出给客户端,即作用主要为响应客户端请求并将服务器的响应返回给用户,在页面的临时跳转中,也可使用R ...
- 2019.9.27,SAP成都研究院数字创新空间团队建设,射箭和游泳
2019年9月27日,秋高气爽,SAP成都研究院数字创新团队全体成员又迎来了一次团队建设活动.这次的主题是:射箭. 在正式活动之前,大家先享用了一顿泰式海鲜火锅: 吃饱喝足之后,我们来到了名为&quo ...
- MongoDB的基础概念
1.MongoDB和传统数据库的概念区别 database database 数据库table collection 数据库表/集合row ...
- docker 部署oracle
Oracle数据库服务器Docker映像文档 Oracle Database Server 12c R2是行业领先的关系数据库服务器.Oracle数据库服务器Docker映像包含在Oracle Lin ...
- 【DRF框架】序列化组件——ModelSerializer
ModelSerializer 1.ModelSerializer类似于ModelForm 2.根据模型自动生成一组字段 3.自带实现了.update()以及.create()方法 ModelSeri ...
- mysql安装和遇到的问题处理
遇到需要在新系统上安装MySQL的事情,简单记录一下过程. 声明:最好的文档是官方文档,我也是看的官方文档,只是中间遇到点问题,记录一下出现的问题和处理方式.贴一些官方文档地址. 用tar包的安装方式 ...
- Ajax 的简介与使用
一.什么是Ajax Ajax 的全称是 Asynchronous JavaScript and XML(即异步的 JavaScript 和 XML),是一种在无需重新加载整个网页的情况下,能够更新部分 ...
- 牛客练习赛6 C 手铐
手铐 思路: 缩环+树形dp 代码: #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(4) #include& ...
- 《Hello--world团队》第三次作业:团队项目的原型设计
项目 内容 这个作业属于哪个课程 2016级计算机科学与工程学院软件工程(西北师范大学) 这个作业的要求在哪里 实验七 团队作业3:团队项目原型设计与开发 团队名称 <hello--world团 ...
- 安装pip的三种方式
pip是python的一个工具,用来安装python包特别方便.Linux系统是是内置python程序,因为许多Linux内置文件都是使用python来编写的,比如说yum. 1.脚本安装 通过脚本的 ...