String源码分析

 

 

 

一、类定义

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

 

String是一个final的类,不能被继承。  

实现了Serializable表示当前的类是能够进行实例化的对象。

实现了  Comparable<String>  接口。其中包含的方法有:public int compareTo(T o);。这个方法表示String比较大小。

实现了 CharSequence接口,其中的方法有:

int length();//获取到当前String中包含的字符串的长度。

char charAt(int index);//获取支付串某个位置上的字符

CharSequence subSequence(int start, int end);//截取支付串。

二、属性字段:

private final char value[];//支付串

private int hash; //当前字符串对应的hash值

private static final long serialVersionUID = -6849794470754667710L;

三、内部类:

四、构造函数:

//无参的类,基本是构造一个长度为0的变量

public String() {

    this.value = new char[0];

}
//复制一个String的对象。内部地址不同。equals = true

public String(String original) {

    this.value = original.value;

    this.hash = original.hash;

}
//根据一个char[]来创建一个  String。

public String(char value[]) {

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

}

其中Arrays.copyOf(value, value.length):首先创建一个char[],然后调用System.arraycopy方法将就的数据复制给新的数据。

其中System.arraycopy 是一个native的方法。在底层实现内存块的拷贝

 

/**

*根据一个char[]数组的部分字段创建一个String。也是调用了Arrays.copyOfRange.

**/

public String(char value[], int offset, int count) {

    if (offset < 0) {

        throw new StringIndexOutOfBoundsException(offset);

    }

    if (count < 0) {

        throw new StringIndexOutOfBoundsException(count);

    }

    // 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);

}

 

 

 /**
     * 根据一个int数组来创建一个String.
     **/
    public String(int[] codePoints, int offset, int count) {

        //检查边界
        //计算宽度,主要是由于有些int —> char可能需要两位来表示。对于一些无法转变为 char的int就直接抛出异常。
        //前面部分代码省略
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // 将当前的int[]转变为 一个char[]。java使用unicode。16位,但是不够,所以拿出了一部分使用32位来表示字符。
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))//使用一位char来表示当前的对象
                v[j] = (char) c;
            else
                Character.toSurrogates(c, v, j++);//否者需要两位char来表示一个int。这个是 16位表示所有的unicode但是不够,所以使用了两位来表示。
        }
    }


 

/**
*将byte[]转变为  String,
* 首先是byte是8位,而char 是16位。如果hibyte 表示高8位的值。
* 所以如果,hibyte = 0 就默认的 低八位 & 0xff就好
* 如果   hibyte != 0 ,指定了高八位  != 0 。首先将  hibyte左移8位,然后和低8位拼成一个新的char。
*/
@Deprecated
public String(byte ascii[], int hibyte, int offset, int count) {
    checkBounds(ascii, offset, count);
    char value[] = new char[count];

    if (hibyte == 0) {
        for (int i = count; i-- > 0;) {
            value[i] = (char)(ascii[i + offset] & 0xff);
        }
    } else {
        hibyte <<= 8;
        for (int i = count; i-- > 0;) {
            value[i] = (char)(hibyte | (ascii[i + offset] & 0xff));
        }
    }
    this.value = value;
}

 

/**

*通过 byte[]以及  chartSet进行编码,将byte转变为 Char 。

* 其中StringCodeing中编码的单写一篇。

**/

public String(byte bytes[], int offset, int length, String charsetName)

        throws UnsupportedEncodingException {

    if (charsetName == null)

        throw new NullPointerException("charsetName");

    checkBounds(bytes, offset, length);

    this.value = StringCoding.decode(charsetName, bytes, offset, length);

}
/**

*将一个StringBuffer转变为一个String。

**/

public String(StringBuffer buffer) {

    synchronized(buffer) {

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

    }

}

StringBuffer是线程不安全的。这里给这个对象进行加锁。

 

综上所述:

对于java中String的构造方法主要有这样几种类型:

char[],String,int[],byte[],StringBuilder/StringBuffer.

其中char[] 中使用了Array.copy的方法来帮助创建一个新的char[].

int[]转变为 一个char的时候,会有  char 是一个16位,但是 int 是32位,只有部分  int能够转变为char。java中使用Unicode来表示char的,16位能够表示65535个字符,但是Unicode不止这么多,所以从16位当中拿出了一部分来拼成32位来表示所有的Unicode。

byte[]转变为char的时候,从8位转为16位。其中就有编码的问题。 StringCoding方法来帮助 byte转变为 char。这个单讲。

StringBuilder/StringBuffer单讲。

 

五、String中常见的方法:

 

//比较两个字符串是否相等。

public boolean equals(Object anObject) {

    if (this == anObject) {//如果两个字符串内存地址相等,就返回true。

        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) {//最多比较n次,从低位向高位进行比较

                if (v1[i] != v2[i])

                        return false;

                i++;

            }

            return true;

        }

    }

    return false;

}
//比较连个字符串的大小,

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,小的那个返回。

        char c1 = v1[k];

        char c2 = v2[k];

        if (c1 != c2) {

            return c1 - c2;

        }

        k++;

    }

    return len1 - len2;//如果前面都一样,长度长的那个大。

}

 

//计算  hashCode。

public int hashCode() {

    int h = hash;

    if (h == 0 && value.length > 0) {//如果当前的hash值没有计算过,同时这个字符串的长度 > 0

        char val[] = value;



        for (int i = 0; i < value.length; i++) {

            h = 31 * h + val[i];//使用一个表达式来表示当前的系统。

        }

        hash = h;//将计算出来的  hash值赋给  当前的  String的hash值。

    }

    return h;

}

Hash值还是依赖  char[]中的值来计算出来的。

 

//判断当前的String从  toffset开始是否以prefix开头。

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++]) {//然后又开头字符串开始一个个的比较,如果不相等就返回false。

            return false;

        }

    }

    return true;

}

 


 

//将字符串str 写到  当前字符串的后面。

public String concat(String str) {

    int otherLen = str.length();//获取到被拼接的支付串的长度

    if (otherLen == 0) {//如果被拼接的支付串的长度  == 0.返回当前的字符串

        return this;

    }

    int len = value.length;

    char buf[] = Arrays.copyOf(value, len + otherLen);//创建一个 当前字符串和拼接支付串长度和 的新字符串。然后将当前的字符串拷贝到新字符串中

    str.getChars(buf, len);//然后将 拼接支付串添加到尾部。

    return new String(buf, true);//根据新字符串创建一个新的   String。

}

 

//  替换字符

public String replace(char oldChar, char newChar) {

    if (oldChar != newChar) {//如果两个字符串确实不相等,确实需要替换。

        int len = value.length;

        int i = -1;

        char[] val = value; /* avoid getfield opcode */



        while (++i < len) {//从第一个字符开始,找到第一个需要进行替换的字符的位置。

            if (val[i] == oldChar) {

                break;

            }

        }

        if (i < len) {//如果这个确实有需要被替换的字符。

            char buf[] = new char[len];

            for (int j = 0; j < i; j++) {//先把不需要替换的字符拷贝到新的char[]来。

                buf[j] = val[j];

            }

            while (i < len) {//从第一需要替换的字符开始,然后挨个进行比较,看是否是需要被替换。

                char c = val[i];

                buf[i] = (c == oldChar) ? newChar : c;

                i++;

            }

            return new String(buf, true);

        }

    }

    return this;

}

替换算法,首先看是否需要进行替换,然后找到第一个需要被替换的字符的位置,接着从第一个需要替换的位置的支付开始进行依次比较。

最后创建新的字符串。


 

/**

*将所有的空格和 回车换行符都去除

**/

public String trim() {

    int len = value.length;

    int st = 0;

    char[] val = value;    /* avoid getfield opcode */



    while ((st < len) && (val[st] <= ' ')) {//从头开始,找出第一个不小于 空格 的char的位置

        st++;

    }

    while ((st < len) && (val[len - 1] <= ' ')) {//从尾部开始,找出第一个 不小于  空格的char的位置。

        len--;

    }

    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;//根据位置来判断创建一个新的支付串。

}

这里先找出第一个的位置,而不是简单的创建。这样做能够使用Array.copy方法更好的发挥当前系统的威力。

 

/**

*将当前的支付串  显示的写到 一个pool中去。同时返回这个对象在 pool中的引用。

**/

public native String intern();

看一段测试代码:

String abc = "abc”; //当前的abc的引用是String的pool中的引用

String aString = new String(abc);//这个引用是在堆上的

String bString = new String(abc);//这个是堆上的

System.out.println(aString == bString);//返回false

aString = aString.intern();//这个时候aString已经指向了  pool中的“abc”

bString = bString.intern();//这个时候bString也指向了  pool中的 “abc"

System.out.println(aString == bString);// true

System.out.println(abc == aString);//true

对于String中的方法,其中都是对这个char[]数组进行操作。

其中做了小步的优化,首先生成小的结果,直接返回。例如  equals,首先判断是否 内存相等,然后判断类型相等人,然后判断长度相等,最后在开始一个个的char的比较。一般人可能会直接想到挨个的比较,这样也是一个正确的算法,但是性能比起java提供的要差很多。很大一部分的equals()方法可能就到不了 逐个比较的时候。

 

 

 

 

转载于:https://my.oschina.net/u/3388174/blog/875372

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值