JAVA String对象的创建

String对象的创建是比较特殊的,普通对象创建都是在堆中创建,String对象确不一定,下面看一段代码

public class StringTest1 {
	public static void main(String[] args) throws Exception{
		String a = "abc";
		String b = "abc";
		String c = "ab";
		String d = new String("ab");
		System.out.println("a=" + a);				//a=abc
		System.out.println("b=" + b);				//b=abc
		System.out.println("c=" + c);				//c=ab
		System.out.println("d=" + d);				//d=ab
		System.out.println("a==b is " + (a == b));	//a==b is true
		System.out.println("c==d is " + (c == d));	//c==d is false
		
		Field field = String.class.getDeclaredField("value");
		field.setAccessible(true);
		
		char[] valueA = (char[]) field.get(a);
		char[] valueB = (char[]) field.get(b);
		char[] valueC = (char[]) field.get(c);
		char[] valueD = (char[]) field.get(d);
		System.out.println("a.value(" + valueA.hashCode() + ")=" + Arrays.toString(valueA));	
		//a.value([1829164700)=[a, b, c]
		
		System.out.println("b.value(" + valueB.hashCode() + ")=" + Arrays.toString(valueB));	
		//b.value([1829164700)=[a, b, c]
		
		System.out.println("c.value(" + valueC.hashCode() + ")=" + Arrays.toString(valueC));	
		//c.value([2018699554)=[a, b]
		
		System.out.println("d.value(" + valueD.hashCode() + ")=" + Arrays.toString(valueD));	
		//d.value([2018699554)=[a, b]
		
		valueA[0] = 'z';
		valueD[0] = 'x';
		System.out.println("a=" + a);				//a=zbc
		System.out.println("b=" + b);				//b=zbc
		System.out.println("c=" + c);				//c=xb
		System.out.println("d=" + d);				//d=xb
	}
}


a跟b都是“abc”,测试结果a跟b的引用是相等的,value数组也是相等的,改变a的第一位,b也跟着变了。

c跟d都是“ab”,但是两个引用是不相等的,d是用new创建的,这个时候才会在堆中创建对象,a,b,c三个引用都是直接指向常量池的对象的,但是下面c和d的value似乎又是同一个数组,改变d的第一位,c也变了,说明d在堆中创建的对象还是指向常量池的,所以用new的话就多创建了个对象。


还有一个经典的问题, String a = "a" + "b";创建了几个对象。

一般来说应该是3个吧,一个"a",一个"b",一个结果"ab",下面写一个简单的类

public class StringTest2 {
	@SuppressWarnings("unused")
	public static void main(String[] args){
		String a = "a" + "b";
	}
}

使用javap看一下字节码

javap -c StringTest2.class
Compiled from "StringTest2.java"
public class test.StringTest2 {
  public test.StringTest2();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":
()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #16                 // String ab
       2: astore_1
       3: return
}

只有一个指令ldc           #16 ,从常量池里加常量#16,很明显这个常量就是ab,我就不去解析class文件了,应该是没错,直接反编译一下,得到的结果是

package test;

public class StringTest2
{
  public static void main(String[] args)
  {
    String a = "ab";
  }
}

所以引用直接指向常量池"ab"了,就一个对象吗,可能是编译器优化的结果吧,如果有时间去单独把class文件一个字节一个字节解析出来结果应该更明了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。