探究底层原理之 String

一、String对象的创建

1.创建形式
  1. String a = “”               ——直接赋值
  2. String b = new String( [String original]… );  ——new
  3. String c = String.class.newInstance();     ——反射
    ----------以上仅用一行代码可以实现----------
  4. clone
  5. 反序列化
2.以下代码块创建出了几个对象?
String s1 = "abc";
  • 1个:(“abc”) - 常量区;
String s1 = new String("abc");
  • 2个:(“abc”) - 常量区;( new String(“abc”) ) - 堆内存

所有在代码里面以双引号""创建出来的东西都会创建在常量区

String s1 = "";
String s2 = new String();
String s3 = new String("");
  • s1:1个:空字符串("") - 常量区
  • s2:2个:空字符串("") - 常量区;( new String() ) - 堆内存
  • s3:同s2
String s1 = new String("abc");
s1 += "xyz";//自动转化(StringBuilder和.toString()返回的String看成是一个对象)
-------------------------------
String s2 = new String("abc");
s2 = s2 + "xyz";
  • s1两行代码:4个:(“abc”)、(“xyz”) - 常量区;
    ( new String(“abc”) )、( new String(“abcxyz”) ) - 堆内存
  • s2两行代码5个:(“abc”)、(“xyz”) - 常量区;
    ( new String(“abc”) )、( new StringBuilder() )、( new String(“abcxyz”) ) - 堆内存

二、String的内存性能优化

1.String对象的底层创建
Demo1
String s1 = "abc";
String s2 = new String("abc");
String s3 = s1 + s2;
//System.out.println(s3);该行代码表示使用了s3对象
String s4 = "abc" + "xyz"// " + " 的左右是两个明文

对于 String s3 = s1 + s2 该行代码创建出几个对象的问题:

  • 如果不使用s3 ——2个:( new StringBuilder() )(编译优化) 、( new String(“abcabc”) ) - 堆内存
  • 如果使用s3 ——1个:( new String(“abcabc”) ) - 堆内存

对于 String s4 = “abc” + “xyz” 该行代码创建出的对象:

  • 1个:“abcxyz” - 常量区,编译优化

用 " + " 连接两个(或多个)String对象创建出一个新的String对象而·不用·,其消耗的空间比用了以后还多,因为其新创建了一个占用较多内存的StringBuilder对象

对于用" + "拼接两个String而后使用它时:(即上述 String s3 = s1 + s2 拼接完s3后打印输出s3)

  • String s3 = s1 + s2 在 执行时 会变成:String s3 = s1.concat(s2);
    即 " + " 在执行时被转化成调用.concat()进行拼接
public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    int len = value.length;
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}
------------------------------------------------------    
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, 0, dst, dstBegin, value.length);
}

concat做了两次数组的循环遍历(将数组拷贝了两遍),所以其性能很,不推荐使用 " + " 进行字符串拼接

用 " + " 连接两个(或多个)String对象创建出一个新的String对象而后续需要·使用·它时,若需要连续拼接,强烈推荐使用StringBuilder

Demo2
String s1 = new String();
String s2 = new String("");     
    public String() {
        this.value = "".value;
    }
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

当创建出的String对象仅为空串时,由String源码知,两种构造方法相比较:

  • s1 和 s2 的地址(value)相等,均指向空串
  • s1.hash == 0,s2多了一个hash的赋值的过程

String类型的对象 hash是一个属性 属性默认值都是0 什么时候调用了hashCode 才会赋值

如果以后我们将String存储在HashMap集合里,用第二种性能会更快,因为在初始化时就已经给hash赋值了,而第一种还要现算hash值

Demo3
 String s1 = "hello";
 String s2 = new String(s1);//从加载性能上讲推荐使用此方法创建对象
 -------------------------------
 String s3 = new String("hello");

s2和s3两种方式相比较:

  • 在运行过程中,两者没有区别
  • 编译 过程中,s2的编译速度要远远快于s1的编译速度

new String()时,参数传引用可以自己索引,参数传字符则需串遍历常量区,查看是否有相同的字符串,如果有相同的则使用常量区里的

2.String方法的应用

(1)字符串长度在10个以内,怎么比较两个字符串是否长度相等速度最快?

//字符串长度在10位以内
String s1 = new String("sadfasdsdf");
String s2 = new String("xcvbxcvbx");
//System.out.println(s1.equals(s2));
//System.out.println(s1.hashCode() == s2.hashCode()); 

equals() 与 hashCode() 方法比较:

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        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;
    }
------------------------------------------------------    
    public int hashCode() { //String类重写的hashCode()直接用字符串内容算出一个数字
        int h = hash;
        if (h == 0 && value.length > 0) { //当hash值为0时才比较
            char val[] = value; //String为了方便判断,使其重写的hashCode()与value相关,即用value进行运算

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
  • equals() 会使用循环依次去比较,非常慢(如果有字符串有事10个字母,用字符串要比较10次)
  • hashCode() 直接返回hash值,只需做1次hash值比较,远高于 equals() 速度

若字符串太长hash值可能重复,即上述比较方法只适用于比较两个比较短的字符串(10个字符串以内hash值基本不会重复)

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值