字符串String源码分析

上节课我们讲了String是什么,怎么使用,这节课我们就来分析分析String的底层源码。

 

public final class String

    implements java.io.Serializable, Comparable<String>, CharSequence {

 

可以看到String类实现了三个接口,前面两个我们已经见过了,就是序列化和可比较,最后一个CharSequence是字符队列,接下来看看CharSequence的代码。

 

public interface CharSequence {



    int length();

 

    char charAt(int index);

  

    CharSequence subSequence(int start, int end);

 

    public String toString();



    public default IntStream chars() {

        class CharIterator implements PrimitiveIterator.OfInt {

            int cur = 0;

            public boolean hasNext() {

                return cur < length();

            }

            public int nextInt() {

                if (hasNext()) {

                    return charAt(cur++);

                } else {

                    throw new NoSuchElementException();

                }

            }

            @Override

            public void forEachRemaining(IntConsumer block) {

                for (; cur < length(); cur++) {

                    block.accept(charAt(cur));

                }

            }

        }

        return StreamSupport.intStream(() ->

                Spliterators.spliterator(

                        new CharIterator(),

                        length(),

                        Spliterator.ORDERED),

                Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED,

                false);

    }

   

    public default IntStream codePoints() {

        class CodePointIterator implements PrimitiveIterator.OfInt {

            int cur = 0;

            @Override

            public void forEachRemaining(IntConsumer block) {

                final int length = length();

                int i = cur;

                try {

                    while (i < length) {

                        char c1 = charAt(i++);

                        if (!Character.isHighSurrogate(c1) || i >= length) {

                            block.accept(c1);

                        } else {

                            char c2 = charAt(i);

                            if (Character.isLowSurrogate(c2)) {

                                i++;

                                block.accept(Character.toCodePoint(c1, c2));

                            } else {

                                block.accept(c1);

                            }

                        }

                    }

                } finally {

                    cur = i;

                }

            }

            public boolean hasNext() {

                return cur < length();

            }

            public int nextInt() {

                final int length = length();

                if (cur >= length) {

                    throw new NoSuchElementException();

                }

                char c1 = charAt(cur++);

                if (Character.isHighSurrogate(c1) && cur < length) {

                    char c2 = charAt(cur);

                    if (Character.isLowSurrogate(c2)) {

                        cur++;

                        return Character.toCodePoint(c1, c2);

                    }

                }

                return c1;

            }

        }

        return StreamSupport.intStream(() ->

                Spliterators.spliteratorUnknownSize(

                        new CodePointIterator(),

                        Spliterator.ORDERED),

                Spliterator.ORDERED,

                false);

    }

}

 

解释一下为什么接口里的一些方法有具体实现。

 

可以看到这些被实现了的方法在声明的时候都加了default关键字,这个是jdk1.8的新特性,表示默认方法,加了这个关键字方法就可以在接口里实现

 

我们主要看那些未被实现的方法。

 

int length();      //返回字符队列的长度

 
    char charAt(int index);    //返回index位置的字符

  
    CharSequence subSequence(int start, int end);     //提取字串,返回值类型为CharSequence

 
    public String toString();   //将本对象转换成字符串
 

 

接下来看看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;

这些是String类的属性

 

主要看value

 

value属性是干什么的?

value是一个字符数组char[],用于储存字符串的值,一切对字符串的操作本质是对这个字符操作。

 

接下来看几个构造器

 

public String() {

    this.value = "".value;

}

 

public String(String original) {

    this.value = original.value;

    this.hash = original.hash;

}

 

public String(char value[]) {

    this.value = Arrays.copyOf(value, value.length);

}

 

说明一下,Arrays.copyOf的功能是构造一个新的数组,然后将原数组里面的元素复制一份放到新数组里,具体源码太复杂了,就不看了。

 

我们主要用到的就这三个构造器,其他的用的不多。

 

接下来看一些方法的实现。

 

public int length() {

    return value.length;

}

可以看到length方法就是直接返回数组的长度,这也说明了value就是保存字符串内容的对象,而对字符串的操作本质上是对value的操作

 

public boolean isEmpty() {

    return value.length == 0;

}

isEmpty方法用于判断字符串是否是空串(“”),如果为空则返回true,否则返回false

 

public char charAt(int index) {

    if ((index < 0) || (index >= value.length)) {

        throw new StringIndexOutOfBoundsException(index);

    }

    return value[index];

}

throw是抛异常,这个以后说,可以看到返回值就是value[index]

 

void getChars(char dst[], int dstBegin) {

    System.arraycopy(value, 0, dst, dstBegin, value.length);

}

getChars()方法用于提取字串到dst,具体作用是复制value到dst的dstBegin位置

 

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {

    if (srcBegin < 0) {

        throw new StringIndexOutOfBoundsException(srcBegin);

    }

    if (srcEnd > value.length) {

        throw new StringIndexOutOfBoundsException(srcEnd);

    }

    if (srcBegin > srcEnd) {

        throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);

    }

    System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);

}

这个很显然是提取子串复制到dst的dstBegin位置

 

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;

}

判断字符串和anObject对象的值是否相等,具体表现为

首先看本引用变量和anObject是否引用同一对象,如果是,则返回true,如果不是

看anObject是不是String类型,如果不是直接返回false,否则

强转一下类型,然后比较本对象和anotherString的value是否一样,不一样就         返回false,否则返回true

 

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;

}

public boolean startsWith(String prefix) {

    return startsWith(prefix, 0);

}

startwith,判断字符串是否以某个字符串开头

 

还有个endWith是判断字符串是否以某个字符串结尾。

 

public int indexOf(String str)

返回str第一次在本字符串对象中出现的位置,如果不存在返回-1

 

还有个lastIndexOf,返回str最后一次在本字符串对象中出现的位置,如果不存在返回-1

 

public String substring(int beginIndex, int endIndex) {

    if (beginIndex < 0) {

        throw new StringIndexOutOfBoundsException(beginIndex);

    }

    if (endIndex > value.length) {

        throw new StringIndexOutOfBoundsException(endIndex);

    }

    int subLen = endIndex - beginIndex;

    if (subLen < 0) {

        throw new StringIndexOutOfBoundsException(subLen);

    }

    return ((beginIndex == 0) && (endIndex == value.length)) ? this

            : new String(value, beginIndex, subLen);

}

 

substring,提取字串,可以看到返回的是否new了一个新的字串

 

public String substring(int beginIndex) {

    if (beginIndex < 0) {

        throw new StringIndexOutOfBoundsException(beginIndex);

    }

    int subLen = value.length - beginIndex;

    if (subLen < 0) {

        throw new StringIndexOutOfBoundsException(subLen);

    }

    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);

}

substring如果只传一个参数就提取beinIndex直到字符串结尾的字串,返回的时候还是new了一个新的String

 

public CharSequence subSequence(int beginIndex, int endIndex) {

    return this.substring(beginIndex, endIndex);

}

这个不用我说都看得懂

 

 

 

public String concat(String str) {

    int otherLen = str.length();

    if (otherLen == 0) {

        return this;

    }

    int len = value.length;

    char buf[] = Arrays.copyOf(value, len + otherLen);

    str.getChars(buf, len);

    return new String(buf, true);

}

这个就是连接字符串

 

public String replace(CharSequence target, CharSequence replacement)

由于String实现了CharSequence接口,可以直接传String对象,作用是用replacement替换target,然后返回被替换后的String对象,注意,replace不会对原对象造成影响,而是new一个新的String对象来进行replace操作

 

之后还有replaceFirst和replaceAll,作用分别是替换第一次和替换所有

 

public String[] split(String regex)

将字符串对象以regex分割成字符串数组

比如:

public static void main(String[] args) {
    String s = "com.helloworld.teach";
    String[] split = s.split("\\.");    //这个\\.是转义字符,regex是一个正则表达式,这个以后再讲
    for (String s1 : split) {
        System.out.println(s1);
    }

    //再举一个例子
    String str = "aaaa/b/ccccd/b/aaaccc";
    String[] bs = str.split("b");
    for (String b : bs) {
        System.out.println(b);
    }
}

 

运行结果:

com

helloworld

teach

aaaa/

/ccccd/

/aaaccc

 

 

最后说一点:

字符串可以用 + 来连接

字符串和数字做+运算会先将数字变成字符串然后进行字符串连接操作

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值