Java中String类的温故知新

18 篇文章 0 订阅
4 篇文章 0 订阅

Java中主要用以处理字符串的类是String,StringBuffer和StringBuilder。也是面试中的常见考点,一块来温故而知新吧~

String
常见用法
    String str1 = "I love China.";
    String str2 = new String("I love China.");
    String str3 = "I love";
    str3 = str3 + "China";
String 内部实现

内部使用一个字符数组表示字符串,定义如下:

private final char value[];

可以根据char数组创建String,定义如下:

 public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
 public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 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);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

值得一提的是:String会根据参数新建一个数组,复制内容,还有一些方法的用法也和char数组有关,譬如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);
    }
不可变性

和包装类型相似,String类也是不可变类,即对象一经创建,就没法修改了。String被final修饰,不能被继承,内部的char数组也是final的,初始化后不可变。并且String中的一些看似修改的方法,也是通过新建String对象实现的,譬如concat()方法:

 public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        // copyOf方法新建了一块新的字符数组,复制原内容,调用new创建新的String对象。
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
常量字符串

Java中的字符串常量本身也可以调用String类的各种方法,就跟对象一样。

    System.out.println("I love China".length());
    System.out.println("I love China".contains("love"));
    System.out.println("I love China".indexOf("love"));

这些常量就是String类型的对象,被存放与常量池中,每一个常量保存一份,为所有的使用者共享。当通过常量的形式使用一个字符串的时候,使用的就是常量池中对应的String类的对象。

    String str1 = "I love China.";
    String str2 = "I love China.";
    System.out.println(str1== str2);
    输出结果是:
    true

上面的代码实际上等同于:

    String str = new String (new char[]{'I','l','o','v','e','C','h','i','n','a' });
    String str1 = str;
    String str2 = str;
    System.out.println(str1== str2);

实际上只有一个String对象,其他对象都指向它,所以str1==str2;就很好理解了。
但如果出现下面这样的情况:

 String str1 = new String ("I love China.");
    String str2 = new String ("I love China.");
    System.out.println(str1== str2);

输出结果是:
false
是不是很奇怪呢?Java中String类中以String为参数的构造器如下:

   public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

从中可见,hash作为String中的另一实例变量,表示缓存的hashCode。str1和str2 指向不同的String对象,只是对象内部的value值指向相同的char数组,所以而str1.equals(str2)只是比较内容,所以返回true。

hashCode

上面也提到了hash值,缓存了hashcode方法的值。我们来见识下hashCode的方法,代码如下:

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            // 使得hash值与每一个字符的值相关,也和每一个字符的位置相关。而使用31的原因大致是因为一方面可以产生更加分散的散列,即不同的字符串hash值一般不同,另一方面是计算效率更高,当31*h时,可以采用h<<5移位操作代替乘法。当
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        // 如果缓存的hash不为0,直接返回。
        return h;
    }
String的+和+=运算符

Java编译器会将此过程转换成StringBuilder的append方法。在稍微复杂的情况下,Java编译器可能没有那么智能,会产生过多的StringBuilder,尤其是循环的时候,应该直接使用StringBuilder较为适合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值