当前位置: 首页 > 科技观察

工作10年,Strings=newString(-xyz-)创建了多少个对象?

时间:2023-03-17 00:36:51 科技观察

这道题相信每个学java的同学都不陌生。作为一个经典的面试题,工作这么多年我真的觉得是他妈的一道题。网上还是可以看到很多关于这个问题的讨论。很多人,其中很多人工作多年,都有纠纷。我觉得有必要谈谈这个问题。从方法区来说,常量池是存在于方法区的,jdk1.7版本前后方法区变化很大,所以先说说方法区的演变。在jdk1.7版本之前,常量池存在于方法区,方法区是堆的一个逻辑部分。它有一个名字叫非堆。1.7版本将字符串常量池放入堆中。1.8之后去掉了永久代,保留了方法区的概念,方法区的实现改为元空间,常量池还在堆中。为什么说方法区发生了变化,只是为了让文章后面的内容不会因为JDK版本的不同而出现分歧,后面的内容都是基于jdk1.8版本来讨论的。Strings=newString("xyz");先来一段代码publicclassTest{publicstaticvoidmain(String[]args){Strings="xyz";}}然后我们用javac编译这段代码,然后用javap反编译,执行javap-c从Test的结果来看,ldc命令在常量池中创建一个“xyz”对象,然后将其压入操作数栈的顶部,然后astore将其保存到一个局部变量中,并返回。再看面试二题中的代码publicclassTest{publicstaticvoidmain(String[]args){Strings=newString("xyz");}},反编译分析也很明显。我们看到new创建了String对象,ldc在常量池中创建了“xyz”字符串对象,然后invokespecial执行构造函数,astore_1赋值,return返回。通过上面两个例子,我们可以知道Strings=newString("xyz");创建2个对象,有的回答说3个对象,但是引用s也算作一个对象。还有一个回答说如果xyz存在就创建2个,不存在就创建3个(包括reference),然后测试一下。publicclassTest{publicstaticvoidmain(String[]args){Strings="xyz";Strings2=newString("xyz");}}从这里可以看出这是我们例子1和2的组合,但是要注意ldctwice下面的#2,#代表索引,说明第二次执行newString("xyz")时没有重新创建xyz对象。一些常见的指令助记符表示:nop,什么都不做。aconst_null,将null压入栈顶。iconst_i(可变数),将int类型i压入栈顶。同样,你应该知道lconst_0和fconst_0是什么意思。ldc将常量池中的int、float或String常量值压入栈顶。iload,将指定的int类型局部变量压入栈顶。istore,将栈顶的int值存入指定的局部变量。同样,astore_i表示将栈顶的引用值存入第i个局部变量。dup,复制栈顶的值,并将复制的值压入栈顶。invokevirtual,调用实例方法。invokespecial,调用超类构造函数,实例初始化方法,私有方法。invokestatic,调用静态方法。invokeinterface,调用接口方法。invokedynamic,调用动态链接方法。new,创建一个对象并将其引用值压入栈顶。总之,创建了多少个对象?如果xyz不存在,引用算对象,则3如果xyz不存在,引用算对象,则2如果xyz存在,引用算对象,则2如果存在,则是1。当然我觉得引用肯定不算对象。最后的答案应该是1或者2。说实话,这个面试题应该不会出现在初级面试题中。