不可变性

public final class String
# 由于是final,所以string是不可变的 

长度限制

由于stirng其实就是一个char数组

char数组的下标是整型,integer

https://segmentfault.com/a/1190000020381075

substring

https://www.hollischuang.com/archives/1232

jdk6和jdk7之后的差别

jdk6

jdk6的时候,当截取字符串的时候,会在堆中new 一个新的string对象,但是这个string对象使用的char数组还是之前的数组,如果你只是在很长的字符串中引用了很小的一块数据,但是由于这个char数组是有引用的,所以无法进行垃圾回收,但是由于你所使用的字符串只是很小的一部分,但是你却用了这么大的char数组,会导致好像那么一大空间不存在似的,这就产生了内存泄露的问题。

内存泄露:在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。 内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

jdk7

既然已经知道了上述问题所在,那么只需要new一个新的string的时候,让这个string指向自己的包含的char数组即可

        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);
        }
        //就是在这里将char数组拷贝过来,截取的offset和截取字符串的值是一样的
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

replaceFirst、replaceAll、replace

    public String replaceFirst(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
    }
        public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    } 
        public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
                this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    } 

replaceFirst的作用是让regex去replacement替换原有string的第一个字符

replaceAll的作用是replacement替换regex

replace的作用是将原有字符串的所有target替换为repalcement

String、StringBuilder、StingBuffer

StringBuilder

    public StringBuilder() {
        super(16);
    }

    public StringBuilder(int capacity) {
        super(capacity);
    }
     public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
        @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    } 

从源码可以看出,默认的stringbuilder是16个字节,如果指定了大小就用指定的大小,如果直接给了个参数就是字符串的长度加上16个字节

方法中没有synchronized,所以这是线程不安全的

StingBuffer

    @Override
    public synchronized int length() {
        return count;
    }

    @Override
    public synchronized int capacity() {
        return value.length;
    }


    @Override
    public synchronized void ensureCapacity(int minimumCapacity) {
        super.ensureCapacity(minimumCapacity);
    }

方法都是synchronized修饰的,所以是线程安全的

为什么要重写equals方法

原因

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

虽然从代码来看string是这样继承的,没有显示声明继承object类,但是实际上所有的基本上都是默认继承object类的,有版本差别,jdk1.7之前的是编译器处理的,jdk1.7之后包括1.7都是由虚拟机来处理的,当然IDE可以提示object类中的方法是因为IDE也对这个object做了相关处理

Object类中有一个equals()方法,它的默认实现是比较两个对象的引用是否相等。但是在实际应用中,我们通常需要比较对象的内容是否相等,而不是比较引用是否相等。因此,String类重写了equals()方法,使得它比较的是两个字符串对象的内容是否相等。

具体来说,String类中的equals()方法会比较两个字符串对象的字符序列是否相同。如果两个字符串的字符序列相同(包括字符的顺序、数量、大小写等方面),则equals()方法返回true,否则返回false。

方法

    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;
    }

String对“+”的重载

https://juejin.im/post/6844903960608784392

package com.test;

public class demo {
    public static void main(String[] args) {
        String a = "1";
        String b = "2";
        System.out.println(a+b);
    }
}

从反编译的过程可以看出,是调用了stringbuilder的append方法,

String.valueOf和Integer.toString的区别

stirng

        //stirng不能为null,为null会报NullPointerException
        public String toString() {
        return this;
    }
    //这个方法的功能就是如果形参是null,那么返回null字符串,而不是直接报NullPointerException异常
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

integer

    public static String toString(int i, int radix) {
        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
            radix = 10;

        /* Use the faster version */
        if (radix == 10) {
            return toString(i);
        }

        char buf[] = new char[33];
        boolean negative = (i < 0);
        int charPos = 32;

        if (!negative) {
            i = -i;
        }

        while (i <= -radix) {
            buf[charPos--] = digits[-(i % radix)];
            i = i / radix;
        }
        buf[charPos] = digits[-i];

        if (negative) {
            buf[--charPos] = '-';
        }

        return new String(buf, charPos, (33 - charPos));
    }

https://blog.csdn.net/lianjiww/article/details/82715250?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

switch对String的支持

package com.test;



public class demo {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
            case "hello":
                System.out.println("hello");
                break;
            case "world":
                System.out.println("world");
                break;
            default:
                break;
        }

    }
}

反编译

由反编译的代码可以看出来,switch中的case是通过hashCode来进行匹配的,使用equals方法来进行值的比较

字符串池

intern

https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html

String比较特别的地方

  1. 直接中双引号引起来的,是放在常量池中,如代码所示String s2="1";
  2. 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

实际例子验证:

package com.test;

public class demo02 {
    public static void main(String[] args) {
        String s="1";
        String s1=new String("1");
        String s2=s.intern();
        System.out.println(s==s1);
        System.out.println(s1==s2);
        System.out.println(s2==s);
    }
}

运行结果:false false true

jdk7之后,intern方法对 intern 操作和常量池都做了一定的修改。主要包括2点:

  1. 将String常量池 从 Perm 区移动到了 Java Heap区
  2. String#intern 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象。

由上图代码及运行结果可以看出,双引号的的String是直接在常量池中的,而new出来的对象是在堆中的,当s2调用intern的方法之后,会去常量池中查找是否有这变量,如果有的话会直接引用常量池中的对象

两个String相加

package com.test;

public class demo01 {
    public static void main(String[] args) {
        String s=new String("1");
        String s1=new String("1");
        String s2=s+s1;
        System.out.println(s==s1);
    }
}

反编译

Compiled from "demo01.java"
public class com.test.demo01 {
  public com.test.demo01();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/String
       3: dup
       4: ldc           #3                  // String 1
       6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
       9: astore_1
      10: new           #2                  // class java/lang/String
      13: dup
      14: ldc           #3                  // String 1
      16: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
      19: astore_2
      20: new           #5                  // class java/lang/StringBuilder
      23: dup
      24: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      27: aload_1
      28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      31: aload_2
      32: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      35: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      38: astore_3
      39: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      42: aload_1
      43: aload_2
      44: if_acmpne     51
      47: iconst_1
      48: goto          52
      51: iconst_0
      52: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
      55: return
}

由上图可以看出是采用StringBuilder的append的方法来进行字符串拼接的

参考