好久之前在写文件上传的时候使用了这样的一段代码
/**
* @param rootUrlStr:保存的路径的文件夹路径 假设就是 D:\save
* @param fileUriStr:需要保存的文件的具体路径
* @about 这是一段精简的代码
* @return 存储文件地址
*/
public String uploadUri1(String rootUrlStr,String fileUriStr) {
//下面我将故意使用很复杂的拼接
//获得文件名,故意不使用UUID.randomUUID()
String fileNameStr=fileUriStr.substring(fileUriStr.lastIndexOf("\\"));
String fileRealUriStr=rootUrlStr+"\\";
fileRealUriStr+="uploadFile\\";
fileRealUriStr+=fileNameStr;//把根路径和文件名拼接在一起
return fileRealUriStr;
}
这种写法很明显的一点就是会生成很多的中间对象。
于是我后来改成了这样
public String uploadUri2(String rootUrlStr,String fileUriStr) {
//下面我将故意使用很复杂的拼接
//获得文件名,故意不使用UUID.randomUUID()
StringBuffer fileRealUriStr=new StringBuffer(rootUrlStr);
String fileNameStr=fileUriStr.substring(fileUriStr.lastIndexOf("\\"));
fileRealUriStr.append("uploadFile\\").append(fileNameStr);
return fileRealUriStr.toString();
}
使用StringBuffer去避免生成太多临时字符串。
当然有更好的方法,那就是使用Paths
public String uploadUri3(String rootUrlStr,String fileUriStr) {
//下面使用Path
//获得文件名,故意不使用UUID.randomUUID()
String fileNameStr=fileUriStr.substring(fileUriStr.lastIndexOf("\\"));
Path path=Paths.get(rootUrlStr,"uploadFile",fileNameStr);
return path.toString();
}
Paths会自动补全”\”,使得代码比较直观好看。他不是今天的主角,所以我就不展开讲了。
//测试代码
public static void main(String[] args) {
String string1=new MyText().uploadUri1("D:save","I:\\JAVA\\java.txt");
String string2=new MyText().uploadUri2("D:save","I:\\JAVA\\java.txt");
String string3=new MyText().uploadUri3("D:save","I:\\JAVA\\java.txt");
System.out.println(string1);
System.out.println(string2);
System.out.println(string3);
}
输出结果
D:save\uploadFile\java.txt
D:save\uploadFile\java.txt
D:save\uploadFile\java.txt
以上是可以跳过不看的内容
String:
String类使用字符串数组保存字符串,因为使用final修饰符,所以可知道String对象是不可变的。所谓的不可变其实是指每次修改都不是在原字符上修改,而是新建了一个新的字符数组,而且如果这个对象没有被引用,那这个对象就是没有用的。
StringBuffer:
继承了AbstractStringBuilder,而且和String不一样的是
String使用的是数组声明为 private final char value[];
StringBuffer的声明是 private transient char[] toStringCache;
transient :临时的
当StringBuffer进行修改时,(比如说删除、更新字符)是在原来的实例对象进行修改的。但是如果是拼接操作时,分成两种情况,1、空间足够,直接拼接;2、空间不够,新建了一个字符串数组,再搬家过去。
StringBuilder:
StringBuffer和StringBuffer都是继承了AbstractStringBuilder,区别只是方法签名上是否有synchronized。因此,StringBuffer是线程安全的,而StringBuilder是线程不安全的。
![]()
HashTable是线程安全的,很多方法都是synchronized方法,而HashMap不是线程安全的,但其在单线程程序中的性能比HashTable要高。StringBuffer和StringBuilder类的区别也是如此,他们的原理和操作基本相同,区别在于StringBufferd支持并发操作,线性安全的,适 合多线程中使用。StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用。新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。
package javaTest;
/**
*@author CHEN
*@time 2016年4月15日
*@about 测试String StringBuffer StringBuilder的性能
*/
public class StringBuilderTester {
private static final String base = " base string. ";
private static final int count = 200000;
public static void stringTest() {
long begin, end;
begin = System.currentTimeMillis();
String test = new String(base);
for (int i = 0; i < count ; i++) {
test = test + " add ";
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used String. ");
}
public static void stringBufferTest() {
long begin, end;
begin = System.currentTimeMillis();
StringBuffer test = new StringBuffer(base);
for (int i = 0; i < count; i++) {
test = test.append(" add ");
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used StringBuffer. ");
}
public static void stringBuilderTest() {
long begin, end;
begin = System.currentTimeMillis();
StringBuilder test = new StringBuilder(base);
for (int i = 0; i < count; i++) {
test = test.append(" add ");
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used StringBuilder. ");
}
public static void main(String[] args) {
stringTest();
stringBufferTest();
stringBuilderTest();
}
}
运行结果:
113730 millis has elapsed when used String.
13 millis has elapsed when used StringBuffer.
9 millis has elapsed when used StringBuilder.
建议在使用的时候,把count的值乘以10,但stringTest中count缩小100倍,有利于比较StringBuffer和StringBuilder。
package hello;
/**
* @about 对StringBuffer StringBuilder String的线程测试
* @author CHEN
* @time 2016年4月15日
*/
public class Test {
public static void main(String[] args) {
StringBuffer sbf = new StringBuffer();
StringBuilder sb = new StringBuilder();
String s=new String();
//10个线程
for (int i = 0; i < 10; i++) {
new Thread(new TestThread(sbf, sb, s)).start();
}
}
}
class TestThread implements Runnable {
StringBuffer sbf;
StringBuilder sb;
String s;
TestThread(StringBuffer sbf, StringBuilder sb,String s) {
this.sb = sb;
this.sbf = sbf;
this.s=s;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb.append("1");
sbf.append("1");
s+="1";
System.out.println(sb.length() + "/" + sbf.length()+"/"+s.length());
}
}
}
运行的结果是:
sb很少次能达到1000次
sbf基本都达到1000次
而s 则是100次
public class Buffer {
public static void main(String[] args) {
String s1 = "aaaaa";
String s2 = "bbbbb";
String r = null;
int i = 3694;
r = s1 + i + s2;
for(int j=0;i<10;j++){
r+="23124";
}
}
}
JVM将会翻译成
偷偷的调用了StringBuilder,为什么呢?当然是因为快啊。但是别以为JVM帮你做了这部分工作,你就可以滥用String了。String转成StringBuilder每次都会建立很多的对象的,所以呢,作为一个好的码农,第一件事就是为JVM多考虑。
就这样我们认识了String、StringBuffer、StringBuilder三兄弟。
大哥String,虽说顽固不变,但是通用性好,用途广泛,占用内存小,大众都喜欢使用它。可是呢,其实大哥String的工作经常是交给小弟StringBuilder做的。
二哥StringBuffer,比大哥通达,改变的时候就会改变。可是,别人让他办事,他每次就答应办一件,每次一件,所以比较可靠安全。
小弟StringBuilder,比较活泼,有时候同时办好几件事,就把事给办坏了。可是呢,小弟他的工作效率是最快的。