Java当中的String类(从源码角度来看(上))

概述

字符串是由多个字符组成的一串数据(字符序列)的字符串常量,java中所有字符串都是String类的实例(对象)。

字符串的值是不能改变的(一旦给定字符串的值,值就是不能改变的)因为它的底层用的是一个final修饰的char 数组

为什么是不可变的,用语言的表达来是苍白的,直接看源码:
在这里插入图片描述

哟,看时数组,我们都知道数组的长度是不可变的,那么String的底层就是将字符串里面的没有字母取出来,以数组的形式进行保存。

那么我们平时在写字符串的时候,感受完全是字符串是可变的。
这是因为当我们在改变字符串的长度或内容的时候(只是因为它是final修饰的),底层代码会另创建一个新的数组,将之前的内容也保存进去。(看下面动图里面的value值,每次在增加字符串里面的元素的时候,就是另外创建了一个数组)
请添加图片描述

String的创建

创建字符串是有两种创建的方式:

  1. String s = “abc”;

  2. 一旦在堆中创建对象,值存储在堆内存的对象中。
    String s = new String(“abc”);

下面我就对这两种创建做一个演示:
在这里插入图片描述

信息有一些小伙伴会对第一个输出为true有些疑惑,前面不刚刚都说了,字符串的底层是数组呀,数组用==号的话,可是看地址的。那么这里出现了true是不是也说明了一个问题,s1和s2的地址就是一个呢?

对,这个想法是对的。在用String s1 = “abc”的时候就是先在栈中创建一个对String类的对象引用变量s,然后去字符串常量池中查找有没有“abc”。如果没有的话才在常量池中添加“abc”,s1引用变量指向常量池中的“abc”,如果常量池中已经存在了的话,则直接指向改地址即可,不用再重复创建。
所以在这s1和s2指向的就是同一东西。这也是java的一种空间的优化。

判断功能的方法

equals()方法

判断两各String字符串是否相等的。
在这里插入图片描述

equalsIgnoreCase()方法

这个也是判断两个字符串是否相等的语法,但是这里的区分是不分大小写的,比如我们经常在登录的时候,输入验证码,就是不区分大小写字母的。
在这里插入图片描述

contains()方法

判断方法里面的字符串是外面字符串的子串。
在这里插入图片描述

返回true/false后面的更上层的源码我就不再这做具体演示了。

isEmpty()方法

用来判断一个字符串是否为空。
在这里插入图片描述

若字符串为空就返回true,否则就返回false。

startsWith()和endWith()方法

startsWith()方法使用的是判断括号内的字符串是否是需要判断的字符串的开始
endWith()方法是用来判断括号内的字符时需要判断的字符串的结尾。

这两个方法在底层其实都用了一个方法进行判断
在这里插入图片描述

startWith()直接调用的代码:

    public boolean startsWith(String prefix) {
        return startsWith(prefix, 0);
        //直接调用了startsWith方法,从初位置开始
    }

endWith()方法指定调用的代码:

    public boolean endsWith(String suffix) {
        return startsWith(suffix, value.length - suffix.value.length);
        //表示suffix这个数组,然后从用数组长度减去传入参数的位置开始。
    }

compareTo()方法

用于判断前面字符是否比后面的大或者小。这里是将字母转换为ASCII码进行比较的。

这里有点说头,先给大家看一下测试代码和源码:
在这里插入图片描述

来在这源码给大家分析一波:

    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]; //拿到当前k位置的两个字符串中的元素
            char c2 = v2[k];
            if (c1 != c2) {  //若当前同一位置的字符不相等的时候
                return c1 - c2; //return出两个字母ASCII码的差
            }
            k++;
        }
        return len1 - len2; 
        //以最小的字符长度循环结束了,都没return的话。就直接
        // return调用方法的字符串长度-传入的字符串长度
    }

获取功能的方法

length()方法

这方法就是返回字符串的长度的方法。
在这里插入图片描述

这个很简单,我也就不多说什么了

charAt()方法

使用这个方法是首先给传入一个int型的参数,返回字符串中传入参数的字符。

多余的话都不说了,一切都在代码中:
在这里插入图片描述

indexOf()方法

indexOf方法传入的是字符,然后在检索字符串,返回字符在字符串当中出现的索引。

看代码:
在这里插入图片描述

当传入两个数的时候(一个要查询的字符,一个开始的索引位置)
在这里插入图片描述

下面我们来对这个底层indexOf()方法进行一个大致分析:

    public int indexOf(int ch, int fromIndex) {
        final int max = value.length;   //获得字符串的长度
        if (fromIndex < 0) {            //若传进来的索引小于0
            fromIndex = 0;              //就将它设置为0
        } else if (fromIndex >= max) {  //若给的索引大于字符串的长度,就返回-1
            // 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) {
                    return i;
                }
            }
            return -1;
        } else {
            return indexOfSupplementary(ch, fromIndex);
        }
    }

substring()方法

这个方法是一个截取字符串的方法,将输入的索引后面的字符串截取出来,保存到一个新的字符串当中来。
在这里插入图片描述

也可以给它传入传入两个索引,那就返回这两个索引之间的字符串。
在这里插入图片描述

那我们接下来就对这两个方法调用的同一个方法进行分析一波:

    public String(char value[], int offset, int count) {
        if (offset < 0) {   //开始截取的索引若小于0,就抛异常
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {  //count是后面的索引-前面的索引
            if (count < 0) {    //若小于0,就抛异常
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {  //若前面的索引小于数组的长度
                this.value = "".value;   //就返回一个""的字符串
                return;
            }
        }
        // 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);
        //一切正常的话,就继续向下执行调用copyOfRange方法
        //传入字符串,前面的索引,后面的索引
    }

来,继续分析源代码:

    public static char[] copyOfRange(char[] original, int from, int to) {
        int newLength = to - from;  //记录需要创建的字符串长度
        if (newLength < 0) //若小于0,抛异常
            throw new IllegalArgumentException(from + " > " + to);
        char[] copy = new char[newLength];  //创建截取的字符串长的数组
        System.arraycopy(original, from, copy, 0,
                Math.min(original.length - from, newLength));  //接下来,就调用了系统的本地代码了
        return copy;    //最后返回处理好的字符串(这里还是数组)
    }

本篇博客也就到此为止了,应为整理源码有点消耗时间,明天一定写完

这也是我重读javase基础的一个系列,比起当时初学的时候,现在看问题多了个高度,理解什么也相对轻松一点全面一些。学习起来更加偏向阅读源码来看,所以多为大家分享看源码。
但毕竟学过时间也较长,有什么不对和漏缺的地方,希望大家指出。
欢迎大家在评论区讨论

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于Java String类的replace方法,其码如下: ``` 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++) { 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; } ``` 这个方法用于将字符串中的一个字符替换成另一个字符,并返回新的字符串。我们来逐行分析一下它的码: 1. 如果要替换的字符和替换后的字符相同,则直接返回原字符串。 ``` if (oldChar != newChar) { ``` 2. 获取字符串的长度和字符数组。 ``` int len = value.length; char[] val = value; /* avoid getfield opcode */ ``` 3. 在字符数组中查找要替换的字符。 ``` int i = -1; while (++i < len) { if (val[i] == oldChar) { break; } } ``` 4. 如果找到了要替换的字符,则将其替换为新字符,否则直接返回原字符串。 ``` if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } ``` 5. 如果要替换的字符不存在,则直接返回原字符串。 ``` return this; ``` 总体来说,这个方法的实现比较简单,就是遍历字符数组,找到要替换的字符,然后再将其替换成新字符,最后返回新的字符串。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值