String
在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。
需要注意的是,String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。
至于为啥是不可变,我们可以来看下面这个图,发现String是用final修饰的,导致了他的不可变。 接下来我们从内存的角度来看一下,操作String类型时候的内存变化
不难看出:初始String值为"Hello",然后在这个字符串后面加上新的字符串"world",这个过程重新在堆中开辟了空间,操作这俩个字符串开辟了3次内存空间,为了解决**经常性字符串相关的操作,**引入了StringBuilding和StringBuffer
StringBuilding和StringBuffer的区别
其实这个问题一直在面试中问,那么为啥要问这么简单的问题呢?
其实这个一般只会在刚开始时候问,因为这个问题就是与线程安全有关,然后问怎么去保证线程安全?各种各样的锁,什么是锁,锁的机制,什么是死锁之类的问题,然后到synchronized,他的原理,怎么使用,synchronized和volatile区别是什么,为什么用volatile,上升到Java内存模型,然后并发包…………,所以说面试这个过程,首先要抛出一个问题,然后根据你的状态,进行循序渐进,那么我们现在就来看这个最简单的问题吧!
与String类不同的是,StringBuffer和StringBuilding类的对象可以被多次修改,而不产生新的对象
StringBuilding类在Java5中被提出,他与StringBuffer最大的不同在于他不是线程安全的,即是不支持同步访问。 有得必有失,StringBuilding由于不需要对应的锁机制(下面会提到),所以**其速度会更快一点,**在一般情况下我们更推荐使用StringBuilding,但是应用程序在要求线程安全的情况下,则必须使用StringBuffer类型
关于字符串中的==
String s1=new String("Hello world");
String s2=new String("Hello world");
System.out.println(s1==s2);
String s3="Hello world";
String s4="Hello world";
System.out.println(s3==s4);
代码1中局部变量s1,s2中存储的是JVM在堆中new出来的两个String对象的内存地址。虽然这两个String对象的值(char[]存放的字符序列)都是"Hello world"。 因此"=="比较的是两个不同的堆地址。
代码2中局部变量s3,s4中存储的也是地址,但却都是常量池中"Hello world"指向的堆的唯一的那个拘留字符串对象的地址 。自然相等了。
StringBuffer与StringBuilder的线程安全性问题
这两者的方法没有很大区别。但在线程安全性方面,StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized 修饰了,而StringBuilder没有。
StringBuffer
StringBuilding
synchronized的含义:
synchronized详解
效率问题
StringBuffer和StringBuilder可谓双胞胎,StringBuilder是1.5新引入的,其前身就是StringBuffer。StringBuilder的效率比StringBuffer稍高,如果不考虑线程安全,StringBuilder应该是首选。另外,JVM运行程序主要的时间耗费是在创建对象和回收对象上。
总结
-
在编译阶段就能够确定的字符串常量,完全没有必要创建String或StringBuffer对象。直接使用字符串常量的"+"连接操作效率最高。 -
StringBuffer对象的append效率要高于String对象的"+"连接操作。 -
不停的创建对象是程序低效的一个重要原因。那么相同的字符串值能否在堆中只创建一个String对象那。显然拘留字符串能够做到这一点,除了程序中的字符串常量会被JVM自动创建拘留字符串之外,调用String的intern()方法也能做到这一点。当调用intern()时,如果常量池中已经有了当前String的值,那么返回这个常量指向拘留对象的地址。如果没有,则将String值加入常量池中,并创建一个新的拘留字符串对象。
|