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 ...
随机推荐
- java 基本--数据类型转换--001
小可转大,大转小可能会损失精度(编译出错,需要强制转换)A: byte,short,char -> int -> long -> float ->doubleB: byte,s ...
- mysql向上递归&向下递归
工作记录 向上递归函数test: BEGIN ); ); SET sTemp = '$'; SET sTempChd =cast(rid as CHAR); WHILE sTempChd is not ...
- Eclipse闪退解决方案
1. 找到Eclipse目录下的eclipse.exe,右键点击->发送到桌面快捷方式,然后右键点击快捷方式,选择属性,修改“目标”(或target),其中红色代表eclipse的路径,绿色代表 ...
- matlab中prod的使用方法
B = prod(A) 将A矩阵不同维的元素的乘积返回到矩阵B. 如果A是向量,prod(A)返回A向量的乘积.如果A是矩阵,prod(A)返回A每一列元素的乘积并组成一个行向量B. B = prod ...
- 在上传文件时候 request.setCharset对文件名有效 对普通文本域无效 需要通过手动转换编码方式编码
在上传文件时候 request.setCharset对文件名有效 对普通文本域无效 需要通过手动转换编码方式编码
- hdu5575 Discover Water Tank
题意: 给出个水箱,水箱两侧有无限高的隔板,水箱内有整数高度的隔板将水箱分成n-1份,现在给出m个限制,每个限制表示某个位置的某个高度有水或没水,问最多能同时满足多少个限制.n,m<=2*10^ ...
- BZOJ4000 TJOI2015棋盘(状压dp+矩阵快速幂)
显然每一行棋子的某种放法是否合法只与上一行有关,状压起来即可.然后n稍微有点大,矩阵快速幂即可. #include<iostream> #include<cstdio> #in ...
- 洛谷 P2757 [国家集训队]等差子序列 解题报告
P2757 [国家集训队]等差子序列 题目描述 给一个\(1\)到\(N\)的排列\(\{A_i\}\),询问是否存在 \[1 \le p_1<p_2<p_3<p_4<p_5& ...
- ACE主动对象模式(1)
转载于:http://www.cnblogs.com/TianFang/archive/2006/12/11/589168.html 主动对象模式用于降低方法执行和方法调用之间的耦合.该模式描述了另外 ...
- Codeforces Round #396 (Div. 2) A B C D 水 trick dp 并查集
A. Mahmoud and Longest Uncommon Subsequence time limit per test 2 seconds memory limit per test 256 ...