String
字符串操作是我们最常用到的操作,通常我们使用的都是String。
String的底层实现是一个 Char 类型的数组。
String作为最基础的一个类,他自身与Array有些许的相似之处。
String的特点
首先我们要明确一点,String是固定长度的,当他在创建的那一刻起,他就不可变了;
如果想要在原有字符串的基础上进行修改,那么不好意思,你只能创建一个新的字符串了。
String str1 = " hello ";
str1 = str1 + "world";
按照我们基本数据类型的运算逻辑来说,我们会认为 str1 直接就是在原本 str1 的基础上加了 “world”部分,但是这种其实是错误的
真实的过程是,先创建了一个“world” ,随后创建一个临时对象,通过临时对象对str1 和 “world”进行拼接,拼接好后再传给str1。
考点1:String对象的创建
一般我们在比试中经常碰见这类题,考察的是对 String 对象和 JVM 内存划分的知识。
// 第一种:直接赋一个字面量
String str1 = "ABCD";
// 第二种:通过构造器创建
String str2 = new String("ABCD");
方法一最多创建一个String对象,最少不创建String对象。
如果常量池中,存在”ABCD”,那么str1直接引用,此时不创建String对象;
如果在常量池中未找到,则会先在常量池先创建”ABCD”内存空间,再引用。
方法二最多创建两个String对象,至少创建一个String对象。
new关键字绝对会在堆空间创建一块新的内存区域,所以至少创建一个String对象。
如果不存在“ABCD”的话,还要先去创建“ABCD”。
考点2:String对象的空值
String str1 = null;
String str2 = "";
str1的方法表示引用为空的空值,即压根就没有初始化,没有分配对应的空间;
str2的方法表示内容为空的空值,即进行了初始化,也分配了内存空间,只不过没往里面放东西。
考点3: == 和 equals()的区别
这个也是比较常考的一个问题
String str1 = "ABCD";
String str2 = new String("ABCD");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
猜猜答案是什么
false
true
同样都是 ABCD 为什么会出现 false 呢?
首先我们知道,String对象保存的都是对应的内存存放地址;
下面,我们需要来了解一下 == 和 equals()的操作实现;
对于 == 而言,其实是要分为两类的:
- 对于基本数据类型,byte,short,char,int,long,float,double,boolean。他们之间的比较,应用双等号(==),比较的是他们的值。
- 对于引用数据类型,当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址(确切的说,是堆内存地址)。
而 equals()虽然他的默认情况下比较的也是引用地址,但是作为 Object类 下的方法,他可以被重写。很显然 在String类中,他就被重写了。我们看一下他被重写后的代码:
1 public boolean equals(Object anObject) {
2 if (this == anObject) {
3 return true;
4 }
5 if (anObject instanceof String) {
6 String anotherString = (String)anObject;
7 int n = value.length;
8 if (n == anotherString.value.length) {
9 char v1[] = value;
10 char v2[] = anotherString.value;
11 int i = 0;
12 while (n-- != 0) {
13 if (v1[i] != v2[i])
14 return false;
15 i++;
16 }
17 return true;
18 }
19 }
20 return false;
21 }
StringBuffer的特点
我们需要知道的是 :
- StringBuffer 是 长度可变的
- StringBuffer 是 线程安全的
StringBuilder的特点
我们需要知道的是 :
- StringBuffer 是 长度可变的
- StringBuffer 是 非线程安全的
考点4:StringBuffer 和 StringBuilder的区别
通常情况下,这两者都是进行对比比较的;
两者最大的区别 主要体现在 线程安全性上,以及线程安全所引入的效率问题;
- StringBuffer 是 线程安全的,所以效率偏低
- StringBuffer 是 非线程安全的,所以效率高
考点5: append() 和 + 的区别
我们在学习Java字符串拼接的时候,或多或少都会被传授一些“尽量少用 + ,多用 append()”的观点,但其实 + 并不是一无是处,在某些方面, + 的性能反而更高一些。
首先,我们来了解一下二者的实现机制:
+拼接 实际上是使用了StringBuilder,所以在循环操作时的效率很低;因为每次拼接都要创建一个新的临时对象。
对于 str = str + “a”,编译器会处理为new StringBuilder().append(str).append(“a”);
append()拼接 使用的是StringBuffer,在循环操作的时候效率较高,但是安全性不能保证。
冷知识:这里是为数不多的 线程安全 反而比 非线程安全 效率高
知识点:+拼接 不管一次性+几个字符串,只要+拼接全部在一条语句中,就只会new一次,因此对于多个数据的拼接, +拼接有优势
注意点:+拼接 和 append()不能混用 不然会报错
小结
对于少量的数据用 String;
对于多线程操作字符串缓冲区下操作大量数据用 StringBuffer;
对于单线程操作字符串缓冲区下操作大量数据用 StringBuilder。