首页 > 代码库 > 字符串类型的对象与引用及字符串常量池详解

字符串类型的对象与引用及字符串常量池详解

package com.smbea.demo.str;

/**
 * 字符串类型的对象与引用及字符串常量池详解
 * @author hapday
 * @2017年2月3日 @下午5:01:59
 */
public class StringDemo {

	public static void main(String[] args) {
		String str = "abc";		// 首先查看字符串常量池中是否有值为“abc”的对象,如果有则直接取出其值,否则在堆内存中创建一个值为“abc”的字符串对象;并在栈内存中开辟一段空间,用于存储该对象的地址,这段内存空间就被称为引用,名称为“str1”,并指向该对象
		System.out.println("str = " + str);
		
		String str1 = "abc";	// 首先查看字符串常量池中是否有值为“abc”的对象,如果有则直接取出其值,否则在堆内存中创建一个值为“abc”的字符串对象;并在栈内存中开辟一段空间,用于存储该对象的地址,这段内存空间就被称为引用,名称为“str1”,并指向该对象
		System.out.println("\nstr1\t\t\t" + str1);
		System.out.println("str == str1\t\t" + (str == str1) );
		System.out.println("str.equals(str1)\t" + str.equals(str1));
		System.out.println("str == str1.intern()\t" + (str == str1.intern()));		// 方法 intern() 的作用是返回字符串对象在字符串常量池中的引用,如果常量池中没有值与此相同的字符串对象则将此对象放入到常量池中并返回此对象的引用

		String str2 = new String("abc");	// 首先查看字符串常量池中是否有值为“abc”的对象,如果有则直接取出其值,并创建一个值为“abc”的字符串对象,这个对象指的是参数中传递的字符串类型的数据,而非引用名称为“str2”所指向的对象;否则在堆内存中创建一个值为“abc”的对象,再创建一个对象,并将前面的对象值复制给后面对象的值,后面对象的引用为“str2”
		System.out.println("\nstr2\t\t\t" + str2);
		System.out.println("str == str2\t\t" + (str == str2) );
		System.out.println("str.equals(str2)\t" + str.equals(str2));
		System.out.println("str == str2.intern()\t" + (str == str2.intern()));

		String str3 = new String("abc");	// 首先查看字符串常量池中是否有值为“abc”的对象,如果有则直接取出其值,并创建一个值为“abc”的字符串对象,这个对象指的是参数中传递的字符串类型的数据,而非引用名称为“str3”所指向的对象;否则在堆内存中创建一个值为“abc”的对象,再创建一个对象,并将前面的对象值复制给后面对象的值,后面对象的引用为“str3”
		System.out.println("\nstr\t\t\t" + str3);
		System.out.println("str == str3\t\t" + (str == str3) );
		System.out.println("str.equals(str3)\t" + str.equals(str3));
		System.out.println("str == str3.intern()\t" + (str == str3.intern()));
		System.out.println("\nstr3\t\t\t" + str3);
		System.out.println("str2 == str3\t\t" + (str2 == str3) );
		System.out.println("str2.equals(str3)\t" + str2.equals(str3));
		System.out.println("str2.intern() == str3.intern()\t\t" + (str == str3.intern()));
		
		String str4 = "123";	// 首先查看字符串常量池中是否有值为“abc”的对象,如果有则直接取出其值,否则在堆内存中创建一个值为“abc”的字符串对象;并在栈内存中开辟一段空间,用于存储该对象的地址,这段内存空间就被称为引用,名称为“str4”,并指向该对象
		System.out.println("\nstr4\t\t\t" + str4);
		System.out.println("str == str4\t\t" + (str == str4) );
		System.out.println("str.equals(str4)\t" + str.equals(str4));
		System.out.println("str == str4.intern()\t" + (str == str4.intern()));
		
		String str5 = new String("_idea");		// 首先查看字符串常量池中是否有值为“abc”的对象,如果有则直接取出其值,并创建一个值为“abc”的字符串对象,这个对象指的是参数中传递的字符串类型的数据,而非引用名称为“str5”所指向的对象;否则在堆内存中创建一个值为“abc”的对象,再创建一个对象,并将前面的对象值复制给后面对象的值,后面对象的引用为“str5”
		System.out.println("\nstr5\t\t\t" + str5);
		System.out.println("str == str5\t\t" + (str == str5) );
		System.out.println("str.equals(str5)\t" + str.equals(str5));
		System.out.println("\nstr5\t\t\t" + str5);
		System.out.println("str2 == str5\t\t" + (str2 == str5) );
		System.out.println("str2.equals(str5)\t" + str2.equals(str5));
		System.out.println("str == str5.intern()\t" + (str == str5.intern()));
		
		String str6;		// 声明了一个字符串类型的引用,名称为“str6”,但是并未在栈中开辟内存空间
		str6 = null;		// 在栈中为名称为“str6”的引用开辟内存空间,并将其执行 null 对象
		System.out.println("\nstr6\t\t\t" + str6);
		
		str6 = "雨尘" + "点滴";	// 在堆内存中创建了三个对象,分别是“雨尘”、“点滴”和“雨尘点滴”,最后将引用“str6”指向了“雨尘点滴”对象
		System.out.println("\nstr6\t\t\t" + str6);
		System.out.println("str == str6.intern()\t" + (str == str6.intern()));
		
		String str7 = "a" + "b" + "c";		// 由于字符串常量池中已有值为“abc”的对象,则直接将引用“str7”指向了字符串常量池中的值为“abc”的对象
		System.out.println("\nstr7\t\t\t" + str7);
		System.out.println("str == str7\t\t" + (str == str7) );
		System.out.println("str.equals(str7)\t" + str.equals(str7));
		System.out.println("str == str7.intern()\t" + (str == str7.intern()));
		
		String str8 = "a";		// 编译器并不能确认对象“a”为常量
		String str9 = str8 + "bc";
		System.out.println("\nstr9\t\t\t" + str9);
		System.out.println("str == str9\t\t" + (str == str9) );
		System.out.println("str.equals(str9)\t" + str.equals(str9));
		System.out.println("str == str9.intern()\t" + (str == str9.intern()));

		final String str10 = "a";	// 由于用 final 限定了此对象的值不可改变,编译器可以确认对象“a”是常量(个人觉得在这一点上编译器设计的不够智能,因为 String 类型的变量本身已经是 final 类型的了,比较好的做法是不需要我们再显式的为其限定,也许编译器设计者是从更宽泛的角度考虑问题的,不关注是不是 String 类型的,统一对待各种引用类型)
		String str11 = str10 + "bc";
		System.out.println("\nstr11\t\t\t" + str11);
		System.out.println("str == str11\t\t" + (str == str11) );
		System.out.println("str.equals(str11)\t" + str.equals(str11));
		System.out.println("str == str11.intern()\t" + (str == str11.intern()));

		final String str12 = getA(str10);		// 虽然将引用“str11”所指向的对象显式的设定为了 final 的,但是由于是通过方法取值的,在编译阶段并不能确定具体的返回值,故不作为常量对待
		String str13 = str12 + "bc";
		System.out.println("\nstr13\t\t\t" + str13);
		System.out.println("str == str13\t\t" + (str == str13) );
		System.out.println("str.equals(str13)\t" + str.equals(str13));
		System.out.println("str == str13.intern()\t" + (str == str13.intern()));
		

		String str14 = "bc";
		String str15 = str8 + str14;	// 如果引用“str8”和“str14”所指向的对象都是常量,则不再创建新的对象,而这里不存在这种情况,故创建新的对象并将地址传给引用“str15”
		System.out.println("\nstr15\t\t\t" + str15);
		System.out.println("str == str15\t\t" + (str == str15) );
		System.out.println("str.equals(str15)\t" + str.equals(str15));
		System.out.println("str == str15.intern()\t" + (str == str15.intern()));
		
		// 字符串类型的对象通过  new String() 或  new String("") 创建的对象值都为空字符""
		System.out.println("\n\"\".equals(new String())\t\t" + "".equals(new String()));
		System.out.println("\"\".equals(new String(\"\")\t" + "".equals(new String("")));
		System.out.println("\"\" == new String())\t\t" + ("" == new String()));
		System.out.println("\"\" == new String(\"\"))\t\t" + ("" == new String("")));
		System.out.println("null == new String())\t\t" + (null == new String()));
		System.out.println("null == new String(\"\"))\t\t" + (null == new String("")));
		
		int num;		// 声明了一个整型的变量,此时并未在栈中开辟内存空间
		num = 3;		// 在栈中为整型变量 num 开辟内存空间并赋值
		System.out.println("\nnum\t\t\t" + num);
	}

	private static final String getA(String str){
		return str;
	}
}

  

字符串类型的对象与引用及字符串常量池详解