String源码浅谈

String这个类可以说是我们使用得最为频繁的类之一了,前几次去面试,都被问到String的底层源码,回答得都不是很好,今天就来谈谈一下String的源码。

一、String类

public final class String implements java.io.Serializable, Comparable<String>, CharSequence{...}

String类被是被final修饰的,表明该类不可被继承,有关于关键词final的作用,请移步我的另外一篇文章【JAVA】关键词final的作用。值得一提的是,StringBuilder,StringBuffer类都是被final修饰的。


二、String类的属性

  /** The value is used for character storage. */private final char value[];/** Cache the hash code for the string */private int hash; // Default to 0/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = -6849794470754667710L;

(1)char value[]

String底层的存储结构是一个字符类型的数组,同样也是被final修饰,因此一旦这个字符数组被创建后,value变量不可再指向其他数组,但是可以改变value数组中某一个元素的值。

(2)int hash

hash用来保存某一个String实例自己的哈希值,可以说是哈希值的一个缓存,因此String特别适合放入HashMap中,作为key来使用。每次插入一个键值対时,不需要重新计算key的哈希值,直接取出key的缓存hash值即可,在一定程度上,加快了HashMap的效率。

(3)long serialVersionUID

用于保证版本一致性。由于String实现了Serializable接口,因此需要拥有一个序列化的ID。序列化时,将此ID与对象一并写入到文件中,反序列化时,检测该类中的ID与文件中的ID是否一致,一致的话,说明版本一致,序列化成功。


三、String类的构造函数

(1)无参构造函数,创建一个空字符串,即"",用得地方不多。

    public String() {this.value = new char[0];}

(2)接收一个String实例的构造函数

    public String(String original) {this.value = original.value;this.hash = original.hash;}

(3)接收一个字符数组,利用Arrays.copyOf()方法进行拷贝

    public String(char value[]) {this.value = Arrays.copyOf(value, value.length);}

进入到Arrays.copyOf()方法中,发现调用的是System.arraycopy()方法,Arrays.copyOf()方法如下

    public static char[] copyOf(char[] original, int newLength) {char[] copy = new char[newLength];System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));return copy;}

而System.arraycopy()方法是一个本地方法,由其他语言实现。

从以上源码,可以看得出,这个构造方法没有直接使用传入的字符数组的引用,而是使用该数组的一个拷贝,保证了String类的不可变性。我们无法通过在外部改变此数组中某些元素的值,来改变构造后的String的值。

同样在toCharArray()方法中,也是返回一个基于字符数组的拷贝,并没有直接直接返回value数组。

    public char[] toCharArray() {// Cannot use Arrays.copyOf because of class initialization order issueschar result[] = new char[value.length];System.arraycopy(value, 0, result, 0, value.length);return result;}

另外,有关于System.arraycopy()、Arrays.copyOf()之间的效率问题,可以参考我的另外一篇文章【JAVA】数组复制效率的比较


(4)接收一个字符数组,从offset位置开始复制,一共选取count位

    public String(char value[], int offset, int count) {if (offset < 0) {throw new StringIndexOutOfBoundsException(offset);}if (count < 0) {throw new StringIndexOutOfBoundsException(count);}// Note: offset or count might be near -1>>>1.if (offset > value.length - count) {throw new StringIndexOutOfBoundsException(offset + count);}this.value = Arrays.copyOfRange(value, offset, offset+count);}

其他的构造函数的原理大同小异,这里就不再说明了。


四、String类的其他方法

(1)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;}

String类重写了equals()方法,判断两个String实例代表的字符串是否相同。

判断规则:

如果两个Stirng实例压根就是一个对象,即它们的内存地址相同,则直接返回true。

之后,对anObject进行类型判断,类型为String后,继续判断,否则直接返回false。

再对两者的长度进行判断,如果相等,继续判断,否则返回false。

两者长度相等后,再从前往后依次比较两者字符数组中的元素是否相等,全相等的后,返回true。

equals()方法一上来没有直接比较两个字符串的字符数组元素,在比较超长的字符串时,节省了大量的时间。


(2)compareTo()方法

    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) {char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {return c1 - c2;}k++;}return len1 - len2;}

比较两个字符串,可以用来排序。String类中还有一个内部类CaseInsensitiveComparator,其中也有一个compare()方法,与compareTo()方不同的是,compare()进行比较时,会忽略两个字符串的大小写。


(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()方法,用于计算String实例的哈希值。

哈希值相同的两个字符串不一定相同,相同的字符串的哈希值一定相同。

至此,String类重写了equals(),也重写了hashCode()方法。关于两个方法之间的关系,可以参考我的另外一篇文章【JAVA】为什么重写equals(),就必须要重写hashCode()?


(4)intern()方法

    public native String intern();

可以看得出,它是一个本地方法。

当调用此方法时,会首先在方法区中的常量池中使用equals()寻找是否存在此字符串,如果存在,直接返回此字符串的引用。如果不存在时,会首先将此字符串添加到常量池中,再返回该字符串的引用。

下面通过一个例子来说明

package day1203;//String类的源码
public class StringTest {public static void main(String[] args) {String a = "abc";//这个abc在常量池中创建String b = new String("abc");//这个abc在堆上创建String c = b.intern();//在常量池中寻找是否存在abc,若存在的话,直接返回它的引用。System.out.println(a == b);//返回falseSystem.out.println(a == c);//返回true}
}

这里涉及字符串的创建与存储方式,可以参考我的另外一篇文章【JAVA】字符串的创建与存储机制


五、总结

String类中还有很多有趣的操作,比如字符串的截取、匹配、替换、大小写转换、分割等操作,这里都没有涉及。这些操作确实都是经常用到的,相信大家也能够理解他们的用法,这里就不再赘述了。

如果还有一些疑问,也欢迎大家在下方评论,我一定会及时回复。

【JAVA】String源码浅谈相关推荐

  1. 程序兵法:Java String 源码的排序算法(一)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 <程序兵法:Java Str ...

  2. Java - String源码解析及常见面试问题

    文章目录 Pre Q1: String 是如何实现的? Q2: String 有哪些重要的方法? 构造函数 equals() compareTo() [equals() vs compareTo() ...

  3. 从 重复叠加字符串匹配 看Java String源码中的contains方法

    原题:重复叠加字符串匹配 解题思路已经写在代码中了: class Solution {public:bool contain(string &a, string &b, long lo ...

  4. Java String源码解析

    String类概要 所有的字符串字面量都属于String类,String对象创建后不可改变,因此可以缓存共享,StringBuilder,StringBuffer是可变的实现 String类提供了操作 ...

  5. Java String源码阅读

    String的源码阅读 主要依赖于ArrayList.Arrays.Comparator.StringJoiner和nio包的Charset类. Java程序中所有的字符串字面量都是由该类的实例来实现 ...

  6. 【java】java String 源码

    1.概述 /*** 1.字符串的值创建之后是无法改变的* 2.String buffers 支持可变字符串.* 3.因为String对象是不可变的,所以可以共享它们.** String str = & ...

  7. jQuery源码浅谈系列---$.removeAttr()

    removeAttr(name) ------- 从每一个匹配的元素中删除一个属性. 参数: name   -----------------要删除的属性名 $('#ID').removeAttr(' ...

  8. Java源码详解四:String源码分析--openjdk java 11源码

    文章目录 注释 类的继承 数据的存储 构造函数 charAt函数 equals函数 hashCode函数 indexOf函数 intern函数 本系列是Java详解,专栏地址:Java源码分析 Str ...

  9. java B2B2C 源码 多级分销springmvc mybatis多租户电子商城系统--配置中心服务化和高可用...

    在前两篇的介绍中,客户端都是直接调用配置中心的server端来获取配置文件信息.电子商务平台源码请加企鹅求求:一零三八七七四六二六. 这样就存在了一个问题,客户端和服务端的耦合性太高,如果server ...

最新文章

  1. 码云gitee最大文件限制
  2. android studio安装教程博客园独王,Android Studio安装与配置
  3. Android 轻松实现仿淘宝地区选择
  4. Android 设置 横屏 竖屏
  5. ajax可以获取作用域的值吗,javascript – 为什么ajax调用中的闭包可以访问外部作用域?...
  6. PHP版本中的VC6,VC9,VC11,TS,NTS区别
  7. Python安装包时出现的问题及解决方案
  8. 微型计算机原理与接口技术(慕课版),微机原理与接口技术
  9. 抖音AI大数据,贼棒。
  10. python爬虫-计算机要点
  11. GD32(4)存储管理
  12. php laravel mix,php – Laravel Mix多个入口点生成一个manifest.js
  13. luogu P5560 [Celeste-B]Golden Feather
  14. 在OEL6.5平台安装Oracle11g 数据库
  15. 计算机英语原文件夹,常用文件夹英文解译
  16. vue使用高德地图的搜索地址和拖拽选址
  17. C# 编写VLC视频事件处理程序 libvlc libvlc_event_attach libvlc_event_manager libvlc_event_type ibvlc_event_e用法
  18. Oracle学习篇章一——基础
  19. python小游戏之外星人入侵之pygame实战应用(含源码下载)
  20. MYSQL基础之浅聊 变量

热门文章

  1. DataReader与DataSet,DataGrid与DataList等相关知识
  2. Win10非常好用的6个使用技巧
  3. pcb走线电流计算器。
  4. 密码学五:RSA算法
  5. vivo pad2和小米平板6 pro参数对比 vivo pad2和小米平板6 pro哪个好
  6. 对于 iOS 开发,人工智能意味着什么?
  7. APISpace 中英文敏感词过滤API
  8. Web3到底是什么?和区块链有啥关系?
  9. 【Delphi】Android 动态改变屏幕亮度
  10. mitmproxy工具