1,String 是不可变类 immutable
不可变类:所谓的不可变类是指这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类:八大包装类和String等。
可变类:相对于不可变类,可变类创建实例后可以改变其成员变量值,开发中创建的大部分类都属于可变类。
以String 为例,不可变类,一般如何设计?
1,类一般会用final 来修饰,无法创建子类
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
}
2,类里面的成员变量等都会用private 来修饰
3, 不会提供setters,append 等方法来改变变量值
2,String 对象的创建发方式
String k = "hello,world"
String i = "hello,world" //通过JVM 虚拟机创建
String j = new String("hello,world") //通过构造器创建
k == i // true
对象的比较是比较地址,JVM创建的对象,如果值相同,会指向同一个地址
i == j // false
通过new 生成的对象,会指向新的地址
3,String 重写了Object 里面的equals 方法
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; }
4,String 因为是不可变类,所以处理字符起来比较麻烦,也比较占用资源,因此可以用这两个可变类:
StringBuilder StringBuffer
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
相同点:StringBuilder StringBuffer 都是 继承于 类 AbstractStringBuilder
不同点:StringBuilder 是现线程不安全,StringBuffer 是线程安全的,因为StringBuffer 的方法上加上了线程同步 syncronized
@Override public synchronized StringBuffer append(String str) { // 线程同步 toStringCache = null; super.append(str); return this; }
@Override public StringBuilder append(String str) { super.append(str); return this; }
另外:StringBuilder是如何append的?
以append(String str) 为例子
StringBuilder 类下面有方法,是重写了父类的append 方法
@Override public StringBuilder append(String str) { super.append(str); return this; }
其父类
public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); //扩容 str.getChars(0, len, value, count); count += len; return this; }
因为String 的底层是char[] StringBuilder 也是将字符存在char []中
str.getChars(0, len, value, count) 通过调用了String 类下面的getChars方法,
len 代表新增的str 的长度
value 代表 StringBuilder 对象的char[]
count 代表 StringBuilder 对象的char[] 的长度
String 类下面的getChars 方法
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { if (srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } if (srcEnd > value.length) { throw new StringIndexOutOfBoundsException(srcEnd); } if (srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); } System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); }
getChars 方法传入了
srcBegin : 0
srcEnd :需要append的str 的长度
dst:StringBuilder 对象的char[]
dstBegin:StringBuilder 对象的char[] 的长度
通过调用 System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin)方法
value:需要append的str char[]
这个方法通俗的的将就是将 str 的 char[] 从0 开始 到 char[] 长度 copy 到目标数组,目标数组是StringBuilder 对象的char[],从它的length 的下标开始复制(也就是接着复制)
因为 system.arraycopy 是浅拷贝,直接改变目标数组的数据结构,虽然对象的引用并没有返回从String 这个方法 返回到StringBuilder 下的append 方法下,但是内存中的值已经改了,所以StirngBuilder 的char[] 已经更改
证明:hashcode 的值是一样的
综上:
- 操作少量的数据: 适用String
- 单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
- 多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer