常用类String

常用类 String

Java 中,字符串不是基本数据类型,而是通过 String 类来表达的。也就是说,Java 中的字符串都是对象!!

String 的定义

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

从上面注意到,String 是被定义为 final 的,也就是不能被继承的。实现了几个常见的接口这里就不再细说了。定义完了接着就是里面的属性了:

//哦豁,原来字符串就是一个字符数组哦
private final char value[];
//用于计算哈希值
private int hash;

完了接下来该构造器上场了,大概数了一下 String 提供了16个构造器,其中有两个已经废弃,也就是还有14个:

//空参构造器,也就是说直接new String() 得到的是一个空串""
public String() {
    this.value = "".value;
}
//参数为一个字符串对象
public String(String original) {
    this.value = original.value;//将参数字符串对象维护的字符数组value赋值给这个新的字符串
    this.hash = original.hash;//将参数字符串对象的hash值赋值给这个新的字符串
}
//参数为一个字符数组
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}
//参数为字符数组,偏移量,复制的数量
public String(char value[], int offset, int count) {
    if (offset < 0) {//偏移量小于0抛出异常
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count <= 0) {//复制数量小于0抛出异常
        if (count < 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);
    }
    //复制数组并赋值给value
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

这里就省略了剩下的构造器,原因就是很少直接去使用这些构造器来创建一个字符串,咱不是直接使用""写就好了么。

String 常用的方法

1. 比较方法

  • equals() —— 判断内容是否相同。注意这里说的是内容!由于 Java 的字符串不是基本类型,而是一个对象,所以两个字符串之间的对比其实就是在拿两对象进行比较,那么问题来了,两个对象是如何比较的呢?可以通过 == 去比较吗?答案是,不太合适!!因为 == 比较的是两个对象的内存地址是否一致,这完全有可能会出现这样的结果:两个字符串的内容是一致的,但是通过运算符 == 比较的结果却是 false ,这往往不是我们想要看到的,咱们如果要拿两个字符串来比较,那么肯定考虑的就是里面的内容是否一致,至于是否是同一个内存地址则没有要求。所以就有了 equals() 这个方法:
public boolean equals(Object anObject) {
    //首先上来还是使用 == 来比较,因为如果两个对象的引用地址是一致的,那么这两个变量其实就是引用着方法区中的同一个区域,那么里面必然是一致的!!
    if (this == anObject) {
        return true;
    }
    //接着通过instanceof运算符检查这个参数是否为String的实例,如果不是,那么不用比较了,直接返回false
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;//字符串长度
        if (n == anotherString.value.length) {//如果长度不相等,则跳出if语句返回false
            char v1[] = value;//字符数组
            char v2[] = anotherString.value;//参数的字符数组
            //遍历,逐一字符比较,如果出现某一个位置上的字符是不相同的,则返回false,否则返回true
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

补充:一般来说通过 “” 方式来使用字符串,但是你可能会说 String 是一个类啊,不用new出一个实例来使用吗?其实,在 Java 虚拟机中存在着一个字符串常量池,使用一个字符串会先去常量池中检索是否存在,若存在则直接引用;若不存在则先添加到常量池中,再返回引用。但是通过 new 关键字创建出来的字符串都拥有独立的内存空间,它不会再去常量池中引用,例如:

String str1 = new String("jonas");
String str2 = new String("jonas");
String str3 = "jonas";
String str4 = "jonas";
System.out.println(str1 == str2);//false  --> 证明str1与str2不是引用着同一块内存地址,尽管字符串内容是一致的,但是new出来两个对象了
System.out.println(str3 == str4);//true --> "" 使用的是常量池里面的引用,所以两者的引用是同一块内存地址
System.out.println(str1 == str3);//false  --> 常量池中与new出来的是独立的
  • compareTo() —— 判断字符串的大小关系。
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类型的哦)。
    int k = 0;
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    return len1 - len2;
}
  • equalsIgnoreCase() —— 与 equals() 基本一致,但是忽略大小写。
  • compareToIgnoreCase() —— 与 compareTo()基本一致,但是忽略大小写。

2. 查找方法

  • charAt(index) —— 返回指定索引位置的字符
public char charAt(int index) {
    if ((index < 0) || (index >= value.length)) {//参数小于0或大于字符串长度则抛出异常
        throw new StringIndexOutOfBoundsException(index);
    }
    //返回字符数组value中的index位置的值
    return value[index];
}
  • indexOf(String str) —— 从索引为0开始检索,返回第一次出现参数 str 的位置的索引。
public int indexOf(String str) {
    return indexOf(str, 0);//调用了indexOf(String str, int fromIndex)方法,而且开始索引默认为0(也就是从0位置开始查找)
}
public int indexOf(String str, int fromIndex) {
    return indexOf(value, 0, value.length,
                   str.value, 0, str.value.length, fromIndex);//又调其他方法了~~
}
//终于找到你了吧,原来几乎所有的indexOf都调用了该方法,所以就来分析这个东西就完事了。
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);
    }
    //如果开始检索小于0,则从索引0的位置开始检索
    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++) {
        /* Look for first character. */
        if (source[i] != first) {
            while (++i <= max && source[i] != first);
        }

        /* Found first character, now look at the rest of v2 */
        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;
            }
        }
    }
    return -1;
}
  • starsWith(String str) —— 是否以 str 开头,返回布尔值
public boolean startsWith(String prefix) {
    return startsWith(prefix, 0);//调用的是一个重载的方法
}

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;
    // 偏移长度不合法,则直接返回false,表示不包含
    if ((toffset < 0) || (toffset > value.length - pc)) {
        return false;
    }
    //参数字符数组逐一比较,若存在不等,则返回false
    while (--pc >= 0) {
        if (ta[to++] != pa[po++]) {
            return false;
        }
    }
    return true;
}
  • endsWith(String str) —— 是否以 str 结尾,返回布尔值
public boolean endsWith(String suffix) {
    return startsWith(suffix, value.length - suffix.value.length);//还是调用了上面的方法。。。
}

3. 截取方法

subString() —— 截取字符串,有两个重载方法,一个参数的时候表示开始索引开始截取到最后;两个参数时则表示截取这个区间,返回值是一个新的字符串,而不是在原来的字符串上修改,字符串具有不可变性!!就拿其中的一个为例:

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);//最后此处是直接new了一个String,所以这是新的对象!
}

4. 替换方法

  • replace(char oldChar, char newChar) —— 替换字符,结果还是会new 一个新的 String
  • replaceAll(String regex,String replacement) —— 第一个参数是正则表达式。

5. 连接字符串

连接字符串最简单的方法就是通过操作符 + 来拼接两个字符串。但是这里要说的是一个方法:

public String concat(String str) {
    //上来先获取参数str的字符长度
    int otherLen = str.length();
    //如果参数字符串的长度为0,那么直接返回this(调用该方法的字符串实例本身)
    if (otherLen == 0) {
        return this;
    }
    //获取字符串长度
    int len = value.length;
    //定义一个新的字符数组,初始值为value本身,但是长度为两个字符串之和
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);//这里调用了类里面的另外一个方法哦,详情看下面
    return new String(buf, true);//最后new一个String来表示新的字符串
}

void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, 0, dst, dstBegin, value.length);//该死的native还不给我看源码...
}

StringBuffer

在上面就提到过 String 具有不可变性,意思就是你不能在原来的字符串上面进行修改操作,上面操作字符串的方法都会新建一个实例。然而,StringBuffer 却相反了,若进行修改操作,它并不会产生新的字符串,而是在原字符串上进行修改。在 StringBuffer中维护着一个字符数组:

private transient char[] toStringCache;//默认初识容量为16

如果需要使用 StringBuffer 就不能简单的“”来表示了,必须手动new实例了。里面的方法大致分为三类(因为很多重载方法,就不详细说了):

  • append() —— 追加内容
  • insert() —— 插入内容
  • delete() —— 移除内容

StringBuilder

StringBuilder 也是一个可变的字符串,他与 StringBuffer 不同之处就在于它是线程不安全的,基于这点,它的速度一般都会比 StringBuffer 快。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值