new String、声明String、String变量常量相加的区别详解

new String、声明String、String变量常量相加的区别详解

1、new String和声明String区别和实现过程

1)String str1 = "abcd"的实现过程:首先栈区创建str引用,然后在String池(独立于栈和堆而存在,存储不可变量)中寻找其指向的内容为"abcd"的对象,如果String池中没有,则创建一个,然后str指向String池中的对象,如果有,则直接将str1指向"abcd";

如果后来又定义了字符串变量 str2 = “abcd”,则直接将str2引用指向String池中已经存在的“abcd”,不再重新创建对象;当str1进行了赋值(str1=“abc”),则str1将不再指向"abcd",而是重新指String池中的"abc",此时如果定义String str3 = “abc”,进行str1 == str3操作,返回值为true,因为他们的值一样,地址一样,但是如果内容为"abc"的str1进行了字符串的+连接str1 = str1+“d”;此时str1指向的是在堆中新建的内容为"abcd"的对象,即此时进行str1==str2,返回值false,因为地址不一样。

2)String str3 = new String(“abcd”)的实现过程:先去常量池中查找有没有这个字符串,没有的话在常量池中创建,并在堆中也创建。如果常量池中有这个字符串就不会在常量池中创建,但依然还是会在堆中创建。返回的都是是堆中分配的空间。如果后来又有String str4 = new String(“abcd”),str4不会指向之前的对象,而是重新创建一个对象并指向它,所以如果此时进行str3==str4返回值是false,因为两个对象的地址不一样,如果是str3.equals(str4),返回true,因为内容相同。因此,new出来的永远指向堆中的空间,和声明出来的==的结果肯定是false

从编译角度分析两者区别:

**String str1 = “abcd”:**在编译期,JVM会去常量池来查找是否存在“abcd”,如果不存在,就在常量池中开辟一个空间来存储“abcd”;如果存在,就不用新开辟空间。然后在栈内存中开辟一个名字为str1的空间,来存储“abcd”在常量池中的地址值。

String str3 = new String(“abcd”): 在编译阶段JVM先去常量池中查找是否存在“abcd”,如果过不存在,则在常量池中开辟一个空间存储“abcd”。在运行时期,通过String类的构造器在堆内存中new了一个空间,然后将String池中的“abcd”复制一份存放到该堆空间中,在栈中开辟名字为str2的空间,存放堆中new出来的这个String对象的地址值。 内存图: img 说明:

首先,通过main()方法进栈。

然后再栈中定义一个对象s1,去堆中开辟一个内存空间,将内存空间的引用赋值给s1,“hello”是常量,然后去字符串常量池 查看是否有hello字符串对象,没有的话分配一个空间存放hello,并且将其空间地址存入堆中new出来的空间中。

在栈中定义一个对象s2,然后去字符串常量池中查看是否有”hello”字符串对象,有,直接把”hello”的地址赋值给s2.

即s1中存的是堆中分配的空间,堆中分配的空间中存的是字符串常量池中分配空间存放”hello”的空间的地址值。而s2中之间存的是字符串常量池中分配空间存放”hello”的空间的地址值。

由于s1与s2中存放的地址不同,所以输出false。因为,类String重写了equals()方法,它比较的是引用类型的 的值是否相等,所以输出true。即结果为false、true。

验证:

  String s1=new String("xyz");
  String s2="xyz";
  Boolean a1 = s1.equals(s2);
  Boolean a2=(s1==s2);
  System.out.println(a1+" "+a2);//true false

  String b1="qwe";
  String b2 = new String("qwe");
  String b3 = b2.intern();
  Boolean c1=b1.equals(b2);
  Boolean c2=(b1==b2);
  Boolean c3=(b1==b3);
  System.out.println(c1+" "+c2+" "+c3);//true false true

c3是true说明:b2(堆中分配的空间)中存放的字符串常量池中分配空间存放“qwe“的空间的地址值(b3)与b1中存放的字符串常量池中分配空间存放”qwe“的地址一致。

即new之前会在常量池中查找是否存在这个字符串,如果存在,就直接把常量池中的地址值存放在新建的堆空间对象中。

inter方法:

JDK1.6中的intern: 调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个String值的引用;如果不存在的话,则会将原先堆中的该字符串拷贝一份到常量池中。

JDK1.7中的intern: 调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个String值的引用;如果不存在的话,则只会将原先堆中该字符串的引用放置在常量池中,并不会将拷贝整个字符串到常量池中。 这也就说明,JDK1.6和JDK1.7对于常量池中不存在此字符串的情况处理不同。

2、拼接、相加问题:

String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
System.out.println(s3 == s1 + s2);// false
System.out.println(s3.equals((s1 + s2)));// true
System.out.println(s3 == "hello" + "world");//true
System.out.println(s3.equals("hello" + "world"));// true
System.out.println(s3 == s1 + "world");//false

代码详解:

  1. equals()比较方法不解释,比较值,均相等,均为true。
  2. s1与s2相加是先在字符串常量池中开一个空间,然后拼接,这个空间的地址就是s1与s2拼接后的地址。与s3的地址不同,所以输出为false。
  3. s3与”hello”+”world”作比较,”hello”+”world”先拼接成“helloworld”,然后再去字符串常量池中找是否有”helloworld”,有,所以和s3共用一个字符串对象,则为true。
  4. 如果拼接的一个是变量一个是常量,依然会在常量池中开一个新的空间,然后拼接,即只要拼接中有变量都会开辟一个新的空间,所以s3 == s1 + "world"是false

3、总结

  • String s = new String(“hello”)会创建2(1)个对象,String s = “hello”创建1(0)个对象。
    注:当字符串常量池中有对象hello时括号内成立!
  • 字符串如果是变量相加(只要有变量),先开空间,再拼接。
    “hello”创建1(0)个对象。
    注:当字符串常量池中有对象hello时括号内成立!
  • 字符串如果是变量相加(只要有变量),先开空间,再拼接。
  • 字符串如果是常量相加,是先加,然后在常量池找,如果有就直接返回,否则,就创建。

猜你喜欢

转载自blog.csdn.net/weixin_45759791/article/details/107780923
今日推荐