对于任何编程语言来说,接触字符串都是不可避免,Java也不例外。Java中String
类位于java.lang
包下,是整个Java语言的基石。同时String
类使用final
关键词修饰,意味着外部调用者无法通过继承和重写来更改其功能。Java中的字符串与语言相比,也有其特殊性。本文深入地理解Java字符串,主要内容有:
- String的初始化
- String与常量池
- String的不变性
- String、StringBuffer与StringBuilder
- “+”操作符
String初始化
首先要强调的是,String并不是Java中的基础类型,它也是一个对象。在源代码层面来说,String有多种不同的初始化方法,本节就介绍这些初始化方法。
字面量法
String
的字面量初始化法如下所示:
String a = "abc";
String b = "hello world";
这种方法首先从常量池中查找是否有相同值的字符串对象,如果有,则直接将对象地址赋予引用变量;如果没有,在首先在常量池区域中创建一个新的字符串对象,然后将地址赋予引用变量。
构造方法法
String
的构造方法初始化法如下所示:
String a = new String("abc");
String b = new String("hello world");
String
类的构造方法有:
public String() {} // 构造空串(注意与null的区别)
public String(String original) {} // 基于另外一个字符串构造一个新字符串对象
public String(char value[]) {} // 使用byte数组构造字符串
public String(char value[], int offset, int count){} // 使用byte数组以及偏移参数构造
public String(int[] codePoints, int offset, int count) {} // 基于Uncode编码数组以及偏移量构造
这种初始化方法与一般对象的初始化方法完全一样。与字面量法不同的是,每次调用构造方法都会在堆内存中创建一个新的字符串对象。下面的例子可以清楚地显示它们的区别:
/*
* 个人主页:http://hinylover.space
*
* Creation Date: 2016年7月6日 下午9:14:56
*/
package demo.blog.java.string;
/**
* 不同初始化方法的区别。
* @author xialei
* @version 1.0 2016年7月6日下午9:14:56
*/
public class StringEqual {
public static void main(String[] args) {
String a1 = "123";
String b1 = "123";
System.out.println(a1 == b1); // true
String a2 = new String("123");
String b2 = new String("123");
System.out.println(a2 == b2); // false
}
}
我们知道,Java中的“==”符比较的变量保存的实际内存数据,由于基础数据类型变量保存的是数据的实际值,而引用类型变量保存的是对象的地址,不同的地址代表着不同的对象。a1 == b1
为true
表明a1和b1指向同一个对象,而a2和b2分别指向不同的对象。
String与JVM常量池
说起String ,就不能不提到JVM常量池,是笔试和面试中经常喜欢出题的点。
常量池
在正式进入常量池之前,首先简单地介绍一下JVM(Java虚拟机,由于市面上有多种不同的JVM,本文中仅考虑Hotspot VM)的内存结构,也是作为Java程序员必须要了解的内容(以后再深入JVM)。由于本文的主体并不是Java虚拟机,内容会比较粗糙,更加详细的JVM知识会在以后撰写。
JVM的内存顶层结构如下图所示:
下面分别来说说主要的内存区域:
- 程序计数器:这是一块比较小的内存区域,可以看做是当前线程所执行的字节码的行号指示器。每一个线程都会需要有一个独立的程序计数器,各个程序计数器之间相互不影响。如果线程当前执行的是Java方