String类
String类具有以下特点:
1. String(java.lang.String)是final类,不能被继承
-
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
2. String类的本质是字符数组char[],因为String类是不可变(immutable)类
-
/** The value is used for character storage. */
-
private final char value[];
3. String的初始化方式,下面的s我们称之为引用(reference)
a. new关键字创建,例String s = new String("abc");
b. 直接赋值,例String s = "abc";
c. 字符串串联,如+或者concat()方法,例String s = "ab" + “c”;
4. String有一个String Pool(String池),这个String Pool用来存放String对象的字面值
5. Java中,String不是基本数据类型
关于不可变
我们知道String类是不可变的,所谓不可变我们可以理解成String类的实例不能被改变,更进一步,不可变还表示每个实例中包含的信息必须在该实例被创建的时候就提供出来,并且在对象的整个生命周期内固定不变
关于初始化
从上面知道,String有三种初始化方式,我们先看前两种a和b,两者都返回String对象的引用,然而JVM对他们的处理不同
a. JVM在堆(heap)中创建一个String对象,返回这个对象的应用,这时String对象在堆上
b. JVM先在String Pool中查找查找是否有字面值是"abc"的String对象(equals方法比较)
1). 找到了,返回String Pool中该String对象的引用,这时堆(heap)上并没有创建String对象,String对象在String Pool中的
2). 没有找到,JVM则在堆(heap)上创建新的String对象,返回String对象(在堆上)的引用,同时将该引用添加至String Pool中
注意,a的情况下,JVM不会把对象放到String Pool中
看如下代码,假设String Pool里不存在字面值"abc"的情况下执行下面的代码
-
//jvm 在堆上创建一个String对象
String str1 = new String("abc");
-
-
//jvm 在strings pool中找不到字面值为“abc”的字符串对象,因此
-
//在堆上创建一个String对象,并将该对象的引用加入至strings pool中
-
//此时堆上有两个String对象,分别是str1指向的"abc"对象,和str2指向的"abc"对象
//此时String Pool上有一个字面值为"abc"的字符串
-
Stirng str2 = "abc";
-
-
if(str1 == str2){
-
System.out.println("str1 == str2");
-
}else{
-
System.out.println("str1 != str2");
-
}
-
//打印结果是 str1 != str2,因为它们是堆上两个不同的对象
-
String str3 = "abc";
-
//此时,jvm发现String Pool中已有字面值是“abc”字符串对象了,因为“abc”equals “abc”
-
//因此直接返回str2指向的对象给str3,也 就是说str2和str3是指向同一个对象的引用
-
if(str2 == str3){
-
System.out.println("str2 == str3");
-
}else{
-
System.out.println("str2 != str3");
-
}
//打印结果是 str2 == str3
理解了上面的例子后,我们要说一说String类的intern()方法,intern()方法是一个本地(native)方法
-
/**
* Returns a canonical representation for the string object.
*
* A pool of strings, initially empty, is maintained privately by the
* class String
.
*
* When the intern method is invoked, if the pool already contains a
* string equal to this String
object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this String
object is added to the
* pool and a reference to this String
object is returned.
*
* It follows that for any two strings s
and t
,
* s.intern() == t.intern()
is true
* if and only if s.equals(t)
is true
.
*
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in §3.10.5 of the
* Java Language
* Specification
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
特地把intern()的注释放在这边来理解这个方法,当执行intern()时,如果String Pool已经包含一个等于该String字面值的字符串对象时(equals()),就返回String Pool中字符串对象的引用,否则,则在堆(heap)上创建一个String对象,同时将该引用添加至String Pool中
我们再来看下面的例子,还是假设String Pool里不存在字面值"abc"的情况下执行下面的代码
-
//jvm 在堆上创建一个String对象
String str1 = new String("abc");
//intern()运行的过程是这样的:首先查看String Pool里面有没有字面值是"abc"的字符串对象
//答案是没有,因为str1用了new关键字,所以只在堆上创建String对象
//既然没有,那么intern()方法又在堆上创建了一个"abc"对象,同时在String Pool中创建具有相同字面值的字符串对象
//这个时候str1原来指向的那个堆上的String对象成为垃圾对象
-
str1 = str1.intern();
//这个时候JVM发现String Pool上已经有字面值是"abc"的字符串对象存在,即执行inter()时创造的
//所以此时str1和str2引用同一个对象
String str2 = "abc";
-
-
if (str1 == str2) {
-
System.out.println("str1 == str2");
-
} else {
-
System.out.println("str1 != str2");
-
}
//打印结果是 str1 == str2
字符串的拼接
由于String类的不可变特性,每次执行字符串的拼接操作(+或者concat()),都会导致大量的新String对象产生
StringBuffer和StringBuilder的出现解决了这个性能问题
String长度
可以使用串接操作符得到一个长度更长的字符串,那么,String对象最多能容纳多少字符呢?
查看String的源代码我们可以得知类String中是使用域 count 来记录对象字符的数量,而count 的类型为 int,因此,我们可以推测最长的长度为 2^32,也就是4G
-
/** The count is the number of characters in the String. */
-
private final int count;
不过,在编写源代码的时候,如果使用 Sting str = "aaaa";的形式定义一个字符串,那么双引号里面的ASCII字符最多只能有 65534 个。为什么呢?因为在class文件的规范中,CONSTANT_Utf8_info表中使用一个16位的无符号整数来记录字符串的长度的,最多能表示 65536个字节,而java class 文件是使用一种变体UTF-8格式来存放字符的,null值使用两个字节来表示,因此只剩下65536-2 = 65534个字节。也正是变体UTF-8的原因,如果字符串中含有中文等非ASCII字符,那么双引号中字符的数量会更少(一个中文字符占用三个字节)。如果超出这个数量,在编译的时候编译器会报错
String Pool
根据深入理解Java虚拟机这本书,JVM在执行Java程序的过程中,会把它管理的内存划分为多个区域,其中有一块叫方法区(Method Area),也就是String Pool所在的区域
正确的说,String Pool即不在堆也不在栈,而在方法区的运行时常量池(Runtime Constant Pool)(请参考深入理解Java虚拟机-JVM高级特性与最佳实践-阅读笔记(第二章-1))
阅读(318) | 评论(0) | 转发(0) |