前言
实际上任何语言都没有提供字符串这个概念,而是使用字符数组来描述字符串。Java里面严格来说也是没有字符串的,在所有的开发里面字符串的应用有很多,于是Java为了应对便创建了String类这个字符串类。使用""定义的内容都是字符串,理解Java的String类需要从类的角度和内存关系上分析这个类。
下面将介绍:
- String类对象的两种实例化方式
- 使用"=="和equals比较字符串是否相等
- String常量为匿名对象
- String两种实例化方式的区别
- 字符串一旦定义则不可变
- 字节与字符串
- 字符串中的方法分类
- 重载"+"与StringBuilder
- StringBuffer与StringBuilder
String类对象的两种实例化方式
String name1 = "Sakura";    //直接赋值方式 String name2 = new String("Sakura");  //利用构造方法实例化
使用"=="和equals比较字符串是否相等

使用"=="比较的是两个对象在内存中的地址是否一致,也就是比较两个对象是否为同一个对象。
使用equals()方法比较的是对象的值是否相等,name1和name2所指对象的值都是"Sakura"所以输出为true。
String常量为匿名对象
像"Sakura"这样的字符串不属于基本数据类型,而是作为String类的
"Sakura"可以调用String类的方法,由此可见"Sakura"是一个对象。
创建String对象的直接赋值方式相当于将一个匿名对象设置了一个名字。在前篇文章里我们说直接使用"new 类名称();"的方法创建的是一个匿名对象,String类的匿名对象却不是这样的,String类的匿名对象是由系统自动生成不是由用户直接创建。
下面会讲JVM中关于字符串的内存分配管理。
| Notice | 
图中的代码实际隐含了一个避免出现NullPointerException的小技巧。
若是我们像下面这样写字符串比较代码:
未知name1是否指向了一个对象,所以会存在抛出空指针异常的情况。为了避免空指针异常我们就可以将字符串常量写在前面。
两种实例化方式的区别
直接赋值方式
前面讲过直接赋值方式就是将一个字符串的匿名对象设置了一个名字。

"=="比较的是两个对象内存地址是否一致,由输出结果可以看出name1和name2指向了同一块堆空间。
为什么不是在堆空间中开辟两个"Sakura"对象而是让name1和name2指向同一个对象呢?
这个需要谈到JVM的共享设计模式。
JVM的底层实现实际上在堆中存在一个对象池(常量池,不一定只保存String对象),当我们使用直接赋值方式定义String类对象,那么JVM会将此字符串对象使用的匿名对象就是如"Sakura"字符串入池保存。如果后面还有其他String对象采用同样方式且设置同样内容时,将不会开辟新的堆空间,而是继续使用相同的空间。
采用构造方法实例化
String name = new String("Sakura");分析以上语句开辟空间情况:
开辟了一块栈内存存储了对象引用; 开辟了两块堆空间,一块在常量池中存储"Sakura"字符串常量另一块在堆中存储这个对象。
当堆中的对象若是没有引用指向就是垃圾对象会被GC清理掉。所以,这种构造方式会造成一块堆空间的浪费。
若是希望,此方式的对象也可以入池保存也是有方法的,就是利用String类的intern方法。
public class Test {     public static void main(String[] args) {         String name1 = new String("Sakura").intern();   //返回一个匿名对象 name1就指向的是常量池中的"Sakura"         String name2 = "Sakura";         System.out.println(name1==name2);     } } /* output: true */但是方法真的显得很麻烦!
总结一下两种实例化方法的区别:
- 直接赋值方式:只会开辟一块堆内存空间,并且自动保存在常量池中,以供我们下次重复使用。
- 构造方法:会开辟两块堆内存空间,其中在常量池的会成为垃圾空间。
字符串一旦定义便不可变
String name = "Amy"; String name = "Smith"; String name = "Amy" + "Smith";Java定义了String内容不能被改变。分析堆内存,是可以知道
