观察下列代码,分析结果()
String s1 = "coder"; String s2 = "coder"; String s3 = "coder" + s2; String s4 = "coder" + "coder"; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s4 == "codercoder");
观察下列代码,分析结果()
String s1 = "coder"; String s2 = "coder"; String s3 = "coder" + s2; String s4 = "coder" + "coder"; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s4 == "codercoder");
false;false; true;
false;true; true;
false;false; false;
true;false; true;
Java内存结构包含以下部分:
1、栈区:由编译器自动分配释放,具体方法执行结束后,系统自动释放JVM内存资源。
其作用有保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用。也可以用来保存加载方法时的帧。
2、堆区:一般由程序员分配释放,JVM不定时查看这个对象,如果没有引用指向这个对象就回收。
其作用为用来存放动态产生的数据,包括new出来的实例,字符数组等。
同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法。
3、数据区:用来存放static定义的静态成员。
public static void main(String args[]) {
// 字符串常量,分配在常量池中,编译器会对其进行优化, Interned table
// 即当一个字符串已经存在时,不再重复创建一个相同的对象,而是直接将s2也指向"hello".
String s1 = "hello";
String s2 = "hello";
// new出来的对象,分配在heap中.s3与s4虽然它们指向的字符串内容是相同的,但是是两个不同的对象.
// 因此==进行比较时,其所存的引用是不同的,故不会相等
String s3 = new String("world");
String s4 = new String("world");
System.out.println(s1 == s2); // true
System.out.println(s3 == s4); // false
System.out.println(s3.equals(s4)); // true
// String中equals方法已经被重写过,比较的是内容是否相等.
} String s1 = "coder"; String s2 = "coder"; String s3 = "coder" + s2; String s4 = "coder" + "coder"; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s4 == "codercoder");
// 字符串常量,分配在常量池中,编译器会对其进行优化, Interned table // 即当一个字符串已经存在时,不再重复创建一个相同的对象,而是直接将s2也指向"hello". String s1 = "hello"; String s2 = "hello";但是若是变量定义赋值,或者new,就会在内存中新找一段地址。
System.out.println(s3 == s4); //false System.out.println(s3 == s5); //false System.out.println(s4 == "codercoder"); //true
String s1 = "coder"; // 在字符串常量池中创建一个字符串"coder",s1指向它 String s2 = "coder"; // 在字符串常量池中找到已经存在的"coder",s2指向它,所以s1和s2指向同一个对象 String s3 = "coder" + s2; // 这里涉及到字符串的连接,会创建新的字符串对象,s3指向这个新的对象 String s4 = "coder" + "coder"; // 这里是常量字符串的连接,编译器会在编译期间就完成连接,所以s4指向字符串常量池中的"codercoder" String s5 = s1 + s2; // 这里也是字符串的连接,会创建新的字符串对象,s5指向这个新的对象 System.out.println(s3 == s4); // s3和s4指向的不是同一个对象,所以结果为false System.out.println(s3 == s5); // s3和s5指向的不是同一个对象,所以结果为false System.out.println(s4 == "codercoder"); // s4和"codercoder"都指向字符串常量池中的同一个对象,所以结果为true
false false true
String str1 = "str"; String str2 = "ing"; String str3 = "str" + "ing"; //对于String str3 = "str" + "ing";编译器会给你优化成String str3 = "string"; String str4 = str1 + str2; //对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的。拼接完成之后调用 toString() 得到一个 String 对象 。 //String str4 = new StringBuilder().append(str1).append(str2).toString(); String str5 = "string"; System.out.println(str3 == str4);//false System.out.println(str3 == str5);//true System.out.println(str4 == str5);//false
对于String str3 = "str" + "ing";编译器会给你优化成String str3 = "string";
引用的值在程序编译期是无法确定的,编译器无法对其进行优化。
对象引用和“+”的字符串拼接方式,实际上是通过StringBuilder调用append()方法实现的,拼接完成之后调用toString()得到一个String对象 。
String str4 = new StringBuilder().append(str1).append(str2).toString();
我们在平时写代码的时候,尽量避免多个字符串对象拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用StringBuilder或者StringBuffer。
不过,字符串使用final关键字声明之后,可以让编译器当做常量来处理。
字符串常量池:Java在运行时维护一个字符串常量池,其中存储了所有的字符串字面量。当创建一个字符串字面量时,JVM会首先检查池中是否已存在该字符串。如果存在,就返回该字符串的引用;如果不存在,就在池中创建一个新的字符串对象。
字符串拼接:使用+操作符拼接字符串时,如果至少有一个操作数是字符串字面量或常量池中的字符串,并且另一个操作数是字符串变量(不是字面量或池中的直接引用),则结果是一个新的字符串对象,而不是常量池中的字符串。
现在,让我们分析每个字符串变量:
接下来,我们分析System.out.println语句:
在两个String变量相加的时候,变量不是显式已知的(编译器不知),底层实际调用了StringBuffer的append方法,s3指向堆区而不是常量池。
在两个String字面量相加的时候,由于字面量是显式已知的,编译器将其优化为相加的结果并存入常量池。
/**对class文件反编译
*在JVM中对String的优化
*采用字面量赋值时 如果+号两边不是变量,JVM会自动进行字符的拼接。
*如果+号两边有变量,JVM不会对这一行进行任何操作
**/
String a="codercoder";
String b="coder";
String c="coder"+"coder";//==>String c="codercoder";
String d="coder"+b;//==>String d=new String("coder"+b);
//a==c a!=d ==:是判断地址值是否相同
由于String的特点,其值是不会改变的,所有的String操作,并且是在JVM的常量池中不存在的字符串值,都会新创建一个String对象。操作是针对在空间中的值进行操作,而不是引用。
并且,操作后的String新对象都是存放在JVM中的堆中。随着JVM的优化,永久代被元空间替代,所以字符串常量池也是存在堆中的。参考:请问下常量池到底是存储在元空间还是堆中呢?-慕课网 (imooc.com)
那么,以上的所有变量,都是单独的String对象。所有的对象比较,都是不同的地址,所以false false
但是由于String的对象是存储在堆中的字符流常量池。所以S1,S2,S4的值都是存储在常量值中。S3和S5都是存储在堆中。
只有存在常量池中的对象才可以复用,比如JDBC连接池,线程池等等。
所以S1和S2以及“coder”指向的引用都是同一个地址。那么“codercoder”和S4也是指向的同一个地址