这篇文章来自我的博客

之前写过一篇关于 String 类、StringBuilder 和 StringBuffer 的基本介绍,今天从 String 类的部分源码来看 String 类(本文基于 JDK 1.8)

  1. String 类的实例化
  2. 常用方法
  3. String 类的不可变
  4. 总结

1. String 类的构造器

先看看 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
复制代码

翻了以下源码,发现从 100 - 600 行左右都是写的构造器,数不胜数,这里就选几个常见的吧:

先来说说连开发者都觉得没用的构造器吧:

    //初始化一个 String 对象,它代表了一个空的字符序列//我们平常直接就 String str = "";就好了public String() {this.value = "".value;}//初始化一个和参数一模一样的对象,除非你是要保留副本,不然没什么用处public String(String original) {this.value = original.value;this.hash = original.hash;}
复制代码

然后是根据字符数组来进行初始化的构造器:

    /*** 用一个字符数组当前所储存的字符来初始化字符串,* 后续对数组的修改将不会对字符串有影响,感觉在做* String 类的题目时,较常用到这个方法*/public String(char value[]) {this.value = Arrays.copyOf(value, value.length);}//截取字符数组中的某一部分来初始化public String(char value[], int offset, int count) {if (offset < 0) {throw new StringIndexOutOfBoundsException(offset);}if (count <= 0) {if (count < 0) {throw new StringIndexOutOfBoundsException(count);}if (offset <= value.length) {this.value = "".value;return;}}// 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);}
复制代码

最后是根据 StringBuffer 和 StringBuilder 来进行初始化的构造器:

    //StringBuffer 是线程安全的,所以需要加锁public String(StringBuffer buffer) {synchronized(buffer) {this.value = Arrays.copyOf(buffer.getValue(), buffer.length());}}public String(StringBuilder builder) {this.value = Arrays.copyOf(builder.getValue(), builder.length());}
复制代码

这里还有个很有意思的包私有构造器(看不懂):

    /** Package private constructor which shares value array for speed.* this constructor is always expected to be called with share==true.* a separate constructor is needed because we already have a public* String(char[]) constructor that makes a copy of the given char[].*/String(char[] value, boolean share) {// assert share : "unshared not supported";this.value = value;}
复制代码

后来去 Google 了一下有关字符串共享的相关内容,还是不太明白,可能是因为常量池的原因,在常量池中存放着字符串的引用,就会导致字符串被共享,这个构造器可能就是共享时的构造吧(待确定),文末给出写文时查询这一部分内容的链接,这一部分内容等下一篇讲述常量池时一起说说

2. 常用方法

1. 基本的

常用的 length(), charAt(), isEmpty() 等等都是根据根据字符数组来做操作的,就看其中一个吧:

    public char charAt(int index) {if ((index < 0) || (index >= value.length)) {throw new StringIndexOutOfBoundsException(index);}return value[index];}
复制代码

2. hashCode()

这个方法是重写了 Object 类的 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;}
复制代码

在 哈希值等于 0 且字符串不为空时,才计算哈希值,否则就返回 0,计算方法就是 for 循环里的,举个简单的例子算一下:

字符串 ab 的哈希值就是 97 * 32 + 98 得出的

通过对字符串的哈希值的比较,能够得出两个字符串的值是否相同,但是却不能用来比较两个对象的引用

3. join()

这个方法以前还都没听过,这次刚好看见了,就说一下用法:

    //第一个参数时分隔符,第二个是可变参数public static String join(CharSequence delimiter, CharSequence... elements) {Objects.requireNonNull(delimiter);Objects.requireNonNull(elements);// Number of elements not likely worth Arrays.stream overhead.StringJoiner joiner = new StringJoiner(delimiter);for (CharSequence cs: elements) {joiner.add(cs);}return joiner.toString();}
复制代码

这里引出另一个类:StringJoiner,这里不介绍,如果只有一个字符串,就不会添加分隔符,若有多个,则在每两个拼接的字符串中添加分隔符:

如果要拼接多个字符,并且要添加分隔符,用 join() 会方便很多

4. equals()

== 和 equals() 的区别相信都应该知道了,前者比较引用(引用相同,值自然就相同),后者只比较值:

    public boolean equals(Object anObject) {//如果引用相同,就直接返回 trueif (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;}
复制代码

3. String 类的不可变

一直都在说 String 类是不可变的,究竟是怎么实现呢?

首先,String 类本身被声明为 final:public final class String,不能被继承,所以不可能重写 String 类的方法(只读),整个 String 类的核心:private final char value[] 也被声明为 final

其次,在源码中的返回值为 String 类型的方法,如果方法中修改了字符串的结构,在最后都是 return new String(xxx) 这样子,如果没有修改字符串结构,就返回这个字符串,也就是所说的:任何修改字符串结构的操作都会产生一个新的 String 对象

4. 总结

其实对于 String 类,最重要的点就是不可变,至于其它的一些操作,感兴趣的可以去翻一下源码(3100 行,不多),注释写的非常清楚,很多操作其实伴随注释看一遍就可理解),就没有写在这篇文章里

最近开始着手准备明年的春招了,所以刚好写博客复习一下 Java 的一些基础知识,下一篇会介绍一下常量池

参考资源:

很早之前的一篇文章,JDK 版本还没到 1.8,所以看一看就好了:

chunlong.github.io/blog/2013/0…

Java基础知识梳理(五)从源码了解字符串相关推荐

  1. 零基础学习Java真的很难?初学者必须了解的Java基础知识梳理

    作为最受欢迎的编程语言之一,Java是很多人转行的首选.对于零基础而言,在学习Java前要了解哪些语言特征和常识呢?下面,小编就来分享一下零基础学习Java者必须了解的Java语言常识以及知识. 从1 ...

  2. 干货!Java基础知识梳理,绝对经典

    作为最受欢迎的编程语言之一,Java是很多人转行的首选.对于零基础而言,在学习Java前要了解哪些语言特征和常识呢?下面,就分享零基础学习Java必须了解的Java语言常识以及知识. 从1995年诞生 ...

  3. android okhttp 源码解析,OkHttp 知识梳理(1) - OkHttp 源码解析之入门

    OkHttp 知识梳理系列文章 一.简介 OkHttp无疑是目前使用的最多的网络框架,现在最火的Retrofit也就是基于OkHttp来实现的,和以前一样,我们将从最简单的例子开始,一步步剖析OkHt ...

  4. Framework 源码解析知识梳理(5) startService 源码分析

    一.前言 最近在看关于插件化的知识,遇到了如何实现Service插件化的问题,因此,先学习一下Service内部的实现原理,这里面会涉及到应用进程和ActivityManagerService的通信, ...

  5. java基础进阶一:String源码和String常量池

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/8046564.html 邮箱:moyi@moyib ...

  6. JAVA 基础知识梳理

    JAVA基础 java 特性 封装: 封装指的是属性私有化,根据需要提供setter和getter方法来访问属性.即隐藏具体属性和实现细节,仅对外开放接口,控制程序中属性的访问级别. 目的:增强安全性 ...

  7. java基础知识梳理_java基础知识点梳理3

    集合框架 特点: 1:对象封装数据,对象多了也需要存储.集合用于存储对象. 2:对象的个数确定可以使用数组,但是不确定怎么办?可以用集合.因为集合是可变长度的. 集合和数组的区别: 1:数组是固定长度 ...

  8. JAVA基础知识(五)数据类型转换

    当使用 +.-.*./.%.运算操作时,遵循如下规则: 1.只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,并且结果也是double类型: 2.如果两个操作数中有一个是 ...

  9. Java基础知识梳理

最新文章

  1. 2022-2028年中国高强度钢行业投资分析及前景预测报告
  2. AccEAP架构介绍(1)---实体的设计
  3. 调试笔记--keil 断点调试小技巧
  4. 年轻10岁简单又易行的妙方
  5. leetcode题解50-Pow(x,n)
  6. angularJS表达式详解!
  7. 机器学习三个部分:输入、算法、输出 资料收集
  8. 【apiPost】-工具
  9. 日本互联网 20 年沧桑路
  10. 怎么把优酷的kux格式转换成mp4?快速转换kux格式的技巧
  11. 为选区添加描边_ps怎么给选区加上虚线描边
  12. SAP UI5 应用开发教程之一百 - 如何修改 SAP UI5 框架的源代码实现,以及使用本地部署的 SAP UI5 SDK 试读版
  13. 民办教育未来10年的发展趋势
  14. STM32F103系列的单片机一共有11个定时器
  15. 微信公众号用户授权登录逻辑
  16. 729. 我的日程安排表 I
  17. 全国身份证号码(项目中用的)
  18. Google Earth Engine(GEE)扩展——gena包文本注释脚本
  19. 机器学习信息熵和热力学定律中的熵有关系吗?
  20. 玖乐解密垃圾某山代刷网废话不多自行看就行了可二开可运营

热门文章

  1. 实战 | F1060路由模式ISIS典型组网配置案例
  2. SSLH:让 HTTPS 和 SSH 共享同一个端口
  3. Nginx何防止流量攻击
  4. 【Oracle】Rman简介
  5. vue+webpack项目打包后背景图片加载不出来问题解决
  6. 微信扫码支付模式二【无法回调】解决方案(转)
  7. vue学习笔记(四)- cmd无法识别vue命令解决方法
  8. 使用kibana可视化报表实时监控你的应用程序,从日志中找出问题,解决问题
  9. jmeter(十三)常见问题及解决方法
  10. 如何git-cherry-pick仅更改某些文件?