logo资料库

十分钟读懂java常量池优化技术.pdf

第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
资料共9页,剩余部分请下载后查看
1、什么是常量池技术 Java中的常量值技术是为了方便快捷的创建某些对象而出现的,当需要一个对象时,就 可以从池中取一个出来(如果没有则创建一个,创建一个比引用一个要耗时),则在需要重 复创建相等变量时节省了好多时间,常量池也就是一个内存空间,常量池存在于方法区中; 【关于常量池的修改问题】 常量池中存放的对象不能通过外界进行修改,所以Java中不允许对常量进行修改; 2、那些包装类实现了常量池 实现了常量池的包装类:Byte、Short、Character、Integer、Long、 Boolean(六个) 未实现常量池的包装类: 类型; String引用类型:也实现了常量池; 3、常量池维护的范围 上述几种基本数据类型的包装类实现了常量池技术,但它们维护的常量仅仅是【-128- 127】这个范围的常量,如果出了这个范围,就会从堆中创建对象,不再从常量池中取; 4、 常量池技术举例 F l o a t 、 D o u b l e
①:i = i0,基本数据类型,存储在栈中,栈中的数据可以共享; ②:常量池技术 ③:Java的数学运算都是在栈中进行的,Java自动对 i2 和 i3进行拆箱操作转换为整形; ④:i4和i5是引用类型,在栈中存储指针,但是它们是new出来的,因此不再从常量池中寻 找数据,而是从堆中各自new一个对象,地址值肯定不一样; ⑤:加法运算,自动拆箱,同理3; ⑥:浮点型没有实现常量池技术,因此Double d1 = 1.0相当于Double d1 = new  Double(1.0);是从堆上new出来的,d2同理,地址不一样; 注意: (1)常量池的维护范围 上述几种基本数据类型的包装类实现了常量池技术,但它们维护的常量仅仅是【-128- 127】这个范围的常量,如果出了这个范围,就会从堆中创建对象,不再从常量池中取; (看下面的源码) (2)String特殊的常量池技术 String类型也实现了常量池技术,但稍微有点不同,String类型是先检测常量池中有没 有对应的字符串,如果有则取出来,如果没有,则把当前的添加进去; (3)误区 只有基本数据类型和对应的包装类型进行比较的时候,才会发生自 动的拆箱进行比较,两个包装类型会遵从上述的比较规格,不会发生自 动的拆箱; 而equals,是Object类的方法,Object是所有对象的父类,如果要 比较的对象没有重新equals方法,就会调用Object的方法,此方法如 下:
直接进行地址的比较,所有如果要比较的对象没有重新equals方 法,相当于==比较方式,直接比较地址;而包装类型都重写了这个方 法; String s1 = "abc"; String s2 = "abc"; System.out.println(s1 == s2); //true System.out.println(s1.equals(s2)); //true 【解析】 (1)我们知道,==比较两个引用类型的时候,比较的是地址值,而,equals只能比较 引用类型,如果此引用类型未重写equals方法,比较的也是地址值,若重写了,那么按照各 自重写的特性,string重写了此方法;比较的字符序列,区分大小写;最后一个为TRUE; (2)关于常量池:abc作为常量,当常量进入常量池之前,先会判断常量池中有无该常 量,如果有,就不会创建新常量,反之,所以上面两个常量是常量池中的同一个常量,具有 相同的地址,所以为true; String s1 = new String("abc"); //2个 【解析】 (1)abc是常量池中的对象,把此常量有用来初始化新的new出来的string,所以s1是 指向堆内存的对象引用; String s1 = new String("abc"); String s2 = "abc"; System.out.println(s1 == s2); //false System.out.println(s1.equals(s2)); //true 【解析】 (1)abc在常量池中,S1在堆内存中,地址值不同,但是字符序列形同; String s1 = "a" + "b" + "c"; String s2 = "abc"; System.out.println(s1 == s2); //true System.out.println(s1.equals(s2)); //true 【解析】 (1)常量优化机制:"a" + "b" + "c"被优化为"abc"进入常量池,而后"abc"进池时判 断已有,公用之; 1 . 判 断 定 义 为 S t r i n g 类 型 的 s 1 和 s 2 是 否 相 等 2 . 下 面 这 句 话 在 内 存 中 创 建 了 几 个 对 象 ? 3 . 判 断 定 义 为 S t r i n g 类 型 的 s 1 和 s 2 是 否 相 等 4 . 判 断 定 义 为 S t r i n g 类 型 的 s 1 和 s 2 是 否 相 等
String s1 = "ab"; String s2 = "abc"; String s3 = s1 + "c"; //常量优化机制只能优化都是常量的值,s1是变量,不能 优化,使用stringbuffer有重新创建了一个对象,地址不同; System.out.println(s3 == s2); //false System.out.println(s3.equals(s2)); //true 【解析】 (1)Java 语言提供对字符串串联符号("+")以及将其他对象转换为字符串的特殊支 持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字 符串转换是通过 toString 方法实现的; s3 = s1 + "c"先通过StringBuilder类的相应方法转换StringBuilder类,再由 tostring转换成string,这是在堆内存中完成的;地址不一样; (2)第二个判断字符序列,相等为true; 5 . 判 断 定 义 为 S t r i n g 类 型 的 s 1 和 s 2 是 否 相 等
一、特殊的String类型  1、String对象两种不同的存储位置: 创建String对象的方法有两种: String s2 = "我在那";                   ----------------① String s1 = new String("我是谁");---------------② ①:在编译期已经创建好(即用双引号定义的),存储在常量池中; ②:在运行期分配空间(new出来),存储在堆中(常量池是否有看情况) -------------------------------------------------------------------- 过程分析: (1)前三种是使用第①种方式创建的,引用在栈中,对象实体分配在常量池中,而且 只有一份; (2)后两种是使用第②种方式创建的,过程并不是在堆中直接创建对象,而是先在常 量池中查找是否有“abc”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中 对 于 e q u a l s 相 等 的 字 符 串 , 在 常 量 池 中 是 只 有 一 份 , 在 堆 中 则 有 多 份 ;
再创建一个常量池中此“abc”对象的拷贝对象, 所以,对于String str = new String("abc"); 如果常量池中如果没有abc则产生两个对象(一个在常量池中,一个在堆中),否则产 生一个对象(在堆中,常量池已经创建过了); -------------------------------------------------------------------- 二、基本类型的变量和常量 过程分析: (1)变量:变量及其引用都存储在栈中;(栈中数据具有共享性,是否底层也实现了 常量池技术????)------- (2)常量:存储在常量池;(并且在编译时,变量名已经就替换成常量值,所以在下 面的情况中,就可以解释为什么加了final修饰符就可以访问变量a了) (3)Integer i = 3;  在内存中i在栈上,3常量池中,把地址给i; 三、基本数据类型的包装类实现常量池(注意是包装类) Integer i = 3 【分析】 1、赋值问题 JDK1.5开始,Integer对象的赋值(除了创建对象赋值)会自动调用 Integer类的valueOf(int i)方法:
所以i = 3,底层调用了valueOf(int i)方法, 可见,在valueOf做了判断,如果值在­128到127之间,就调用IntegerCache类 的成员数组的值作为返回值;(IntegerCache是Integer的内部类),这就是上 述所说的包装类维护范围,所有实现常量池的包装类的维护范围都是­128 ­  127,超出这个范围,就会执行下面的创建对象new Integer(i); 而IntegerCache内部类有一个静态成员常量数组cache[ ],它里面保存 了­128到127范围的常量,代码如下: 所以,i = 3是指向常量池中的地址;
1、常量池的重要应用 内存图----------- 【局部内部类访问外部类的局部变量的时候一定要加final修饰符修饰该变量,也就是说 必须定义为常量。】 【过程分析】 上述案例中int a = 3;是局部变量,Inner是局部内部类,可见method方法在指向 show方法的指针,所以直接无法访问,只有在局部变量前面加上final常量修饰符才可以访 问,原因是当加了final常量修饰符,在编译阶段就会把变量直接替换为这个常量值,然后直 接在常量池中查找这个常量; 【误区讲解】
分享到:
收藏