Java的Integer常量池和String常量池
1.Integer的常量池
看下面一段代码:
package cn.qlq.test;
public class ArrayTest {
public static void main(String[] args) {
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i1.hashCode());
System.out.println(i2.hashCode());
System.out.println(i1 == i2);
System.out.println(i1.equals(i2));
System.out.println("-------------------");
Integer i3 = 1;
Integer i4 = 1;
System.out.println(i3.hashCode());
System.out.println(i4.hashCode());
System.out.println(i3 == i4);
System.out.println(i3.equals(i4));
}
}
1
1
false
true
-------------------
1
1
true
true
基本知识:我们知道,如果两个引用指向同一个对象,用==表示它们是相等的。如果两个引用指向不同的对象,用==表示它们是不相等的,即使它们的内容相同。
解释:Integer i1 = new Integer(1)的时候是在Java堆中创建一个Integer对象,i1指向堆中的对象,i1与常量池没关系,所以i1==i2为false。
Integer i3=1;的时候是从常量池中查找值为1的常量,i3指向该常量;Integer i4=1的时候会直接指向该常量,所以 i3 == i4为true。
这就是它有趣的地方了。如果你看去看 Integer.Java 类,你会发现有一个内部私有类,IntegerCache.java,它缓存了从-128到127之间的所有的整数对象。

所以事情就成了,所有的小整数在内部缓存,然后当我们声明类似——
Integer bInteger=127;
它实际在内部的操作是:
Integer bInteger=Integer.valueOf(127);
现在,如果我们去看valueOf()方法,我们可以看到:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
如果值的范围在-128到127之间,它就从高速缓存返回实例。
所以…下面这两个指向同一个对象:
Integer aInteger=127;
Integer bInteger=127;
我们可以得到true。
现在你可能会问,为什么这里需要缓存?
合乎逻辑的理由是,在此范围内的“小”整数使用率比大整数要高,因此,使用相同的底层对象是有价值的,可以减少潜在的内存占用。
然而,通过反射API你会误用此功能。
现在对代码进行反编译和反汇编查看:
package zd.dms.test;
public class ArrayTest {
public static void main(String[] args) {
Integer i1 = 25;
Integer i2 = new Integer(26);
}
}
反编译:
package zd.dms.test; public class ArrayTest
{
public static void main(String[] paramArrayOfString)
{
Integer localInteger1 = Integer.valueOf(25);
Integer localInteger2 = new Integer(26);
}
}
反汇编:
C:\Users\Administrator\Desktop>javap -c -v ArrayTest.class
Classfile /C:/Users/Administrator/Desktop/ArrayTest.class
Last modified 2018-9-3; size 384 bytes
MD5 checksum 6535da703ea8fa15da765de7bb03300b
Compiled from "ArrayTest.java"
public class zd.dms.test.ArrayTest
SourceFile: "ArrayTest.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Methodref #3.#16 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#3 = Class #17 // java/lang/Integer
#4 = Methodref #3.#18 // java/lang/Integer."<init>":(I)V
#5 = Class #19 // zd/dms/test/ArrayTest
#6 = Class #20 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 ArrayTest.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = NameAndType #21:#22 // valueOf:(I)Ljava/lang/Integer;
#17 = Utf8 java/lang/Integer
#18 = NameAndType #7:#23 // "<init>":(I)V
#19 = Utf8 zd/dms/test/ArrayTest
#20 = Utf8 java/lang/Object
#21 = Utf8 valueOf
#22 = Utf8 (I)Ljava/lang/Integer;
#23 = Utf8 (I)V
{
public zd.dms.test.ArrayTest();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0 public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=1
0: bipush 25
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: new #3 // class java/lang/Integer
9: dup
10: bipush 26
12: invokespecial #4 // Method java/lang/Integer."<init>":(I)V
15: astore_2
16: return
LineNumberTable:
line 6: 0
line 7: 6
line 8: 16
}
bipush 25 将25推至栈顶
invokestatic 调用Integer的静态方法valueOf(int)方法
astore_1 将栈顶引用型数值存入第二个本地变量
new 调用new Integer(int)
dup 复制栈顶数值(数值不能是long或double类型的)并将复制值压入栈顶
bipush 26 将26推至栈顶
invokespecial 调用Integer的初始化方法(init)
astore_2 将栈顶引用型数值存入第三个本地变量
return 返回,类型是void
补充:
aload_0 //将this引用推送至栈顶,即压入栈。
总结:Integer i = value;如果i是在-128到127之间,不会去堆中创建对象,而是直接返回IntegerCache中的值;如果值不在上面范围内则会从堆中创建对象。= 走的是valueOf()方法,valueOf(int)会走缓存。
Integer i2 = new Integer(xxxx);不管参数的value是多少都会从堆中创建对象,与IntegerCache没关系。
2.String常量池问题:
package cn.qlq.test;
public class ArrayTest {
public static void main(String[] args) {
String s1 = new String("1");
String s2 = new String("1");
System.out.println(s1.hashCode());//
System.out.println(s2.hashCode());//
System.out.println(s1 == s2);// false
System.out.println(s1.equals(s2));// true
System.out.println("-------------------");
String s3 = "1";
String s4 = "1";
System.out.println(s3 == s4);// true
System.out.println(s3.equals(s4));// true
System.out.println(s3.hashCode());//
System.out.println(s4.hashCode());//
}
}
String的hashCode不是返回地址,是对其值进行遍历运算。与地址没关系,只对值计算,所以所有的hashCode一样。
String s1 = new String("1"); 是在堆中创建一个String对象,并检查常量池中是否有字面量为"1"的常量,没有的话在常量区创建"1"并将堆中的对象指向该常量,有的话堆中的对象直接指向"1";
String s2 = new String("1"); 又在堆中创建一个String对象,并将s2指向该对象,其字面量"1"在前面已经创建,所以不会再创建常量区中创建字符串;
String s3 = "1"; 检查常量池中有没有字面量为"1"的字符串,如果没有则创建并将s3指向该常量;有的话直接指向该该常量;
String s4 = "1" 的时候常量池已经有1,所以不会再创建对象,也就是s3与s4指向同一个对象。
所以我们可以用下面图解解释,String s = new String("xxx")在检查常量池的时候会涉及到堆中创建对象;String s = "x"直接检查常量池,不会涉及堆。
如下图解:

一道经典的面试题:new String("abc")创建几个对象?
简单的回答是一个或者两个,如果是常量区有值为"abc"的值,则只在堆中创建一个对象;如果常量区没有则会在常量区创建"abc",此处的常量区是方法区的运行时常量池(也称为动态常量区)。
我们需要明白只要是new都会在堆中创建对象。直接String s = "xxx"不会涉及堆,只在常量区检查是否有该常量。
Java的Integer常量池和String常量池的更多相关文章
- JAVA中Integer.valueOf, parsetInt() String.valueOf的区别和结果
先来看段代码 public class IntegerDemo { public static void main(String[] args) { String num = null; System ...
- java基础知识回顾之---java String final类 容易混淆的java String常量池内存分析
/** * 栈(Stack) :存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放 在常量池中). 堆(heap):存 ...
- Integer 的 valueOf 方法 与 常量池(对 String Pool 的部分理解)
举例: public class Test { @org.junit.Test public void intTest() { Integer t1 = 128; Integer t2 = 127; ...
- java中Integer常量池
我们先看一个关于Integer的例子 public static void main(String[] args) { // TeODO Auto-generated method stu Integ ...
- Java堆/栈/常量池以及String的详细详解(转)------经典易懂系统
一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部.但是寄存器的数量极其有限,所以寄存器由编译器根据 ...
- JAVA String介绍、常量池及String、StringBuilder和StringBuffer得区别. 以及8种基本类型的包装类和常量池得简单介绍
一.概述 String是代表字符串的类,本身是一个最终类,使用final修饰,不能被继承. 二.定义方式 方式一:直接赋值法 String str1 = "hello"; 方式 ...
- 0024 Java学习笔记-面向对象-包装类、对象的比较、String常量池问题
包装类 基本类型-->包装类 byte-->Byte short-->Short int-->Integer long-->Long char-->Characte ...
- java中的堆、栈、常量池以及String类型的两种声明
参考自http://blog.sina.com.cn/s/blog_798b04f90100ta67.html http://www.cnblogs.com/fguozhu/articles/2661 ...
- java基础进阶一:String源码和String常量池
作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/8046564.html 邮箱:moyi@moyib ...
随机推荐
- C# Parsing 类实现的 PDF 文件分析器
下载示例 下载源代码 1. 介绍 这个项目让你可以去读取并解析一个PDF文件,并将其内部结构展示出来. PDF文件的格式标准文档可以从Adobe那儿获取到. 这个项目基于“PDF指南,第六版,Adob ...
- 苹果IOS、安卓推送功能开发
IOS推送开发:以下是基于开源javapns推送开发1.DerInputStream.getLength(): lengthTag=111, too big.先排除是否由于打包时证书 .p12 文件被 ...
- 第22天:js改变样式效果
一.输出语句 1.alert:弹出警示框(用的非常少,用户体验不好)完整写法:window.alert(“执行语句”):window对象,窗口,一般情况可省略alert(123); 2.console ...
- 【bzoj2938】[Poi2000]病毒 AC自动机
题目描述 二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码.如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的.现在委员会已经找出了所有的病毒代码段,试问,是否 ...
- [HAOI2010]计数 数位DP+组合数
题面: 你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数.比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等. ...
- BZOJ1854:[SCOI2010]连续攻击游戏——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1854 https://www.luogu.org/problemnew/show/P1640 lxh ...
- [APIO2017]商旅
link 这题卡我精度,调了一晚上才调对,因为没有想到图还可以不连通 其实可以预处理出好多东西,距离($dis(u,v)$),买卖物品(从$u$到$v$买卖物品的最大利润,例($max{S_{u,i} ...
- 【BZOJ 2322】[BeiJing2011]梦想封印 利用"环基"+线性基特征值
很容易想到离线加边并且把环和链拆开搞(就是对于每个终点求出起点到他的路径(其实就是dfs树),然后bzoj2115),而且维护也很简单,然而我们发现不同的终点可能得到相同的值,这就是我们遇到的最大的问 ...
- 剑桥offer(11~20)
11.题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. class Solution { public: int NumberOf1(int n) { ; unsigned ...
- JavaScript定义类与对象的一些方法
最近偶然碰到有朋友问我"hoisting"的问题.即在js里所有变量的声明都是置顶的,而赋值则是在之后发生的.可以看看这个例子: 1 var a = 'global'; 2 (fu ...