java的String创建细节

本文主要讲述String创建的内存分布

示例代码如下:

 1 public class StringExercise {
 2     public static void main(String[] args) {
 3         // 创建方式不止这两种
 4         String s1 = "zwz";
 5         /**
 6          * String类的成员:private final char value[];
 7          * public String(String original) {
 8          *      this.value = original.value;
 9          *      this.hash = original.hash;
10          * }
11          */
12         String s2 = new String ("zwz");
13         System.out.println(s1 == s2); // false
14     }
15 }

内存分布图,如下图所示:

vip

注意:String类的成员:private final char value[],即字符串常量永远存储在常量池中,value是指向字符串常量的地址。

String s1 = "zwz";

zwz字符串存放方法区的常量池中,将其地址直接返回给s1

String s2 = new String ("zwz");

在堆中开辟存储空间,创建String类的对象,value初始化为null,zwz字符串存放方法区的常量池中,并将其地址返回给value,再将String类对象的地址返回给s2。

测试题1,代码如下:

 1 public class StringExercise {
 2     public static void main(String[] args) {
 3         // 创建方式不止这两种
 4         String s1 = "zwz";
 5         String s2 = new String ("zwz");
 6         System.out.println(s1 == s2);// false
 7         // s2.intern()返回value指向的地址,即字符串在常量池中的地址。
 8         System.out.println(s1 == s2.intern()); // true
 9         System.out.println(s2 == s2.intern()); // false
10     }
11 }

注意:s2.intern(),返回value指向的地址,即字符串在常量池中的地址

测试题2,代码如下:

 1 public class StringWork01 {
 2     public static void main(String[] args) {
 3         Person p1 = new Person("tom");
 4         Person p2 = new Person("tom");
 5         System.out.println(p1.getName().equals(p2.getName())); true
 6         System.out.println(p1.getName() == p2.getName() ); // fasle
 7         System.out.println(p1.getName() == "tom"); // fasle
 8     }
 9 }
10 
11 class Person {
12     private String name;
13 
14     public Person(String name) {
15         this.name = new String(name);
16 //        this.name = name;
17     }
18 
19     public String getName() {
20         return name;
21     }
22 
23     public void setName(String name) {
24         this.name = name;
25     }
26 }

注意:在Person类的构造器中,如果是this.name = name,那么直接将常量池中字符串的地址返回给Person类对象的name属性;

如果是this.name = new String(name),那么将常量池中字符串的地址,返回给String类对象的value成员中,并且将该对象的地址返回给Person类对象的name属性。

测试题3,代码如下:

1 public class StringWork02 {
2     public static void main(String[] args) {
3         // public final class String
4         String s1 = "123";
5         s1 = "345";
6     }
7 }

问创建了几个对象?

具体创建过程如图所示:

vip   vip

String类是final类型,不能被继承,不能修改,因此当执行s1 = "345"后,"123"字符串并没有被销毁。

证明见如下代码:

public class StringWork02 {
    public static void main(String[] args) {
        // public final class String
        String s1 = "123";
        System.out.println(s1.hashCode());
        s1 = "345";
        String s2 = "123";
        System.out.println(s2.hashCode());
    }
}
48690
48690
View Code

运行结果来看,"123"字符串常量在常量池中的地址没有发生改变。

 测试题4,代码如下:

 1 public class StringWork03 {
 2     public static void main(String[] args) {
 3         String s = "123"+"456";
 4 
 5         String s1 = "123";
 6         String s2 = "456";
 7         String s3 = s1 + s2;
 8 
 9         System.out.println(s3 == s); // false
10     }
11 }

具体·创建过程如图所示:

vip

String s = "123"+"456";

  创建时,不是创建”123“和”456“两个字符串常量,直接就是创建”123456“一个字符串常量。

String s3 = s1 + s2;

  创建时,首先new StringBuilder(),创建了StringBuilder对象std,再调用append(String str)方法,依次将"123",”456“传入,组成”123456“字符串。【解释不清,请看源码】

 综合测试题,代码如下:

 1 public class StringWork04 {
 2     String str = new String("hsp");
 3     final char[] ch = {'j','a','v','a'};
 4     public void change(String str,char[] ch){
 5         str = "java";
 6         ch[0] = 'h';
 7     }
 8     public static void main(String[] args) {
 9         StringWork04 sw = new StringWork04();
10         sw.change(sw.str,sw.ch);
11         System.out.print(sw.str + " and ");
12         System.out.println(sw.ch);
13 
14     }
15 }

具体内存分布情况,如下图所示:

    vip   vip

注意① :

final char[] ch = {'j','a','v','a'};
ch[0] = 'h';

此处,ch存储的是java字符数组的地址,地址0x41自始至终没有发生变化,所以,并没有违反final的性质,因此可以修改指向【0x41】存储空间的内容。

注意② :

str = "java";

str是change()方法中的形参,传入的参数是sw.str【0x31】,str = "java",即原本指向0x31地址的str,指向了常量池地址为0x12的java字符串,但是并没有改变sw.str指向的地址【0x31】,即sw.str仍指向常量池中地址为【0x11】的hsp。

运行结果如下:

hsp and hava

 

posted @ 2022-12-27 10:21  zwGitOne  阅读(204)  评论(0编辑  收藏  举报