JDK源码解析(3)——String

一、类的定义 

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

与其他几种类不同的是,String实现了CharSequence 接口,表示是一个有序字符的可读集合。

二、类的属性

与Integer类似,Boolean的私有属性是boolean。

1、私有属性

//String的值一旦创建就无法更改,String的值就被保存在了char数组里了,jdk9中value是用byte[]来作为存储单元,因为 char 大小为2字节,而我们使用String 存储得最多的拉丁文只占用1个字节。
private final char value[];

//hash值将用于String类的hashCode()方法的计算
private int hash; // Default to 0

private static final long serialVersionUID = -6849794470754667710L;

//serialPersistentFields用于指定哪些字段需要被默认序列化
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];

2、公共属性

//用来比较两个字符串大小
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();

三、类的方法

1、构造方法

Integer提供了很多构造方法:

2、常用方法

equals

在重写equals()时也必须要重写hashCode()方法

    public boolean equals(Object var1) {
//1、先比较是否是同一个常量
        if (this == var1) {
            return true;
        } else {
//2、再比较类型
            if (var1 instanceof String) {
                String var2 = (String)var1;
                int var3 = this.value.length;
//3、比较长度
                if (var3 == var2.value.length) {
                    char[] var4 = this.value;
                    char[] var5 = var2.value;
//4、逐个比较
                    for(int var6 = 0; var3-- != 0; ++var6) {
                        if (var4[var6] != var5[var6]) {
                            return false;
                        }
                    }
                    return true;
                }
            }
            return false;
        }
    }

hashCode

//String类中的hashCode是以31为权,每一位为字符的ASCII值进行运算,用自然溢出来等效取模。
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

charAt

//传入的索引(数组下标),返回指定索引的单个字符
	public char charAt(int index) {
        //如果传入的索引大于字符串的长度或者小于0,直接抛出索引越界异常
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];//返回指定索引的单个字符
    }

compareTo

//该方法是按字母顺序比较两个字符串,是基于字符串中每个字符的 Unicode 值。
//当两个字符串某个位置的字符不同时,返回的是这一位置的字符 Unicode 值之差,
//当两个字符串都相同时,返回两个字符串长度之差。
    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;
    }

concat

//该方法是将指定的字符串连接到此字符串的末尾。
    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);
    }

indexOf

//indexOf(int ch),参数 ch 其实是字符的 Unicode 值,这里也可以放单个字符(默认转成int)
//作用是返回指定字符第一次出现的此字符串中的索引。
//其内部是调用 indexOf(int ch, int fromIndex),这里的 fromIndex =0 ,从 0 开始搜索
public int indexOf(int ch) {
    return indexOf(ch, 0);//从第一个字符开始搜索
}

//indexOf(int ch, int fromIndex) 作用也是返回首次出现的此字符串内的索引,但是从指定索引处开始搜索。
public int indexOf(int ch, int fromIndex) {
    final int max = value.length;//max等于字符的长度
    if (fromIndex < 0) {//指定索引的位置如果小于0,默认从 0 开始搜索
        fromIndex = 0;
    } else if (fromIndex >= max) {
        //如果指定索引值大于等于字符的长度(因为是数组,下标最多只能是max-1),直接返回-1
        return -1;
    }

    if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {//一个char占用两个字节,如果ch小于2的16次方(65536),绝大多数字符都在此范围内
        final char[] value = this.value;
        for (int i = fromIndex; i < max; i++) {//for循环依次判断字符串每个字符是否和指定字符相等
            if (value[i] == ch) {
                return i;//存在相等的字符,返回第一次出现该字符的索引位置,并终止循环
            }
        }
        return -1;//不存在相等的字符,则返回 -1
    } else {//当字符大于 65536时,处理的少数情况,该方法会首先判断是否是有效字符,然后依次进行比较
        return indexOfSupplementary(ch, fromIndex);
    }
}

split

//split(String regex) 将该字符串拆分为给定正则表达式的匹配。
public String[] split(String regex) {
    return split(regex, 0);
}

//split(String regex , int limit) 也是一样,不过对于 limit 的取值有三种情况:
//1 limit > 0 ,则pattern(模式)应用n - 1 次
//2 limit = 0 ,则pattern(模式)应用无限次并且省略末尾的空字串
//3 limit < 0 ,则pattern(模式)应用无限次
public String[] split(String regex, int limit) {
    /* 1、单个字符,且不是".$|()[{^?*+\\"其中一个
     * 2、两个字符,第一个是"\",第二个大小写字母或者数字
     */
    char ch = 0;
    if (((regex.value.length == 1 &&
         ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
         (regex.length() == 2 &&
          regex.charAt(0) == '\\' &&
          (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
          ((ch-'a')|('z'-ch)) < 0 &&
          ((ch-'A')|('Z'-ch)) < 0)) &&
        (ch < Character.MIN_HIGH_SURROGATE ||
         ch > Character.MAX_LOW_SURROGATE))
    {
        int off = 0;
        int next = 0;
        boolean limited = limit > 0;//大于0,limited==true,反之limited==false
        ArrayList<String> list = new ArrayList<>();
        while ((next = indexOf(ch, off)) != -1) {
            //当参数limit<=0 或者 集合list的长度小于 limit-1
            if (!limited || list.size() < limit - 1) {
                list.add(substring(off, next));
                off = next + 1;
            } else {//判断最后一个list.size() == limit - 1
                list.add(substring(off, value.length));
                off = value.length;
                break;
            }
        }
        //如果没有一个能匹配的,返回一个新的字符串,内容和原来的一样
        if (off == 0)
            return new String[]{this};

        // 当 limit<=0 时,limited==false,或者集合的长度 小于 limit是,截取添加剩下的字符串
        if (!limited || list.size() < limit)
            list.add(substring(off, value.length));

        // 当 limit == 0 时,如果末尾添加的元素为空(长度为0),则集合长度不断减1,直到末尾不为空
        int resultSize = list.size();
        if (limit == 0) {
            while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                resultSize--;
            }
        }
        String[] result = new String[resultSize];
        return list.subList(0, resultSize).toArray(result);
    }
    return Pattern.compile(regex).split(this, limit);
}

replace

//将原字符串中所有的oldChar字符都替换成newChar字符,返回一个新的字符串
public String replace(char oldChar, char newChar) {}

//将匹配正则表达式regex的匹配项都替换成replacement字符串,返回一个新的字符串。
public String replaceAll(String regex, String replacement) {
    return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}

substring

//返回一个从索引 beginIndex 开始一直到结尾的子字符串。
    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);
    }

//返回一个从索引 beginIndex 开始,到 endIndex 结尾的子字符串。
    public String substring(int beginIndex, int endIndex) {}

四、常量的创建

创建常量我们常用两种方法:

String s1 = "11";
String s3 = new String("11");

方式一会先去字符串常量缓冲区中寻找是否有“11”的常量,如果存在则将s1的地址指向“11”,如果不存在,则创建字符串在常量缓冲区,并将栈空间存的是这个字符串的地址给s1。

方式二则在创建字符串对像的同时,还会在String pool中创建一个对象。因此执行了上面语句后实际上创建了两个实例对象。一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例。

String str1 = "hello";
String str2 = "helloworld";
String str3 = str1+"world";//编译器不能确定为常量(会在堆区创建一个String对象)
String str4 = "hello"+"world";//编译器确定为常量,直接到常量池中引用

System.out.println(str2==str3);//fasle
System.out.println(str2==str4);//true
System.out.println(str4==str3);//fasle

//str3 由于含有变量str1,编译器不能确定是常量,会在堆区中创建一个String对象。
//而str4是两个常量相加,直接引用常量池中的对象即可。

五、StringBuffer、StringBuilder

由于String不可被修改,当需要对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象

public StringBuilder append(String str) {
    super.append(str);
    return this;
}

public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

可以看到,StringBuffer的方法都被synchronized关键词修饰,所以是线程同步。

  • (1)如果要操作少量的数据用 String;
  • (2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
  • (3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值