JDK源码之String、StringBuffer、StringBuilder

就String而言,平时工作中用得最多,但是很多时候还是用不好,有必要对他进行整体的分析下。如果看过Thinking in java,再看下JDK的源码,很多东西就会变得十分明了。现在对String的底层实现进行下分析。

首先是对构造函数而言,我工作中最常用到的可能就是new String(str)这个构造函数了,所以再在此关注这一个。这个构造函数是对传进来的String进行解析,将其放进一个数组当中,我们设定为arrayOfChar,String类定义了全局变量offset(偏移量),count(大小),value(char类型的数组),此时,会将offset设为0,count设置为arrayOfChar.length,value设置为arrayOfChar,此时整个String构造器完成了初始化工作,三个全局变量全部设定了初始值,当String类型的对象调用类中的方法时,其实就是对这三个变量进行的操作,下面的方法分析中会涉及到。在String的方法中,很多方法返回的都是新的String对象,原因是因为String初始化后,大小就固定了,如下面讲解到的方法SubString、concat等方法。

常用方法分析:1、最简单的isEmpty,底层也是很简单的一行代码,return this.count == 0,清晰明了,很好的完成了相应的功能。在此,就使用了构造器中的count。

        2、charAt,获取指定下标的字符。之前说过,构造器将String处理成了一个数组,此时要想得到指定下标字符,就很easy了,直接使用数组中的[n]就可以拿到了,不过它加入了偏移量,之前做了简单的异常处理,防止数组越界。

        3、equals将字符串与指定对象的比较,开发中最常用的方法之一。在方法开始,就做了一个判断,如果当前对象和指定对象一样,直接返回true,这里当前对象使用的是this关键字,如果不等于,则进行接下来的操作。当指定对象不是String类型(使用instanceof判断),直接返回false,如果是String类型,则取出当前对象的数组value和指定对象的数组value1,然后根据偏移量对数组进行逐个比较,若数组相等,则返回true,反之有任何一个元素不等,当即返回false。

        4、compareTo将两个字符串按照字典顺序比较。返回负数,0,正数。其实底层实现方式跟equals相似,也是使用数组实现的。但是,他的逻辑却不一样,它会逐个拿到相同下标的数组元素,然后拿到字典顺序进行比较,如果相等,继续,如果不等,直接相减并return,这是就是正数或者负数了,如果全部相等,最后返回0。

        5、startWith(String,int)判断字符串是否是已制定String开始,这里分析带两个参数的,原因是因为startWith(String),endsWith(String)都是调用这个方法实现的,这也进一步展现了代码重用性带来的方便,搞笑、简洁。它的实现依旧是使用数组,将参数String与当前对象数组逐个进行比较,最后得出结果。不过,第二个参数int说明了指定String要从当前对象数组中的哪个下标进行比较。 startWith(String)实际上默认给了startWith(String,int)中int的值为0,表明从数组下标为0的开始比较。endsWith(String)实际就是从数组下标为当前对象数组的长度-指定字符数组的长度进行比较。这样一来,所以的方法最后实现都是startWith(String,int)来实现的。这一点在工作中也应该使用,即代码的重用性,最后上升到代码的简洁。代码虽短(一行到两行),但是却十分完美的实现了其功能,且清晰明了。个人觉得,方法contains也可以使用startWith(String,int)方法来实现,JDK使用indexOf实现的,不过使用startWith需要循环,效率当然会低很多的。【此处我只是举一反三,考虑效率,肯定是indexOf好多了】。

        6、substring(int1,int2)截取字符串,substring(int)调用前一个,不过第二个int2设置为当前对象数组的长度。它表示从下标int1到下标int2的元素取出来,如果缺失int2,默认为数组长度,最后的实现使用的是String的一个构造函数Stirng(int,int,char[])实现的,返回一个新的String对象。在这个构造函数中,对新的String对象初始化了,包括offset,count,value,这样截取的方法就返回了一个符合要求的全新的String对象。

        7、其实还有concat、replace等比较常用的方法,但是,其底层都是针对数组进行的操作,以上讲了几个典型的,这几个就不在一一讲了,可以直接看源码,很容易就理解了。只不过这两个都是返回了全新的String对象,还使用了一些其他的方法,但是思想都差不多一样。其他还有很多常用方法如getChars(char[], int),replaceAll(String, String),split都是调用了相关类的方法,就不在这里讲了。

        8、在String中有个静态内部类CaseInsensitiveComparator,此处还是看下比较好。这个类中只包含了一个方法compare,这个方法是比较连个字符串的,但是忽略其大小写。方法中使用charAt取出对应的数组元素,然后Character.toUpperCase进行转换比较,为了安全,他还使用了Character.toLowerCase进行转换比较,如果相等,进行下一个比较,如果不等,直接求差返回。这个静态内部类中的方法是不能在其他类中方法进行直接访问的,而必须要通过外部类的compareToIgnoreCase方法进行访问。这样做的目的,我个人认为是防止其他类直接访问静态类中的方法,有点类似代理模式,不过这点我还有疑惑,在下面的疑惑中我会提出。这种做法在遍历器中也用到了,不过在遍历器的做法稍有不同,那里的做法是返回内部类的对象,然后利用这个对象去访问内部类中的方法。

        总体来说,String我就理解了这么多,了解了底层怎么实现的,在使用String方法时也就更加得心应手了。看完了底层实现,收获很多,但是也有很多疑惑,先将疑惑列出,希望知道的IT朋友们帮助解答。

      疑惑:1、方法equalsIgnoreCase的实现为return (this == paramString),为什么这样就可以忽略大小写?具体的实现方式是怎么样的?

           2、刚刚提到的内部类,为什么要使用内部类,我直接将内部类的方法设置为private的,然后在相应的方法中调用,也能起到同样的效果,可能是我对内部类不了解,但是为什么要这样做?好处是什么?

            3、hashCode方法给类的全局变量hash赋值了,但是在哪个地方使用到了呢?仅仅赋值而已?会不会跟String的==有关?有的话是什么关系?

以上纯属个人阅读JDK源码的理解,欢迎大虾们指出不对或者补充,同时对于疑惑部分肯定大家赐教,可以评论或者私信,也可以发邮件给我[email protected],有什么好的学习方法,也可以随时交流。

欢迎各位IT朋友私信我,相互交流、学习!

     StringBuffer、StringBuilder未完待续。。。。。。      

        

时间: 2024-10-13 21:29:38

JDK源码之String、StringBuffer、StringBuilder的相关文章

JDK源码学习--String篇(二) 关于String采用final修饰的思考

JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JDK源码的时候,有粗略的思考过,今天下班后又把<Thinking in Java>中关于final的内容重新看了一遍,对此写下一些关于自己的理解和想法. String类中final关键字的使用 final关键字,用来描述一块数据不能被改变,两种可能理由:设计.效率 final使用的三种情况:数据.方

JDK 1.8 源码解析 String、StringBuilder和StringBuffer的异同

JDK提供了String.StringBuilder和StringBuffer这三个类来处理字符串,其中StringBuilder类是在JDK 1.5中新增的. 不同点如下: 1 是否有父类 String没有父类. // String类不能被继承 // 实现了Serializable.Comparable和CharSequence(字符序列)接口 public final class String implements java.io.Serializable, Comparable<Strin

JDK源码学习--String篇(三) 存储篇

在进一步解读String类时,先了解下内存分配和数据存储的. 数据存储 1.寄存器:最快的存储区,位于处理器的内部.由于寄存器的数量有限,所以寄存器是按需分配. 2.堆栈:位于RAM中,但是通过堆栈指针可以从处理器哪里获得直接支持.堆栈指针向下移动,则分配新的内存:堆栈指针向上移动释放内存. 注:堆栈中存储基本的数据类型和[对象引用],但是Java对象存储在堆中. 3.堆:通用内存池,位于RAM中,用于存放所有的Java对象. 注:堆中存储的 new创建的对象和数组. 4.常量存储:存放常量.

jdk源码理解-String类

String类的理解 简记录一下对于jdk的学习,做一下记录,会持续补充,不断学习,加油 1.String的hash值的计算方法. hash值的计算方法多种多样,jdk中String的计算方法如下,比较简单,由字符串中的字符的ASCII值计算出来. /** * Returns a hash code for this string. The hash code for a * <code>String</code> object is computed as * <block

JDK源码分析之String篇

------------------------------String在内存中的存储情况(一下内容摘自参考资料1)----------------------------------- 前提:先了解下什么是声明,什么时候才算是产生了对象实例 其中x并未看到内存分配,变量在使用前必须先声明,再赋值,然后才可以使用.java基础数据类型会用对应的默认值进行初始化 一.首先看看Java虚拟机JVM的内存块及其变量.对象内存空间是怎么存储分配的 1.栈:存放基本数据类型及对象变量的引用,对象本身不存放

jdk 源码阅读有感(一)String

闲暇之余阅读 jdk 源码. (一)核心属性 /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 String的核心结构,char型数组与 int 型 hash值. (二)构造器 构造器方面,由于上述两个值是不可更改的,所以直接 对 String

JDK 源码 阅读 - 2 - 设计模式 - 创建型模式

A.创建型模式 抽象工厂(Abstract Factory) javax.xml.parsers.DocumentBuilderFactory DocumentBuilderFactory通过FactoryFinder实例化具体的Factory. 使用例子: DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilder

jdk源码分析总览

今天看到了一个源码分析按照重要性排序的例子, 这里拿过来用了,之后按照这个顺序不断的完善源码的内容. 引用的出处忘记了(对作者说声抱歉) 很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Object 12) String 13) AbstractStringBuilder 14) StringBuffer 15) StringBuilder 16) Boo

重温java中的String,StringBuffer,StringBuilder类

任何一个系统在开发的过程中, 相信都不会缺少对字符串的处理. 在 java 语言中, 用来处理字符串的的类常用的有 3 个: String.StringBuffer.StringBuilder. 它们的异同点: 1) 都是 final 类, 都不允许被继承; 2) String 长度是不可变的, StringBuffer.StringBuilder 长度是可变的; 3) StringBuffer 是线程安全的, StringBuilder 不是线程安全的. String 类已在上一篇随笔 小瓜牛