String字符串

9 篇文章 0 订阅
8 篇文章 0 订阅

常量池

java中的常量池在我的理解中有三种,分别是calss常量池,运行时常量池和string 常量池

class文件常量池

class文件中的常量池是在硬盘上的,通过javap -verbose xxx.class可以看到:

Constant pool:
   #1 = Methodref          #7.#28         // java/lang/Object."<init>":()V
   #2 = String             #29            // 2
   #3 = Fieldref           #4.#30         // com/xx/Test.scount:Ljava/lang/String;
   #4 = Class              #31            // com/xx/Test
   #5 = Fieldref           #32.#33        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Methodref          #34.#35        // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #7 = Class              #36            // java/lang/Object
   #8 = Utf8               scount
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               ConstantValue

所以class文件中的常量池会为每个类生成一个Constant pool的常量池,里面包含了:
1.类和接口的全限定名;
2.字段的名称描述符;
3.方法的名称和描述符。

运行时常量池

运行时常量池在JVM中InstanceKlass中的的一个属性
ConstantPool* _constants; 在方法区中的元空间,我们用HSDB来证明
我们编写一个类T0813,运行,拿到进程号,用HSDB查看如下:
在这里插入图片描述

String常量池

StringTable ,底层是HashTable
字符串常量池是放在堆区中,在堆区中开辟的一块空间,用于存放字符串常量池,然后整个堆区中的字符串常亮池只存在一份
其实string对象在底层维护了一个char的数组,用来存放字符串。

字符数组的存储方式

我们看下以下程序:

public class T0813 {

    public static void main(String[] args) throws IOException {
        char [] c  = new char[]{1,2,4};
        System.in.read();
    }
}

通过jps拿到进程号6072,我们使用HSDB(jdk lib目录)来查看这个进程
java -cp .\sa-jdi.jar sun.jvm.hotspot.HSDB
在这里插入图片描述
其实在jvm中就是一个TypeArrayKlass,也就是一个TypeArrayOopDesc
在这里插入图片描述
上图中的char[]aarr = new char[]{“2”,“3”};在jvm中的存在形式如上图
我们在虚拟机栈中有个s1的char数组对象,然后指向了堆区的typeArrayOopDesc对象,其实堆区的字符数是对s1的引用

不同方式创建字符串在JVM中的存在形式

String str1 =“1”;

public class Str1 {

    public static void main(String[] args) {
        String s1 = "11";
        System.out.println(s1);
    }
}

上述程序共创建了几个普通对象oop,几个string对象
其实 String s1 = “11”;创建了一个string对象,一个char数组对象;
在这里插入图片描述
如图:在堆区创建了一个string对象和char数组对象,而char数组对象是指向了常量池typeArrayOopDesc
流程如下:
1.首先在栈中创建一个sring对象s1,然后堆区先去常量池中找字符串11是否有,如果有直接返回;
2.如果没有,在常量池中创建一个字符串对象然后指向typeArrayOopdesc,所以这个表达式在堆区创建了两个对象,string和char数组对象,而图中的
在这里插入图片描述
是我们的value在c++中的存在形式
String str1 =“11”;
String str2 =“11”;
在这里插入图片描述
两个string对象,都指向了同一个字符串11;
第一次s1创建的时候是创建了一个string对象,一个char数组对象,
而第二次s2创建的时候发现常量池有,所以这个时候就不会创建char数组对象,所以这个表达式创建了2个string对象,一个char数组对象,也就是2个对象

new Strig

public class Str1 {

    public static void main(String[] args) {
        String s1 = new String("11");
        System.out.println(s1);
    }
}

在这里插入图片描述
首先String s1 = new String(“11”);会在堆区创建两个string对象,一个字符串char数组对象
因为表达式的左边是一个string对象,而表达式的右边经过new String出来的,也是一个string对象,而他们两个都同时指向了typeArrayOopdesc对象,也就是字符串11,这个字符串对象是在常量池的。

两个new String

public static void main(String[] args) {
    String s1 = new String("11");
    String s2 = new String("11");
    System.out.println(s1);
}

在这里插入图片描述
其实和一个new string的感念差不多,就是第一次s1创建过后,会有两个string对象,一个char数组对象,那么s2new的时候发现 常量池中已经有了字符串11,那么这个时候是指向了11的,所以这个时候只会创建一个string对象并且指向了typeArrayOopDesc,而typeArrayOopDesc是指向了常量池的11的。

字符串连接

public class Str1 {

    public static void main(String[] args) {
        String s1 = "1";
        String s2 = "1";
        String s = s1 + s2;
        System.out.println(s);
    }
}

字节码如下:

0: ldc           #2                  // String 1
         2: astore_1
         3: ldc           #2                  // String 1
         5: astore_2
         6: new           #3                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        13: aload_1
        14: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilde
r;
        17: aload_2
        18: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilde
r;
        21: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: astore_3
        25: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;

可以看到字符串连接在底层就是用StrigBuilder进行的
这个程序创建了2个String,4个普通对象
4个普通对象:
2个char数组
2个string对象
为什么会创建2个string,2个char数组呢?我们来分析下:
首先,s1 和s2执行完毕过后,会创建一个string对象,一个char数组;
然后s = s1+s2进行字符串连接过后,这个时候会产生一个string,一个s1+s2的字符串数组,请注意,这个时候的s是没有放入常量池的,只是在堆区中产生的,所以我们的stirngtable不一定全部都在常量池里面,和intern有关。

再来看下以下程序:

public static void main(String[] args) {
    String s1 = "1";
    String s2 = "1";
    String s = s1 + s2;
    String ss = "11";
    System.out.println(s==ss);
}

上前面理解的知识我们可以得到上面的程序
创建了3个string对象,3个char数组对象
为甚呢?因为s1 ,s2,ss都是放在常量池的,而s是没有放入常量池,所以2,3行代码创建了一个string,一个char数组 ,而第四行代码创建了一个string,一个未放入常量池的char数组对象,所以是3个string,3个char数组,并且第6行代码时false,因为内存地址都不一样,直接==是false

我们修改下程序

public static void main(String[] args) {
    String s1 = "1";
    String s2 = "1";
    String s = s1 + s2;
    s.intern();
    String ss = "11";
    System.out.println(s==ss);
}

这个程序增加了intern方法,那么这个时候就创建了2个string,2个char数组,因为intern把s加入了常量池,而我们的第6行代码执行的时候,发现常量池里面有11,所以就不会创建,直接返回了,所以是2个 string,2个char数组,并且s==ss 是true,因为都在常量池里面,对比的就是真实的vlaue。
所以由上述可知,我们的intern就是把我们的字符串强制放入了常量池,这也就是为什么上面我说的stringTable为什么不一定全部在常量池里面,这和intern方法有关。

最后看下不同的构造方法

public static void main(String[] args) {
    String s1 = new String(new char[]{'1', '1'}, 0, 2);
    System.out.println(s1);
}

上述创建了一个string,一个char,所以是两个对象,这种做法是不会放入常量池的,所以只有一个stirng,一个char

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值