最近一直看到很多关于string的java面试题,下面通过代码进行测试并说明,针对不同角度,写了一下几个测试方法,供参考。另外从网上看到一个例子,用于比较string和stringbuffer的效率,也直接拿过来了。
/**
* 常量池(constant pool)指的是在编译期被确定,
* 并被保存在已编译的.class文件中的一些数据.它包括了关于类、方法、接口等中的常量,也包括字符串常量.
* 我们要知道Java会确保一个字符串常量只有一个拷贝.
* @author Administrator
*
*/
public class StringTest {
public static void main(String[] args) {
StringTest t = new StringTest();
t.test1();
// t.test2();
/*t.test3();
t.test4();
t.test5();*/
// t.test6();
}
/*栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。
但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
另外,栈数据可以共享。
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,
Java的垃圾收集器会自动收走这些不再使用的数据。
但缺点是,由于要在运行时动态分配内存,存取速度较慢。
例子:
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理int a = 3;
首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,
没找到,就开辟一个存放3这个字面值的地址,
然后将a指向3的地址。接着处理int b = 3;
在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。
这样,就出现了a与b同时均指向3的情况。
*/
/*我们在使用诸如String str = "abc";的格式定义类时,
总是想当然地认为,我们创建了String类的对象str。担心陷阱!
对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。
至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,
除非你通过new()方法来显要地创建一个新的对象。
因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,
这个对象引用变量指向了某个值为"abc"的String类。
清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。*/
/**
* s0和s1中的"kvill"都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;
* 而"kv"和"ill"也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,
* 所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中"kvill"的一个引用.
*/
public void test1(){
String s0="kvill";
String s1="kvill";
//有3个字符串常量,首先"kv"和"ill"生成了"kvill"存在内存中,
//然后"kvill"又和" " 生成 "kvill "存在内存中,
//String的"不可变"产生了很多临时变量,这也就是为什么建议用StringBuffer的原因
String s2="kv" + "ill";
//实际上在编译的时候,s3是一个对象,被分别分配了一个地址,
// s3 = s3 + "ill"执行时,原来的s3被释放,然后重新分配。s3原本指向一 String object instance ("kv"),
//s3 = s3 + "ill"会造出另一新的 String object instance ("kvill"), 然后s3再指向这新的 String instance。
String s3 = "kv";
s3 = s3 + "ill";
String s4 = s0;
String s5 = s3;
System.out.println( s0==s1 );//是true
System.out.println( s0==s2 );//是true
System.out.println( s0==s3 );//是false
System.out.println( s0==s4 );//是true
System.out.println( s0==s5 );//是false
}
/**
* s0还是常量池中"kvill"的应用,s1因为无法在编译期确定,所以是运行时创建的新对象"kvill"的引用,
* s2因为有后半部分new String("ill")所以也无法在编译期确定,所以也是一个新创建对象"kvill"的应用;
*/
public void test2(){
String s0="kvill";
String s1=new String("kvill");
String s2="kv" + new String("ill");
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
System.out.println("hashcode:");
System.out.println("s0:"+s0.hashCode());
System.out.println("s1:"+s1.hashCode());
System.out.println("s2:"+s2.hashCode());
System.out.println(s0.getClass());
}
/*String:
是对象不是原始类型.
为不可变对象,一旦被创建,就不能修改它的值.
对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.
String 是final类,即不能被继承.
StringBuffer:
是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象
它只能通过构造函数来建立,
StringBuffer sb = new StringBuffer();
note:不能通过付值符号对他进行付值.
sb = "welcome to here!";//error
对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer
中付值的时候可以通过它的append方法.
sb.append("hello");
总结:
(1)由于String 对象是不可变对象,每次操作Sting 都会重新建立新的对象来保存新的值.
这样原来的对象就没用了,就要被垃圾回收.这也是要影响性能的.
(2)如果在程序中需要对字符串进行频繁的修改连接操作的话.使用StringBuffer性能会更高
*/
public void test3(){
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart1 = System.currentTimeMillis();
String str = "";
for (int i = 0; i < times; i++) {
str += tempstr;
}
long lend1 = System.currentTimeMillis();
long time = (lend1 - lstart1);
System.out.println(time);
}
public void test4(){
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart2 = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < times; i++) {
sb.append(tempstr);
}
long lend2 = System.currentTimeMillis();
long time2 = (lend2 - lstart2);
System.out.println(time2);
}
public void test5(){
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart2 = System.currentTimeMillis();
String str = "";
for (int i = 0; i < times; i++) {
StringBuffer sb = new StringBuffer(str);
sb.append(tempstr);
str = sb.toString();
}
long lend2 = System.currentTimeMillis();
long time2 = (lend2 - lstart2);
System.out.println(time2);
}
/**
* StringBuffer类中没有重新定义equals这个方法,因此这个方法就来自Object类,
而Object类中的equals方法是用来比较地址的,所以等于false.
String类中重新定义了equals这个方法,而且比较的是值,而不是地址。所以会是
true。
*/
public void test6(){
StringBuffer s1 = new StringBuffer("a");
StringBuffer s2 = new StringBuffer("a");
String s3 = new String("a");
String s4 = new String("a");
System.out.println(s1.equals(s2));//是false
System.out.println(s3.equals(s4));//是true
}
}