前言: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源码分析的更多相关文章

  1. (转)Java中的String为什么是不可变的? -- String源码分析

    背景:被问到很基础的知识点  string  自己答的很模糊 Java中的String为什么是不可变的? -- String源码分析 ps:最好去阅读原文 Java中的String为什么是不可变的 什 ...

  2. string源码分析 ——转载 http://blogs.360.cn/360cloud/2012/11/26/linux-gcc-stl-string-in-depth/

    1. 问题提出 最近在我们的项目当中,出现了两次与使用string相关的问题. 1.1. 问题1:新代码引入的Bug 前一段时间有一个老项目来一个新需求,我们新增了一些代码逻辑来处理这个新需求.测试阶 ...

  3. Java中的String为什么是不可变的? — String源码分析

    原文地址:http://www.importnew.com/16817.html 什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为 ...

  4. String源码分析(1)--哈希篇

    本文基于JDK1.8,首发于公众号:Plus技术栈 让我们从一段代码开始 System.out.println("a" + "b" == "ab&qu ...

  5. 【转】Java中的String为什么是不可变的? -- String源码分析

    什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不 ...

  6. String 源码分析

    Java 源码阅读 - String String 类型看起来简单,实际上背后的复杂性基本可以涵盖了整个 Java 设计,涉及到设计模式(不可变对象).缓存(String Pool 的理念).JVM( ...

  7. Java中的String为什么是不可变的? -- String源码分析

    众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不能改变状态的意思是, ...

  8. String 类源码分析

    String 源码分析 String 类代表字符序列,Java 中所有的字符串字面量都作为此类的实例. String 对象是不可变的,它们的值在创建之后就不能改变,因此 String 是线程安全的. ...

  9. Java-Integer源码分析

    除了两种浮点型,剩下的几种基本数据类型的包装类几乎都实现了常量池,有好处用数据的时候直接去拿,没有再去创建,坏处是在程序编译的时候就存入大量数据不管用不用到.下面是一篇很好的文章,很详细,转自:htt ...

随机推荐

  1. win10重装系统修改信息

    在安装win10系统之前要先进行ahci硬盘模式更改 ,以防止win10系统安装完成后出现蓝屏现象,那么如何进行ahci硬盘模式bios设置呢?今天我们就以映泰主板为大家介绍u盘装win10系统硬盘模 ...

  2. vue锚点

    第一种: router.js中添加 mode: 'history', srcollBehavior(to,from,savedPosition){ if(to.hash){ return { sele ...

  3. LCD驱动的学习

    简介: LCD是基于液晶的. LCD(liquid crystal display)按驱动方式分类可以分为静态驱动,简单矩阵驱动,主动矩阵驱动.其中,简单矩阵又可以分为扭转向列型(TN)和超转向列型( ...

  4. 安装配置nginx之后访问不了nginx的问题

    我刚开通的服务器,没有设置安全组规则. 进入云服务控制台 配置规则 其他不要动,授权对象加0.0.0.0/0 就可以访问nginx了

  5. TLS1.3对CIP的影响(对密码套件的解释)

    1.术语定义的即使(算法)Definition of terms (optional) Cipher Suite  :通信数据保护规范,对TLS指定对端身份验证,关键技术机制,后续数据加密和数据验证机 ...

  6. @EnableCircuitBreaker熔断超时机制

    客户端请求服务端的时候总是报超时,默认熔断机制是1S

  7. Python 操作Zabbix API 获取ERROR级别告警信息并打印

    1.需求:有一个语音合成播报项目,要实时获取zabbix的ERROR级别以上告警信息,将该信息合成语音播报出去.(合成语音及播报已经完成) 2.现实:整理zabbix告警级别,将不太重要的告警放到ER ...

  8. Mysql 安装,及其闪退的问题解决

    1.下载 Mysql 64下载链接(华军软件) http://soft.onlinedown.net/soft/3573.htm 2.安装 2.1 傻瓜式安装 不再赘述,略过. 2.2安装完成打开闪退 ...

  9. clean()方法的简单应用

    clean()方法主要用于验证相互依赖的字段,例如注册时,填写的“密码”和“确认密码”要相等时才符合要求. 在调用表单clean() 方法的时候,所有字段的验证方法已经执行完(表单字段的默认验证(如C ...

  10. PHP yield占用内存测试

    function com($start) { $tmp = []; for($i=0; $i<300000; $i++){ $tmp[] = $i; } $end = memory_get_us ...