zoukankan      html  css  js  c++  java
  • 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[] 数组和数组字符串常量池有异曲同工之妙。

     

     

     

     

  • 相关阅读:
    IntelliJ IDEA 如何在同一个窗口创建多个项目--超详细教程
    spring IOC原理
    java工作错误总结
    java跬步积累
    简单易懂设计模式——简单工厂模式
    Argo 项目入驻 CNCF,一文解析 Kubernetes 原生工作流
    电子书下载 | 超实用!阿里售后专家的 K8s 问题排查案例合集
    在生产环境中,阿里云如何构建高性能云原生容器网络?(含 PPT 下载)
    SIG Cloud Provider Alibaba 网研会第 2 期顺利召开 | 云原生生态周报 Vol. 46
    提问赠书 | 我们请了 7 位云原生专家,等你来问
  • 原文地址:https://www.cnblogs.com/dogeLife/p/11154475.html
Copyright © 2011-2022 走看看