java源码阅读String

1类签名与注释

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

String类被定义为final类型的,所以String对象一旦创建了,就是不可变的。

String类实现了Serializable接口,表示可以序列化。

String类实现了Comparable<String>接口,表示String类型可以相互比较。(通过compareTo方法)

String类实现了CharSequence接口,CharSequence是char值的可读序列。

String类代表字符串。 Java程序中的所有字符串文字(例如"abc" )都被实现为此类的实例。Strings是不可变的,它们的值在创建后不能被更改。字符串缓冲区支持可变字符串。

Java语言为字符串连接运算符(+)提供特殊支持,并为其他对象转换为字符串。 字符串连接是通过StringBuilder (或StringBuffer )类及其append方法实现的。 字符串转换是通过toString方法来实现,该方法定义在Object类中。

2基本属性

private final char value[];

private int hash; // Default to 0

String用字符数组存储值,内部还保留了自己的hash值。为什么要将hash值作为成员属性呢?我们先看一下String的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     }

当调用String的hashCode方法时,首先会从hash属性取值,当hash属性值为0时(第一次调用该方法),才会重新计算hash值。hash值得计算如line7所示。计算后会将值赋给hash属性。这里保证只在第一次调用hashCode方法的时候计算hash值,之后直接从hash属性取值。这种优化只有当String是final的时候才可以,若String可变的话,hash值也会变化。

3构造方法

String的构造方法有很多,下面是eclipse中outline的所有构造方法的截图。

红色方框部分表示废弃方法(我的版本JDK1.8)。通过outline可以看出,String可以通过String、字符数组、int数组、字节数组、StringBuffer/StringBuilder构造。下面贴出几个常用构造方法的代码。

//1构造空String
public String() {
        this.value = "".value;
    }

//2通过String构造
public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

//3通过char数组构造
public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

//4通过byte数组构造
 public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }
public String(byte bytes[], int offset, int length) {
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(bytes, offset, length);
    }

//5通过字符串缓冲区构造
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());
    }

这里有几点自己的理解:

(1)在通过char数组构造的时候,是通过Arrays的copyOf复制了一个新的数组(开辟了新的数组存储空间),这里不是通过简单的引用传递。

(2)byte数组不能直接变成char数组,需要通过StringCoding的decode方法。

(3)注意通过StringBuffer构造的时候加了同步,因为StringBuffer是线程安全的,这里复制的时候加锁也要保证线程安全。

4常用方法

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

只有满足类型一样,长度相等,char数组所有的值都相等时equals方法才返回true。

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

挨个比较字符数组的值,当较短的串所有字符都和较长串的相等时返回串的长度差。当compareTo方法的返回值等于0时,表示被比较的字符串和当前字符串相等,返回值大于0时表示当前字符串较大,返回值小于0表示当前字符串较小。

(3)startsWith和endsWith

//startsWith
public boolean startsWith(String prefix) {
        return startsWith(prefix, 0);
    }

public boolean startsWith(String prefix, int toffset) {
        char ta[] = value;
        int to = toffset;
        char pa[] = prefix.value;
        int po = 0;
        int pc = prefix.value.length;
        // Note: toffset might be near -1>>>1.
        if ((toffset < 0) || (toffset > value.length - pc)) {
            return false;
        }
        while (--pc >= 0) {
            if (ta[to++] != pa[po++]) {
                return false;
            }
        }
        return true;
    }

//endsWith
public boolean endsWith(String suffix) {
        return startsWith(suffix, value.length - suffix.value.length);
    }

starsWhith方法是从偏移量开始,挨个比较自身字符数组的值和参数对应的字符数组的值。endsWith用长度差当作偏移量。

(4)indexOf(String)方法

 1 public int indexOf(String str) {
 2         return indexOf(str, 0);
 3     }
 4
 5 public int indexOf(String str, int fromIndex) {
 6         return indexOf(value, 0, value.length,
 7                 str.value, 0, str.value.length, fromIndex);
 8     }
 9
10 static int indexOf(char[] source, int sourceOffset, int sourceCount,
11             String target, int fromIndex) {
12         return indexOf(source, sourceOffset, sourceCount,
13                        target.value, 0, target.value.length,
14                        fromIndex);
15     }
16
17 static int indexOf(char[] source, int sourceOffset, int sourceCount,
18             char[] target, int targetOffset, int targetCount,
19             int fromIndex) {
20         if (fromIndex >= sourceCount) {
21             return (targetCount == 0 ? sourceCount : -1);
22         }
23         if (fromIndex < 0) {
24             fromIndex = 0;
25         }
26         if (targetCount == 0) {
27             return fromIndex;
28         }
29
30         char first = target[targetOffset];
31         int max = sourceOffset + (sourceCount - targetCount);
32
33         for (int i = sourceOffset + fromIndex; i <= max; i++) {
34             /* Look for first character. */
35             if (source[i] != first) {
36                 while (++i <= max && source[i] != first);
37             }
38
39             /* Found first character, now look at the rest of v2 */
40             if (i <= max) {
41                 int j = i + 1;
42                 int end = j + targetCount - 1;
43                 for (int k = targetOffset + 1; j < end && source[j]
44                         == target[k]; j++, k++);
45
46                 if (j == end) {
47                     /* Found whole string. */
48                     return i - sourceOffset;
49                 }
50             }
51         }
52         return -1;
53     }

主要实现在line17-53,这里就是挨个对比匹配。通过indexOf(String str)调用line17的方法是没有什么问题,因为sourceOffset偏移量总是为0,但是看源码的时候总感觉max(line31)的逻辑会导致数组越界问题。举个例子如下:

public class Test {

    public static void main(String[] args) {

        String a = "abc1234";
        char[] aa = {‘a‘,‘b‘,‘c‘,‘1‘,‘2‘,‘3‘,‘4‘};
        String b = "25";
        char[] bb = {‘2‘,‘5‘};
        System.out.println(indexOf(aa,2,aa.length,
                bb,0,bb.length,0));

    }

    static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
              //省略...
        }

}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7
    at com.ouym.test.Test.indexOf(Test.java:44)
    at com.ouym.test.Test.main(Test.java:19)

(5)

原文地址:https://www.cnblogs.com/ouym/p/8981074.html

时间: 2024-11-11 05:44:46

java源码阅读String的相关文章

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

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

Java源码阅读的真实体会

原文:http://zwchen.iteye.com/blog/1154193 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的

[收藏] Java源码阅读的真实体会

收藏自http://www.iteye.com/topic/1113732 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码

Java源码阅读

源码阅读目的是为了了解Java原理,学习优秀的类设计,整体阅读顺序和侧重主要参考基础类和常用类,参考网上整体归纳如下: 包 java.lang 1) Object 1 2) String 1 3) AbstractStringBuilder 1 4) StringBuffer 1 5) StringBuilder 1 6) Boolean 2 7) Byte 2 8) Double 2 9) Float 2 10) Integer 2 11) Long 2 12) Short 2 13) Thr

从Java源码看String的两种比较方式

String的两种字符串比较方式 == 和 equals方法 ==: ==比较的是字符串在内存中的地址 代码示例: 1 public class EqualsDemo { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 String s1 = "String"; 8 String s2 = "String"; 9 String s3 = new String(&quo

Java源码分析——String的设计

Tip:笔者马上毕业了,准备开始Java的进阶学习计划.于是打算先从String类的源码分析入手,作为后面学习的案例.这篇文章寄托着今后进阶系列产出的愿望,希望能坚持下去,不忘初心,让自己保持那份对技术的热爱. 因为学习分析源码,所以借鉴了HollisChuang成神之路的大部分内容,并在此基础上对源码进行了学习,在此感谢. 问题的引入 关于String字符串,对于Java开发者而言,这无疑是一个非常熟悉的类.也正是因为经常使用,其内部代码的设计才值得被深究.所谓知其然,更得知其所以然. 举个例

Java源码之String

本文出自:http://blog.csdn.net/dt235201314/article/details/78330377 一丶概述 还记得那会的“Hello World”,第一个程序,输出的String,下面介绍String源码,颇有计算机二级考试习题的感觉. 二丶源码及案例 1.String是final类型的 在Java中,被 final 类型修饰的类不允许被其他类继承,被final修饰的变量赋值后不允许被修改. 什么是不可变类? 所谓不可变类,就是创建该类的实例后,该实例的属性是不可改变

【java SE】java 源码阅读 —— Object

看源码也不太懂怎么看,Object类的 java 代码很少,方法实现都靠 C/C++ ,主要看注释,然后自己理解.有不对的地方请指正 1. 概览 import jdk.internal.HotSpotIntrinsicCandidate; /** * native 关键字修饰的方法: * 1. 没有方法体 * 2. 将调用C/C++实现的方法(可以搜索一下JNI) */ public class Object { /** * 这个方法使JVM能找到本地的方法(C/C++实现的方法) */ pri

Java源码解析|String源码与常用方法

String源码与常用方法 1.栗子 代码: public class JavaStringClass { public static void main(String[] args) { String s ="hello"; s = "world"; //内存地址已经修改 原来地址上的值还是不变的 String s2 = "hello"; //从常量值中找到并引用 String s4 = new String("hello"