深入理解java虚拟机中的常量池源码分析
常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = “java”这种申明方式;当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。
Java是一种动态链接的语言,常量池的作用非常重要,常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:
类和接口的全限定名;
字段的名称和描述符;
方法和名称和描述符。
java常量池技术
java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,不同于使用new关键字创建的对象所在的堆空间。
常量池的好处
常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
public static void main(String[] args) {
String str1 = new String("wwe");
String str2 = new String("wwe");
System.out.println(str1 == str2);
System.out.println(str1.intern() == str2.intern());
Integer a1 = 1; Integer a2 = new Integer(1); System.out.println(a1.equals(a2)); System.out.println(a1 instanceof Integer);}
输出:false true true true
1.false 。 String str1 = new String("wwe"); String str2 = new String("wwe"); System.out.println(str1 == str2);类中的 ‘==’ 比较的是内存地址,通过new生成的对象会在堆中创建一个新的对象,内存地址明显不同。
2.trueSystem.out.println(str1.intern() == str2.intern());。String.intern(),比较的是常量池中的值。当常量池中不存在“wwe”时,会在常量池中新建一个常量。若存在,则直接返回该常量。
源码介绍如下:
/**
* Returns a canonical representation for the string object.
*
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
*
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
*
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
*
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* The Java™ Language Specification.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
一道思考题 new String("wwe").equals("11去去去") 创建了几个对象
这里创建了1或2 个对象。如果常量池不存在“11去去去”则需要创建一个对象。
3.true System.out.println(a1.equals(a2)); 以下是interger类型的equals函数,不同的复合类是不一样的
/**
* Compares this object to the specified object. The result is
* {@code true} if and only if the argument is not
* {@code null} and is an {@code Integer} object that
* contains the same {@code int} value as this object.
*
* @param obj the object to compare with.
* @return {@code true} if the objects are the same;
* {@code false} otherwise.
*/
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
4.true。 System.out.println(a1 instanceof Integer);
用来在运行时指出对象是否是特定类的一个实例
Integer与常量池
Integer i1 = 40;Integer i2 = 40;Integer i3 = 0;Integer i4 = new Integer(40);Integer i5 = new Integer(40);Integer i6 = new Integer(0);System.out.println("i1=i2 " + (i1 == i2));System.out.println("i1=i2+i3 " + (i1 == i2 + i3));System.out.println("i1=i4 " + (i1 == i4));System.out.println("i4=i5 " + (i4 == i5));System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); System.out.println("40=i5+i6 " + (40 == i5 + i6));i1=i2 truei1=i2+i3 truei1=i4 falsei4=i5 falsei4=i5+i6 true40=i5+i6 true
Integer i1=40;Java在编译的时候会直接将代码封装成Integer i1=Integer.valueOf(40);,从而使用常量池中的对象。Integer i1 = new Integer(40);这种情况下会创建新的对象。语句i4 == i5 + i6,因为+这个操作符不适用于Integer对象,首先i5和i6进行自动拆箱操作,进行数值相加,即i4 == 40。然后Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较。
String与常量池-普通方法赋值
String str1 = "abcd";String str2 = new String("abcd");System.out.println(str1==str2);//falseString str1 = "str";String str2 = "ing";String str3 = "str" + "ing";String str4 = str1 + str2;System.out.println("string" == "str" + "ing");// trueSystem.out.println(str3 == str4);//falseString str5 = "string";System.out.println(str3 == str5);//true
"abcd"是在常量池中拿对象,new String("abcd")是直接在堆内存空间创建一个新的对象。只要使用new方法,便需要创建新的对象。连接表达式 +,只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入常量池中。对于字符串变量的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,其属于在运行时创建的字符串,具有独立的内存地址,所以不引用自同一String对象。
关注头条号每天分享java知识,欢迎留言评论转发私信回复“555”赠送一些Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式资料
查看原文 >>