先用一个问题引入常量池技术。
public class Test2 {
public static void main(String[] args) {
String s1="Hello Java";
String s2="Hello Java";
String s3=new String("Hello Java");
System.out.println(s1==s2);//①
System.out.println(s3==s2);//②
}
}
问:①处的结果是多少?②处的结果是多少?很多初学者被这个问题弄的一头雾水。在java中像这样的问题还有其他的,比如说int与Integer(后面会提到这个)。这些都跟java的常量池有关。
什么是常量池?
常量池说白了就是一块内存区域,这块内存会对一些对象进行缓存,在需要的时候直接将其地址复制给变量,以达到加快效率,节省空间的作用。不仅String类具有常量池技术,而且大部分基本数据类型的包装类(Integer,long,short,byte,Character,Boolean)都实现了包装类。
常量池有什么作用?
常量池将一个类中的字符或者基本数据类型放入缓冲区(各自的常量池)中,如果程序后来想再创建此类对象,则可以直接生成,不需要new一个,这样可以节省时间和空间。
一:字符串常量池
接下来,我将常量池分成两部分,一部分是常见的String的常量池,叫做字符串常量池。这个常量池会存储类中已经初始化的字符串。举个例子:定义一个hello的字符串String s = “hello”;,虚拟机的工作原理是:首先在字符串常量池中寻找有没有“hello”,如果找到了,就将此字符串的地址赋值给s,如果没有找到,就会创建一个(new一个)新的字符串,当程序想创建第二个字符串“hello”的时候,就会直接将此地址赋值给新的变量。所以上面的程序①处输出true。
如果 程序是使用new来创建一个字符串的时候,会直接在堆内存中创建一个字符串对象。所以程序②处输出false。
总结:①当两个字符串没有通过new来创建的话,只要它们的值相同,那么它们进行相等运算(==)时返回true。
②当两个字符串都是使用new来创建的话,进行上述操作,都会返回false,因为它们不是同一个对象,也就不在同一个地址。示例代码:
public class Test4 {
/**
* @param args
*/
public static void main(String[] args) {
String s2=new String("Java");
String s3=new String("Java");
System.out.println(s3==s2);//false
}
}
③当一个字符串使用new来创建,另外一个没有使用new来创建的话,进行上述操作,都会返回false,也是因为它们不是同一个对象。示例代码:见开篇时得代码,程序②处。
接下来不得不提到一个知识点:
String类中有一个intern方法,当调用这个方法时,虚拟机会查找常量池中是否有此字符串,如果查到,就返回常量池中的字符串,如果没有,就返回此字符串。示例代码:
public class Test2 {
public static void main(String[] args) {
String s1="Hello Java";
String s2="Hello Java";
String s3=new String("Hello Java");
//System.out.println(s1==s2);//①
//System.out.println(s3==s2);//②
System.out.println(s1==s3.intern());//③
}
}
还是上面那个示例代码,只是最后一行有所改变,调用了s3的intern方法,此方法返回常量池中的Hello Java,所以程序③处放回true;
Interger类的常量池(-128--+127惹的祸)
废话不多说:直接上代码:
public class Test5 {
/**
* @param args
*/
public static void main(String[] args) {
int i1=128;
Integer i2=128;
System.out.println(i1==i2);//①自动拆箱true
Integer i3=128;
System.out.println(i2==i3);//②常量池技术的体现。-128--+127false
Integer i4=127;
Integer i5=127;
System.out.println(i4==i5);//③常量池技术的体现true
Integer i6=new Integer(128);
Integer i7=new Integer(128);
System.out.println(i6==i7);//④两个不同的对象的体现。false
}
}
程序①,④处很容易得到该结论,根据②的结论,③处也应该和②一样啊,但是为什么是true呢?这里涉及到常量池技术。
原来,在使用Integer i = 一个int型的数字的时候,会自动进行装箱操作,调用了integer类的valueof方法,通过网上搜索和查看源代码,会发现,此方法内部使用了一个缓冲技术,也就是常量池,但是此常量池只会对—128到+127之间的int型数字进行插入缓存的操作。当后续代码中出现类似前面的把一个int型数据赋值给Integer的时候,会直接从缓存里面将该值赋给变量。程序③处为127,小于128,所以会把i4存入常量池,当定义i5的时候,会把127赋值给i5,所以他们是同一个数,程序②处根据上面可知,他们会调用new来创建两个Integer对象。所以是false。
结论:①两个都是new出来的话,他们进行“==”操作时,一定返回false;
②一个是new出来的,一个是int型的,进行“==”操作,一定放回true,此时会进行拆箱。
③ 一个new出来的,另一个是将int赋值的,肯定返回false,因为此时不会拆箱操作。
④两个都是讲int型的赋值给Integer的话,如果int值介于-128到+127时,放回true,其它的返回false。
其它的使用到常量池技术的:
public class Test6 {
/**
* @param args
*/
public static void main(String[] args) {
Long l1=127l;
Long l2=127l;
System.out.println(l1==l2);//①true
Long l3=128l;
Long l4=128l;
System.out.println(l3==l4);//②false Long型与Short型与Integer一样介于-128---+127之间
Boolean b1=false;
Boolean b2=false;
System.out.println(b1==b2);//③true Boolean实现了常量池技术
Float f1=1.0f;
Float f2=1.0f;
System.out.println(f1==f2);//④false Float与Double型没有实现常量池技术。
}
}
希望通过讲解,可以对常量池技术有一定的了解