String和StringBuilder以及StringBuffer之间的注意事项

我们得知道为何会出现String,StringBuilder,StringBuffer

(1)String 字符串常量 (每次都会创建对象)
(2)StringBuilder 字符串变量(非线程安全)
(3)StringBuffer 字符串变量(线程安全)
从上面可以看到String 因为每次创建的时候都会创建对象,太消耗内存了,所以就有了StringBuilder,但是StringDuilder是线程不安全的,所以就有了StringBuffer。
基本来说都是在性能上都是 StringBuilder > StringBuffer > String 的了

一、String

要知道,字符串就是一个字符数组,也相当于一个常量,因为一旦赋值就不改变,只能让变量指向另一个地址值。

1.String的内存机制

通过构造器new出来的字符串对象每一次new都会申请一个内存空间,虽然内容一样,但是地址值是不同的;第一次出现的字符串,会在栈内存和堆的字符串常量池中存处,下次再new一个相同内容的字符串时,愧就只会再栈内存中创建对象而已。

直接赋值得到的字符串,只要字符序列相同(顺序,大小写等),无论在代码里面出现几次,JVM都只会创建一个String对象,且是在堆的字符串常量池中创建维护

2.String对象的创建方式

首先,我们得知道String是一个引用类型的数据.那String对象就是new出来的?new出来的存在堆内存中吗?
是不是会有这么一个疑问,其实并不是这样的。由于,开发者经常用这个String类,所以java对String有单独的处理,比如重写了其的toString()方法,让其可以i直接打印数值,重写了equals()和hashCode()方法,让方法.equals()是比较数值,而不是地址值。
那常用的创建String对象的方法有两种:

第一种:构造方法

String a = new String(“aaaa”);
相当于创建了两个对象,一个对象放在栈内存中,一个对象放在堆的字符串常量池中。
所以,这就是1-2个对象,假如堆的字符串常量池中这里面有这个对象的话,就不会在创建。

第二种:直接赋值

String b = “bbb”;
只创建了一个对象而已,放字符串常量池中
所以,这就是0-1个对象,同上

举例:
String name1 = “test”;
String name2 = “test”;
name1 == name2 这是对的,因为name1只在字符串常量池中创建对象,name2直接使用这个对象。

String name3 = “test11”;
String name4 = new String(“test11”);
name3 == name4 这是错的,因为name4是有存在栈的对象

3.拓展:

拓展一:
字符串如果是变量相加,那是先开空间,在拼接;
字符串如果是常量相加(直接赋值),是先相加,然后再堆的字符串常量池中找,没找到再在栈中创建对象
这儿就是变量和常量的区别的,可以看我以后开辟的文章。
例子:
String s1 = “hello”;
String s2 = “world”;
String s3 = “helloworld”;
s3 == s1 + s2 false s1+s2先开辟了空间
s3.equals(s1+s2) true 比较的是内容
s3 == “hello” + “world” true 字符串常量池里面有
s3.equals( “hello” + “world”) true 比较内容

拓展二:
== 对于基本类型是比较值,对于引用类型比较的是地址值是否相等;
equals对于引用类型比较的是地址值,但是如果重写了Object的equals和hashcode方法那就是比较值,如String类型

二、StringBuilder

1.概念

StringBuilder类称为字符串构造者类。
是一个可变的字符串类(与String 的区别),说明还是同一个对象,但是里面的内容发生了改变,就相当于一个容器。 ----- 所以StringBuilder多用于字符串的拼接,从而不会产生多余的对象。

2.创建方式

只有一种创建方式:new
StringBuilder sb = new StringBuilder(“sbtest”);

3.内存位置

只会创建一个内存,存处在堆的的字符串常量池中。

4.底层原理

底层会创建一个可自动扩容的初始长度为16的数组(扩容后就是新的数组了)
也重写了toString()方法,可以直接输出
但是没有重写equals()方法,所以这个方法还是比较地址值

5.常用方法

①.StringBuilder append(任意类型); 字符串的拼接,支持链式
这个方法时直接在堆的字符串常量池中修改内容,不会创建新的对象,且以前对象的地址值不会改变。
----总结就是以前的对象不变,但是内容改变了

②.StringBuilder reverse(); 反序; 对象不变,只是内容变了

③.String toString(); 把StringBuilder转换成String类型,且创建了String对象

例子1:
StringBuilder sb1 = new StringBuilder(“sbtest”);
StringBuilder sb2 = new StringBuilder(“sbtest”);
sb1 == sb2吗?false new出来的就是不同的对象

例子2:
StringBuilder sb1 = new StringBuilder(“sbtest”);
sb1.append(“change”);
StringBuilder sb2 = new StringBuilder(“sbtest”);
sb1 == sb2吗?false new出来的就是不同的对象

问题:StringBuilder重写了toString()方法,为何还要转换成String呢?

因为StringBuilder没有重写equals()方法,所以这个方法还是比较地址值;所以要想比较内容就得先转成String才行再用equals()

三.StringBuffer

既然StringBuilder类对象可变,那么当其声明成全局变量,必然会带来线程安全问题(一个类是否线程安全取决于类的全局变量状态是否可以改变,能改变则说明该类线程不安全,否则线程安全。来自《Java并发编程的艺术》)。
为了解决StringBuilder类线程不安全的问题,StringBuffer类就出来了。除了这一点外,StringBuffer类与StringBuilder类完全一样。StringBuffer类线程安全的实现方式是用synchronized关键字修饰方法,即同步方法的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LC超人在良家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值