Java 常量池

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

 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。spa

final类型若是不赋值是要报错的,若是这样赋值没有报错3d

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

再看一下下面的状况调试

{ qwe=2; } public int qwe;

若是打应qwe的值是为2,由于断点调试的时候public int 这行没有执行。再看一种状况code

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

若是打印qwe的值为1,由于断点调试的时候public int这行执行了。同理若是两个都加上static都同样对象

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

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

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;

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

相关文章
相关标签/搜索