java.lang.String是使用频率非常高的类。要想更好的使用java.lang.String类,了解其源代码实现是非常有必要的。由java.lang.String,自然联想到java.lang.StringBuffer和java.lang.StringBuilder,下篇文章再来研究java.lang.StringBuffer和java.lang.StringBuilder。

重要属性

java.lang.String对象中字符串主要是以字符数组的形式存储。当调用对象方法获取字符串长度时,直接返回数组长度。判断字符串是否为空isEmpty()时,也直接检查数组长度是否为0。其部分发生代码如下所示:

  1. /** The value is used for character storage. */
  2. private final char value[];
  3.  
  4. /** Cache the hash code for the string */
  5. private int hash; // Default to 0

value:存储字符串的字符数组。该数组为final变量,一旦赋值,将不会更改。

hash:该String对象的哈希值。

构造方法

java.lang.String对象构造方法比较多,列举如下:

  1. public String()
  2. public String(String original)
  3. public String(char value[])
  4. public String(char value[], int offset, int count)
  5. public String(int[] codePoints, int offset, int count)
  6. @Deprecated
  7. public String(byte ascii[], int hibyte, int offset, int count)
  8. @Deprecated
  9. public String(byte ascii[], int hibyte)
  10. public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException
  11. public String(byte bytes[], int offset, int length, Charset charset)
  12. public String(byte bytes[], String charsetName) throws UnsupportedEncodingException
  13. public String(byte bytes[], Charset charset)
  14. public String(byte bytes[], int offset, int length)
  15. public String(byte bytes[])
  16. public String(StringBuffer buffer)
  17. public String(StringBuilder builder)

在 public String(StringBuffer buffer) 中,传入形参为StringBuffer,StringBuffer为线程安全类。则在此构造方法内部进行了synchronized关键字锁同步。代码如下:

  1. public String(StringBuffer buffer) {
  2. synchronized(buffer) {
  3. this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
  4. }
  5. }

在 public String(StringBuilder builder) 中,传入形参为StringBuilder,StringBuilder为非线程安全类。则在此构造方法内部内部未做同步处理,对比 public String(StringBuffer buffer) 。代码如下:

  1. public String(StringBuilder builder) {
  2. this.value = Arrays.copyOf(builder.getValue(), builder.length());
  3. }

常用方法

java.lang.String对象中封装方法非常多,仅针对常用方法源代码进行分析。如:equals(),replace(), indexOf(),startsWith(),compareTo(),regionMathes(),hashCode()。

public boolean equals(Object anObject)

用于比较两对象存储内容是否相同。采用比较巧妙的方式进行排除比较:(1)先“==”比较两对象是否是同一对象,若是,直接返回true, 否则进一步判断;(2)判断待比较对象类型是否是java.lang.String,若不是,直接返回false,否则进一步判断;(3)判断两字符串长度是否相等,若不是直接返回false,否则进一步判断;(4)从字符数组中第一个字符开始,依次进行比较,一旦发现不相同字符直接返回false,若所在字符均相同则返回true。对字符数组中字符依次进行比较是一件非常耗时的操作,将此操作放在最后执行,先利用其它条件进行对其进行判断。比较巧妙!

  1. public boolean equals(Object anObject) {
  2. if (this == anObject) {
  3. return true;
  4. }
  5. if (anObject instanceof String) {
  6. String anotherString = (String)anObject;
  7. int n = value.length;
  8. if (n == anotherString.value.length) {
  9. char v1[] = value;
  10. char v2[] = anotherString.value;
  11. int i = 0;
  12. while (n-- != 0) {
  13. if (v1[i] != v2[i])
  14. return false;
  15. i++;
  16. }
  17. return true;
  18. }
  19. }
  20. return false;
  21. }

public String replace(char oldChar, char newChar)

将字符串中指定字符替换为新的字符。(1)先判断待替换字符和新字符是否相同,若相同,则直接返回原字符串,若不同,则继续执行;(2)找出第一次出现待替换字符位置i,创建新的等长字符数组,将该位置之前的字符依次放入新的字符数组中;(3)从位置i处依次遍历比较原字符数组中字符是否是待替换字符,若是,则将新字符放入新字符数组对应位置,若不是,则将原字符数组中字符放入对应位置。巧妙做了一个小优化,直接找出第一次出现待替换字符的位置,再从此处开始遍历,提高效率。

  1. public String replace(char oldChar, char newChar) {
  2. if (oldChar != newChar) {
  3. int len = value.length;
  4. int i = -1;
  5. char[] val = value; /* avoid getfield opcode */
  6.  
  7. while (++i < len) {
  8. if (val[i] == oldChar) {
  9. break;
  10. }
  11. }
  12. if (i < len) {
  13. char buf[] = new char[len];
  14. for (int j = 0; j < i; j++) {
  15. buf[j] = val[j];
  16. }
  17. while (i < len) {
  18. char c = val[i];
  19. buf[i] = (c == oldChar) ? newChar : c;
  20. i++;
  21. }
  22. return new String(buf, true);
  23. }
  24. }
  25. return this;
  26. }

public String replace(CharSequence target, CharSequence replacement)

该方法是我们通常意义所用到的 public String replace(String target, String replacement) ,java.lang.String实现了java.lang.CharSequence接口。方法内部调用正则表达式匹配替换来实现。

  1. public String replace(CharSequence target, CharSequence replacement) {
  2. return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
  3. this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
  4. }

public int indexOf(String str)

该方法是找出目标字符串是第一次出现指定子字符串的位置,若不存在,则返回-1,若存在,则返回位置坐标。具体实现是调用 static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) 方法。先对目标字符串中出现子字符串的位置可能范围,然后在此范围中遍历找出与子字符串第一个字符相同的位置,并对后面字符进行比较分析。

  1. /**
  2. * Returns the index within this string of the first occurrence of the
  3. * specified substring.
  4. */
  5. public int indexOf(String str) {
  6. return indexOf(str, 0);
  7. }
  8.  
  9. /**
  10. * Returns the index within this string of the first occurrence of the
  11. * specified substring, starting at the specified index.
  12. */
  13. public int indexOf(String str, int fromIndex) {
  14. return indexOf(value, 0, value.length,
  15. str.value, 0, str.value.length, fromIndex);
  16. }
  17.  
  18. /**
  19. * Code shared by String and StringBuffer to do searches. The
  20. * source is the character array being searched, and the target
  21. * is the string being searched for.
  22. *
  23. * @param source the characters being searched.
  24. * @param sourceOffset offset of the source string.
  25. * @param sourceCount count of the source string.
  26. * @param target the characters being searched for.
  27. * @param targetOffset offset of the target string.
  28. * @param targetCount count of the target string.
  29. * @param fromIndex the index to begin searching from.
  30. */
  31. static int indexOf(char[] source, int sourceOffset, int sourceCount,
  32. char[] target, int targetOffset, int targetCount,
  33. int fromIndex) {
  34. if (fromIndex >= sourceCount) {
  35. return (targetCount == 0 ? sourceCount : -1);
  36. }
  37. if (fromIndex < 0) {
  38. fromIndex = 0;
  39. }
  40. if (targetCount == 0) {
  41. return fromIndex;
  42. }
  43.  
  44. char first = target[targetOffset];
  45. int max = sourceOffset + (sourceCount - targetCount);
  46.  
  47. for (int i = sourceOffset + fromIndex; i <= max; i++) {
  48. /* Look for first character. */
  49. if (source[i] != first) {
  50. while (++i <= max && source[i] != first);
  51. }
  52.  
  53. /* Found first character, now look at the rest of v2 */
  54. if (i <= max) {
  55. int j = i + 1;
  56. int end = j + targetCount - 1;
  57. for (int k = targetOffset + 1; j < end && source[j]
  58. == target[k]; j++, k++);
  59.  
  60. if (j == end) {
  61. /* Found whole string. */
  62. return i - sourceOffset;
  63. }
  64. }
  65. }
  66. return -1;
  67. }

public int compareTo(String anotherString)

该方法是对字符串集合进行排序的基础,通过此方法可比较两字符串大小,原理很简单,源代码如下:

  1. public int compareTo(String anotherString) {
  2. int len1 = value.length;
  3. int len2 = anotherString.value.length;
  4. int lim = Math.min(len1, len2);
  5. char v1[] = value;
  6. char v2[] = anotherString.value;
  7.  
  8. int k = 0;
  9. while (k < lim) {
  10. char c1 = v1[k];
  11. char c2 = v2[k];
  12. if (c1 != c2) {
  13. return c1 - c2;
  14. }
  15. k++;
  16. }
  17. return len1 - len2;
  18. }

public boolean startsWith(String prefix)

判断目标字符串是否以指定字符子串开关,该方法内部是调用 public boolean startsWith(String prefix, int toffset) 方法实现,原理很简单,代码如下:

  1. /**
  2. * Tests if this string starts with the specified prefix.
  3. *
  4. * @param prefix the prefix.
  5. */
  6. public boolean startsWith(String prefix) {
  7. return startsWith(prefix, 0);
  8. }

public int hashCode()

其hashCode()代码如下:

  1. public int hashCode() {
  2. int h = hash;
  3. if (h == 0 && value.length > 0) {
  4. char val[] = value;
  5.  
  6. for (int i = 0; i < value.length; i++) {
  7. h = 31 * h + val[i];
  8. }
  9. hash = h;
  10. }
  11. return h;
  12. }

Java源码学习 -- java.lang.String的更多相关文章

  1. Java源码学习 -- java.lang.StringBuilder,java.lang.StringBuffer,java.lang.AbstractStringBuilder

    一直以来,都是看到网上说“ StringBuilder是线程不安全的,但运行效率高:StringBuffer 是线程安全的,但运行效率低”,然后默默记住:一个是线程安全.一个线程不安全,但对内在原因并 ...

  2. 从源码学习Java并发的锁是怎么维护内部线程队列的

    从源码学习Java并发的锁是怎么维护内部线程队列的 在上一篇文章中,凯哥对同步组件基础框架- AbstractQueuedSynchronizer(AQS)做了大概的介绍.我们知道AQS能够通过内置的 ...

  3. 在IDEA中搭建Java源码学习环境并上传到GitHub上

    打开IDEA新建一个项目 创建一个最简单的Java项目即可 在项目命名填写该项目的名称,我这里写的项目名为Java_Source_Study 点击Finished,然后在项目的src目录下新建源码文件 ...

  4. Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库

    http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...

  5. Java 源码学习系列(三)——Integer

    Integer 类在对象中包装了一个基本类型 int 的值.Integer 类型的对象包含一个 int 类型的字段. 此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还 ...

  6. Java源码之 java.util.concurrent 学习笔记01

    准备花点时间看看 java.util.concurrent这个包的源代码,来提高自己对Java的认识,努力~~~ 参阅了@梧留柒的博客!边看源码,边通过前辈的博客学习! 包下的代码结构分类: 1.ja ...

  7. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  8. 从Java源码到Java字节码

    Java最主流的源码编译器,javac,基本上不对代码做优化,只会做少量由Java语言规范要求或推荐的优化:也不做任何混淆,包括名字混淆或控制流混淆这些都不做.这使得javac生成的代码能很好的维持与 ...

  9. java源码学习(二)Integer

    Integer类包含了一个原始基本类型int.Integer属性中就一个属性,它的类型就是int. 此外,这个类还提供了几个把int转成String和把String转成int的方法,同样也提供了其它跟 ...

随机推荐

  1. 使用composer下拉组件失败,出现killed解决办法

    做项目时下载composer组件,出现killed提示,如图 一般是因为内存太小,将虚拟机内存设置大一点即可,在虚拟机关机的时候设置 下载成功

  2. Java关于e.printStackTrace()介绍

    public void printStackTrace()将此 throwable 及其追踪输出至标准错误流.此方法将此 Throwable 对象的堆栈跟踪输出至错误输出流,作为字段 System.e ...

  3. 用Rvm安装Ruby,Rails运行环境及常见错误解决方法

    一.安装Rvm 1.下载安装Rvm $ curl -L https://get.rvm.io | bash -s stable 此时可能出现错误:"gpg: 无法检查签名:找不到公钥&quo ...

  4. Mac下安装BeautifulSoup

    1.输入命令su,输入root密码,进入root用户 2.执行命令安装pip: sudo easy_install pip 详细pip介绍:http://blog.csdn.net/olanlanxi ...

  5. js中的IP格式正则匹配校验详解~

    IPV4的格式为x:y:z:w,其中{x,y,z,w}属于{0~255}的正整数: 下面是其校验的正则表达式: function isIP(ip) { var re =  /^(\d{1,2}|1\d ...

  6. 深入理解MVC

    首先我们来看看MVC架构的示意图:             和访问者交互的是控制层(Controller层),控制器(controller)是同类交互的集合,每一个交互的操作,都对应了一个动作(act ...

  7. wow.js中各种特效对应的类名

    一.(页面在向下滚动的时候,有些元素会产生细小的动画效果.虽然动画比较小,但却能吸引你的注意.) 刚知道wow.js这个插件,之前访问别的网站下拉滚动条会出现各种效果感觉特别神奇,现在自己依葫芦画瓢也 ...

  8. 【转载】stm32中断学习

    中断对于开发嵌入式系统来讲的地位绝对是毋庸置疑的,在C51单片机时代,一共只有5个中断,其中2个外部中断,2个定时/计数器中断和一个串口中断,但是在STM32中,中断数量大大增加,而且中断的设置也更加 ...

  9. CF #299 div1 B. Tavas and Malekas KMP-next数组

    题目链接:http://codeforces.com/contest/536/problem/B 一个原始字符串,一个未知字符串,每一次从pos[i]开始覆盖未知字符串,问最后字符串的形式,以及判断过 ...

  10. rsync远程同步

    一.概念 Rsync是一个开源的快速备份工具,可以在不同主机之间镜像同步整个目录树,支持增量备份,保持连接和权限,且采用优化的同步算法,传输前执行压缩,因此非常适用于异地备份,镜像服务器等应用.rsy ...