String/StringBuffer/StringBuilder的区别及源码分析

1.String详解

  • String 底层是一个最终类,即字符串是一个常量,不可变也不可继承,且String底层是一个不可变的char数组,所以每次对字符串的操作都为创建一个新的数组
  • String实现的接口Serializable是序列化的标识,仅表示序列化语义;Comparable接口用于实现对象的自然排序,该接口只有一个方法:compareTo()方法,用于比较对象,小于返回负整数,等于返回0,大于返回正整数
  • String实现的接口 CharSequence(可读字符序列),可以实现对不同类型的char数组只读访问,StringBuffer和StringBuilder也实现了这个接口
package java.lang;

import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
     
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // 哈希值的缓冲,默认为o
  • String支持多种参数类型的初始化方法,但本质上就是将接收的参数传递给成员变量value[],由于String内部实际上是由char[]数组实现的,所以String的方法本质上都是在内部调用数组的方法
/**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
     
        this.value = original.value;
        this.hash = original.hash;
    }
/**
     * Allocates a new {@code String} so that it represents the sequence of
     * characters currently contained in the character array argument. The
     * contents of the character array are copied; subsequent modification of
     * the character array does not affect the newly created string.
     *
     * @param  value
     *         The initial value of the string
     */
    public String(char value[]) {
     
        this.value = Arrays.copyOf(value, value.length);
    }

  • 1.1.equals()和HashCode()方法

  • equals()方法先比较地址值,如果地址值即引用相同,返回true;String重写了equals方法,加上比较两个对象的内容,如果长度相遍历每个字符相等且顺序相同则返回true,所以equals方法是String能广泛用于Map[key,value]中的key的关键所在,另外String也提供了contentEquals()方法只用于比较内容
  • HashCode()方法,由于String封装了一个哈希值的缓存hash,当对象不是新创建时直接调用hash将哈希值赋给对象,新建对象的hash缓存默认为0,需要根据哈希公式计算,Java 底层提供了一个计算公式(使用质数31可以有效的减少哈希值计算相同导致的哈希碰撞)
//String的equals()方法,比较的是地址值或内容
/**
     * Compares this string to the specified object.  The result is {@code
     * true} if and only if the argument is not {@code null} and is a {@code
     * String} object that represents the same sequence of characters as this
     * object.
     *
     * @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    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的HashCode方法,使用了hash缓存,hash是String实例化的HashCode的一个缓存,因为字符串常用于比较,将HashCode缓存在hash中,进行比较时就不需要重新计算哈希值,即可直接比较,提高程序的效率
    /**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * 
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * 
* using {@code int} arithmetic, where {@code s[i]} is the * ith character of the string, {@code n} is the length of * the string, and {@code ^} indicates exponentiation. * (The hash value of the empty string is zero.) * * @return a hash code value for this object. */
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }

1.2.字符串常量池

String—亨元模式(Flyweigth):可以理解为缓存(cache)是一种设计模式或者说优化策略

  • 常量池(Constant pool)被确定的,并且直接保存在已编译的class文件中的一些数据,包括类,方法,接口中的常量以及字符串常量等,程序执行时,常量池会储存在方法区.

  • JVM在内存中专门开辟了一块内存区域用于存放字符串对象,成为字符串常量池,在Java1.7以后字符串常量池移到了堆内存区域

    在JDK6.0及之前版本中,String Pool里放的都是字符串常量
    在JDK7.0中,由于String#intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用.
    
  • Java中的八种基本类型以及String类型,由于会频繁使用这些类型,为了使其运行更快,节省内存,Java引入了常量池的概念,常量池就是类似一个Java提供的缓存

1.3.Java中有两种创建字符串的方式:

String a = "Hello World";
String b = "Hello World";
System.out.print(a==b);//true

1.采用字面值创建String对象,JVM会先去字符串常量池中查找是否有 "Hello World"这个对象,如果没有则在池中创建一个 "Hello World"对象,然后将这个对象的引用赋给a,a就会指向池中的 "Hello World"对象,但创建b对象时,由于JVM在池中找到了 "Hello World"对象,所以JVM会将池中这个对象的引用直接赋给b,这样a,b两个引用都指向了同一个对象,所以a==b为true

String c = new String("Hello World");
String d = new String("Hello World");
System.out.print(c==d);//false

2.采用new创建对象,JVM会先去字符串常量池中查找是否有 "Hello World"这个对象,如果没有则在池中创建一个 "Hello World"对象,并将引用指向堆中,然后new String在堆中开辟了一个空间,栈中的引用a指向这个空间,每new一次都会在堆中开辟一个空间,所以b指向了堆中新开辟的new String空间,c,d在堆中的地址值不同,即c==d为false.

String/StringBuffer/StringBuilder的区别及源码分析_第1张图片

  • String字面值运算时,并不会把每个String对象放入池中,只会把计算结果放入池中

  • JVM对字符串常量池的优化可以总结为:未声明放结果,已声明放引用,所以字符串常量池中可以同时放字符串常量和引用

    2.AbstractStringBuilder

    AbstractStringBuilder 在java.lang 包中,是一个抽象类,实现 Appendable 接口和 CharSequence 接口,这个类的诞生是为了解决 String 类在创建对象的时候总是创建新的对象的问题的。AbstractStringBuilder 有两个子类,分别是 StringBuilder 和 StringBuffer.

    AbstractStringBuilder类和String类很类似,String类内部维护了一个char数组,用final修饰,即不可变.

    AbstractStringBuilder类中同样维护了一个char数组但是是个变量,即是可变数组,这就是为了解决String每次操作都要创建新对象的关键所在.

    package java.lang;
    
    import sun.misc.FloatingDecimal;
    import java.util.Arrays;
    abstract class AbstractStringBuilder implements Appendable, CharSequence {
           
        /**
         * The value is used for character storage.
         */
        char[] value;//可变数组
    
        /**
         * The count is the number of characters used.
         */
        int count;//计数
    
        /**
         * This no-arg constructor is necessary for serialization of subclasses.
         */
        AbstractStringBuilder() {
           //无参构造
        }
    
        /**
         * Creates an AbstractStringBuilder of the specified capacity.
         */
        AbstractStringBuilder(int capacity) {
           //含参构造
            value = new char[capacity];
        }
    
        /**
         * Returns the length (character count).
         *
         * @return  the length of the sequence of characters currently
         *          represented by this object
         */
    

    2.1.主要方法:

    1.length(),返回char[]的实际长度(count),重写的CharSequence接口的length()方法.

    2.capacity():capacity意思是’容量’,即得到目前该value数组的实际大小

      @Override
        public int length() {
           
            return count;
        }
    	public int capacity() {
           
            return value.length;//初始容量为16
        }
        	StringBuilder builder = new StringBuilder("adadsagfaesgsa");
    		System.out.println(builder.length());//14,数组的长度
    		System.out.println(builder.capacity());//30,数组的容量,不够时自动扩容(扩容机制:容量不够用时,先将当前容量+1的二倍(newCapacity)与需要的容量(minimumCapacity)比较如果比需要的容量大,那就将容量扩大到容量+1的二倍;如果比需要的容量小,那就直接扩大到需要的容量。)
    

    3.toString是AbstractStringBuilder抽象类唯一的一个抽象方法;唯一的一个final方法:getValue(),得到value数组。可以对其直接操作

    	 @Override
        public abstract String toString();//继承自Object类
        
        final char[] getValue() {
            return value;
        }
    
    

    4.append()方法—AbstractStringBuilder类及其子类中最重要的操作;Appendable接口的具体实现,源码中所有的append()方法返回值都是原类型AbstractStringBuilder,所以append()可以无限调用.

     	/*append(Object obj):利用Object(或任何对象)的toString方法转成字符串然后添加到该value[]中*/
     	public AbstractStringBuilder append(Object obj) {
           
            return append(String.valueOf(obj));
        }
     	/*public static String valueOf(Object obj) {
            return (obj == null) ? "null" : obj.toString();
        }*/
    
    	/**/
        public AbstractStringBuilder append(String str) {
           
            if (str == null)
                return appendNull();
            int len = str.length();
            ensureCapacityInternal(count + len);//扩容
            str.getChars(0, len, value, count);//将原数组拼接到目标数组(value从str下标为0处开始拼接count长度)
            count += len;
            return this;
        }
    
        // Documentation in subclasses because of synchro difference
        public AbstractStringBuilder append(StringBuffer sb) {
           
            if (sb == null)
                return appendNull();
            int len = sb.length();
            ensureCapacityInternal(count + len);
            sb.getChars(0, len, value, count);
            count += len;
            return this;
        }
    
    
        // Documentation in subclasses because of synchro difference
        @Override
        public AbstractStringBuilder append(CharSequence s) {
           
            if (s == null)
                return appendNull();
            if (s instanceof String)//判断s是否是String的实例
                return this.append((String)s);
            if (s instanceof AbstractStringBuilder)
                return this.append((AbstractStringBuilder)s);
    
            return this.append(s, 0, s.length());
        }
    
    	private AbstractStringBuilder appendNull() {
           
            int c = count;
            ensureCapacityInternal(c + 4);
            final char[] value = this.value;//如果str 是 null,就賦予str = "null",即字符串长度为4
            value[c++] = 'n';
            value[c++] = 'u';
            value[c++] = 'l';
            value[c++] = 'l';
            count = c;
            return this;
        }
    

    5.setCharAt(int index, char ch):直接设置下标为index的字符为ch

     public void setCharAt(int index, char ch) {
            if ((index < 0) || (index >= count))
                throw new StringIndexOutOfBoundsException(index);
            value[index] = ch;
        }
    

    6.replace(int start, int end, String str):用字符串str替换掉value[]数组的[start,end)部分–左闭右开

    public AbstractStringBuilder replace(int start, int end, String str) {
           
            if (start < 0)
                throw new StringIndexOutOfBoundsException(start);
            if (start > count)
                throw new StringIndexOutOfBoundsException("start > length()");
            if (start > end)
                throw new StringIndexOutOfBoundsException("start > end");
    
            if (end > count)
                end = count;
            int len = str.length();
            int newCount = count + len - (end - start);
            ensureCapacityInternal(newCount);
    
            System.arraycopy(value, end, value, start + len, count - end);
            str.getChars(value, start);
            count = newCount;
            return this;
        }
    

    7.insert(int index, char str[], int offset,int len):在value[]的下标为index位置插入数组str的一部分[offset,offest+len)–左闭右开

    
    public AbstractStringBuilder insert(int offset, Object obj) {
           
            return insert(offset, String.valueOf(obj));//插入数组
        
        }
    public AbstractStringBuilder insert(int index, char[] str, int offset,
                                            int len)
        {
           
            if ((index < 0) || (index > length()))
                throw new StringIndexOutOfBoundsException(index);
            if ((offset < 0) || (len < 0) || (offset > str.length - len))
                throw new StringIndexOutOfBoundsException(
                    "offset " + offset + ", len " + len + ", str.length "
                    + str.length);
            ensureCapacityInternal(count + len);//扩容
            System.arraycopy(value, index, value, index + len, count - index);
            System.arraycopy(str, offset, value, index, len);//数组增加
            count += len;
            return this;
        }
    

    8.在value[]查找字符串str,返回其下标

     public int indexOf(String str) {
            return indexOf(str, 0);//返回str在value[]第一次出现的下标值
        }
    
      public int lastIndexOf(String str) {
            return lastIndexOf(str, count);//返回str在value[]最后一次出现的下标值
        }
    

    9.删除方法,删除value[]中下标为[start,end)的一段数组,左闭右开

     
    public AbstractStringBuilder delete(int start, int end) {
           
            if (start < 0)
                throw new StringIndexOutOfBoundsException(start);
            if (end > count)
                end = count;
            if (start > end)
                throw new StringIndexOutOfBoundsException();
            int len = end - start;
            if (len > 0) {
           
                System.arraycopy(value, start+len, value, start, count-end);
                count -= len;//删除指定长度数组
            }
            return this;
        }
    

    3.StringBuilder

    StringBuilder类继承自AbstractStringBuilder类,是一个可变的字符序列(char[] value,可变数组),相对于String,有其特有的append(),insert(),delete()方法,由于StringBuilder的字符串拼接调用的是append()方法,所以不会产生大量的临时新对象,提高了拼接效率,线程不安全,在JDK1.5之后引入,也是作为String对象在进行"+"操作时,其实本质上就是调用的StringBuilder的append()方法.

     	String a = "HelloWorld";
        String b = "Hello" + "World";
    
        String c = "Hello";
        String d = "World";
        String e = c + d;
    c
        System.out.println(a);//输出为HelloWorld
        System.out.println(e);//HelloWorld
        System.out.println(a == e);//false,两个String对象+操作时,底层调用的是StringBuiler的append()方法,先拼接Hello,再拼接上World,返回的结果e存入一个新的对象,所以说String字符串引用相加的计算效率要比直接相加的效率低
        System.out.println(a == b);//true
        System.out.println(b == e);//false
    
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
     
     public StringBuilder() {
     
        super(16);//初始化容量
     public StringBuilder(int capacity) {
     
        super(capacity);
    }
     public StringBuilder(String str) {
     
        super(str.length() + 16);
        append(str);//使用str初始化,容量str大小的基础上加16
    }
    @Override
    public StringBuilder append(String str) {
     
        super.append(str);
        return this;
    }
}

4.StringBuffer

StringBuffer和StringBuilder都是继承自AbstractStringBuilder类(char[] value,可变数组),两者实现的方法功能基本一致,不过StringBuffer的方法都是被 synchronized关键字修饰,所以StringBuffer是线程安全的,但是保证安全的同时牺牲了效率(同步锁),所以效率:StringBuilder>StringBuffer.

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
     
    @Override
    private transient char[] toStringCache;
    public synchronized StringBuffer append(String str) {
     //同步关键字--线程安全
        toStringCache = null;
        super.append(str);
        return this;
    }
  }

总结:1.String,StringBuilder和StringBuffer的区别

String StringBuilder StringBuffer
最终类不可继承 最终类不可继承 最终类不可继承
String字符串是常量不可变 变量可变 变量可变
线程不安全 线程安全
进行大量字符串拼接时,回新建大量临时对象,浪费内存空间且效率低下 采用append()拼接字符串,可变数组拼接,效率高,常用于字符串拼接 与StirngBuilder相比,加入了同步锁(synchronized),保证了线程安全,当降低了效率
String继承自Object类 StringBuilder继承自AbstractStringBuilder类 StringBuffer继承自AbstractStringBuild

2.三者的继承关系:

String/StringBuffer/StringBuilder的区别及源码分析_第2张图片

你可能感兴趣的:(Java基础,java,jdk)