String StringBuffer,StringBuilder 原理

String StringBuffer,StringBuilder 原理



String,StringBuffer,StringBuilder 是经常使用的,了解其原理十分重要


查看String源码

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; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

   }

发现String底层的数据结构是 字符数组 char[]

其次 常用的String 操作:

1.创建对象

private static void testString() {
		//查看下 str1 str2区别
		String str1 = "hello world";
		String str2 = new String("hello world");
		String str3 =  new String("hello world");
		String str4= "hello world";
		System.out.println(str1==str2); //false
		System.out.println(str1== str3); //false
		System.out.println(str2== str3); //true
		System.out.println(str1==str4); //true
		
	}


String str ="hello world" 是存在字符串常量区的 这歌可以看到 str1 str2 是完全一样的  这个字符串常量区,在JDK1,8之前存放的是永久区 PermGen,JDK1.8之后又个metaSpace进行存放。


而String str = new String("hello world") 存放的是堆 区, 每次new 都新建一个对象 可以如下验证


验证字符串常量存放

private static void testStringPermgen() {
		    String str = "abc";  
	        char[] array = {'a', 'b', 'c'};  
	        String str2 = new String(array);  
	        //使用intern()将str2字符串内容放入常量池  
	        str2 = str2.intern();  
	        //这个比較用来说明字符串字面常量和我们使用intern处理后的字符串是在同一个地方  
	        System.out.println(str == str2);  
	        //那好,以下我们就拼命的intern吧  
	        ArrayList<String> list = new ArrayList<String>();  
	        for (int i = 0; i < 10000000; i++) {  
	            String temp = String.valueOf(i).intern();  
	            list.add(temp);  
	        }  


验证对象 堆中存放



常用方法有 replace  substring  concat

replace 先找到第一个和新替换值相同的位置,将前面的值不相同的先进行for循环赋值

再用while循环判断数组中的值是否oldchar 一样的有的话,替换成新值,没有的还有原来的值

public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }


再次看substring方法 SubString方法判断截取是否合法之后,用数组 ArrayCopy的方式进行生成了String


public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
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);
    }


再看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);
    }


对String相关的操作都不会影响到原来的对象,相关的操作,都会产生新对象

javap -c命令反编译生成的class文件进行验证。


StringBuilder 


比较常用的是append方法

@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

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


有个问题:

string+=”hello”的操作
是怎么干的。实际上是哟如下三个操作:

StringBuilder str = new StringBuilder(string);

str.append(“hello”);

str.toString();

String +="hello“每次都会new 一个String,会造成一定的资源浪费,而对于StringBuilder 来说每次append 都是在原有基础上增加的相对来说 比String+= 这个方式少用了很多内存资源,因此推荐StringBuilder 代替 String+= 这个方式, 但是如果有多次append会影响效率的

如String str = “hello”+ “world”的效率就比 StringBuilder st  = new StringBuilder().append(“hello”).append(“world”)要高。

StringBuilder在一次+时候 资源,还是性能相对来说都比较高

问题来了StringBuilder 对String类进行了一定优化,为啥还要StringBuffer?

这个就是线程安全的问题了

append方法

 @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }


    @Override
    public synchronized StringBuffer replace(int start, int end, String str) {
        toStringCache = null;
        super.replace(start, end, str);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     * @since      1.2
     */
    @Override
    public synchronized String substring(int start) {
        return substring(start, count);
    }

String StringBuilder StringBuffer 都是不可继承的都是final的

StringBuffer 方法大多都加了synchronized ,线程安全。


























猜你喜欢

转载自blog.csdn.net/wangming520liwei/article/details/79809253