经典面试题 String str = new String(“hello world”);创建了几个对象。
学习 java 准备面试,不能光背面试题的答案,更要深入面试题,了解面试题背后的java基础。
那么让我们来看一下 String str = new String("hello world");
这段代码到底涉及到了多少 java 基础。
我知道的创建 String 对象的方式共有五种。
-
new String(char[ ])
通过 char 数组创建 String 对象
-
new String(byte[ ])
通过 byte 数组创建 String 对象。因为 JDK1.8 中,String 中的数据是由 char[ ]存储的,当通过 byte 数组创建 String 对象时,需要将 byte 数组转换为 char 数组。由于 byte 是一个字节,而 char 是两个字节,而且 byte 的数据范围包含负数。所以两个数组相互转换时,如果不指定编码方式,可能会导致乱码 (java 中 char 为 unicode 编码,而以 byte[ ] 形式创建 String 对象通常是网络请求或者IO读入时使用的,包含多种编码方式),以及 byte 数组前后不一致的错误。
String str = new String(new byte[]{-24,97,98,99}); System.out.println(str); for (byte aByte : str.getBytes()) { System.out.print(aByte + " "); } //output �abc -17 -65 -67 97 98 99 !!前后 byte 数组不一致
需要指定编码方式例如:
String str = new String(new byte[]{-24,97,98,99},"ISO-8859-1"); System.out.println(str); for (byte aByte : str.getBytes("ISO-8859-1")) { System.out.print(aByte + " "); } //output èabc -24 97 98 99
-
new String(int[ ])
-
String str = "123";
最常用的字面量形式创建String对象 -
String str = new String("123");
本节只涉及 4、5 两种创建 String 对象的方式。
1.字面量形式创建
String str = "123";
我们称 123 为字面量,当你点击编译软件的运行按钮,当前 .java 文件会被编译为 .class 字节码文件。字面量 123 存在于 class 文件的常量池中。其中,常量池中会存在两个与 123 相关的常量。
- CONSTANT_Utf8_info 就是 123
- CONSTANT_String_info 存储 CONSTANT_Utf8_info 的索引
当这个类被加载时,常量池会被加载进入运行时常量池,以上两个常量的类型会发生变化。
- CONSTANT_Utf8_info -> Symbol* ,指针指向 C++ 类型的 Symbol 对象,symbol 对象的值就是 123
- CONSTANT_String_info -> JVM_CONSTANT_UnresolveString_info(未被解析的 String 引用)
在加载阶段不会产生 String 对象。直到解析阶段才会创建 String 对象。当虚拟机运行到 ldc 等操作符号引用的字节码指令时,会触发这个类的解析阶段,将符号引用替换为直接引用。而此时 JVM_CONSTANT_UnresolveString_info 也就会被 resolve 为真正的 String 对象的引用。当然此时需要在堆中创建内容为 123 的 String 对象,但也不是傻傻的创建对象。创建时会先去判断字符串常量池中是否有内容一样也是 123 的对象的引用。如果有则不需要创建新的 String 对象。
至此,第四种创建 String 的方式讲解完成,至于创建时如何判断,以及如何向字符串常量池中添加新的数据,涉及到字符串常量池的实现原理 StringTable,笔者能力不够,暂不讲解。有兴趣的老铁可以去阅读 StringTable 源码(查询方法 StringTable::lockup(),添加方法 StringTable::basic_add() )。
总结:通过字面量形式创建 String 对象,对象会在类的解析阶段被创建,并且相同内容的对象只有一个。如果创建时已经存在重复的,则不会创建新的 String 对象。
2.有参构造创建 String 对象
String str = new String("123");
同样, 123 是字面量,在类的解析阶段会创建 String 对象。与第四种创建方式不同的地方是,使用了 String 的有参构造。
当使用构造方法创建 String 对象,不光会在类加载过程中创建,还会在程序运行时在堆中创建 String 对象。并且这个对象是野生的,不归字符串常量池管。
只有在类加载过程中产生的 String 对象,以及运行时通过调用 String::intern() 方法把 String 的监护权给字符串常量池的 String 对象,才归字符串常量池管。不太懂?那反过来想,如果所有 String 对象都归字符串常量池管的话,在程序运行期间,可能会出现无数个 String,那字符串常量池早就炸了。不然为什么叫字符串常量池,而不叫字符串池。
3.总结
这个面试题,涉及到的 java 基础
- String 的几种创建形式
- .class 文件的常量池
- 类的加载阶段会将 class 文件中的常量池加载进入运行时常量池中,并且改变常量的结构
- ldc 等操作符号引用的字节码指令会触发类的解析阶段
- 类的解析阶段会将符号引用替换为直接引用,本文涉及到 String 对象的创建
- 字符串常量池
new String("123");
会在运行时在堆中创建 String 对象,这个对象是野生的不归字符串常量池管理
想更了解字符串常量池的同学,可以看我上一篇博客,深入Java基础——字符串常量池,感谢捧场。
如果发现本文有错误请直接指出,感谢。