Java—String类型及编译器优化
我们先来看一道题目:
public class StringTest{
public static void main(String[] args){
String str = "xiyou" + "3g" + "backend";
String str2 = "xiyou3gbackend";
String str3 = str + "test";
String str4 = "a" + "b";
System.out.println(str2 == str);
System.out.println(str3 == "xiyou3gbackendtest");
System.out.println(str4 == "ab");
}
}
这段代码的运行结果为:
这其中涉及到好几个知识点,接下来我们先来介绍String的一些特性
1.String
Java中的String类型并不是基本类型,但是有时候和基本类型差不多,如String b = “tao”;可以对变量直接赋值,而不用new一个对象(当然也可以new)
Java中的变量和基本类型的值存放于栈,而new出来的对象本身存放于堆内存,指向对象的引用还是放在栈内存。例如如下的代码
int i = 1;
String s = new String("Hello World");
此时变量i,常量1,和字符串对象的引用s放在栈内存,而字符串对象 “Hello World”放在堆内存
- 栈内存的一个特点是:数据共享,这样设计是为了减小内存消耗,在定义了i=1之后,如果再定义一个j=1,此时将j放入栈内存,然后查找占内存中是否有1,如果有则j指向1.如果再给j赋值2,则再栈内存中查找是否有2,如果没有就在栈内存放一个2,然后j指向2。也就是说如果常量在栈内存中,就将变量指向该常量,如果没有就在栈内存中增加一个该常量,并将变量指向该常量
这种基本类型之间比较大小和我们逻辑上判断大小是一致的。如定义i和j都是赋值1,则i==j的结果为true。==用于判断两个变量指向的地址是否一样。既然变量在栈区是数据共享的,那么他们所指的也就是同一个常量,所以地址也是相同的
- 对于直接赋值的字符串常量(例如:String s = “Hello World”;中的 “Hello World”)也是放在栈区,而new出来的字符串对象是存放在堆内存中。所以如果定义:
String str = "Hello World";
String str2 = "Hello World";
这时 str == str2 就是true。但如果换成:
String str = new String("Hello World");
String str2 = new String("Hello World");
str == str2 就是false了
2.StirngBuilder与StringBuffer
StringBuilder与StringBuffer的用法基本相似,但StringBuffer是线程安全的(所有字符操作的方法被synchronized关键字修饰)。
很多人应该都知道:String是不可变的,我们进行下面的测试
public class StringBuilderTest {
public static void main(String[] args) {
String str = "0";
for (int i = 1; i < 10; i++){
str += i;
}
System.out.println(str);
}
}
运行结果:
String类确实是不可变的,但在上面的代码里,每次循环执行的str += i; 其实都是新创建了一个String对象,然后再将其引用赋值给str,这样就大大浪费了空间。于是我们出现了StringBuilder
使用StringBuilder完成上述代码:
StringBuilder sb = new StringBuilder("0");
for (int i = 1; i < 10; i++){
sb.append(i);
}
System.out.println(sb.toString());
输出结果是一样的,但是效率却比上面的代码高很多,因为整个追加过程始终是对一个对象进行操作
编译器对String的优化
从上一段代码中我们可以看到,由于String的不可变性,对其进行操作的效率会大大降低,但对 “+”操作符,编译器也对其进行了优化:
例如执行下面这句:
String temp = "ABC" + 200 + 'D'
等同于:
String temp = new StringBuilder().append("ABC").append(200).append('D').toString();
这样就免于中间创建对象的浪费了
当进行 “+”操作符的 几个对象都是字符串常量
String temp = "ABCD" + "E" + "fg";
编译器也会自动优化为:
String temp = "ABCDEfg";