创建String 对象的方式

  1. 直接通过字符串常量创建
String a="a";
  1. New一个String对象
String a=new String("a");

jvm字符串常量池

字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:

使用字符串常量池:每当我们创建字符串常量池时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么久直接返回常量池中的实例引用,如果字符串不存在常量池中,就会实例化该字符串并且放到常量池中

两种String创建的区别

方式一:首先检查字符串常量池中有没有a,如果有,就直接返回引用给String a;如果没有就在字符串常量池中创建一个a,再把引用返回给String a;

java 字符常量池 javastring常量池_java

方式二:首先还是再字符串常量中检查是否有,没有就创建,然后就执行String对象的new操作。并且这个String对象会指向对应的常量池中的常量。

java 字符常量池 javastring常量池_字符串常量池_02

字符串截取subString方法解析

两种:substring(int beginIndex);开始下标截取到字符串末尾
substring(int beginIndex, int endIndex) ;前标开始不包括后标
第二种源码如下:

public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) { //前标小于0
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {  //后表越界
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;   //截取后的字符串长度
        if (subLen < 0) {  //不能小于0,也就是后标必须大于前标
            throw new StringIndexOutOfBoundsException(subLen);
        }
        //最主要的一部,如果返回的还是当前字符串直接就返回this,否则创建新的String对象。
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

看一个如下例题:

String a="qdqwdq";
                   String b="qdq";
                   String c=a.substring(0,3);
                   System.out.println(b == c);
                   System.out.println(b.equals(c));

输出:

false
true
  • 原因:
    c是一个新创建的string对象,c是堆中的一个对象的引用,而b指向常量池中的abc,当软地址不等,所以==判断的时候是false.但是用equals判断的时候当然是返回true. 只有一种特殊情况会有一样的地址,那就是截取的字符串与原字符串相同,源码中可以看到返回的是this.

字符串的拼接 + 操作

加号拼接字符串jvm底层其实是调用StringBuilder来实现的,也就是说”a” + “b”等效于下面的代码片。

//String c=a+"b"的实质如下:
String a = "a";
StringBuilder sb = new StringBuilder();  //只会创建一个StringBuilder对象
sb.append(a).append("b");
String str = sb.toString();   //最后是会调用toString方法的。

但并不是说直接用“+”号拼接就可以达到StringBuilder的效率了,因为用“+”号每拼接一次都会新建一个StringBuilder对象,并且最后toString()方法还会生成一个String对象。在循环拼接十万次的时候,就会生成十万个StringBuilder对象,十万个String对象,这简直就是噩梦。先不管效率,就看看加号拼接的底层也就StringBuilder的append方法是怎样实验字符串拼接的。
源码如下

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

toString方法如下

@Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

可以看到每次调用toString方法都是返回了一个String对象。

总结:加号拼接字符串返回的都是String对象