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 ,线程安全。
转载于:https://blog.csdn.net/wangming520liwei/article/details/79809253