• Java 语言设计中的部分共享策略


    Java 语言的设计者认为共享带来的效率远远高于提取、拼接字符串所带来的低效率。    ——Core Java

     

      在之前的学习和使用过程中,遇到过字符串常量池的概念,对于整形,在其源码中也有缓存数组的概念。其实这些类似概念,都是JVM对获取常用的字符串、整形对象这一操作所做的优化。

    下面我们来分析下创建字符串的两种方式 

     1         String s1 = "hello";
     2         String s2 = "hello";
     3         String s3 = new String("hello");
     4      // equals比较字符串字符值是否相等,均为 true 
     5         System.out.println(s1.equals(s2));
     6         System.out.println(s2.equals(s3));
     7         System.out.println(s1.equals(s3));
     8      
     9         System.out.println(s1==s2); // true
    10         System.out.println(s1==s3); // false
    11         System.out.println(s2==s3); // false 

    对于第一种方式

      用字面值的方式创建一个字符串时,JVM首先会去字符串常量池中查找是否存在"hello"这个对象,如果不存在,则在字符串常量池中创建"hello"这个对象,然后将池中"hello"这个对象的引用地址返回给"hello"对象的引用s1,这样s1会指向字符串常量池中"hello"这个字符串对象;如果存在,则不创建任何对象,直接将池中"hello"这个对象的地址返回,赋给引用s2。因为s1、s2都是指向同一个字符串池中的"hello"对象,所以“==”比较结果true。

    对于第二种方式

      采用new关键字新建一个字符串对象时,JVM会首先在字符串池中查找有没有"hello"这个字符串对象,如果有,则不在池中再去创建"hello"这个对象了,直接在堆中创建一个"hello"字符串对象,然后将堆中的这个"hello"对象的地址返回赋给引用s3,这样,s3就指向了堆中创建的这个"hello"字符串对象;如果没有,则首先在字符串池中创建一个"hello"字符串对象,然后再在堆中创建一个"hello"字符串对象,然后将堆中这个"hello"字符串对象的地址返回赋给s3引用,这样,s3指向了堆中创建的这个"hello"字符串对象。所以在进行“==”比较的时候,s1 和 s2 都是指向常量池中的字符串对象。s3 是指向堆中字符串对象。结果肯定为false。

    考虑下面的整形恒等判断的代码

    1         Integer a = Integer.valueOf(3);
    2         Integer b = Integer.valueOf(3);
    3 
    4         Integer c = Integer.valueOf(323);
    5         Integer d = Integer.valueOf(323);
    6 
    7         System.out.println(a==b);
    8         System.out.println(c==d);

    输出结果为

    1 ```
    2 true
    3 false
    4 ```
    为什么会有这样的输出结果呢?
    点击查看Integer.valueOf()方法的源码,发现:
        @HotSpotIntrinsicCandidate
        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
      其方法注释的意思是:返回表示指定 Integer 的实例。如果不需要新的 integer 实例,则通常应优先使用此方法,而不是使用构造函数 integer(int),因为通过缓存频繁请求值,此方法可能会显著提高空间和时间性能。此方法将始终缓存范围为-128到127(含)的值,并可能缓存此范围之外的其他值。
     
      再去看看IntegerCache类的源码,发现在这个静态内部类声明了一个Integer cache[],对于-128~127的整形,也就是占用1个字节的整形来说,如果用Integer.valueOf()方法进行初始化,就会从这个缓存数组中取值,因为JVM对这种操作进行了优化,,所以可以用一个缓存数组进行速度上的提升。
    再看看下面一段代码:
    1         Integer e  = 42;
    2         Integer f = 42;
    3 
    4         Integer g  = 423;
    5         Integer h = 423;
    6         
    7         System.out.println(e==f); // true
    8         System.out.println(g==h); // false
     
    让我们来分析一下产生如此结果之原因,对于 e, f 他们的代码等同于
            Integer e  = 42; // Integer d = Integer.valueOf(42);
            Integer f = 42;  // Integer d = Integer.valueOf(42);
    所以 e==f 返回true;对于 g 和 h
    1         Integer g  = 423; 
    2         Integer h = 423;    
    由于423超过了-128~127(含)的范围,所以不会再去使用缓存数组中的整形值,而是使用了:
    1         Integer g = new Integer(423);
    2         Integer h = new Integer(423); 

      都是使用了 new 关键字进行整形的创建,g 和 h指向的是堆中两个不同的地址,他们进行恒等判断结果当然就是false。IntegerCache 类的 cache[] 数组和数组字符串常量池有异曲同工之妙。

     

     

     

     

  • 相关阅读:
    URAL——DFS找规律——Nudnik Photographer
    URAL1353——DP——Milliard Vasya's Function
    URAL1203——DPor贪心——Scientific Conference
    递推DP HDOJ 5389 Zero Escape
    区间DP UVA 1351 String Compression
    树形DP UVA 1292 Strategic game
    Manacher HDOJ 5371 Hotaru's problem
    同余模定理 HDOJ 5373 The shortest problem
    递推DP HDOJ 5375 Gray code
    最大子序列和 HDOJ 1003 Max Sum
  • 原文地址:https://www.cnblogs.com/dogeLife/p/11154475.html
Copyright © 2020-2023  润新知