package java.lang;
import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // 哈希值的缓冲,默认为o
/**
* 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;
}
/**
* Allocates a new {@code String} so that it represents the sequence of
* characters currently contained in the character array argument. The
* contents of the character array are copied; subsequent modification of
* the character array does not affect the newly created string.
*
* @param value
* The initial value of the string
*/
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
//String的equals()方法,比较的是地址值或内容
/**
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
*
* @param anObject
* The object to compare this {@code String} against
*
* @return {@code true} if the given object represents a {@code String}
* equivalent to this string, {@code false} otherwise
*
* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
//String的HashCode方法,使用了hash缓存,hash是String实例化的HashCode的一个缓存,因为字符串常用于比较,将HashCode缓存在hash中,进行比较时就不需要重新计算哈希值,即可直接比较,提高程序的效率
/**
* Returns a hash code for this string. The hash code for a
* {@code String} object is computed as
*
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
*
* using {@code int} arithmetic, where {@code s[i]} is the
* ith character of the string, {@code n} is the length of
* the string, and {@code ^} indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
String—亨元模式(Flyweigth):可以理解为缓存(cache)是一种设计模式或者说优化策略
常量池(Constant pool)被确定的,并且直接保存在已编译的class文件中的一些数据,包括类,方法,接口中的常量以及字符串常量等,程序执行时,常量池会储存在方法区.
JVM在内存中专门开辟了一块内存区域用于存放字符串对象,成为字符串常量池,在Java1.7以后字符串常量池移到了堆内存区域
在JDK6.0及之前版本中,String Pool里放的都是字符串常量
在JDK7.0中,由于String#intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用.
Java中的八种基本类型以及String类型,由于会频繁使用这些类型,为了使其运行更快,节省内存,Java引入了常量池的概念,常量池就是类似一个Java提供的缓存
String a = "Hello World";
String b = "Hello World";
System.out.print(a==b);//true
1.采用字面值创建String对象,JVM会先去字符串常量池中查找是否有 "Hello World"这个对象,如果没有则在池中创建一个 "Hello World"对象,然后将这个对象的引用赋给a,a就会指向池中的 "Hello World"对象,但创建b对象时,由于JVM在池中找到了 "Hello World"对象,所以JVM会将池中这个对象的引用直接赋给b,这样a,b两个引用都指向了同一个对象,所以a==b为true
String c = new String("Hello World");
String d = new String("Hello World");
System.out.print(c==d);//false
2.采用new创建对象,JVM会先去字符串常量池中查找是否有 "Hello World"这个对象,如果没有则在池中创建一个 "Hello World"对象,并将引用指向堆中,然后new String在堆中开辟了一个空间,栈中的引用a指向这个空间,每new一次都会在堆中开辟一个空间,所以b指向了堆中新开辟的new String空间,c,d在堆中的地址值不同,即c==d为false.
String字面值运算时,并不会把每个String对象放入池中,只会把计算结果放入池中
JVM对字符串常量池的优化可以总结为:未声明放结果,已声明放引用,所以字符串常量池中可以同时放字符串常量和引用
AbstractStringBuilder 在java.lang 包中,是一个抽象类,实现 Appendable 接口和 CharSequence 接口,这个类的诞生是为了解决 String 类在创建对象的时候总是创建新的对象的问题的。AbstractStringBuilder 有两个子类,分别是 StringBuilder 和 StringBuffer.
AbstractStringBuilder类和String类很类似,String类内部维护了一个char数组,用final修饰,即不可变.
AbstractStringBuilder类中同样维护了一个char数组但是是个变量,即是可变数组,这就是为了解决String每次操作都要创建新对象的关键所在.
package java.lang;
import sun.misc.FloatingDecimal;
import java.util.Arrays;
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;//可变数组
/**
* The count is the number of characters used.
*/
int count;//计数
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
//无参构造
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
//含参构造
value = new char[capacity];
}
/**
* Returns the length (character count).
*
* @return the length of the sequence of characters currently
* represented by this object
*/
1.length(),返回char[]的实际长度(count),重写的CharSequence接口的length()方法.
2.capacity():capacity意思是’容量’,即得到目前该value数组的实际大小
@Override
public int length() {
return count;
}
public int capacity() {
return value.length;//初始容量为16
}
StringBuilder builder = new StringBuilder("adadsagfaesgsa");
System.out.println(builder.length());//14,数组的长度
System.out.println(builder.capacity());//30,数组的容量,不够时自动扩容(扩容机制:容量不够用时,先将当前容量+1的二倍(newCapacity)与需要的容量(minimumCapacity)比较如果比需要的容量大,那就将容量扩大到容量+1的二倍;如果比需要的容量小,那就直接扩大到需要的容量。)
3.toString是AbstractStringBuilder抽象类唯一的一个抽象方法;唯一的一个final方法:getValue(),得到value数组。可以对其直接操作
@Override
public abstract String toString();//继承自Object类
final char[] getValue() {
return value;
}
4.append()方法—AbstractStringBuilder类及其子类中最重要的操作;Appendable接口的具体实现,源码中所有的append()方法返回值都是原类型AbstractStringBuilder,所以append()可以无限调用.
/*append(Object obj):利用Object(或任何对象)的toString方法转成字符串然后添加到该value[]中*/
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
/*public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}*/
/**/
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);//扩容
str.getChars(0, len, value, count);//将原数组拼接到目标数组(value从str下标为0处开始拼接count长度)
count += len;
return this;
}
// Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
// Documentation in subclasses because of synchro difference
@Override
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)//判断s是否是String的实例
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;//如果str 是 null,就賦予str = "null",即字符串长度为4
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
5.setCharAt(int index, char ch):直接设置下标为index的字符为ch
public void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
value[index] = ch;
}
6.replace(int start, int end, String str):用字符串str替换掉value[]数组的[start,end)部分–左闭右开
public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
int len = str.length();
int newCount = count + len - (end - start);
ensureCapacityInternal(newCount);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount;
return this;
}
7.insert(int index, char str[], int offset,int len):在value[]的下标为index位置插入数组str的一部分[offset,offest+len)–左闭右开
public AbstractStringBuilder insert(int offset, Object obj) {
return insert(offset, String.valueOf(obj));//插入数组
}
public AbstractStringBuilder insert(int index, char[] str, int offset,
int len)
{
if ((index < 0) || (index > length()))
throw new StringIndexOutOfBoundsException(index);
if ((offset < 0) || (len < 0) || (offset > str.length - len))
throw new StringIndexOutOfBoundsException(
"offset " + offset + ", len " + len + ", str.length "
+ str.length);
ensureCapacityInternal(count + len);//扩容
System.arraycopy(value, index, value, index + len, count - index);
System.arraycopy(str, offset, value, index, len);//数组增加
count += len;
return this;
}
8.在value[]查找字符串str,返回其下标
public int indexOf(String str) {
return indexOf(str, 0);//返回str在value[]第一次出现的下标值
}
public int lastIndexOf(String str) {
return lastIndexOf(str, count);//返回str在value[]最后一次出现的下标值
}
9.删除方法,删除value[]中下标为[start,end)的一段数组,左闭右开
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;//删除指定长度数组
}
return this;
}
StringBuilder类继承自AbstractStringBuilder类,是一个可变的字符序列(char[] value,可变数组),相对于String,有其特有的append(),insert(),delete()方法,由于StringBuilder的字符串拼接调用的是append()方法,所以不会产生大量的临时新对象,提高了拼接效率,线程不安全,在JDK1.5之后引入,也是作为String对象在进行"+"操作时,其实本质上就是调用的StringBuilder的append()方法.
String a = "HelloWorld";
String b = "Hello" + "World";
String c = "Hello";
String d = "World";
String e = c + d;
c
System.out.println(a);//输出为HelloWorld
System.out.println(e);//HelloWorld
System.out.println(a == e);//false,两个String对象+操作时,底层调用的是StringBuiler的append()方法,先拼接Hello,再拼接上World,返回的结果e存入一个新的对象,所以说String字符串引用相加的计算效率要比直接相加的效率低
System.out.println(a == b);//true
System.out.println(b == e);//false
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
public StringBuilder() {
super(16);//初始化容量
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);//使用str初始化,容量str大小的基础上加16
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
StringBuffer和StringBuilder都是继承自AbstractStringBuilder类(char[] value,可变数组),两者实现的方法功能基本一致,不过StringBuffer的方法都是被 synchronized关键字修饰,所以StringBuffer是线程安全的,但是保证安全的同时牺牲了效率(同步锁),所以效率:StringBuilder>StringBuffer.
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
@Override
private transient char[] toStringCache;
public synchronized StringBuffer append(String str) {
//同步关键字--线程安全
toStringCache = null;
super.append(str);
return this;
}
}
String | StringBuilder | StringBuffer |
---|---|---|
最终类不可继承 | 最终类不可继承 | 最终类不可继承 |
String字符串是常量不可变 | 变量可变 | 变量可变 |
线程不安全 | 线程安全 | |
进行大量字符串拼接时,回新建大量临时对象,浪费内存空间且效率低下 | 采用append()拼接字符串,可变数组拼接,效率高,常用于字符串拼接 | 与StirngBuilder相比,加入了同步锁(synchronized),保证了线程安全,当降低了效率 |
String继承自Object类 | StringBuilder继承自AbstractStringBuilder类 | StringBuffer继承自AbstractStringBuild |