[[toc]]
引用与内存
当我们new一个对象,并引用他时,在java内存里到底是什么情景?所谓的引用传递与值传递的本质到底是什么?
其实java里并没有严格意义的值传递和引用传递,之所以有这个叫法,是因为java的数据类型引起的。java有两种数据类型,一种是基本类型,还有一种是引用类型。因为使用基本类型的时候,你给他进行变化操作的时候,比如int类型进行加1操作
int a =1;//a在栈中,值为1
a = a+1; //a在栈中,值变为2。
test(a);//test方法里将a+1。这里其实是在test栈中的a的值变为3,原来栈中a的值不变。
这样其实就是在栈中的变更值,就像值传递一样。这是区别于引用类型最关键的地方,引用类型给他进行赋值变化,并不会影响它在栈中的指向,变化的是堆中的内存,也就像引用传递一样。
举个栗子:假设我们有个Student对象,他有name属性。
Student student = new Student("wang");//栈中的申明是student,而new是将student指向堆中的对象,其中name="wang"。
test(student);//test方法里将student.setName("gu").这个时候,在test方法里,原来栈的引用没有变化,test栈的student引用与原来栈是指向的同一个堆内存对象,堆对象里的name变为"gu".
System.out.print(student.getName());//这边使用的是原来栈中student引用指向的堆内存,堆内存已经在test栈里改成了"gu"。
先说下类的初始化,有且只有5种情形会初始化
1)遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初
始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字
实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常
量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
3)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
5)当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
以下几种情形的引用是不会触发初始化:
1.调用一个类中final修饰的静态常量
2.通过子类调用父类中的静态变量,子类不会初始化,只会初始化父类。
3.创建这个类的数组的时候,由虚拟机自动生成的、直接继承于java.lang.Object的子类,创建动作由字节码指令newarray触发,所以原来的类不会被初始化。
关于接口
接口与类真正有所区别的是前面讲述的5种“有且仅有”需要开始初始化场景中的第3种:当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。