飞道的博客

从头再学java系列之字符串,String,StringBuffer,StringBulider的联系与区别

392人阅读  评论(0)

前言

初学java,其实只是知道String,后来才发现有StringBuffer和StringBulider。

String 类型

java有八种基本数据类型,byte,char,int,float,double,boolean,short,long
String是一种引用数据类型,所谓引用数据类型,就是它在内存中,存放的不是本事的内容,而是一个地址,而这个地址指向的内存才是存放着真正的字符串的内容。

字符串的比较

字符串既然是一种引用数据类型了,然后前面我的博文写到了equals和==的区别,所以,我们比较字符串是否相等的时候,应该使用equals,而不是使用==。因为equals比较的是字符串的内容,而= =比较的字符串的地址。

字符串在内存中的存储

例子

上面我们讲到,字符串是一种引用数据类型,所以,变量存放的不是字符串本身的内容,而是对应内容在内存中存放的地址。
然后,下面看代码:

        String a="123";
        String b="123";
        System.out.println(a==b);//true

结果居然是true,那这是为什么?
首先,String肯定是一种引用数据类型,那么,它存放的,肯定是一个地址。然后,上面又打印结果显示a==b,那么只有一种可能,就是,a与b的地址相同。

原因

字符串在内存中的储存,确实是指向一个地址,然后存放的地址指向对应的内容。不过,在我们创建字符串b时,它不会立即去创建,而是会去常量池里面找,看看有没有这个字符串,如果有,就把地址指向b即可。那么,这样一来,就可以上面代码出现的原因了。如果没有,那么系统再去创建一个字符串,并将该字符串的地址指向给变量。

常量池

好处:常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

我们都知道,字符串的分配,和其他的对象分配一样,需要耗费高昂的时间与空间代价。所以,有什么办法来减少这个操作呢?

JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量字符串类维护了一个字符串的常量池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中, 就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享

String类的intern方法

源码:

 public native String intern();

这是一个native方法,如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符添加到常量池中,并返回此String对象的引用执行此方法的时候要注意避免字符串常量池有过多的字符串而降低运行速度

字符串常量池

本质是一个HashSet,这是一个纯运行时的结构,而且是惰性维护的。注意它只存储String对象的引用,而不存储String对象的内容,根据这个引用可以得到具体的String对象。


String是一个不可改变的字符串

String类里面没有提供修改某个字符的方法,即不可能修改一个String字符串的单个字符。为什么?请看String类的源码,我们新建一个字符串,所谓字符串,字符拼接起来的串,String类其实是将我们的字符存放在一个被final 和 private修饰的字符数组中

    private final char value[];

所以,每当我们拼接字符串时,其本质上,并不是在原来的基础上拼接的,而是,另外创建一个新的字符串来接收原来的字符串和新的字符串。做题的时候,就吃过这个亏。
使用String与StringBuffer的区别

可变字符串

先看介绍,有时候,我们需要由较短的字符串构建字符串,但是采用字符串拼接的形式来达到这个目的,效率会比较低。每次拼接字符串的时候,都会构建一个String对象,既耗时,又浪费空间。使用StringBulider就不会有这个问题

源码:

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
    {}
    
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
	{}

可以看到,StringBulider和StringBuffer都是继承了一个叫AbstractStringBulider的类,并且实现了可序列化接口和CharSequence接口,第一个就是表示该类可以进行序列化,第二个接口是一个字符序列接口,相当于一个“字符串类”,把他们几个共有的属性抽象出来的一个接口。

为什么可变?

上面讲到,他们两个都实现了一个叫AbstractStringBulider的类请看源码,这个是AbstractStringBulider类的源码,我们可以发现,它与String类不同的是,他们的字符串存储并没有使用final修饰,所以是可以变的。

   char[] value;

StringBulider和StringBuffer的区别

String:不可变
StringBuffer:可变的,线程安全
StringBuilder:可变的,线程不安全

线程安全性

String中的对象是不可变的,也就可以理解为常量,线程安全。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

ps:这个地方先说到这里,等以后学到了再回来更新,因为对于多线程和同步锁不是很能够理解,不能写,写了错了就是胡说八道了。如果文章有问题的,欢迎大家帮我指出来,谢谢大家!


转载:https://blog.csdn.net/weixin_44750790/article/details/106866523
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场