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); }
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 ,线程安全。