1)public String():空构造
2)public String(byte[] bytes):把字节数组转成字符串
3)public String(byte[] bytes,int offset,int length):把字节数组的一部分转成字符
4)public String(char[] value):把字符数组转成字符串
5)public String(char[] value,int offset,int count):把字符数组的一部分转成字符串
6)public String(String original):把字符串常量值转成字符串
(一)判断功能
boolean equals(Object obj):比较字符串的内容是否相同,区分大小写
boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写
boolean contains(String str):判断大字符串中是否包含小字符串
boolean startsWith(String str):判断字符串是否以某个指定的字符串开头
boolean endsWith(String str):判断字符串是否以某个指定的字符串结尾
boolean isEmpty():判断字符串是否为空。
(二)获取功能
int length():获取字符串的长度。
char charAt(int index):获取指定索引位置的字符
int indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引:为什么这里是int类型,而不是char类型?原因是:'a'和97其实都可以代表'a'
int indexOf(String str):返回指定字符串在此字符串中第一次出现处的索引。
int indexOf(int ch,int fromIndex):返回指定字符在此字符串中从指定位置后第 一次出现处的索引。
int indexOf(String str,int fromIndex):返回指定字符串在此字符串中从指定位置 后第一次出现处的索引。
String substring(int start):从指定位置开始截取字符串,默认到末尾。
String substring(int start,int end):从指定位置开始到指定位置结束截取字符 串。
(三)转换功能
byte[] getBytes():把字符串转换为字节数组。
char[] toCharArray():把字符串转换为字符数组
static String valueOf(char[] chs):把字符数组转成字符串。
static String valueOf(int i):把int类型的数据转成字符串。该方法的入参可以是任何基本数据类型
String toLowerCase():把字符串转成小写。
String toUpperCase():把字符串转成大写。
String concat(String str):把字符串拼接。
(四)替换功能
String replace(char old,char new)
String replace(String old,String new)
(五)去空格
String trim()
(六)按字典比较
int compareTo(String str)
int compareToIgnoreCase(String str)
(七)分割功能
String [] split(String,int)
String [] split(String)
public final class String
implements java.io.Serializable, Comparable, CharSequence {
private final char value[];
private int hash; // Default to 0
//下面省略。。。
}
String类是用final修饰的,这意味着String不能被继承,所有的成员方法都默认为final方法。
String类实现的接口:
java.io.Serializable:这个序列化接口仅用于标识序列化的语意。
Comparable
CharSequence:这个接口是一个只读的字符序列。包括length(), charAt(int index), subSequence(int start, int end)这几个API接口,值得一提的是,StringBuffer和StringBuild也是实现了该接口。
String的成员变量:
value[] :char数组用于储存String的内容。
hash :String实例化的hashcode的一个缓存,String的哈希码被频繁使用,将其缓存起来,每次使用就没必要再次去计算,这也是一种性能优化的手段。这也是String被设计为不可变的原因之一,后面会讲到。
字符串常量池存在运行时常量池之中(在JDK7之前存在运行时常量池之中,在JDK7已经将其转移到堆中,也对intern 方法做了一些修改。因为字符串常量池和new的对象都存于Java堆中)。字符串常量池的存在使JVM提高了性能和减少了内存开销
创建字符串的两种形式:
1)String s1=”1”;
2)String s2=new String(“1”);
String s1=”1”;创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s1(引用s1在Java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s1
String s2=new String(”1”);创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么不再在字符串常量池创建该字符串对象,而直接堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s2,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s2。
“+”连接形式创建字符串:
1)String s1=”1”+”2”+”3”;
2)String s2=”1”+”3”+new String(“1”)+”4”;
当使用“+”连接字符串中含有变量时,也是在运行期才能确定的。首先连接操作最开始时如果都是字符串常量,编译后将尽可能多的字符串常量连接在一起,形成新的字符串常量参与后续的连接(可通过反编译工具jd-gui进行查看)。
接下来的字符串连接是从左向右依次进行,对于不同的字符串,首先以最左边的字符串为参数创建StringBuilder对象(可变字符串对象),然后依次对右边进行append操作,最后将StringBuilder对象通过toString()方法转换成String对象(注意:中间的多个字符串常量不会自动拼接)。
实际上的实现过程为:String s2=new StringBuilder(“13”).append(new String(“1”)).append(“4”).toString();所以当使用+进行多个字符串连接时,实际上是产生了一个StringBuilder对象和一个String对象。
String.intern()解析:
String.intern()是一个Native方法,底层调用C++的 StringTable::intern 方法,当调用 intern 方法时,如果常量池中已经存在该字符串,则返回池中字符串;否则直接存储堆中的引用,也就是字符串常量池中存储的是指向堆里的对象。所以结果为true。
1)String s3=new String(“1”)+new String(“1”);
(1)对于==,如果作用于基本数据类型的变量(byte,short,char,int,long,float,double,boolean ),则直接比较其存储的"值"是否相等;如果作用于引用类型的变量(String),则比较的是所指向的对象的地址(即是否指向同一个对象)。
(2)equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。在Object类中,equals方法是用来比较两个对象的引用是否相等。
(3)对于equals方法,注意:equals方法不能作用于基本数据类型的变量。如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;而String类对equals方法进行了重写,用来比较指向的字符串对象所存储的字符串是否相等。其他的一些类诸如Double,Date,Integer等,都对equals方法进行了重写用来比较指向的对象所存储的内容是否相等。
内部可变数组,存在初始化StringBuffer对象中字符数组容量为16,存在扩容。
StringBuffer类继承AbstractStringBuilder抽象类,其中StringBuffer的大部分方法都是直接调用的父类的实现。
部分源码:
//构造方法
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
//方法
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
//...省略部分
由上图可以看出StringBuffer大部分方法使用了synchronized关键字,来实现多线程下的线程安全,synchronized代表方法加锁,例如线程A,要运行到声明了synchronized 方法时,先检查有没有其他线程B,C正在使用这synchronized 方法,若有就先等B,C线程运行完这个synchronized 方法后,在运行A,若无直接运行。StringBuilder其继承关系、方法与StringBuffer是一样的,区别在于StringBuilder类所有方法没有加synchronized关键字,所以StringBuffer与StringBuilder的区别在于StringBuffer线程是安全的,StringBuilder线程不是安全的,其本质区别是方法有没有使用synchronized关键字
获得要追加的字符串的长度,判断当前字符数组是否能够存储追加的字符串,如果容量不够则确定新的字符数组的容量,申请新的字符数组,将以前字符数组的内容复制到新字符数组。最后将要追加的字符串,复制到新字符数组中
源码:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];//StringBuffer 的构造函数本质是调用了父类 AbstractStringBuilder 类的构造函数,该构造函数初始化了一个默认大小为16的字符数组。
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);//将追加的字符串str的0-len位置的字符复制到 字符数组 value 的起始位置 count 处。
count += len;
return this;
}
//这是一个私有的方法。首先确保容量足够,其次我们看到所谓的 null 本质就是追加了'null'这样的四 个字符到字符数组中。
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// minimumCapacity指的就是当前字符串的最小容量,如果这个容量比当前字符数组的容量要大,则需要重新申请新的字符数组,并将以前字符数组的内容复制到新的字符数组中。
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;//申请原有容量2倍+2的容量
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
private transient char[] toStringCache;//这里比StringBuilder多了一个参数,作用就是去缓存toString的
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
//下面省略
}
StringBuffer的toString方法,如果StringBuffer对象此时存在toStringCache,在多次调用其toString方法时,其new出来的String对象是会共享同一个char[] 内存的,达到共享的目的。但是StringBuffer只要做了修改,其toStringCache属性值都会置null处理。这也是StringBuffer和StringBuilder的一个区别点。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
//下面省略
}
String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。
StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容, 其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。
StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修饰,以此来实现在多线程下的操作字符串的安全性。其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。