java String类,底层自学自看 笔记(实时更新)2

本文详细解析了Java中String类的实现细节,包括getBytes方法的字符集编码转换、equals方法的字符串比较逻辑、compareTo方法的大小比较过程以及substring方法的子串截取机制等。深入探讨了String类在内存中的存储方式,以及各种方法背后的算法原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

25

//与24类似,这不过
public byte[] getBytes(Charset charset) {
        if (charset == null) throw new NullPointerException();
        return StringCoding.encode(charset, value, 0, value.length);①
}
①
static byte[] encode(Charset cs, char[] ca, int off, int len) {
        CharsetEncoder ce = cs.newEncoder();
        int en = scale(len, ce.maxBytesPerChar());//获取可能达到的最长长度
        byte[] ba = new byte[en];//创建一个绝对不会超出长度的数组,这部方法其实在日常项目中也可应用
        //好处,不需要该数组的自增功能  1:可以增加性能,少一步提升之后还要复制数组的操作
        //2:节省空间,自增后可能出现多余的空间根本用不到
        if (len == 0)
            return ba;//如果长度为0,就是空串,但是不会报错,没有转码的必要,直接返回
        boolean isTrusted = false;
        if (System.getSecurityManager() != null) {//检查系统安全管理器是否存在,有安全管理器在,这段要被转码
        //的数据,可以降低被篡改的风险
            if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {//判断是否被允许访问私有包
                ca =  Arrays.copyOfRange(ca, off, off + len);//如果安全管理器都没有,就不用判断是否允许了,执行可能会报错
                off = 0;
            }//这个复制的意义就是产生一个新的对象,防止因为该对象在其他的地方被用到了,发生了改变,产生的不可预知的情况
        }
        ce.onMalformedInput(CodingErrorAction.REPLACE)
          .onUnmappableCharacter(CodingErrorAction.REPLACE)
          .reset();//之前见过的工厂类型的初始化对象
        if (ce instanceof ArrayEncoder) {
            int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
            return safeTrim(ba, blen, cs, isTrusted);
            //ArrayEncoder是一个接口,没有备注等解释。
            //这应该是扩展的接口,如果字符集实现了这个接口,那么该接口提供的功能可以直接使用,不需要自己来实现
        } else {
            ByteBuffer bb = ByteBuffer.wrap(ba);
            CharBuffer cb = CharBuffer.wrap(ca, off, len);//buffer可变长功能,wrap,构造缓冲区,对流的操作会更快
            try {
                CoderResult cr = ce.encode(cb, bb, true);//核心,最后的转码操作
                //内部没啥可看的 主要是 cr = encodeLoop(in, out);  主要的实现放在更深的语言了,只能到此为止
                if (!cr.isUnderflow())
                    cr.throwException();
                cr = ce.flush(bb);
                if (!cr.isUnderflow())
                    cr.throwException();
            } catch (CharacterCodingException x) {
                throw new Error(x);
            }
            return safeTrim(ba, bb.position(), cs, isTrusted);
            //这步判断了一下前后的长度,如果长度一样,直接返回了,如果不一样就复制一下,生成新长度的数组返回
            //结束
        }
}

26:获取对象默认编码的byte

public byte[] getBytes(Charset charset) {
        if (charset == null) throw new NullPointerException();
        return StringCoding.encode(charset, value, 0, value.length);
}
static byte[] encode(char[] ca, int off, int len) {
        String csn = Charset.defaultCharset().name();
        try {
            // use charset name encode() variant which provides caching.
            return encode(csn, ca, off, len);//关于encode不再重复
        } catch (UnsupportedEncodingException x) {
            warnUnsupportedCharset(csn);
        }
        try {
            return encode("ISO-8859-1", ca, off, len);
        } catch (UnsupportedEncodingException x) {
            // If this code is hit during VM initialization, MessageUtils is
            // the only way we will be able to get any kind of error message.
            MessageUtils.err("ISO-8859-1 charset not available: "
                             + x.toString());
            // If we can not find ISO-8859-1 (a required encoding) then things
            // are seriously wrong with the installation.//翻译:未找到该字符集,安装严重错误
            System.exit(1);//这个就厉害了,关掉虚拟机,而且是非正常关闭
            return null;
        }
}
//当给定的字符串并没有指定编码。就会使用iso

27最常用的比较

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;//如果hash值相同,就认为相同
        }
        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;
    }
    //其实没啥可说的,就是把字符串拆成字符数组,然后一个一个比较,

28

public boolean contentEquals(StringBuffer sb) {
        synchronized (sb) {
            return contentEquals((CharSequence) sb);
        }
}


public boolean contentEquals(CharSequence cs) {
        if (value.length != cs.length())//判断长度
            return false;
        // Argument is a StringBuffer, StringBuilder
        if (cs instanceof AbstractStringBuilder) {//这是针对可变长字符串的。
            char v1[] = value;
            char v2[] = ((AbstractStringBuilder) cs).getValue();
            int i = 0;
            int n = value.length;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
        // Argument is a String
        if (cs.equals(this))判断hash是否相等
            return true;
        // Argument is a generic CharSequence
        char v1[] = value;
        int i = 0;
        int n = value.length;
        while (n-- != 0) {
            if (v1[i] != cs.charAt(i))//这是继承CharSequence 通用的获取某个位置字符的方法
                return false;
            i++;
        }
        return true;
}
//虽然绕来绕去,但是其实和equals差不多
//CharSequence 是字符序列,
定义了四个实现
length();toString();subSequence();charAt()
subSequence()是返回的CharSequence 类型的 ,和subString功能一样
String重写了,可以只返回String

29,忽略大小写的对比

public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
}

30.比较大小

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;//一看就不是返回-1、0、1了。应该是不一样的那个字母的差值
            }
            k++;
        }
        return len1 - len2;
}

31.重写了一下comparator 的比较方法

public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
    private static class CaseInsensitiveComparator
            implements Comparator<String>, 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) {
                            // No overflow because of numeric promotion
                            return c1 - c2;
                        }
                    }
                }
            }
            return n1 - n2;
        }
}

32:调用上面那个方法,忽略大小写

public int compareToIgnoreCase(String str) {
        return CASE_INSENSITIVE_ORDER.compare(this, str);
}

33:判断包含

public boolean regionMatches(int toffset, String other, int ooffset,
            int len) {
        char ta[] = value;
        int to = toffset;
        char pa[] = other.value;
        int po = ooffset;
        // Note: toffset, ooffset, or len might be near -1>>>1.
        if ((ooffset < 0) || (toffset < 0)//起点不能小于0,且不能大于长度
                || (toffset > (long)value.length - len)
                || (ooffset > (long)other.value.length - len)) {
            return false;
        }
        while (len-- > 0) {//基于长度做while循环。
            if (ta[to++] != pa[po++]) {//基于起点开始对每个字符进行比较。
                return false;
            }
        }
        return true;
}

tooffset:被比较的字符串的起点
other:要比较的字符串
ooffset:要比较的字符串的起点
len比较的长度

34:忽略大小写的比较,都是很熟悉的逻辑。

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;
        // Note: toffset, ooffset, or len might be near -1>>>1.
        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;
                }
            }
            return false;
        }
        return true;
}

有点重复,个人感觉可以和33合成一个

35:

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

36:默认从第一个开始比较,调用35

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

37,判断结尾处有没有,也是调用的35

public boolean endsWith(String suffix) {//起点为被比较长度,减去比较的长度
        return startsWith(suffix, value.length - suffix.value.length);
}

38:获取字符串的哈希值

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {//如果是同一个String对象调用此方法,不会重复计算
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];//String类型的hash算法
            }
            hash = h;
        }
        return h;
}

39:indexOf,返回输入第一次出现的位置
参数可以使int类型,也可以是String类型
首先是int类型

	public int indexOf(int ch) {
	        return indexOf(ch, 0);
	}

    public int indexOf(int ch, int fromIndex) {
        final int max = value.length;
        if (fromIndex < 0) {
            fromIndex = 0;
        } else if (fromIndex >= max) {
            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;
	             }
	         }
	         return -1;
	     } else {
	         return indexOfSupplementary(ch, fromIndex);
	     }
    }

关于int类型参数其实还是很坑的,搞不好会出错

String iString = new String(new char[]{2,1,3});
System.out.println(iString.indexOf(1));
System.out.println(iString);
//只有这样才会输出是1,代表找到了位置

//下面这种形式是找不到的,因为int类型与字符类型不同
String iString = new String(new char[]{'2','1','3'});
System.out.println(iString.indexOf(1));
System.out.println("213".indexOf(1));
//但是第一种方式,控制台输出是乱码,是无法正常使用的
//所以indexOf当参数为int类型时,很有可能达不到预期的效果

//下面另一种用法
String iString = new String(new char[]{2,1,3});
char c = 1;
System.out.println(iString.indexOf(c));
//这个方法如果是正常项目中还是很少见的,基本用不到。

char c = 97;
System.out.println(c);//a
String i = "2a3";
System.out.println(i.indexOf(c));//1

下面是源码

public int indexOf(int ch, int fromIndex) {
        final int max = value.length;
        if (fromIndex < 0) {
            fromIndex = 0;
        } else if (fromIndex >= max) {
            // Note: fromIndex might be near -1>>>1.
            return -1;
        }

        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            for (int i = fromIndex; i < max; i++) {
                if (value[i] == ch) {//把字符与int类型进行比较
                    return i;
                }
            }
            return -1;
        } else {
            return indexOfSupplementary(ch, fromIndex);
        }
}

接下来是参数为String的查找第一处的源码

public int indexOf(String str) {
        return indexOf(str, 0);
}

public int indexOf(String str, int fromIndex) {
        return indexOf(value, 0, value.length,
                str.value, 0, str.value.length, fromIndex);
}
//每个参数的意思可以从上个调用他的方法猜出来
@param source 当前字符串
@param sourceOffset 开始
@param sourceCount 结束
@param target 被对比字符串
@param targetOffset开始
@param targetCount结束
@param fromIndex当前字符串起始对比位置
static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (fromIndex < 0) {
            fromIndex = 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);
            }
            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) {
                    return i - sourceOffset;
                }
            }
        }
        return -1;
}

经过测试发现该方法看不懂,因为不管fromIndex传什么都不会影响返回结果,当然只要不大于某个值。
似乎fromIndex这个字段要实现的功能没有实现,jdk10也有很大改动
该发放,没有引用到自己的私有属性,也没有引用什么受保护的jar包,可以粘出来直接测试。


40:截取字符串

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值