String作为java最常用的类,被广大程序员们蹂躏已久,让我们又爱又恨,应该如何正确的使用它呢,老姜带你一起来揭开它神秘的面纱。
一、String类定义时被冠上了final的标识符,决定了它终生不可变的特质。
final:修饰类时,类不可被继承。
设计初衷:类不需要有子类,类的实现细节不允许改变,并且确信这个类不会被扩展;编译器会对final进行优化,提高执行效率。
二、String类的任何操作都会生成新的字符串对象。
前面我们讲过,String是final的,既然不可改变,自然会生成新的对象。我们看下面一段代码:
String a = "abc123";
a.subString(3);
system.out.println(a);
以上代码会输出什么呢?答案是abc123,因为a从未被改变,a.subString(3)的结果并未重新赋值给变量a。
三、String常量池
相信有过面试经验的同学们,肯定遇到过类似这样的问题:String a = new String("a");这段代码生成了几个对象?
我的回答是1个或者2个。
1:如果此行代码之前定义过“a”这个变量,那么只会生成new String("a")这么一个对象。
2:相反,之前未定义过,就会存在“a”以及new String("a")这两个对象。
ps:有些同学要问了,那么变量a呢?它不是对象吗?
a只是一个变量,它存放对象new String("a")的引用,它不能称之为对象,仅仅是一个别名而已。
我们知道,在函数中定义的一些基本类型的变量和对象的引用变量在函数的栈内存中分配,new创建的对象和数组存放在堆内存中。
除此之外还有一个常量池存在,它是在编译期被确定的,被保存在class文件中,String常量就包含其中。例如,
String s = “abcd”;
String s2 = “abcd”;
此处只会产生一个对象“abcd”,且存放在常量池中,并且s1==s2 为true。
String s3 = "ab" + "cd";
s3 == s?答案是true,因为编译器会对直接字符串常量的“+”运算优化为s3 =“abcd”且被存放至常量池中。
四、字符串的拼接
1)少量字符串相加,直接用“+”,如:String a = a1+a2+a3;
2)大量字符串拼接,用StringBuilder,如:
StringBuilder sb = new StringBuilder();
for(int i=0;i<100;i++){
sb.append(i);
}
String s = sb.toString();
StringBuilder内部维护了一个初始值为16的char型数组,容积不足时2倍扩充。所以如果能够预测到你最终拼接的字符串大小,在初始化时不妨这样写:
StringBuilder sb = new StringBuilder(100);避免空间不足,反复开辟内存,影响效率。
ps:String作为常量,每次“+”都会生成新的对象,反复开辟内存,效率自然下降。
3)考虑到线程安全的情况,就要用到StringBuffer了,它的内部实现与StringBuilder类似,不过实现了数据同步,效率要低于StringBuilder。
五、字符串的比较
1、为空比较:
if(null == s || "".equals(s)){...}
把null==s放在左面以及""放在equals左边,因为s有可能为null,直接造成程序空指针异常:java.lang.NullPointerException。
ps:另一种写法,if(null==s||s.length()==0){...},话说这种效率要高于equals。
2.两个对象进行比较
大多数情况下,我们关心的是字符串的值,而非内存地址,用equals进行比较。
在运行期,使用"=="进行字符串的比较,结果往往是不会让你满意的,有时会让我们踩在坑里好久好久,不停的问自己,为什么这段逻辑始终不正确呢?因为永远执行不到。
今天就写到这里,有没有能触动到你的地方呢?
关注老姜谈技术,微信号:helojava,或者扫描下面二维码。
每日一帖,技术鸡汤。