String对象的引用,使用字面值和new操作符创建String对象的区别。

String类

String对象是不可改变的。字符串一旦创建,内容不能再改变。

String s = "java";
System.out.println(s);  // 打印结果为java 
s = "html";
System.out.println(s);  //打印结果为html 

以上代码会改变字符串的内容吗?

  • 答案是并不会
    这个你可以直接看到的打印结果和我给的结论看上去是相悖论的,如果看到这里有疑问,那么说明你基本数据类型掌握的还算不错,带着疑惑向下看。
    因为在java中除了1 基本数据类型外,像String和数组这样的数据类型都是归类于对象的也就是引用数据类型。
    让我们看一个基本类型的代码实列
int a = 1;
System.out.print(a); //打印结果为1
a = 2;
System.out.print(a); // 打印结果为2

基本数据类型代码及打印结果如上
我们将基本数据类型打印结果和String数据类型打印结果进行对比,发现形式上都大致相同,第二次打印结果都是最新的赋值(分别为html和2),那为什么说字符串一旦创建,内容不能再改变呢?


形象比喻

  1. 在做基本数据类型的赋值时(例:int a = 1;),相当有一个篮子(a就可看作这个篮子),我手里有1和2两个数字卡片,但是这个篮子里一次只能有一张卡片,我第一次把1放进篮子,如果有人问我篮子里有什么,那肯定是1这张卡片了,我又想把2这张卡片放进篮子,那么1这张卡片就需要拿出来,将1这张卡片修改为2这张卡片,我们一定可以在篮子看到有一张卡片(数据)的存在。

  2. 在做引用数据类型的赋值是(例:String s = “java”; ),我就需要两个篮子了,我想把"java"这张卡片放到一个篮子里,然后在这个篮子上面写上一个地址(例:写上111作为地址),再用一个篮子放入一个写着111地址的卡片(这个篮子只能由一张地址卡片),在这个篮子里我们能看到一个地址数据,我们去找写着这个地址的篮子,就能发现那个篮子里有一张"java"卡片,这也是我们最用想要看到的卡片(数据)。
    那么我们做(s = “html” ;)的时候,你应该有些许的明白了,"html"这张卡片也放到一个篮子里,这个篮子和放入"Java"卡片的篮子不同,所以篮子上的地址也不同,而存放地址的s篮子只能放一个地址卡片,我们第二次把存放"html"卡片篮子的地址放入了s篮子里,那么现在我们看向s篮子里时,也只能找到放着"html"卡片的篮子了,虽然我们找不到放着"java"卡片的篮子了,但是它却一直在那里,里面也一直放着"java"卡片。
    有人会问为什么不像基本数据类型那样是一个"篮子"呢,这就是引用数据类型的赋值的特别之处。


科学解释

  1. 基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上。
int a = 1;
a = 2;

也就是说我们将数据直接存入这个分配的内存中,我们访问这个内存地址就会获得我们想要的数据,对比上面的形象比喻

  1. 引用数据类型在被创建时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。
String s = "java";
s = "html";

我们真正想要的数据存在堆中,我们需要通过栈去访问堆里的数据,而栈内存里存的正是堆分配的内存的地址,堆的内存中保存的是我们想要访问的数据


java虚拟机为了提高效率并节约内存,对具有相同字符序列的字符串字面值使用同一个实例(驻留字符串)

String  s1 = "welcome to java";  //这里将这句代码取个代号【1】,为了方便下面文字解释与描述 
String s2 = new String("welcome to java");  // 这里将这句代码取个代号【2】,为了方便下面文字解释与描述 
String s3 = "welcome to java";  // 这里将这句代码取个代号【3】,为了方便下面文字解释与描述
String s4 = new String("welcome to java");  //这里将这句代码取个代号【4】,为了方便下面文字解释与描述
System.out.println("s1 == s2 is" + (s1 == s2));  //打印结果为false
System.out.println("s1 == s3 is" + (s1 == s3)); //打印结果为true 
System.out.println("s1 == s3 is" + (s2 == s4)); //打印结果为false 

这里通过字面值创建String对象(保存在常量池中)和用new创建对象(保存在栈中)保存在不同的地方
常量池和栈参考:

  1. 【1】语句将创建一个"welcome to java"对象,保存在常量池中,s1中保存的地址指向常量池中"welcome to java"对象,当【3】语句想要再次创建一个"welcome to java"对象时,java虚拟机(jvm),开始在常量池中寻找是否有一个equals相等的对象,在我们的例子中可以找到这个对象,jvm很聪明,那我们既然原先常量池中有一个一模一样的对象,我们就不必要在浪费内存在创建一遍这个对象了,那么如果s3中保存的地址和s1中保存的地址是一样的,是不是就可以指向同一个对象了,答案是可以,jvm也是这样做的,这时s1和s3这两个对象的引用中保存的是一个相同的地址,所以执行s1 == s3的结果为true、
  2. 【2】语句用new操作符创建"welcome to java",保存在堆中,执行(s1 == s2)时s1和s2中保存的是不相同的对象引用,【4】语句执行,jvm不执行像在常量池里的操作,所以会新建立一个"welcome to java"保存在堆的另一块内存中,所以s2和s4中保存的地址也不同,(s2 == s4)的结果为false。

  1. (byte(位)、short(短整数)、int(整数)、long(长整数)、float(单精度)、double(双精度)、char(字符)和boolean(布尔值)) ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值