大厂面试题:
1、一面小试牛刀选择题
String s = new String(“xyz”);创建了几个StringObject?
A、两个或一个都有可能
B、两个
C、一个
D、三个
【答案】
这个问题自身就没有合理的答案。
解释引用于: https://www.iteye.com/blog/rednaxelafx-774673
先换成另一个问题来问:
问题:
String s = new String("xyz");
在运行时涉及几个String实例?
一种合理的解答是:
答案:两个,一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例
引用是不是对象?
对象其实就是一个容器,它可以存储属性和方法。引用不是对象。引用变量只是指向对象,他本身没有属性和方法。
- String s = new String("xyz");
创建了几个String Object?
所谓的答案:两个(一个是“xyz”,一个是指向“xyz”的引用对象s)
用归谬法论证。假定问题问的是“在执行这段代码片段时创建了几个String实例”。如果“标准答案”是正确的,那么下面的代码片段在执行时就应该创建4个String实例了:
- String s1 = new String("xyz");
- String s2 = new String("xyz");
马上就会有人跳出来说上下两个"xyz"字面量都是引用了同一个String对象,所以不应该是创建了4个对象。
那么应该是多少个?
运行时的类加载过程与实际执行某个代码片段,两者必须分开讨论才有那么点意义。
为了执行问题中的代码片段,其所在的类必然要先被加载,而且同一个类最多只会被加载一次(要注意对JVM来说“同一个类”并不是类的全限定名相同就足够了,而是<类全限定名, 定义类加载器>一对都相同才行)。
根据上文引用的规范的内容,符合规范的JVM实现应该在类加载的过程中创建并驻留一个String实例作为常量来对应"xyz"字面量;具体是在类加载的resolve阶段进行的。这个常量是全局共享的,只在先前尚未有内容相同的字符串驻留过的前提下才需要创建新的String实例。
等到真正执行原问题中的代码片段时,JVM需要执行的字节码类似这样:
- 0: new #2; //class java/lang/String
- 3: dup
- 4: ldc #3; //String xyz
- 6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
- 9: astore_1
这之中出现过多少次new java/lang/String就是创建了多少个String对象。也就是说原问题中的代码在每执行一次只会新创建一个String实例。
这里,ldc指令只是把先前在类加载过程中已经创建好的一个String对象("xyz")的一个引用压到操作数栈顶而已,并不新创建String对象。
所以刚才用于归谬的代码片段:
- String s1 = new String("xyz");
- String s2 = new String("xyz");
每执行一次只会新创建2个String实例。
---------------------------------------------------------------
为了避免一些同学犯糊涂,再强调一次:
在Java语言里,“new”表达式是负责创建实例的,其中会调用构造器去对实例做初始化;构造器自身的返回值类型是void,并不是“构造器返回了新创建的对象的引用”,而是new表达式的值是新创建的对象的引用。
对应的,在JVM里,“new”字节码指令只负责把实例创建出来(包括分配空间、设定类型、所有字段设置默认值等工作),并且把指向新创建对象的引用压到操作数栈顶。此时该引用还不能直接使用,处于未初始化状态(uninitialized);如果某方法a含有代码试图通过未初始化状态的引用来调用任何实例方法,那么方法a会通不过JVM的字节码校验,从而被JVM拒绝执行。
能对未初始化状态的引用做的唯一一种事情就是通过它调用实例构造器,在Class文件层面表现为特殊初始化方法“<init>”。实际调用的指令是invokespecial,而在实际调用前要把需要的参数按顺序压到操作数栈上。在上面的字节码例子中,压参数的指令包括dup和ldc两条,分别把隐藏参数(新创建的实例的引用,对于实例构造器来说就是“this”)与显式声明的第一个实际参数("xyz"常量的引用)压到操作数栈上。
在构造器返回之后,新创建的实例的引用就可以正常使用了。
2、二面进阶问答题
static类、static变量会不会被GC回收?
万物皆对象,对象皆可以回收。
直接=null就回收了,方法区也可以回收的,而且要是你这个类都没了。