浅谈JAVA String类型的个人理解

先谈一谈String 是什么:
JAVA的一种数据类型,属于引用类型,底层实现部分是有基本数据类型char类型组成的一种数组,但是又由于在实际开发过程中,被大量的运用,所以String又被特殊设计成可以像基本类型一样直接赋值。
好,总结一下。像基本数据类型,一种char类型组成的数组,引用类型。很复杂对吧,还有更复杂的。我们都知道,多个引用指向同一个对象的时候,不会创建新的对象,而是这多个引用,都引用的是同一个对象,即使这个对象的值被改变了,而且并不会产生新对象是吧?但是String会。
好的,有同学会说,String是数组啊,数组长度不可变,被改变了,就要创建新的数组去引用他。真的是这样吗?
我们知道,数组的特性之一是,长度不可更改,所以如果我们要添加或者删除元素,是不能对原数组进行操作的。只能创建一个新数组,对原数组的内容进行修改,然后拷贝内容到新数组。如果这样来解释String 要创建新对象可以吗?
答案是不可以,太片面了。我们对String 的数据进行改动时,不改变他的长度,只改变他的内容,他会创建新对象吗?答案是的。但是普通的数组会吗?比如改变普通数组的第一个元素,他会创建新数组吗?很明显不会。那为什么String 会呢?
在探究这个问题之前,我们要先明白一个东西,常量池。JAVA在加载类文件的时候,会把定义好的数值,统一存放在一个叫常量池的地方。常量池是什么呢?顾名思义,就是存放常量的地方。常量是什么?常见的值,且值的内容不能被更改,所以自定义的常量都会用 final 修饰。但是还有一种常量,叫做字面常量。字面常量就是,普通的,常见的值,且每个值都是不能被改变的,比如,1,2,3,4 A,B,C,D.我们在定义基本数据类型的值的时候,其实首先JVM都会在常量池里面找,有的话就会直接把常量池的值赋给你定义的变量。
理解了常量池之后,我们再来讨论我们开始的问题。由于String 类型被广泛的运用,所以如果每次我们定义一个String 类型的话,都要去内存中创建一个String 对象,这对于设计而言,是十分不友好的,因为会导致过度的占用内存空间。那么怎么办呢?
首先,我们假设常量池里面什么都没有。然后

String a = "abc";
String b = "abc";
System.out.println(a == b);

这段代码的输出结果是什么呢?
答案是true;为什么呢?我们来分析一下代码
首先,声明一个String 类型的变量 a ,值是"abc";
这个值是哪里来的呢?
当类加载,加载到这一行的时候,会在常量池里面创建一个String 对象,这个对象的值是"abc";然后再把这个对象的引用赋给a;
再继续执行下一行的时候,创建一个String 类型的变量 b,然后会优先在常量池里面找,有没有值为"abc"的String 类型的对象,如果有,也把这个对象的引用赋给 b。
所以 a == b 是真,因为双等号比较引用类型比较的是引用,不是具体的值。我们再看下一个:

String a = "abc";
String b = new String("abc");

System.out.println(a == b);

这段代码的输出结果是什么呢?
答案是false,为什么呢?我们来分析一下代码:
第一行的执行流程和上面一样,关键在第二行
创建一个String 类型的变量b,在 堆空间中,初始化一个String类型的对象,然后把常量池里面值为"abc"的String 类型的对象的值,赋给堆控件的String对象。 这里的new String 就是在堆控件中,创建一个新的String 对象,这个对象的引用赋给了 变量 b,然后这个new String 的值是怎么来的呢?
是把常量池中的String 对象的值,拷贝给b 这个对象了。这里的 b的引用 就不是常量池中的String 对象了,而是在堆空间里开辟的 String 对象的引用
所以,a == b 是false,因为他们不是引用的同一个对象
好了,了解到了这里,我们可以再回到最开始的问题,为什么String 的值被改变了,就会创建新对象呢?
我们先看一个普通数组的情况

	int[] a = {1,2,3,4}
	int[] b = a;
	b[0] = 5;
	System.out.println(a[0]);

执行上面的代码,会输出多少呢?
答案是 :5;很明显。为什么呢?
很简单,第一行声明一个整型数组 a 并赋值。内存的执行效果就是,开辟了一块空间,一次存放1,2,3,4,然后把第一个的引用给a对吧。
第二行,把a的引用给b,说明,b也指向a指向的那个对象,b改变了数组第一个元素的值,自然a引用的数组的值被改变了,因为他们指向的是用一个数组,那么String 呢?这个字符数组呢?也会是这样吗?

String a = "abc";
String b = a;
a = a.replace("a","Z");//这个方法是把指定的字符串,替换成你给的字符串
System.out.println(a == b);

执行上面的代码,会输出什么呢?
答案是 false;先说一下第三行的意思,从数组的角度来说就是把第一个元素一换成Z。改了a的引用对象的值,b的引用是a,那为什么a变量只是改了值以后,a 和 b 的引用都变了呢?

首先我们解析第一行,在常量池中创建一个String 类型的对象,值是 “abc”,把这个变量的引用赋给a。

再执行第二行,创建一个String 类型的变量 b,b 的引用就是 a的引用对象,
这个时候 a 和 b 都是指向的同一个对象,也就是常量池里面创建的对象

在执行第三行,关键的来了。先执行右边,改变变量a 也就是常量池里面的对象的值"abc",改成"Zbc"了。这个时候 JAVA规定,你不可以直接改变常量池String 对象的值因为你一旦在常量池创建了值为 “abc” 的String 类型的对象,那么以后所有值是"abc"的String 类型,都会指向常量池里面的这个对象。这个规定保证了不会重复产生相同值的 String 对象。所以一旦你想改变这个对象的值,那么不好意思,你不可以改,你只能在常量池重新创建一个String 对象,把常量池原对象的值拷贝过来,再改。改完之后,把重新创建的这个对象赋给了原变量 a;所以在第三行的时候,变量 a 不再指向 “abc” 这个对象,而是指向新创建的"Zbc"这个对象,原"abc"对象的引用是 b 了,所以 a 和 b 不再指向同一个对象,所以 a == b 为 false。
如果频繁改变某一个String 对象的值的话,就会不断的产生新的String 对象,这也违背了内存设计的初衷,这也才有了后来的 StringBuufer 类型的出现。StringBuufer 允许在改变对象的值得同时,不创建新的对象。通常我们称这种机制为缓存机制。
StringBuilder与StringBuufer 类似,只不过StringBuufer 是线程安全的,运行速度慢。StringBuilder是非线程安全的,运行速度块。
小结:(1)如果要操作少量的数据用 String;

(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;

(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值