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 ...
随机推荐
- 【Docker 命令】- create命令
docker create :创建一个新的容器但不启动它 语法 docker create [OPTIONS] IMAGE [COMMAND] [ARG...] OPTIONS同run命令 实例 使用 ...
- 什么是Oracle的分区表 (转 作者 陈字文)
假设我们现在正在酝酿经营一家图书馆,最初,我们只有十本书提供给大家来阅读和购买.对于十本书而言,我们可能只需要一个书架格子将其作为保存这十本书的容器就足够了,因为任何一个人都可以很轻松的扫一眼就可以将 ...
- SQL SERVER技术内幕之5 表表达式
表表达式是一种命名的查询表达式,代表一个有效的关系表.可以像其他表一样,在数据处理语句中使用表表达式.SQL Server支持4种类型的表表达式:派生表(derived table).公用表表达式(C ...
- solr 学习之solrJ
solrJ是访问Solr服务的JAVA客户端,提供索引和搜索的请求方法,SolrJ通常嵌入在业务系统中,通过solrJ的API接口操作Solr服务. <!-- https://mvnreposi ...
- BZOJ 1197 花仙子的魔法(递推)
数学归纳法. dp[i][j]=dp[i][j-1]+dp[i-1][j-1]. # include <cstdio> # include <cstring> # includ ...
- BZOJ4260 Codechef REBXOR(trie)
用trie求出前缀最大区间异或和.后缀最大区间异或和即可.注意空间是nlog的. #include<iostream> #include<cstdio> #include< ...
- [CF1037H] Security
题目链接 codeforces. 洛谷. Solution 按照套路,可以\(SAM\)上线段树合并求出\(endpos\)集合,然后随便贪心一下就好了. #include<bits/stdc+ ...
- bzoj1901: Zju2112 Dynamic Rankings(BIT套主席树)
带修改的题主席树不记录前缀,只记录单点,用BIT统计前缀. 对于BIT上每一个点建一棵主席树,修改和询问的时候用BIT跑,在主席树上做就行了. 3k4人AC的题#256...应该不算慢 #incl ...
- 使用snmp4j实现Snmp功能(三)
相关链接:Snmp学习笔记使用snmp4j实现Snmp功能(一)使用snmp4j实现Snmp功能(二)使用snmp4j实现Snmp功能(三) 前两篇文章讲了如何使用Snmp4j实现Set.Get(使用 ...
- ioctrl 获取本机IP及MAC地址
通过使用ioctl可以获得本机的一些信息,这里记录获得interface IP及MAC的过程. 1:ioctl 函数的作用是什么 man ioctl: DESCRIPTION The ioctl() ...