摘要:我们在对较为熟悉之后,完全可以去尝试阅读一些源码,打开源码后,如果你英文能力稍微过得去,那么源码有相当详细的注释告诉你的含义,具体用法。
写在前面
说说这几天看源码的感受吧,其实 jdk 中的源码设计是最值得进阶学习的地方。我们在对 api 较为熟悉之后,完全可以去尝试阅读一些 jdk 源码,打开 jdk 源码后,如果你英文能力稍微过得去,那么源码有相当详细的注释告诉你 api 的含义,具体用法。假设平时在写代码的过程中突然忘记了某个 api 的用法,那么有些新手没读过源码的可能顺手就打开百度或者谷歌,搜索 api 怎么用?哈哈哈,面向谷歌编程,这样的状态可能会让你一年的经验重复n年, 如果是阅读过源码,则直接进去看看源码英文注释,回想一下源码的实现即可使用,而且看过源码后,里面有些代码细节是可以在平时编码的过程中直接借鉴的。
废话有点多啦~~滴滴滴,上车了。。。
上一篇 String 源码浅析(一) 中已经对String前半部分源码做了解析,这篇把剩下的方法粗略的总结下...
String 成员方法
判断字符串是否相等,该方法继承自Object类的重写实现,原则上也是比较字符串中的字符是否相等。
public boolean equals(Object anObject) { //判断形参跟当前字符串对象地址是否相等,即是否为同一个对象,如果相等,则返回true if (this == anObject) { return true; } //如果形参为String类型对象 if (anObject instanceof String) { //强转为String类型对象 String anotherString = (String)anObject; //当前字符串对象的字符数组长度 int n = value.length; //如果当前字符串对象的字符数组长度等于形参字符串字符数组长度 if (n == anotherString.value.length) { //当前字符串字符数组 char v1[] = value; //形参字符串字符数组 char v2[] = anotherString.value; //遍历索引起始位置0 int i = 0; //遍历当前字符串字符数组,每个索引位置的字符与形参字符串索引位置字符比较,如果不相等则返回false while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } //以上条件都不满足,最后返回false return false; }
传入CharSequence接口形参,实际是与StringBuffer,StringBuilder比较是否相等,因为StringBuffer,StringBuilder都实现了CharSequence接口
public boolean contentEquals(CharSequence cs) { //判断形参是否是AbstractStringBuilder抽象类,实则因当传入的是其子类:StringBuffer, StringBuilder if (cs instanceof AbstractStringBuilder) { //如果形参是StringBuffer类型对象 if (cs instanceof StringBuffer) { //同步锁,调用nonSyncContentEquals方法比较两种是否相等 synchronized(cs) { return nonSyncContentEquals((AbstractStringBuilder)cs); } } else { //如果形参对象是StringBuilder,则调用nonSyncContentEquals方法比较两种是否相等 return nonSyncContentEquals((AbstractStringBuilder)cs); } } // 如果形参是String对象,则直接调用equals方法返回 if (cs instanceof String) { return equals(cs); } // 如果是其他的CharSequence实现类,则遍历,一个个字符进行比较,找到一个字符不相等则直接返回false char v1[] = value; int n = v1.length; if (n != cs.length()) { return false; } for (int i = 0; i < n; i++) { if (v1[i] != cs.charAt(i)) { return false; } } //以上代码都不成立,走到最后直接返回true return true; }
私有方法,非同步方式(线程不安全)比较与 AbstractStringBuilder 是否相等,实则是与其子类:StringBuffer, StringBuilder 比较大小,contentEquals(CharSequence cs) 方法中核心比较代码就是调用该方法。
private boolean nonSyncContentEquals(AbstractStringBuilder sb) { //当前字符串对象字符数组 char v1[] = value; //获取形参字符数组 char v2[] = sb.getValue(); //当前字符串对象字符数组长度 int n = v1.length; //如果当前字符串对象字符数组长度不等于形参字符数组长度,则直接返回false if (n != sb.length()) { return false; } //遍历当前字符串对象字符数组,与形参字符数组逐一比较字符,找到一个字符不相等,则直接返回false for (int i = 0; i < n; i++) { if (v1[i] != v2[i]) { return false; } } //以上条件都不成立,代码走到最后则直接返回true return true; }
公有方法,比较与StringBuffer对象是否相等,内部实则直接调用的contentEquals(CharSequence cs)方法,可以说该方法是StringBuffer的特别版吧。
public boolean contentEquals(StringBuffer sb) { return contentEquals((CharSequence)sb); }
匹配两个字符串部分片段是否相等
public boolean regionMatches(int toffset, String other, int ooffset, int len) { //当前字符串字符数组 char ta[] = value; //当前字符串开始比较的起始位置,即偏移量 int to = toffset; //待比较的字符串字符数组 char pa[] = other.value; //待比较的字符串起始位置,即偏移量 int po = ooffset; //索引检查 1.偏移量小于0 2. 偏移量大于总长度-待比较的长度 //以上两种情况直接返回false if ((ooffset < 0) || (toffset < 0) || (toffset > (long)value.length - len) || (ooffset > (long)other.value.length - len)) { return false; } //遍历,找出不相等的字符,则返回false while (len-- > 0) { if (ta[to++] != pa[po++]) { return false; } } //不出意外,最终则返回true return true; }
匹配两个字符串部分片段是否相等,同时判断是否忽略大小写
public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { //当前字符串字符数组 char ta[] = value; //当前字符串开始比较的起始位置,即偏移量 int to = toffset; //待比较的字符串字符数组 char pa[] = other.value; //待比较的字符串起始位置,即偏移量 int po = ooffset; //索引检查 1.偏移量小于0 2. 偏移量大于总长度-待比较的长度 //以上两种情况直接返回false if ((ooffset < 0) || (toffset < 0) || (toffset > (long)value.length - len) || (ooffset > (long)other.value.length - len)) { return false; } //遍历检查字符是否相等,相等则跳过 while (len-- > 0) { char c1 = ta[to++]; char c2 = pa[po++]; if (c1 == c2) { continue; } //如果字符不相等,且需要忽略大小写比较 if (ignoreCase) { //字符转换为大写 char u1 = Character.toUpperCase(c1); char u2 = Character.toUpperCase(c2); //如果相等,则继续跳过 if (u1 == u2) { continue; } //转换为小写进行比较,如果相等则继续跳过 if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { continue; } } //否则发现不相等,则直接返回false return false; } //不出意外,最终返回true return true; }
忽略大小写比较字符串大小
public boolean equalsIgnoreCase(String anotherString) { //一句三目运算直接搞定 //如果当前字符串对象地址与形参字符串相等,则返回true //否则判断形参字符串是否为空,形参字符串长度是否与当前字符串长度相等,直接调用regionMatches比较两个字符串所有字符是否相等,同时忽略大小写比较 //以上全部为true则相等 return (this == anotherString) ? true : (anotherString != null) && (anotherString.value.length == value.length) && regionMatches(true, 0, anotherString, 0, value.length); }
比较两个字符串是否相等,该方法实现自Comparable接口,返回 int 结果,等于0,则字符串相等,小于0,则前者小于后者,大于0,则前者大于后者
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; //索引位置0开始 int k = 0; //遍历较小的字符数组 while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; //如果字符不相等 if (c1 != c2) { //返回字符之差 return c1 - c2; } k++; } //如果字符都相等,则返回长度之差 return len1 - len2; }
使用默认比较器不区分大小写比较两个字符串大小
//初始化默认的比较器 public static final ComparatorCASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); //默认的比较器,不区分大小写 private static class CaseInsensitiveComparator implements Comparator , java.io.Serializable { // use serialVersionUID from JDK 1.2.2 for interoperability private static final long serialVersionUID = 8575799808933029326L; public int compare(String s1, String s2) { //第一个字符串长度 int n1 = s1.length(); //第二个字符串长度 int n2 = s2.length(); //取小 int min = Math.min(n1, n2); //遍历较小的字符串 for (int i = 0; i < min; i++) { //获取指定索引字符 char c1 = s1.charAt(i); char c2 = s2.charAt(i); //如果字符不相等 if (c1 != c2) { //转化为大写 c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); //转化为大写后比较,不相等 if (c1 != c2) { //转化为小写继续比较 c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); //转化为小写字符,不相等 if (c1 != c2) { //直接返回字符之差 return c1 - c2; } } } } //不出意外,最终返回长度之差 return n1 - n2; } /** Replaces the de-serialized object. */ private Object readResolve() { return CASE_INSENSITIVE_ORDER; } } //内部直接调用默认比较器的compare方法 public int compareToIgnoreCase(String str) { return CASE_INSENSITIVE_ORDER.compare(this, str); }
从指定偏移量开始,判断是否以指定字符串开头
public boolean startsWith(String prefix, int toffset) { //当前字符串字符数组 char ta[] = value; //偏移量 int to = toffset; //指定字符串前缀字符数组 char pa[] = prefix.value; //索引位置0开始 int po = 0; //指定字符串前缀数组长度 int pc = prefix.value.length; //偏移量小于0 或者 //偏移量大于总长度-字符串前缀长度,则直接返回false if ((toffset < 0) || (toffset > value.length - pc)) { return false; } //遍历前缀字符串 while (--pc >= 0) { //从偏移量开始检索,找到字符不相等,则返回false if (ta[to++] != pa[po++]) { return false; } } //不出意外,最后则返回true return true; }
从字符串开头,判断是否以指定字符串开头
public boolean startsWith(String prefix) { //直接调用startsWith重载方法,偏移量为0 return startsWith(prefix, 0); }
判断是否以指定字符串结尾,内部直接调用的 startsWith 方法,偏移量为总字符串长度-后缀字符串长度即可。
public boolean endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length); }
从指定偏移量开始,搜索指定字符在字符串中第一次出现的索引位置
public int indexOf(int ch, int fromIndex) { //当前字符串字符数组长度 final int max = value.length; //如果偏移量小于0,则重置为0 if (fromIndex < 0) { fromIndex = 0; } else if (fromIndex >= max) { //偏移量大于总长度,则返回-1,意味着找不到指定字符 return -1; } if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { //当前字符串字符数组 final char[] value = this.value; //从偏移量位置开始遍历 for (int i = fromIndex; i < max; i++) { //找到相等的字符,则返回索引 if (value[i] == ch) { return i; } } //找不到则返回-1 return -1; } else { return indexOfSupplementary(ch, fromIndex); } }
查找指定字符在字符串中第一次出现的索引位置,从偏移量0开始遍历查找
public int indexOf(int ch) { return indexOf(ch, 0); }
查找指定字符在字符串中最后一次出现的索引位置,从指定偏移量开始遍历
public int lastIndexOf(int ch, int fromIndex) { if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { //当前字符串字符数组 final char[] value = this.value; //偏移量与字符串最后的位置取小 int i = Math.min(fromIndex, value.length - 1); //从后遍历字符数组 for (; i >= 0; i--) { //找到相等的字符,返回索引 if (value[i] == ch) { return i; } } //找不到则返回-1 return -1; } else { return lastIndexOfSupplementary(ch, fromIndex); } }
查找指定字符在字符串中最后一次出现的索引位置,从字符串最后一个索引位置开始遍历查找
public int lastIndexOf(int ch) { return lastIndexOf(ch, value.length - 1); }
从指定位置开始,查找字符串在源字符串中第一次出现的的索引位置,内部实则直接调用的indexOf内部静态方法
public int indexOf(String str, int fromIndex) { return indexOf(value, 0, value.length, str.value, 0, str.value.length, fromIndex); }
查找字符串在源字符串中第一次出现的的索引位置,内部实则直接调用的上述indexOf方法,fromIndex默认从0开始
public int indexOf(String str) { return indexOf(str, 0); }
从指定位置开始,查找字符串在源字符串中最后一次出现的的索引位置,内部实则直接调用的lastIndexOf内部静态方法
public int lastIndexOf(String str, int fromIndex) { return lastIndexOf(value, 0, value.length, str.value, 0, str.value.length, fromIndex); }
查找字符串在源字符串中最后一次出现的的索引位置,内部实则直接调用的上述lastIndexOf方法,fromIndex默认从value.length开始
public int lastIndexOf(String str) { return lastIndexOf(str, value.length); }
按照指定区间裁剪字符串,返回子字符串,beginIndex起始位置(包含),endIndex结束位置(不包含)
public String substring(int beginIndex, int endIndex) { //起始位置小于0,抛出索引越界异常 if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } //结束位置大于字符串总长度,则抛出索引越界异常 if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } //待截取的字符串长度 int subLen = endIndex - beginIndex; //待截取的字符串长度小于0,则抛出索引越界异常 if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } //三目判断 起始位置等于0,并且结束位置等于字符串长度,则表示为截取总长度,返回当前字符串对象 //否则重新new一个字符串实例,从beginIndex开始,截取subLen长度 return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
从起始位置beginIndex开始截取源字符串到结尾,返回子字符串
public String substring(int beginIndex) { //起始位置小于0,抛出索引越界异常 if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } //待截取的字符串长度 int subLen = value.length - beginIndex; //待截取的字符串长度小于0,则抛出索引越界异常 if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } //三目判断 起始位置等于0,则表示为截取总长度,返回当前字符串对象,否则重新new一个新的字符串实例,从beginIndex开始,截取subLen长度 return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }
按照指定区间裁剪字符串,返回CharSequence接口,beginIndex起始位置(包含),endIndex结束位置(不包含),内部实则直接调用的substring方法,只是返回的是最上层接口罢了
public CharSequence subSequence(int beginIndex, int endIndex) { return this.substring(beginIndex, endIndex); }
字符串拼接,将目标字符串拼接在尾部,返回新的字符串
public String concat(String str) { //待拼接的字符串长度 int otherLen = str.length(); //如果待拼接的字符串长度等于0,则直接返回当前字符串对象 if (otherLen == 0) { return this; } //当前字符串长度 int len = value.length; //将当前字符串字符数组拷贝到新的字符数组buf[]中,长度扩容至len + otherLen char buf[] = Arrays.copyOf(value, len + otherLen); //将待拼接的字符数组拷贝至buf[]中,从len开始,理论上这部操作完之后,字符数组已经拼接成功了 str.getChars(buf, len); //利用最新的字符数组,重新new一个新的字符串实例返回 return new String(buf, true); }
将字符串中指定字符oldChar替换为新的字符newChar
public String replace(char oldChar, char newChar) { //如果旧的字符跟新的字符不相等,才执行替换逻辑,否则直接返回当前字符串对象 if (oldChar != newChar) { //当前字符串长度 int len = value.length; //索引从-1开始 int i = -1; //当前字符串字符数组 char[] val = value; //遍历当前字符数组,从0开始,因为++i之后等于0 while (++i < len) { //找到与oldChar相等的字符,则中断循环 if (val[i] == oldChar) { break; } } //如果oldChar索引位置i小于当前字符数组长度,意味着在当前字符串中找到了oldChar if (i < len) { //初始化字符串长度的字符数组 char buf[] = new char[len]; //从0开始遍历到oldChar所在位置,将字符放入buf for (int j = 0; j < i; j++) { buf[j] = val[j]; } //从oldChar索引位置i开始遍历到字符串结尾 while (i < len) { //当前字符 char c = val[i]; //如果当前字符等于oldChar,则newChar赋值给buf[i],否则不变 buf[i] = (c == oldChar) ? newChar : c; i++; } //最后根据buf数组重新new一个新的字符串实例返回 return new String(buf, true); } } return this; }
根据字符串正则regex匹配字符串,返回boolean,内部实则是调用正则匹配的api方法
public boolean matches(String regex) { return Pattern.matches(regex, this); }
判断字符串是否包含指定字符序列CharSequence,内部实则是调用indexOf方法,判断返回结果是否大于-1
public boolean contains(CharSequence s) { return indexOf(s.toString()) > -1; }
根据给定的新的子字符串replacement,替换第一个匹配给定的正则表达式regex的子字符串,内部实则调用的是Matcher类的replaceFirst方法
public String replaceFirst(String regex, String replacement) { return Pattern.compile(regex).matcher(this).replaceFirst(replacement); }
根据给定的新的子字符串replacement,替换所有匹配给定的正则表达式regex的子字符串,内部实则调用的是Matcher类的replaceAll方法
public String replaceAll(String regex, String replacement) { return Pattern.compile(regex).matcher(this).replaceAll(replacement); }
更加通用的字符串替换方法,将匹配到的target字符序列全部替换为replacement字符序列,内部调用的也是Matcher类的replaceAll方法
public String replace(CharSequence target, CharSequence replacement) { return Pattern.compile(target.toString(), Pattern.LITERAL).matcher( this).replaceAll(Matcher.quoteReplacement(replacement.toString())); }
去除字符串前后空格
public String trim() { //当前字符串长度 int len = value.length; //索引标志位 int st = 0; //当前字符串字符数组 char[] val = value; //从0开始遍历循环,查找到空格的字符,则索引往前+1 while ((st < len) && (val[st] <= " ")) { st++; } //从字符串尾部开始遍历循环,查找到空格字符,则长度往后-1 while ((st < len) && (val[len - 1] <= " ")) { len--; } //如果st索引大于0或者长度len小于总长度,则返回裁剪字符串st偏移量开始,裁剪len长度,否则直接返回当前字符串对象 return ((st > 0) || (len < value.length)) ? substring(st, len) : this; }
字符串转化toString,继承自Object重写的方法,直接返回当前字符串对象
public String toString() { return this; }
字符串转化为字符数组
public char[] toCharArray() { //初始化字符串长度的字符数组 char result[] = new char[value.length]; //将字符串本身的字符数组拷贝至result[]并返回结果 System.arraycopy(value, 0, result, 0, value.length); return result; }String 静态方法
不对外公开的内部静态方法,从字符串source指定索引fromIndex开始遍历,从偏移量sourceOffset,在原始字符串长度sourceCount范围内查找目标字符串target中偏移量targetOffset开始,长度为targetCount的字符串索引第一次出现的位置。
static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) { //如果起始位置大于等于源字符串指定长度 if (fromIndex >= sourceCount) { //如果目标字符串查找的长度为0,则直接返回源字符串长度,否则返回-1表示未找到 return (targetCount == 0 ? sourceCount : -1); } //起始位置小于0 if (fromIndex < 0) { //重置为0 fromIndex = 0; } //目标字符串长度为0,代表为空字符串”“ if (targetCount == 0) { //直接返回起始位置 return fromIndex; } //目标字符串第一个字符 char first = target[targetOffset]; //从源字符偏移量开始,计算最大的遍历次数 int max = sourceOffset + (sourceCount - targetCount); //遍历 for (int i = sourceOffset + fromIndex; i <= max; i++) { //循环找出第一个字符 if (source[i] != first) { while (++i <= max && source[i] != first); } //todo 这段暂时没看明白 by zhangshaolin if (i <= max) { int j = i + 1; int end = j + targetCount - 1; for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++); if (j == end) { /* Found whole string. */ return i - sourceOffset; } } } //最终没找到 则返回-1 return -1; }
不对外公开的静态方法,上述方法的另一个重载形式,内部实则直接调用的上述方法,targetCount默认传入target.value.length
static int indexOf(char[] source, int sourceOffset, int sourceCount, String target, int fromIndex) { return indexOf(source, sourceOffset, sourceCount, target.value, 0, target.value.length, fromIndex); }
不对外公开的内部静态方法,从字符串source指定索引fromIndex开始遍历,从偏移量sourceOffset,在原始字符串长度sourceCount范围内查找目标字符串target中偏移量targetOffset开始,长度为targetCount的字符串索引第一次出现的位置。
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) { //源字符长度-目标字符长度,获取起始位置 int rightIndex = sourceCount - targetCount; //起始位置小于0,直接返回-1表示未找到目标 if (fromIndex < 0) { return -1; } //如果形参起始位置大于计算的实际起始位置,则直接赋值给fromIndex if (fromIndex > rightIndex) { fromIndex = rightIndex; } //如果目标字符串长度为0,表示为空字符串,则直接返回起始位置 if (targetCount == 0) { return fromIndex; } //获取目标字符串最后一个索引位置 int strLastIndex = targetOffset + targetCount - 1; //获取目标字符串最后一个字符 char strLastChar = target[strLastIndex]; //获取最小遍历次数 int min = sourceOffset + targetCount - 1; //最小遍历次数+起始位置 int i = min + fromIndex; //循环查找最后一个字符 startSearchForLastChar: while (true) { while (i >= min && source[i] != strLastChar) { i--; } //如果istart) { if (source[j--] != target[k--]) { i--; //找不到,继续跳过外层循环 continue startSearchForLastChar; } } return start - sourceOffset + 1; } }
不对外公开的静态方法,上述方法的另一个重载形式,内部实则直接调用的上述方法,targetCount默认传入target.value.length
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, String target, int fromIndex) { return lastIndexOf(source, sourceOffset, sourceCount, target.value, 0, target.value.length, fromIndex); }
将任意Object对象转化为字符串对象返回,内部实则也是调用的Object的toString方法,返回结果取决于该方法的具体实现
public static String valueOf(Object obj) { //如果obj为空,则返回"null",否则返回对象的toString返回的字符串结果 return (obj == null) ? "null" : obj.toString(); }
将字符数组转化为字符串对象返回,内部实际是用字符数组为参数 重新 new 了一个新的字符串对象,并返回
public static String valueOf(char data[]) { return new String(data); }
将字符数组转化为字符串对象返回,同时指定索引偏移量offset,截取的长度count,内部实际是重新 new 了一个新的字符串对象,并返回
public static String valueOf(char data[], int offset, int count) { return new String(data, offset, count); }
与上一个方法效果一致,只是方法名称不同罢了
public static String copyValueOf(char data[], int offset, int count) { return new String(data, offset, count); }
与valueOf(char data[])效果一致,只是方法名称不同罢了
public static String copyValueOf(char data[]) { return new String(data); }
将boolean类型数据转化为字符串对象返回
public static String valueOf(boolean b) { //为真 则返回"true" 否则返回"false" return b ? "true" : "false"; }
将字符转化为字符串对象返回
public static String valueOf(char c) { //初始化字符数组 char data[] = {c}; //重新new一个新的字符串对象返回 return new String(data, true); }
将int数据转换为字符串对象返回,内部实际是调用的Integer.toString()方法
public static String valueOf(int i) { return Integer.toString(i); }
将long数据转换为字符串对象返回,内部实际是调用的Long.toString()方法
public static String valueOf(long l) { return Long.toString(l); }
将float数据转换为字符串对象返回,内部实际是调用的Float.toString()方法
public static String valueOf(float f) { return Float.toString(f); }
将double数据转换为字符串对象返回,内部实际是调用的Double.toString()方法
public static String valueOf(double d) { return Double.toString(d); }简单总结
把String源码全部大致过了一遍之后,感慨 jdk 代码设计的强大,几天时间要完全看懂是不容易的,目前也还有很多地方没有完全明白
源码并不可怕,可怕的是自己的畏惧心理,认为源码很难啃不动,其实不然,下定决心看下去,遇到不懂的可以先pass,后面再回头看可能就豁然开朗了。
String 内部本质就是操作字符数组 value[]
因为本质就是操作字符数组,内部用到了大量的Arrays.copyOf,以及System.arraycopy方法
最后看源码不易,如果文中有错误之处,还请留言指出,一起学习,一起进步,谢谢!
更多原创文章会第一时间推送公众号【张少林同学】,欢迎关注!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/72857.html
摘要:支持形式的调用这其实是非常经典的无构造,其实就是一个构造函数,的结果就是一个对象实例,该实例有个属性,属性值是。 前言 终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度。十一月,多事之秋,最近好多事情搞的楼主心力憔悴,身心俱疲,也想尽快把这个系列完结掉,也好了却一件心事。 本文...
摘要:经过以上几步的学习,我们终于来到最后一个步骤了,应用也接近尾声。一个应用的一个平台对应了一个证书证书的是应用的标示,更准确的说,平台证书中的包名平台证书中的是标示应用唯一性的凭证。 经过以上几步的学习,我们终于来到最后一个步骤了,应用APP也接近尾声。 通过之前的几节教程,不知道您对使用DeviceOne开发一个应用是不是已经得心应手了,本节教程将教会大家如何在开发完成之后通过Devi...
摘要:经过以上几步的学习,我们终于来到最后一个步骤了,应用也接近尾声。一个应用的一个平台对应了一个证书证书的是应用的标示,更准确的说,平台证书中的包名平台证书中的是标示应用唯一性的凭证。 经过以上几步的学习,我们终于来到最后一个步骤了,应用APP也接近尾声。 通过之前的几节教程,不知道您对使用DeviceOne开发一个应用是不是已经得心应手了,本节教程将教会大家如何在开发完成之后通过Devi...
摘要:方法的产生式如下由得这个函数,包含了除布尔值的表达式之外的,各个表示数据得表达式的解析部分。这里我的链接直接指向了上关于线性渐变的形式语法部分,可以看到这部分对线性渐变语法的描述,和我上面解析的时候所用的产生式如出一辙。 博客源地址:https://github.com/LeuisKen/l...相关评论还请到 issue 下。 方法说明 san.parseExpr是San中主模块下的...
阅读 917·2021-11-25 09:43
阅读 1285·2021-11-17 09:33
阅读 3001·2019-08-30 15:44
阅读 3304·2019-08-29 17:16
阅读 474·2019-08-28 18:20
阅读 1627·2019-08-26 13:54
阅读 549·2019-08-26 12:14
阅读 2166·2019-08-26 12:14