揭秘Java中的String

一、创建String

创建一个String对象,主要有两种方式:

  1. String s=”Hello world!”;
  2. String s=new String(“Hello world”);

两种方式虽然都实现了创建一个String对象的功能,但实现的原理却大不相同。在讨论这两种方法的不同之前,我们先来了解一下JVM里的常量池概念,对接下来的理解很有帮助。

相信大家都知道,Java程序在运行之前,编译器首先要把源代码编译成字节码文件(.class文件),然后JVM再解释执行.class文件。其中,在.class文件中有一个非常重要的项—常量池,我们上面代码中的”Hello world”字符串被编译之后,就被存放在class常量池中的字符串常量表中。改用网上一段话:

在Java源代码中的每一个字面值字符串,都会在编译成class文件阶段,形成标志号 为8(CONSTANT_String_info)的常量表 。 当JVM加载 class文件的时候,会为对应的常量池建立一个内存数据结构,并存放在方法区中。同时JVM会自动为CONSTANT_String_info常量表中 的字符串常量字面值 在堆中创建 新的String对象(intern字符串对象)。然后把CONSTANT_String_info常量表的入口地址转变成这个堆中String对象的直接地址(常量池解析)。

源代码中所有相同字面值的字符串常量只可能建立唯一一个intern字符串对象。另外,我们也可以调用String的intern()方法来使得一个常规字符串对象成为intern字符串对象。

好了,下面我们来讨论一下第一种方法,也是大家非常常用的方法:

String s=”Hello world!”

首先在编译期,也就是在运行这段指令之前,JVM就已经为”Hello world”在堆中创建了一个intern字符串,局部变量s存储的是早已创建好的intern字符串的堆地址。也就是说,不管有几条String s1=”Hello world”,堆中都只有1个值为”Hello world”的字符串。

那么第二种方法呢?

String s=new String(“Hello world”)

同样在编译期,JVM也为”Hello world”在堆中创建了一个intern字符串,然后用这个intern字符串的值来初始化new出来的新String对象,局部变量s实际上存储的是new出来的堆对象地址。 此时在JVM管理的堆中,有两个相同字符串值的String对象:一个是intern字符串对象,一个是new新建的字符串对象。

最后,来段代码做个更形象的比较:

 
   
//代码1
String sa = "ab";
String sb = "cd";
String sab=sa+sb;
String s="abcd";
System.out.println(sab==s); // false
//代码2
String sc="ab"+"cd";
String sd="abcd";
System.out.println(sc==sd); //true
 

代码1中局部变量sa,sb存储的是堆中两个intern字符串对象的地址。而当执行sa+sb时,JVM首先会在堆中创建一个StringBuilder类,同时用sa指向的intern字符串对象完成初始化,然后调用append方法完成对sb所指向的intern字符串的合并操作,接着调用StringBuilder的toString()方法在堆中创建一个String对象,最后将刚生成的String对象的堆地址存放在局部变量sab中。而局部变量s存储的是常量池中”abcd”所对应的intern字符串对象的地址。 sab与s地址当然不一样了。这里要注意了,代码1的堆中实际上有五个字符串对象:三个intern字符串对象、一个String对象和一个StringBuilder对象。
代码2中”ab”+”cd”会直接在编译期就合并成常量”abcd”, 因此相同字面值常量”abcd”所对应的是同一个拘留字符串对象,自然地址也就相同。

二、String,StringBuffer和StringBuilder

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值