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常量池的更多相关文章

  1. JAVA中Integer.valueOf, parsetInt() String.valueOf的区别和结果

    先来看段代码 public class IntegerDemo { public static void main(String[] args) { String num = null; System ...

  2. java基础知识回顾之---java String final类 容易混淆的java String常量池内存分析

    /** *   栈(Stack) :存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放  在常量池中). 堆(heap):存 ...

  3. Integer 的 valueOf 方法 与 常量池(对 String Pool 的部分理解)

    举例: public class Test { @org.junit.Test public void intTest() { Integer t1 = 128; Integer t2 = 127; ...

  4. java中Integer常量池

    我们先看一个关于Integer的例子 public static void main(String[] args) { // TeODO Auto-generated method stu Integ ...

  5. Java堆/栈/常量池以及String的详细详解(转)------经典易懂系统

    一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部.但是寄存器的数量极其有限,所以寄存器由编译器根据 ...

  6. JAVA String介绍、常量池及String、StringBuilder和StringBuffer得区别. 以及8种基本类型的包装类和常量池得简单介绍

    一.概述 String是代表字符串的类,本身是一个最终类,使用final修饰,不能被继承. 二.定义方式   方式一:直接赋值法 String str1 = "hello"; 方式 ...

  7. 0024 Java学习笔记-面向对象-包装类、对象的比较、String常量池问题

    包装类 基本类型-->包装类 byte-->Byte short-->Short int-->Integer long-->Long char-->Characte ...

  8. java中的堆、栈、常量池以及String类型的两种声明

    参考自http://blog.sina.com.cn/s/blog_798b04f90100ta67.html http://www.cnblogs.com/fguozhu/articles/2661 ...

  9. java基础进阶一:String源码和String常量池

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/8046564.html 邮箱:moyi@moyib ...

随机推荐

  1. 在DBGrid中可选中行而又可进入编辑状态

    如何在DBGrid中选中行,而又让它可以进入编辑状态? 也许你会问我这有什么用?呵呵,做数据库应用的兄弟们会深有感触,当用DBGrid显示的字段过多时,用户不得不拉动最下面的滚动条,去看最右边的东西, ...

  2. Bootstrap 轮播图的使用和理解

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...

  3. BZOJ 1070 修车(费用流)

    如果能想到费用流,这道题就是显然了. 要求所有人的等待平均时间最小,也就是所有人的总等待时间最小. 每辆车只需要修一次,所以s连每辆车容量为1,费用为0的边. 现在需要把每个人拆成n个点,把车和每个人 ...

  4. Javascript-基础2

    1. Javascript 字符串里面的方法: obj.length 长度 obj.trim() 移除空白 obj.trimLeft() obj.trimRight) obj.charAt(n) 返回 ...

  5. BZOJ4566:[HAOI2016]找相同字符——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4566 https://www.luogu.org/problemnew/show/P3181 给定 ...

  6. HDU5726:GCD——题解

    题目:hdu的5726 (我原博客的东西,正好整理过来,属于st表裸题) (可以看出我当时有多么的菜--) 这道题写了一遍,然而蒟蒻的我的时间爆炸了-- 于是看了一下学长的代码(顺便在此处%一下学长) ...

  7. 洛谷4245:【模板】任意模数NTT——题解

    https://www.luogu.org/problemnew/show/P4245 给两个多项式,求其乘积,每个系数对p取模. 参考: 代码与部分理解参考https://www.luogu.org ...

  8. 洛谷 P2898 [USACO08JAN]haybale猜测Haybale Guessing 解题报告

    [USACO08JAN]haybale猜测Haybale Guessing 题目描述 给一段长度为\(n\),每个位置上的数都不同的序列\(a[1\dots n]\)和\(q\)和问答,每个问答是\( ...

  9. 浴谷夏令营例题Codeforces827DBest Edge Weight(三个愿望,一次满足~(大雾

    这题在浴谷夏令营wyx在讲的最小生成树的时候提到过,但并没有细讲怎么写... 这题可以用三种写法写,虽然只有两种能过...(倍增/倍增+并查集/树链剖分 先跑出最小生成树,分类讨论,在MST上的边,考 ...

  10. pexpect正确的打开方式

    今天在写一个Python脚本时遇到一个问题——同样的代码,在Python终端执行没问题,在脚本里执行却不行,就是下面这段代码: import pexpect password = 'test2014' ...