前言
String 类我们在写代码的时候是用的最多的一种类了,那么我们有深入理解它吗?想深入理解Java中的String,那么就要分析源码
分析String源码
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
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
* Object Serialization Specification, Section 6.2, "Stream Elements"
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
/**
* Initializes a newly created {@code String} object so that it represents
* an empty character sequence. Note that use of this constructor is
* unnecessary since Strings are immutable.
*/
public String() {
this.value = "".value;
}
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
.....
复制代码
1.从源码分析,String 是一个 final 类,说明是不能被继承的类
2.String自身继承Serializable,Comparable,CharSequence三个接口,说明String也有它们3个接口的功能,可以序列号,排序,可读序列
String类代表字符串。Java程序中的所有字符串,如 "abc",实现为这个类的实例。
字符串常量,它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为他们可以共享字符串对象是不可变的。例如:
String str = "abc";
复制代码
等价于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
复制代码
这里有一些如何使用字符串的更多示例:
System.out.println("abc");
String cde = "cde";
System.out.println("abc" + cde);
String c = "abc".substring(2,3);
String d = cde.substring(1, 2);
复制代码
类String包括方法研究单个字符的序列,来比较字符串,搜索字符串,提取子字符串,用于创建一个字符串的副本与所有字符转换为大写或小写。情况下映射基于Unicode标准版Character指定的类。
Java语言提供特殊支持字符串连接操作符(+),和其他对象转换成字符串。字符串连接实现通过StringBuilder(或StringBuffer)类及其append方法。toString字符串转换是通过实现的方法,定义为Object和继承的所有Java类
除非另外注明,null参数传递给这个类的构造函数或方法将导致NullPointerException抛出。
分析方法
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
public String replace(char oldChar, char newChar){
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
复制代码
可以看到一些方法concat,replace都是自身的操作,发现到最后都是从新生成一个String对象,也就是说最原始的对象还是存在的。
所以可以得到一个结论:String类一旦创建就不会改变,对String对象做任何操作都会影响原始数据,相关的方法会创建一个新的String对象。
常量池
在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:
1.直接使用双引号声明出来的String对象会直接存储在常量池中。
2.如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);
复制代码
结果是 true;
采用字面值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在"abc"这个对象,如果不存在,则在字符串常量池中创建"abc"这个对象,然后将池中"abc"这个对象的引用地址返回给"abc"对象的引用s1,这样s1会指向字符串常量池中"abc"这个字符串对象;如果存在,则不创建任何对象,直接将池中"abc"这个对象的地址返回,赋给引用s2。因为s1、s2都是指向同一个字符串池中的"abc"对象,所以结果为true。
String s3 = new String("xyz");
String s4 = new String("xyz");
System.out.println(s3==s4);
复制代码
结果是 false
采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"xyz"这个字符串对象,如果有,则不在池中再去创建"xyz"这个对象了,直接在堆中创建一个"xyz"字符串对象,然后将堆中的这个"xyz"对象的地址返回赋给引用s3,这样,s3就指向了堆中创建的这个"xyz"字符串对象;如果没有,则首先在字符串池中创建一个"xyz"字符串对象,然后再在堆中创建一个"xyz"字符串对象,然后将堆中这个"xyz"字符串对象的地址返回赋给s3引用,这样,s3指向了堆中创建的这个"xyz"字符串对象。s4则指向了堆中创建的另一个"xyz"字符串对象。s3 、s4是两个指向不同对象的引用,结果当然是false。
!
Intern的实现原理
/**
* Returns a canonical representation for the string object.
*
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
*
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
*
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
*
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* 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();
复制代码
String#intern方法中看到,这个方法是一个 native 的方法,但注释写的非常明了。“ 当调用 intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。