本文主要是介绍java基础 浅析string,StringBuffer与StringBuilder,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
常用的类出来八个基本数据,就是string。而string类在程序编写中是无法避免的的一个类,很多时候程序中都需要对string进行处理。所以本章聊一下string以及StringBuffer和 StringBuilder。
string
String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。
上面两句话是摘自官方文档,意思是string是字符串类型的数据,同时string数据是无法改变的,也就是在其定义赋值的时候就确定。
不过这个时候就有疑问了为什么string是不可改变的,还有那对string数据进行修改难道不是数据的改变吗?
带着这些疑问我们需要先看一下string的源码,然后进行查看。
这个地方可以看出两点
- string的数据存储是一个char类型的数组。
- 为什么不可改变因为char数组前面又关键字final。(这个在接口中讲过:其修饰的变量就是代表不可变。当然不可变不是绝对的,反射可以修改其值)
既然不可变,那么问题就来了那就是两个字符串是如何连接的?所以可以看其源码,当然string中连接方法是concat方法,所以我们先看代码:
/*** Concatenates the specified string to the end of this string.* <p>* If the length of the argument string is {@code 0}, then this* {@code String} object is returned. Otherwise, a* {@code String} object is returned that represents a character* sequence that is the concatenation of the character sequence* represented by this {@code String} object and the character* sequence represented by the argument string.<p>* Examples:* <blockquote><pre>* "cares".concat("s") returns "caress"* "to".concat("get").concat("her") returns "together"* </pre></blockquote>** @param str the {@code String} that is concatenated to the end* of this {@code String}.* @return a string that represents the concatenation of this object's* characters followed by the string argument's characters.*/public String concat(String str) {int otherLen = str.length();//otherLen是需要连接目标字符串的长度if (otherLen == 0) {return this;//如果长度为0,那就是直接返回this自己即可。}int len = value.length;// len 就是本身字符串中的char的长度char buf[] = Arrays.copyOf(value, len + otherLen);//Arrays.copyOf(value, len + otherLen)方法是复制字char[]数组,长度是两个字符串长度之和。//Arrays.copyOf 如果目标数组没有长度长,可以赋值其类型的默认值。str.getChars(buf, len);//这个方法就是将目标字符串放入buf这个char数组return new String(buf, true);//返回一个新的string}/*** Copy characters from this string into dst starting at dstBegin.* This method doesn't perform any range checking.*/void getChars(char dst[], int dstBegin) {System.arraycopy(value, 0, dst, dstBegin, value.length);}
// 这个方法的参数值dst的值为buf。dstBegin是指原来字符串char的长度
//System.arraycopy(value, 0, dst, dstBegin, value.length);中的参数讲解 这个时候value值就不是原来的char数组了,而是目标字符串的char数组。 value.length也就是目标字符串的char[]数组长度 因为value其实是this.value
通过源码我们可以看出了string字符串相互连接的时候,其实就是复制一个char数组长度是两者之和,然后再通过new stirng(char[])构造方法重新新建一个string
这个时候估计有人疑问,因为数组长度不可变,所以新建了一个string,但是如果我不变长度,直接修改其中一个char的值其实就不用新建一个string对象了?(这种想法其实有点忽略final关键字,不过我们可以看源码)
/*** Returns a string resulting from replacing all occurrences of* {@code oldChar} in this string with {@code newChar}.* <p>* If the character {@code oldChar} does not occur in the* character sequence represented by this {@code String} object,* then a reference to this {@code String} object is returned.* Otherwise, a {@code String} object is returned that* represents a character sequence identical to the character sequence* represented by this {@code String} object, except that every* occurrence of {@code oldChar} is replaced by an occurrence* of {@code newChar}.* <p>* Examples:* <blockquote><pre>* "mesquite in your cellar".replace('e', 'o')* returns "mosquito in your collar"* "the war of baronets".replace('r', 'y')* returns "the way of bayonets"* "sparring with a purple porpoise".replace('p', 't')* returns "starring with a turtle tortoise"* "JonL".replace('q', 'x') returns "JonL" (no change)* </pre></blockquote>** @param oldChar the old character.* @param newChar the new character.* @return a string derived from this string by replacing every* occurrence of {@code oldChar} with {@code newChar}.*/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;}
具体算法就是如上 不过我们也可以看到一个 return new String(buf, true);所以也是新建了一个元素。除非是(oldChar = newChar) 才会不新建对象,直接返回this。
知道了string的一些原理,不过我们需要再进一步的了解其再jvm中存在。不然就会对==和equals直接的区别有些模糊。以及string字符串通过**+**号连接有什么什么区别。以及new string(字符串)和=字符串有有什么区别。
这个首先就要明白一个事情,那就是string值存在的位置,无法避免的一个jvm中的存储区域常量池。常量池的位置其实随着版本的变化,其存在的位置再不停的变化,但是其所拥有的意义还是一样的。
常量池:常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式;当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。
其拥有的特点:
- 1:常量池存储着常量数据,包括其类,方法,以及接口中的常量等。
- 2:常量池中的数据都是唯一的。
jdk1.6如下
jdk1.7 如下
jdk1.8 其中常量池在元空间中。
其实三个图都是一个大概的存储结构,而对于jvm的结构划分,如果需要会单独出一篇。目前先这样简单看一下结构理解一下。
讲解string字符串看了源码,不先开始讲解其方法而讲这个结构也是有原因的,因为其中在string中常用来区别或者解释==和equals区别,以及在创建一个string对象的时候创建了几个对象等问题。所以我们需要对其结构进行一个简单的了解才可以理解。
先看代码,我们先讲解一下==这个逻辑符合再string中的效果
public class Test {public static void main(String[] args) {String a="hello";String b="hello";String a1=new String("hello");String b1=new String("hello");System.out.println("a==b "+(a==b));System.out.println("a1==b1 "+(a1==b1));String c="hello"+" world";String d="hello"+" world";String c1=new String("hello")+" world";String d1=new String("hello")+" world";System.out.println("c==d "+(c==d));System.out.println("c1==d1 "+(c1==d1));String e="hello wor"+"ld";String f=a+" world";System.out.println("c==e "+(c==e));System.out.println("c==f "+(c==f));}
}//输出
a==b true
a1==b1 false
c==d true
c1==d1 false
c==e true
c==f false
看到这个结构是不是有些不敢相信。其实对比的是两个数据所在jvm中的地址,不但值要一样,而且地址也要一样。这个用图说明,毕竟方便,所以我们直接用jdk1.6的结构图来讲解。
看图应该可以理解a,b与a1,b1在声明变量的生活两者的区别。看图我们应该明白常量池中存在的数据值是唯一的,而为什么a1,b1的地址不同,是因为a1的value与b1value的存在堆中地址不一样所以使用的生活会返回false。
而直接通过=给string赋值,和通过new的区别不同,而是直接指向常量池中的数据。需要先在堆中先建立一个对象。
两个字符串值通过 “hello”+" world" 连接其实"hello world"一样,直接指向常量池中的数据。但是如果参数了声明的变量a+" world" 就不会直接指向常量池了,而是等于和new一个对象然后相加的效果一样。
现在我举出一个例子。最好先不要看结果,而是自己先猜测一下,然后再看结果
public class Test {public static void main(String[] args) {final String a="hello";String b="hello"+" world";String c=a+" world";System.out.println("c==b "+(c==b));}
}//输出
c==b true
有人会看到这个结果,然后看了一下标红的总结,觉得是两个矛盾,不过本质又无区别,只是那句话没有考虑全部情况。这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。
上面我对比字符串的时候用的==两个等于号,其对比的是地址,如果字符串内容是否相等,我们使用的是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) {//如果地址相同,自然会返回true。return true;}if (anObject instanceof String) {//元素是否为string的实例,然后将其字符串蝙蝠char[]数组,然后依次对比。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;}
如此看明白==和equals方法的区别了,一个是看其引用的地址是否一样,一个是看其内容是否一样。
- 补充
前面又说值传递和引用传递两个方式。其中值传递不会改变原来变量的值,而引用传递会改变其变量的值(String 类型除外)。
public class Test {public static void main(String[] args) { int a=12;String b="main";int[] c= {1,2,3};System.out.println("main 方法中 前"+a);System.out.println("main 方法中前"+b);System.out.println("main 方法中前"+Arrays.toString(c));System.out.println("=============================");show(a,b,c);System.out.println("=============================");System.out.println("main 方法中后"+a);System.out.println("main 方法中后"+b);System.out.println("main 方法中后"+Arrays.toString(c));}private static void show(int a,String b,int[] c) {a=24;b="show";c[2]=5;System.out.println("show 方法中"+a);System.out.println("show 方法中"+b);System.out.println("show 方法中"+Arrays.toString(c));}
}
//输出
main 方法中 前12
main 方法中前main
main 方法中前[1, 2, 3]
=============================
show 方法中24
show 方法中show
show 方法中[1, 2, 5]
=============================
main 方法中后12
main 方法中后main
main 方法中后[1, 2, 5]
这个可以看出string 的一个特点就是其不可变性,string虽然是引用类型,但是其实参不会因为形参值的变化而变化。形参重新赋值以后,有string源码可以知道等于新建了一个string。
因为string是程序中最常用的声明变量之一,下面可以看一下其常用的方法。我们直接用代码演示吧。
public class Test {public static void main(String[] args) {String a="abcdefg";System.out.println(a.length());//7 length() 返回字符串的长度System.out.println(a.concat("123"));// abcdefg123 System.out.println(a);// abcdefg concat("123") 连接两个字符串,同时返回两个字符串连接后的值,但是其原来的值不变System.out.println(a.contains("m"));//false contains() 查看字符串中是否包含字符或字符串,如果包含返回true,反之返回falseSystem.out.println(a.isEmpty());//false isEmpty()判断字符串是否为空,如果为空返回true,反之返回falseString b="bcdefgabcd";System.out.println(b.indexOf("a"));//6 indexOf() 如果字符串含有这个字符或字符串返回第一个所在的位置,如果没有返回-1System.out.println(b.indexOf("b",5));//7 indexOf(str, index) 如果字符串从index之后含有这个字符或字符串返回第一个所在的位置,如果没有返回-1System.out.println(b.lastIndexOf("b1"));//7 lastIndexOf()字符串包含某字符和字符串的最后一个所在的位置,如果没有返回-1System.out.println(b.lastIndexOf("b",5));//0 lastIndexOf(str,index)字符串在index自强包含某字符和字符串的最后一个所在的位置,如果没有返回-1System.out.println(b.startsWith("a"));//false startsWith() 判断字符串是否以某字符串开头,如果是就返回true ,反之返回falseSystem.out.println(b.startsWith("a",6));//true startsWith(str.index) 判断字符串在index处是否以某字符串开头,如果是就返回true ,反之返回falseSystem.out.println(b.endsWith("cd"));//true endsWith() 字符串是否以某字符或者字符串结尾,如果是返回true,如果不是返回false。System.out.println(b.substring(4));// fgabcd substring()字符串冲index出开始街区字符串到最后并返回System.out.println(b.substring(4,8));// fgab substring(start,end)字符串冲index出开始街区字符串到end,并返回。(一般返回都是包含前而不包含后,所以substring(start,end)也不例外)System.out.println(b.equals("bcdefgabcd"));//true equals() 对比两个字符串的内容是否一样。System.out.println(b.charAt(0));//b charAt(index) 返回在字符串index的字符。System.out.println(Arrays.toString(b.toCharArray()));//[b, c, d, e, f, g, a, b, c, d] toCharArray()将此字符串转换为一个新的字符数组。System.out.println(b.replace("cd", "kk"));//bkkefgabkk replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
// replaceAll(String regex, String replacement) 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。System.out.println(Arrays.toString(b.split("c")));// [b, defgab, d] split() 根据给定正则表达式的匹配拆分此字符串。 System.out.println(Arrays.toString(b.split("c",2)));// [b, defgabcd] split(String regex, int limit) 根据给定正则表达式的匹配拆分此字符串。 但是数组长度只能为limit,超过的就不再拆分System.out.println(Arrays.toString(b.getBytes())); // [98, 99, 100, 101, 102, 103, 97, 98, 99, 100] 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。try {System.out.println(Arrays.toString(b.getBytes("gbk")));//[98, 99, 100, 101, 102, 103, 97, 98, 99, 100] getBytes() 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 上面两个的编码可以通过 String(byte[] bytes, String charsetName) 进行解码。String c=" df df ";System.out.println(c.trim());//df df trim() 只会去掉 收尾两处的空格,中间的不会去掉并返回字符串System.out.println(String.valueOf(2345));//2345 valueOf(boolean b) 其他类型转成string}}
String 是不可变的,自然也有可变的字符串stringbuffer和stringbuilder。
StringBuffer和 StringBuilder
两个是可变的字符串,但是两者又有所区别
StringBuffer:线程安全,效率相对于StringBuilder低。
StringBuilder:线程不安全,效率相对于StringBuffer高。
* @author Arthur van Hoff* @see java.lang.StringBuilder* @see java.lang.String* @since JDK1.0*/public final class StringBufferextends AbstractStringBuilderimplements java.io.Serializable, CharSequence* @author Michael McCloskey* @see java.lang.StringBuffer* @see java.lang.String* @since 1.5*/
public final class StringBuilderextends AbstractStringBuilderimplements java.io.Serializable, CharSequence
通过源码可以看出stringbuffer和stringbuilder 两者的父类以及实现的接口完全一样。以及stringbuilder是1.5版本出现的,而stringbuffer是1.0版本出现的。
因为父类和接口一样,所以两者的方法几乎一样。所以我们当然既然一个安全,一个不安全还是有区别的,这个我们在看源码的时候在详解。
所以我们先看stringbuffer的源码,然后看其与string的实现区别。
/*** Constructs a string buffer with no characters in it and an* initial capacity of 16 characters.*/public StringBuffer() {super(16);}//可见其默认实例调用了父类的构造方法/*** Creates an AbstractStringBuilder of the specified capacity.*/AbstractStringBuilder(int capacity) {value = new char[capacity];}
//这个地方可以看出其默认会创建一个长度为16的字符数组
然后看起添加字符串的方法,查看其在源码中是如何实现的。
@Overridepublic synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);//调用父类的方法return this;//返回其本身,不像string返回一个新建的对象。}
////父类中的方法/*** Appends the specified string to this character sequence.* <p>* The characters of the {@code String} argument are appended, in* order, increasing the length of this sequence by the length of the* argument. If {@code str} is {@code null}, then the four* characters {@code "null"} are appended.* <p>* Let <i>n</i> be the length of this character sequence just prior to* execution of the {@code append} method. Then the character at* index <i>k</i> in the new character sequence is equal to the character* at index <i>k</i> in the old character sequence, if <i>k</i> is less* than <i>n</i>; otherwise, it is equal to the character at index* <i>k-n</i> in the argument {@code str}.** @param str a string.* @return a reference to this object.*/public AbstractStringBuilder append(String str) {if (str == null)return appendNull(); //如果为空,调用添加空的方法。int len = str.length();//要添加的字符串长度ensureCapacityInternal(count + len); //判断先在字符数组长度是否够,不够就改变其长度str.getChars(0, len, value, count);count += len;return this;}//看代码需要知道两个参数的意思如下/*** The value is used for character storage.*/char[] value;//存储字符数组/*** The count is the number of characters used.*/int count;//字符数组中存的字符个数
下面是判断修改字符数组的长度的代码
/*** For positive values of {@code minimumCapacity}, this method* behaves like {@code ensureCapacity}, however it is never* synchronized.* If {@code minimumCapacity} is non positive due to numeric* overflow, this method throws {@code OutOfMemoryError}.*/private void ensureCapacityInternal(int minimumCapacity) {// overflow-conscious codeif (minimumCapacity - value.length > 0) {//minimumCapacity 为字符数组中包含字符个数加上要添加的字符串的长度。 然后判断minimumCapacity是否大于存储字符的字符数组长度。value = Arrays.copyOf(value,newCapacity(minimumCapacity));}}
rrays.copyOf() 我们知道复制value字符串。然后给一个新的长度,然后数据不够补充默认值。所以这个看起变长需要看 newCapacity(minimumCapacity)
private int newCapacity(int minCapacity) {// overflow-conscious codeint newCapacity = (value.length << 1) + 2;//新的数组长度是原来数组长度的2倍+2if (newCapacity - minCapacity < 0) {newCapacity = minCapacity; //如果原来字符个数+新增字符串长度大于2倍元字符数组的,则返回长度为两者之和}return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)//三目运算如果新长度为零或者大于字符数组最大长度那么就调用hugeCapacity(minCapacity)否则返回新长度? hugeCapacity(minCapacity): newCapacity;}/*** The maximum size of array to allocate (unless necessary).* Some VMs reserve some header words in an array.* Attempts to allocate larger arrays may result in* OutOfMemoryError: Requested array size exceeds VM limit*/private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;private int hugeCapacity(int minCapacity) {if (Integer.MAX_VALUE - minCapacity < 0) { // 如果新长度大于 Integer.MAX_VALUE - 8;就会抛出异常throw new OutOfMemoryError();}return (minCapacity > MAX_ARRAY_SIZE)? minCapacity : MAX_ARRAY_SIZE;}
通过上面的源码我们可以看出stringbuffer也是以数组作为存储的,以及其改变长度后依然返回自己,return this。而string返回的是 new string(新字符串数组)。
string实例后只要改变后,其引用的数组就会改变(如果长度不变的话,其引用数组不会改变,只是会改变其存储的数据。),然后引用的数组就会创建一个新的对象。
stringbuffer实例后改变,其引用的字符数组也会改变(如果长度不变的话,其引用数组不会改变,只是会改变其存储的数组。),然后实例引用新的数组。
因此可见其string是比较占用内存,以及效率低于stringbuffer的。因为string每次都需要创建新的对象。
明白其原理,所以现在看一下其构造的方法。
StringBuffer() 构造一个其中不带字符的字符串缓冲区,初始容量为 16 个字符。
StringBuffer(CharSequence seq) 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。
StringBuffer(int capacity) 构造一个不带字符,但具有指定初始容量的字符串缓冲区。
StringBuffer(String str) 构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容
一般创建的时候使用默认实例化,但是如果知道要创建的长度,就直接带有长度创建,这样可以节省内存。
常用方法
public class Test {public static void main(String[] args) {StringBuffer strbuf= new StringBuffer();//构造方法。strbuf.append("abc");System.out.println(strbuf.length());//3 length() stringbuffer的长度System.out.println(strbuf.capacity());//16 capacity() 得到stringbuffer生成的char数组的容量System.out.println(strbuf.append(12));//abc12 append() 可以添加字符串以及数字浮点类型,返回添加后的StringBufferSystem.out.println(strbuf);//abc12System.out.println(strbuf.insert(3,"A"));//abcA12 insert(offset,obk) 可以添加数据在offset处,返回添加后的StringBufferchar[] str= {'H','M','G'};System.out.println(strbuf.insert(0, str, 1, 2));//MGabcA12 insert(index, str, offset, len) 插入一个char数组,从char数组第offset开始,长度为len。返回添加后的StringBufferSystem.out.println(strbuf.deleteCharAt(2)); //MGbcA12 deleteCharAt(index) 删除index出的字符,返回stringbufferSystem.out.println(strbuf);// MGbcA12System.out.println(strbuf.delete(2, 70));// MG delete(start, end) 删除这个范围内的字符,如果end大于stringbuffer的长度,不会报错在源码中会变成stringbuffer的长度,返回stringbufferSystem.out.println(strbuf);// MG strbuf= new StringBuffer("abcdab");//重新实例化一个对象System.out.println(strbuf.indexOf("b"));//1 indexOf(str) 字符或字符串在stringbuffer中的第一位置,如果没有返回-1System.out.println(strbuf.indexOf("b",3));//5 indexOf(str,index) 字符或字符串在stringbuffer中从index开始的第一位置,如果没有返回-1System.out.println(strbuf.lastIndexOf("b"));//5 lastIndexOf(str) 字符或字符串在stringbuffer中的最后的位置,如果没有返回-1System.out.println(strbuf.lastIndexOf("b", 1));//1 lastIndexOf(str,index) 字符或字符串在stringbuffer中的从开始到index中最后的位置(包含index),如果没有返回-1System.out.println(strbuf.reverse());// badcba reverse() 将strbuffer中的字符串反转System.out.println(strbuf);//badcba 这可以看出reverse()反转的是本体System.out.println(strbuf.replace(0, 3, "ABCD"));//ABCDcba strbuf.replace(start, end, str)将str替换stringbufff中的start到end的字符System.out.println(strbuf);//ABCDcbastrbuf.setCharAt(0, 'G');//setCharAt (index,chr) chr替换了stringbuffer中index位置的字符System.out.println(strbuf);// GBCDcbaSystem.out.println(strbuf.substring(3));//Dcba substring(index) 截取stringbuffer中字符从index开始,然后返回截出的字符串System.out.println(strbuf);// GBCDcbaSystem.out.println(strbuf.substring(2, 4));//CD substring(star,end) 截取stringbuffer中字符从star开始到end,然后返回截出的字符串}}
stringbuilder的方法和stringbuffer中的方法使用常用的方法一样,所以不再单独展示。既然一样那么其安全和不安全的原因是什么呢?
//stringbuffer 中的方法@Overridepublic synchronized StringBuffer append(int i) {toStringCache = null; //super.append(i);return this;}//stringbuilder 中的方法@Overridepublic StringBuilder append(int i) {super.append(i);return this;}
对append方法可以看出两点
-
stringbuffer安全的原因是其方法由synchronized修饰。而stringbuilder的方法却没有synchronized修饰
-
可以看出,StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以,缓存冲这也是对 StringBuffer 的一个优化吧,不过 StringBuffer 的这个toString 方法仍然是同步的。
既然我们前面一直说string与stringbuffer,stringbuilder的区别,也说了其运行速度,下面我们做一个代码测试即可。
public class Test {public static void main(String[] args) {String str=new String();long start =System.currentTimeMillis();for(int i=0;i<10000;i++) {str=str+i;}long end=System.currentTimeMillis();System.out.println("String 的时间"+(end-start));StringBuffer strbuf=new StringBuffer();start =System.currentTimeMillis();for(int i=0;i<10000;i++) {strbuf.append(i);}end=System.currentTimeMillis();System.out.println("StringBuffer 的时间"+(end-start));StringBuilder strbud=new StringBuilder();start =System.currentTimeMillis();for(int i=0;i<10000;i++) {strbud.append(i);}end=System.currentTimeMillis();System.out.println("StringBuilder 的时间"+(end-start));}}
//输出
String 的时间278
StringBuffer 的时间2
StringBuilder 的时间1
可以看出运行速度StringBuilder>StringBuffer>String.
区别
- string 是对象而非基本数据,其是final类不可以继承,而其创建的时候就不可变。对于其修改都是新建一个string值。
- StringBuilder,StringBuffer 也是对象,其值可变。其也是final也不可以继承。而两者操作字符串运行速度都比string快
- StringBuilder线程不安全,如果单例模式的话可以用次类来操作字符串增加运行速度。不可用于多线程。
- StringBuffer 线程安全多线程下操作字符串可以用StringBuffer 。
这篇关于java基础 浅析string,StringBuffer与StringBuilder的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!