• Java基础二:常量池


    目录:

    1. 自动装箱与拆箱
    2. 常量池
    3. ==与equals()区别

       1. 自动装箱与拆箱

      Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),如int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。 
    Java 为每个基本类型提供了包装类型: 
    • 原始类型: boolean,char,byte,short,int,long,float,double
    • 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

      首先看一个简单例子:

     1 public class AutoUnboxingTest {
     2  public static void main(String[] args) {
     3         Integer a = new Integer(3);
     4         Integer b = 3;                  // 将3自动装箱成Integer类型
     5         int c = 3;
     6         System.out.println(a == b);     // false 两个引用没有引用同一对象
     7         System.out.println(a == c);     // true a自动拆箱成int类型再和c比较
     8        
     9         Integer f1 = 3, f2 = 3, f3 = 150, f4 = 150;
    10         System.out.println(f1 == f2);//true
    11         System.out.println(f3 == f4);//false
    12        
    13         Integer p1 = new Integer(3);
    14         Integer p2 = new Integer(3);
    15         Integer p3 = new Integer(0);
    16         System.out.println(p1 == p2);//false,两个不同的对象
    17         System.out.println(p1 == p2+p3); //true p2和p3自动拆箱为int类型,p1也会自动拆箱,本质为基本数据类型比较
    18     }
    19 }

       用new关键字创建一个新对象,它们的内容可以相同,但其内存中存放的地址不同。而==对对象来说,是比较的内存地址,不同的对象自然不同,故(a、b)(p1、p2)比较为false。那么f1、f2、f3、f4四个变量都是Integer对象引用,为什么会出现一个false一个true呢?这就涉及装箱的本质,当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,想知道发生了什么,直接上源码:

     1 /**
     2      * Returns an {@code Integer} instance representing the specified
     3      * {@code int} value.  If a new {@code Integer} instance is not
     4      * required, this method should generally be used in preference to
     5      * the constructor {@link #Integer(int)}, as this method is likely
     6      * to yield significantly better space and time performance by
     7      * caching frequently requested values.
     8      *
     9      * This method will always cache values in the range -128 to 127,
    10      * inclusive, and may cache other values outside of this range.
    11      *
    12      * @param  i an {@code int} value.
    13      * @return an {@code Integer} instance representing {@code i}.
    14      * @since  1.5
    15      */
    16     public static Integer valueOf(int i) {
    17         assert IntegerCache.high >= 127;
    18         if (i >= IntegerCache.low && i <= IntegerCache.high)
    19             return IntegerCache.cache[i + (-IntegerCache.low)];
    20         return new Integer(i);
    21     }

      其调用了Integer的内部类IntegerCache,源码如下:

     1 /**
     2      * Cache to support the object identity semantics of autoboxing for values between
     3      * -128 and 127 (inclusive) as required by JLS.
     4      *
     5      * The cache is initialized on first usage.  The size of the cache
     6      * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
     7      * During VM initialization, java.lang.Integer.IntegerCache.high property
     8      * may be set and saved in the private system properties in the
     9      * sun.misc.VM class.
    10      */
    11     private static class IntegerCache {
    12         static final int low = -128;
    13         static final int high;
    14         static final Integer cache[];
    15         static {
    16             // high value may be configured by property
    17             int h = 127;
    18             String integerCacheHighPropValue =
    19                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    20             if (integerCacheHighPropValue != null) {
    21                 int i = parseInt(integerCacheHighPropValue);
    22                 i = Math.max(i, 127);
    23                 // Maximum array size is Integer.MAX_VALUE
    24                 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    25             }
    26             high = h;
    27             cache = new Integer[(high - low) + 1];
    28             int j = low;
    29             for(int k = 0; k < cache.length; k++)
    30                 cache[k] = new Integer(j++);
    31         }
    32         private IntegerCache() {}
    33     }

      源码中标红的部分是重点,在第一次自动装箱时,程序即会创建[-128 ~ 127]区间的Integer,存在cache数组中,并且数组和256个Integer对象都在堆内存中。所有在该区间的比较对应结果为true,故(f1、f2)为true;超出该区间将重新创建对象,故(f3、f4)为false。其实这就涉及常量池的概念了。

       2. 常量池

      常量池是JVM的一种内存空间,用来存放编译后确定的数据文本形式的符号引用,其采用11种常量表进行存储(后续博客介绍),并且对6种基本数据类型(对一定范围内的数值)和String等都有常量池的应用,用来快速创建对应的对象。

      其中6种基本数据类型(原理与上边一致,另外两种浮点数类型的包装类则没有实现)常量范围如下:

    类型 范围
    Boolean true false
    Character 'u0000' to 'u007F'(0-127)
    Byte all byte values are cached
    Short -128 ~ 127
    Integer -128 ~ 127
    Long -128 ~ 127

      百度百科对常量池的介绍:Java是一种动态链接的语言,常量池的作用非常重要,常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:类和接口的全限定名;字段的名称和描述符;方法的名称和描述符。在源码编译成字节码时,存在一块叫做“Constant pool”的区域,用来存放常量和符号引用等。

      下一篇博客将介绍String与常量池的内容。

      3. ==与equals()区别

      Java中数据类型分为两类:
      基本数据类型,采用==比较的是它们的值;
      复合数据类型(类),采用==比较的是它们的内存地址。
      Java中多数的类都继承于Object基类,其中有定义equals()方法,该方法的初始行为是比较对象的内存地址。一些类库重写了该方法,如String、Integer、Date等,equals()直接对其值进行比较,而不是类在堆内存中的存放地址;对没有重写该方法的类,比较的仍然是内存地址,相当于==。上源码:

      Object中的equals()方法:

    1 public boolean equals(Object obj) {
    2         return (this == obj);
    3     }

       String重写后(对每个char进行比较):

     1 /**
     2      * Compares this string to the specified object.  The result is {@code
     3      * true} if and only if the argument is not {@code null} and is a {@code
     4      * String} object that represents the same sequence of characters as this
     5      * object.
     6      *
     7      * @param  anObject
     8      *         The object to compare this {@code String} against
     9      *
    10      * @return  {@code true} if the given object represents a {@code String}
    11      *          equivalent to this string, {@code false} otherwise
    12      *
    13      * @see  #compareTo(String)
    14      * @see  #equalsIgnoreCase(String)
    15      */
    16     public boolean equals(Object anObject) {
    17         if (this == anObject) {
    18             return true;
    19         }
    20         if (anObject instanceof String) {
    21             String anotherString = (String) anObject;
    22             int n = value.length;
    23             if (n == anotherString.value.length) {
    24                 char v1[] = value;
    25                 char v2[] = anotherString.value;
    26                 int i = 0;
    27                 while (n-- != 0) {
    28                     if (v1[i] != v2[i])
    29                             return false;
    30                     i++;
    31                 }
    32                 return true;
    33             }
    34         }
    35         return false;
    36     }

      

    全文总结:

    •  自动装箱与拆箱,涉及基本数据类型的常量池(一定范围内);
    •  常量池的概念,后续会在介绍Java内存模型时详细介绍;
    • ==与equals()方法的区别,会在面试中问到,尤其涉及String,下一篇博客再详细阐述。
  • 相关阅读:
    Asp.Net MVC 常用开发方式之EF Code First
    整理一下Entity Framework的查询
    C#中yield return用法分析
    SQL Server表和字段说明的增加和更新
    C#中一个问号和两个问号(a ?? b)的作用
    你应该知道的25道Javascript面试题
    ASP.NET Core Razor 页面路由
    ASP.NET Core MVC – Tag Helper 组件
    ASP.NET Core 防止跨站请求伪造(XSRF/CSRF)攻击
    ASP.NET Core 使用Cookie验证身份
  • 原文地址:https://www.cnblogs.com/shuimuzhushui/p/6607995.html
Copyright © 2020-2023  润新知