一、概述
String
类的一个最大特性是不可修改性,而导致其不可修改的原因是在String
内部定义了一个常量数组,因此每次对字符串的操作实际上都会另外分配分配一个新的常量数组空间。
二、创建字符串对象的方式
2.1 四种方式
方式一:直接赋值(常用)
// 直接赋值方式创建对象是在方法区的常量池
String str1 = "hello word";
方式二:通过构造方法产生对象
// 通过构造方法创建字符串对象是在堆内存
String str2 = new String("hello word");
方式三:通过字符数组产生对象
char[] data = new char[]{'a', 'b', 'c'};
String str3 = new String(data);
方式四:通过String
的静态方法valueOf
(任意数据类型) = >转为字符串(常用)
//在方法区的常量池
String str4 = String.valueOf(10);
2.2 实例化方式的比较
1). 编写代码比较
public class TestString {
public static void main(String[] args) {
String str1 = "Lance";
String str2 = new String("Lance");
String str3 = str2; // 引用传递,str3直接指向st2的堆内存地址
String str4 = "Lance";
String str5 = String.valueOf("Lance");
/**
* ==:
* 基本数据类型:比较的是基本数据类型的值是否相同
* 引用数据类型:比较的是引用数据类型的地址值是否相同
* 所以在这里的话:String类对象==比较,比较的是地址,而不是内容
*/
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // false
System.out.println(str3 == str2); // true
System.out.println(str1 == str4); // true
System.out.println(str1 == str5); // true
System.out.println(str2 == str4); // false
}
}
2). 内存图分析
可能这里还是不够明显,构造方法实例化方式的内存图:String str = new String("Hello");
首先:
当我们再一次的new
一个String
对象时:
3). 字符串常量池
在字符串中,如果采用直接赋值的方式(String str = "Lance"
)进行对象的实例化,则会将匿名对象“Lance”放入对象池,每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象,
这样,所有直接赋值的String
对象,如果利用相同的“Lance”,则String
对象==
返回true
;
比如:对象手工入池
public class TestString {
public static void main(String args[]) {
// 对匿名对象"hello"进行手工入池操作
String str = new String("Lance").intern();
String str1 = "Lance";
System.out.println(str == str1); // true
}
}
4). 总结:两种实例化方式的区别
- 直接赋值
(String str = "hello")
:只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。 - 构造方法
(String str= new String("hello");)
:会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要通过intern()
方法进行手工入池。
在开发的过程中不会采用构造方法进行字符串的实例化。
5). 避免空指向
首先了解:==
和equals()
比较字符串的区别
==
在对字符串比较的时候,对比的是内存地址,而equals
比较的是字符串内容,在开发的过程中,equals()
通过接受参数,可以避免空指向。
举例:
String str = null;
if (str.equals("hello")) {// 此时会出现空指向异常
//...
}
if ("hello".equals(str)) {// 此时equals会处理null值,可以避免空指向异常
//...
}
6). String
类对象一旦声明则不可以改变;而改变的只是地址,原来的字符串还是存在的,并且产生垃圾
三、源码分析
3.1 成员变量
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** String的属性值 */
private final char value[];
/** The offset is the first index of the storage that is used. */
/** 数组被使用的开始位置 **/
private final int offset;
/** The count is the number of characters in the String. */
/** String中元素的个数 **/
private final int count;
/** Cache the hash code for the string */
/** String类型的hash值 **/
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
}
final
修饰类名:String
作为