一、常量池
常量池:一块在Class文件结构中,用于存放常量的数据区域。一旦该Class被加载,该常量池将会load进入方法区中的运行时常量池。
运行时常量池:并不常态。它具有动态的属性,除了预置入Class文件的常量外,它还能将运行期产生的常量存储其中。
常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从常量池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。
二、基本类型
对于int num=0的存储,都将是变量num和数值0开辟一个固定内存空间,所以才会有声明时,最大值和最小值的限制(很尬,这居然是今天才理解到)。至于存储的区域,需要分以下种情况:
1.1、当在方法中赋值时,存储在该方法的栈帧中;
1.2、当i为实例的成员变量时,存储在堆中;
1.3、当num为实例的静态成员,存储在方法区中;
1.4、当num为final常量,存储在运行时常量池中。
三、引用类型
Object obj = new Object(),将是变量obj和对象Object的地址引用开辟一个固定的内存空间,存储在分情况的内存区域中。
总结:基本类型变量=数值,引用类型变量=对象地址
四、由以上总结,我们能解决一下几个面试经常遇到的问题。
3.1、java传参什么时候值传递、什么时候引用传递呢?
例子如:mr.method(int myNum, Object myObj); void method(int num, Object obj){}
对于变量myNum,因为基本类型的它存储的是数值,所以传递的是值;对于变量myObj,因为引用类型的它存储的是地址引用,所以传递的是引用。
至于为什么要区分这个,那是因为,如果只是简单的值传递,那么方法体内任何对参数num的操作,均不会影响到myNum。但如果是引用从传递,那么变量obj将获取到的myObj的引用地址,意味着变量myObj将Object实例对象的控制权交付给变量obj。所以,变量obj任何的操作,都将影响到暂时失去对Object对象控制权的变量myObj。
3.2、双等号==的含义
基本数据类型之间应用双等号,比较的是他们的数值。
引用类型之间应用双等号,比较的是他们在内存中的存放地址。
五、基本类型和常量池技术
java中基本类型的包装类的大部分都实现了常量池技术,
5.1、 其中Byte,Short,Integer,Long,Character,Boolean,这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,只有超出此范围才会去创建新的对象。
5.2、两种浮点数类型的包装类Float,Double并没有实现常量池技术。
5.3、Integer比较更丰富的一个例子
对结果解释:语句i4 == i5 + i6,因为+这个操作符不适用于Integer对象,首先i5和i6进行自动拆箱操作,进行数值相加,即i4 == 40。然后Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较。(Java自动拆箱与装箱传送门)
六、String类和常量池技术
6.1、字符串何时出现在常量池中
主要使用方法有两种:
first:直接使用双引号声明出来的String对象会直接存储在常量池中。
second:如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
6.1、连接表达式 +
(1)只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。
(2)对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中。
6.2、String str = new String("xyz"); **创建了几个对象? **
采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"xyz"这个字符串对象,如果有,则不在池中再去创建"xyz"这个对象了,直接在堆中创建一个"xyz"字符串对象,然后将堆中的这个"xyz"对象的地址返回赋给引用str,这样,str就指向了堆中创建的这个"xyz"字符串对象;如果没有,则首先在字符串池中创建一个"xyz"字符串对象,然后再在堆中创建一个"xyz"字符串对象,然后将堆中这个"xyz"字符串对象的地址返回赋给str引用,这样,str指向了堆中创建的这个"xyz"字符串对象。所以得分情况,才知道要创建几次。
6.3、java.lang.String.intern()
对于结果是不是很惊讶。intern()只是执行顺序变了一下,结果就不一样啦。欲知所以然,且容先生步步分析。
jdk1.7中定义,intern()方法只是将第一次出现的字符串实例的地址引用,记录在常量池中。常量池会建立其索引,索引key代表“str01”,value代表“str1指向的对象的地址引用”。
于是开始分析:步骤01:根据6.1中的总结,还会在常量池中创建三个String对象,分别是对象“str”、对象“01”。且建立其引用的索引表。同时还会在堆中创建对象“str”、对象“01”和对象“str01”,并将对象“str01”地址引用赋值给变量str1;
步骤02:变量str2获取“str01”在常量池中存储的value,注意此时的引用指向常量池中”str01“对象;
步骤03:根据jdk1.7,intern()方法的定义。字符串“str01”已经在常量池中存在,所以并不会操作。如需返回,返回值是常量池中索引表的地址引用;
步骤04:根据3.2双等号的结论。变量str2和str1,比较的是引用。此时,str1指向堆中对象“str01”,而str2指向常量池中对象“str01”.故为false;
步骤06:因为变量str3指向的堆对象“str02”还未在常量池中出现过。所以,常量池会建立字符串“str02”和堆对象“str02”的引用的索引;
步骤07:变量str4获取”str02“在常量池中存储的value,注意此时的引用指向堆中的”str02“对象.
【温馨提示,根据步骤,画张对象创建过程图,效果杠杠的好哟哟哟】
网友评论