先看一下两种的字节码表示(只截取了循环体的部分):
第一种:one变量声明在循环体之外
第二种:one变量声明在循环体内部第二种方法会在栈开辟10个空间存引用地址,在堆开辟10个空间存实际内容。
这种说法是错误的。aload_n指令表示加载栈帧局部变量表偏移量为n的变量;astore_n反之,表示存储[1]。对比源码和字节码可以看出,在第二种方法中,one只在栈中开辟了一个空间,位于局部变量表下标为3的位置。同理,在第一种方法中,one也只开辟了一个空间,位于局部变量表下标为2的位置。至于堆空间,每次新建对象都会在堆中为对象开辟空间,因此两种方法都会在堆中开辟10个对象所需的空间。
那么list存放的是什么?是栈引用地址么?
list存放的是对象在堆中的引用,栈中的变量保存的也是对象在堆中的引用。这个引用可以理解为对象的内存地址,但是具体实现需要看jvm。
list底层是怎么实现存,怎么实现取的?
list保存了对象的引用,因此可以通过引用来访问对象。可以类比C语言的指针访问。
如果存的是对象的引用地址,那这10个引用地址存在了哪里?
这些引用地址栈上有,由变量one存储(方法结束退栈后,这些空间会被回收)。堆上也有,位于list对象的空间上。
list又是怎么通过对象的引用地址去堆里面寻找的,还要通过栈空间去寻找么?
这个看jvm怎么实现。假设引用存储的是对象的内存地址,那么假如我要访问这个对象的某个属性,其实就是找个这个对象所在的地址,然后计算这个属性的偏移,就可以得到这个属性的内存地址。不需要通过栈空间,栈帧会在方法返回后被回收。其实看个图就明白它们的关系了。