zoukankan      html  css  js  c++  java
  • 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:UsersAdministratorDesktop>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());// 49
            System.out.println(s2.hashCode());// 49
            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());// 49
            System.out.println(s4.hashCode());// 49
    
        }
    
    }

      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"不会涉及堆,只在常量区检查是否有该常量。

  • 相关阅读:
    经典的HTML5游戏及其源码分析
    Java支付宝支付功能开发
    vue cli4升级sass-loader(v8)后报错
    postman请求参数中文乱码
    spring-boot整合log4j和logback
    java架构师
    IntelliJ IDEA “duplicated code fragment(6 lines long)”提示如何关闭
    在Java聚合工程里使用mybatis-generator
    对spring-boot的自动装配理解
    spring-boot里配置事务管理
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/8759152.html
Copyright © 2011-2022 走看看