博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 常量池
阅读量:6093 次
发布时间:2019-06-20

本文共 8321 字,大约阅读时间需要 27 分钟。

最近在网上看到一些Android的面试题,关于String和Integer常量池的,就总结了一下,暂时先记录下来,以后说不定能用到

1 public class JavaBase {    2     3     public static final String STRING20; // 常量    4     public static final String STRING21; // 常量    5     static {    6         STRING20 = "hello";    7         STRING21 = "World";    8     }    9    10     public static void main(String[] args) {   11    12         Integer mInteger1 = new Integer("3");   13         Integer mInteger2 = new Integer("3");   14         System.out.println(mInteger1 == mInteger2);// false 创建两个对象   15    16         // 对象无法与数值进行比较,所以对象会自动拆箱变成数值在进行比较   17         int mInteger3 = new Integer("3");   18         Integer mInteger4 = new Integer("3");   19         System.out.println(mInteger3 == mInteger4);// true   20    21         // 首先mInteger6 == (mInteger7+mInteger5),因为+这个操作符不适用于Integer对象,mInteger7   22         // 和mInteger5进行自动拆箱操作,进行数值相加,即mInteger6 ==3。然后Integer对象无法与数值进行直接比较,   23         // 所以mInteger6自动拆箱转为int值3,最终转为3 ==3进行数值比较   24         Integer mInteger5 = new Integer(0);   25         Integer mInteger6 = new Integer(3);   26         Integer mInteger7 = new Integer(3);   27         System.out.println(mInteger6 == (mInteger7 + mInteger5));// true 在栈中计算   28    29         Integer mInteger8 = new Integer(3);   30         Integer mInteger9 = 3;   31         System.out.println(mInteger8 == mInteger9);// false 一个在栈中一个在堆中   32    33         Integer mInteger10 = 3;   34         Integer mInteger11 = 3;   35         System.out.println(mInteger10 == mInteger11);// true 实现了常量池   36    37         // 除Float和Double以外, 其它六种都实现了常量池,但是它们只在大于等于-128并且小于等于127时才使用常量池。   38         Double mDouble0 = 3d;   39         Double mDouble1 = 3d;   40         System.out.println(mDouble0 == mDouble1);// false 没有实现常量池,相当于分别new一个   41    42         Integer mInteger12 = 400;   43         Integer mInteger13 = 400;   44         System.out.println(mInteger12 == mInteger13);// false大于127则在堆中创建,相当于new一个   45    46         // Boolean类也实现了常量池技术   47         Boolean bool1 = true;   48         Boolean bool2 = true;   49         System.out.println(bool1 == bool2);// 输出true   50    51         Boolean bool3 = true;   52         Boolean bool4 = new Boolean(true);   53         System.out.println(bool3 == bool4);// 输出false 一个在常量池中一个在堆中   54    55         // JVM对于字符串常量的"+"连接优化为连接后的值,"hello" + "World"经编译器优化后就已经是helloWorld,在编译期   56         // 字符串常量的值就确定下来。而对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法   57         // 确定的,所以string0 +"World"无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给string1。   58         /**  59          * String string2 = "hello" + "World"会查找常量池中时候存在内容为"helloWorld"字符串对象,如存在则  60          * 直接让string2引用该对象,  61          */   62         String string0 = "hello";   63         String string1 = string0 + "World";   64         String string2 = "hello" + "World";   65         System.out.println(string1 == "helloWorld"); // false   66         System.out.println(string2 == "helloWorld"); // true   67         System.out.println(string1 == string2); // false   68    69         /**  70          * String str = "hello"创建对象的过程  71          *1 首先在常量池中查找是否存在内容为"hello"字符串对象  72          *2 如果不存在则在常量池中创建"hello",并让str引用该对象  73          *3 如果存在则直接让str引用该对象  74          *  75          *String str = new String("hello")创建实例的过程  76          *1 首先在堆中(不是常量池)创建一个指定的对象"hello",并让str引用指向该对象  77          *2 在字符串常量池中查看,是否存在内容为"hello"字符串对象  78          *3 若存在,则将new出来的字符串对象与字符串常量池中的对象联系起来  79          *4 若不存在,则在字符串常量池中创建一个内容为"hello"的字符串对象,并将堆中的对象与之联系起来  80          *intern 方法可以返回该字符串在常量池中的对象的引用,  81          */   82         // string3,string4分别位于堆中不同空间   83         String string3 = new String("hello");   84         String string4 = new String("hello");   85         System.out.println(string3 == string4);// 输出false   86    87         // string5,string6位于池中同一空间,常量池   88         String string5 = "hello";   89         String string6 = "hello";   90         System.out.println(string5 == string6);// 输出true   91    92         // intern首先检查字符串常量池中是否有该对象的引用,如果存在,则将这个引用返回给变量,否则将引用加入并返回给变量。   93         String string7 = new String("hello");   94         String string8 = string7.intern();   95         String string9 = "hello";   96         System.out.println(string8 == string9);// true   97    98         String string10 = "hello";   99         String string11 = new String("hello");  100         System.out.println(string10 == string11);// 输出false 一个在常量池中一个在堆中  101   102         /** 103          * 对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。 104          * 所以此时的string12 + string13和"hello" + "World"效果是一样的。 105          */  106         final String STRING12 = "hello";  107         final String STRING13 = "World";  108         String string14 = STRING12 + STRING13; // 将两个常量用+连接进行初始化  109         String string15 = "helloWorld";  110         System.out.println(string14 == string15);// ture  111   112         String string16 = "hello";  113         String string17 = "World";  114         String string18 = string16 + string17;  115         String string19 = "helloWorld";  116         System.out.println(string18 == string19);// false  117         System.out.println(string18.intern() == string19);// true  118   119         /** 120          * STRING20和STRING21虽然被定义为常量,但是它们都没有马上被赋值。在运算出string22的值之前,他们何时被赋值,以及被赋予什么样的值, 121          * 都是个变数。因此STRING20和STRING21在被赋值之前,性质类似于一个变量。那么string22就不能在编译期被确定,而只能在运行时被创建了。 122          */  123   124         String string22 = STRING20 + STRING21;  125         String string23 = "helloWorld";  126         System.out.println(string22 == string23);// false  127   128         /** 129          * string25 == string24当然不相等,string24虽然也是拼接出来的,但new String("lo")这部分不是已知字面量, 130          * 是一个不可预料的部分,编译器不会优化,必须等到运行时才可以确定结果,结合字符串不变定理,所以地址肯定不同。 131          */  132         String string24 = "Hel" + new String("lo");  133         String string25 = "Hello";  134         System.out.println(string25 == string24);// false  135   136         String string26 = "Hello";  137         System.out.println(string26 == "Hello");// true  138     }  139 }

在上面我们看到Integer在-128~127之间是使用常量池的,如果不在这个区间就不会使用,其实是重新new了一个Integer,我们看一下源码

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);  }

我们看到如果i >= IntegerCache.low && i <= IntegerCache.high就会调用IntegerCache的cache方法,而不会重新new一个integer,继续,我们找到IntegerCache这个类

1 private static class IntegerCache {   2     static final int low = -128;   3     static final int high;   4     static final Integer cache[];   5    6     static {   7         // high value may be configured by property   8         int h = 127;   9         String integerCacheHighPropValue =  10             sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");  11         if (integerCacheHighPropValue != null) {  12             int i = parseInt(integerCacheHighPropValue);  13             i = Math.max(i, 127);  14             // Maximum array size is Integer.MAX_VALUE  15             h = Math.min(i, Integer.MAX_VALUE - (-low) -1);  16         }  17         high = h;  18   19         cache = new Integer[(high - low) + 1];  20         int j = low;  21         for(int k = 0; k < cache.length; k++)  22             cache[k] = new Integer(j++);  23     }  24   25     private IntegerCache() {}  26 }

有一个static的代码块,里面初始化了一些Integer,如果范围在-128~127之间就会从这里面取,如果不在这个范围内就会new一个Integer。

final类型如果不赋值是要报错的,如果这样赋值没有报错

static {    asd="asd";  }    public static final String asd ;    {    qwe="qwe";  }    public final String qwe;

再看一下下面的情况

{      qwe=2;  }    public int qwe;

如果打应qwe的值是为2,因为断点调试的时候public int 这行没有执行。再看一种情况

{      qwe=2;  }    public int qwe=1;

如果打印qwe的值为1,因为断点调试的时候public int这行执行了。同理如果两个都加上static都一样

static {      qwe=2;  }    public static int qwe;

这个结果也是为2,因为断点的时候public那行没有执行,

static {      qwe=2;  }    public static int qwe=1;

这种情况就为1了,因为是按照顺序执行的。如果一个是static一个不是,又会是上面结果

{      qwe=2;  }    public static int qwe=1;

这种情况下结果为2,因为static先执行

{      qwe=2;  }    public static int qwe;

同理这种情况下也为2,尽管调试的时候public那行没有执行,因为是static先执行的。

{      qwe=2;  }    public static final int qwe;

如果上面这样写会报错的,提示qwe没有初始化

static {      qwe=2;  }    public static final int qwe;

同理上面这个结果也为2,

static {      qwe=2;  }    public static final int qwe=1;

那么这种就要报错了。变量如果没有赋初值,在调试的时候就不会执行。

转载地址:http://bmlwa.baihongyu.com/

你可能感兴趣的文章
Highmaps网页图表教程之图表配置项结构与商业授权
查看>>
mysql 5.6.33发布
查看>>
java 获取URL链接 内容
查看>>
Linux 命令详解(二)awk 命令
查看>>
Android动态载入Dex机制解析
查看>>
PostgreSQL数据库中的常见错误
查看>>
jquery 控制 video 视频播放和暂停
查看>>
XCode调试多线程遭遇海森伯效应一例
查看>>
ie6下浮动使绝对定位元素莫名消失的问题
查看>>
FBReaderJ 1.6.3 发布,Android 电子书阅读器
查看>>
Java编程常见问题汇总(四)
查看>>
Hadoop 学习系列(四)之 MapReduce 原理讲解
查看>>
函数throttle、debounce介绍
查看>>
源码阅读:SDWebImage(三)——NSData+ImageContentType
查看>>
十六、类的真正形态
查看>>
spring-cloud Sleuth
查看>>
Python 进阶之路 (十一) 再立Flag, 社区最全的itertools深度解析(下)
查看>>
微信分享,二次分享(移动web端)
查看>>
蚂蚁金服智能推荐引擎解决方案与实践
查看>>
PC比电脑好玩的秘密是什么?答案就是因为有这些神奇的网站!
查看>>