修正前:new出来的对象,会在堆中存放真正的值; 大错特错!!!!java
修正后:new出来的对象,堆存放的并非真正的值,而是常量池中字符串常量的地址。面试
不知道你们在作面试题时是否会遇到关于String的题,记得校招时,楼主常常遇到String的题,有时候会很懵逼。先来看一个例子:sql
public class StringTest {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = "he" + "ll" + "o";
System.out.println(str1 == str2);
System.out.println(str1 == str3);
String str = "o";
String str4 = "hell" + str;
System.out.println(str1 == str4);
String str5 = new String("hello");
System.out.println(str1 == str5);
System.out.println(str4 == str5);
System.out.println(str1.equals(str5));
//intern()函数做用:直接获取常量池中字符串常量的地址并返回。
System.out.println(str1==str5.intern());
String str6 = new String("beyond");
}
}
//输出的结果
//true
//true
//false
//false
//false
//true
//true
复制代码
你答对了吗?app
首先咱们要知道,String是Java的一个类,有两种建立对象的方式【不考虑反射、clone、反序列化】。函数
(我这个图画得太丑了。。。。。。[摊手])工具
经过方式一建立String对象,会先查看常量池中是否有这个字符串常量,若是存在,则直接将引用指向这个常量(即例子中:str1==str2为true)。比较疑惑的会是str3,str4。由于str3由字符串常量“he”、"ll"、“o”组成,编译器发现这三个常量组成的字符串常量已经存在了常量池中,即编译期间就已经肯定了最终值,则也会将引用指向“hello”字符串常量。对于str4,由于编译器不能在编译时就肯定字符串最终的值,因此会将字符串常量“hell”存放在常量池,再在堆中生成最终的对象【引伸一下:经过“+”链接字符串,底层是经过new StringBuilder()的append()方法进行拼接,因此应避免在循环中使用“+”来拼接字符串,以避免建立大量垃圾对象】。ui
经过方式二而建立String对象,首先会先查看常量池中是否存在这个字符串常量,若是存在,则直接在对内存中new出新对象;不然,会先在常量池生成这个字符串常量,再在堆中生成新对象(如图中str6)。特别注意:(new出对象的值为常量池中这个字符串常量的地址,也就是堆中存放的都是字符串常量中的地址)spa
javap是jdk自带的反解析工具。它的做用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。命令行
javap的用法格式:javap <options> <classes>
其中classes就是你要反编译的class文件。
在命令行中直接输入javap或javap -help能够看到javap的options有以下选项:code
-help --help -? 输出此用法消息
-version 版本信息,实际上是当前javap所在jdk的版本信息,不是class在哪一个jdk下生成的。
-v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类 和成员 (默认)
-p -private 显示全部类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-classpath <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
复制代码
好比本例子:javap -verbose StringTest
String在内存的存在形式有两种,若是经过直接赋值的形式(方式一)会将对象直接放到常量池中【若是由几个字符串常量拼接而成,而且在编译时就肯定了最终的结果(即常量池中存在最终拼接而成的字符串),则直接将引用指向这个字符串常量;若是在编译时不能肯定最终的结果,则会将最终结果在堆中生成,并其中的字符串常量会在常量池中存在】;若是经过new的方式(方式二),先在常量池生成字符串常量,再在堆中生成字符串对象,对象的值为这个字符串常量在常量池中的地址。
小弟才疏学浅,若有错误,敬请指正,十分感谢。