目录
4. String, StringBuffer and StringBuilder
1. Object
1.1 基本介绍
Object类是类层次结构的根类。每个类都使用 Object 作为超类。每个类都直接或者间接的继承自Object类。
1.2 常用方法
1.2.1 equals() 方法
- 等价关系
自反性
x.equals(x); // true
对称性
x.equals(y) == y.equals(x); // true
传递性
if (x.equals(y) && y.equals(z))
x.equals(z); // true;
一致性
多次调用 equals() 方法结果不变
x.equals(y) == x.equals(y); // true
- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y); // false
实现
- 检查是否为同一个对象的引用,如果是直接返回 true;
- 检查是否是同一个类型,如果不是,直接返回 false;
- 将 Object 对象进行转型;
- 判断每个关键域是否相等。
public class EqualExample {
private int x;
private int y;
private int z;
public EqualExample(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public boolean equals(Object o) {
if (this == o) return true; //检查是否为同一个对象的引用,如果是直接返回 true;
if (o == null || getClass() != o.getClass()){
//检查是否是同一个类型,如果不是,直接返回 false
return false;
}
// 将 Object 对象进行转型
EqualExample that = (EqualExample) o;
// 判断每个关键域是否相等。
if (x != that.x) return false;
if (y != that.y) return false;
return z == that.z;
}
}
1.2.2 hashCode() 方法
hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。 等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。 下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。 我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法, 因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。
EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size()); // 2
理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。 这就要求了散列函数要把所有域的值都考虑进来。 可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。 R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。
1.2.3 toString() 方法
如果输出一个对象的时候,会使用Object类的toString方法,结果会输出一个对象的哈希code码,地址的字符串
Bridge bri=new Bridge();
System.out.println(bri);
通过重写toString方法来输出对象的属性
String name="子类的名字";
int age=3;
@Override
public String toString() {
return "Bridge [name=" + name + ", age=" + age + "]";
}
1.2.4 clone() 方法
Cloneable clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。
浅拷贝 拷贝对象和原始对象的引用类型引用同一个对象。
public class ShallowCloneExample implements Cloneable {
private int[] arr;
public ShallowCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override
protected ShallowCloneExample clone() throws CloneNotSupportedException {
return (ShallowCloneExample) super.clone();
}
}
// 拷贝对象和原始对象的引用类型引用同一个对象。
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 222
深拷贝 拷贝对象和原始对象的引用类型引用不同对象。
public class DeepCloneExample implements Cloneable {
private int[] arr;
public DeepCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override
protected DeepCloneExample clone() throws CloneNotSupportedException {
DeepCloneExample result = (DeepCloneExample) super.clone();
// 创建新对象
result.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}
return result;
}
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 2
clone() 的替代方案 使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。 Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。
public class CloneConstructorExample {
private int[] arr;
public CloneConstructorExample() { //构造函数
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public CloneConstructorExample(CloneConstructorExample original) { // 拷贝构造函数
arr = new int[original.arr.length];
for (int i = 0; i < original.arr.length; i++) {
arr[i] = original.arr[i];
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
}
CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 2
2. String
2.1 基本介绍
String 被声明为 final,因此它不可被继承。
内部使用 char 数组存储数据,该数组被声明为 final, 这意味着 value 数组初始化之后就不能再引用其它数组。 并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
2.2 常用方法
public String() //空构造
public String(byte[] bytes) //把字节数组转成字符串
public String(byte[] bytes,int index,int length) //把字节数组的一部分转成字符串
public String(char[] value) //把字符数组转成字符串
public String(char[] value,int index,int count) //把字符数组的一部分转成字符串,第三个参数表示的是数目
public String(String original) //把字符串常量值转成字符串
2.2.1 使用示例
public class StringDemo {
public static void main(String[] args) {
//public String():空构造
String s=new String();
System.out.println("s:"+s);
System.out.println("s.length="+s.length());
System.out.println("-----------------------");
//public String(byte[] bytes):把字节数组转成字符串
byte[] bys={97,98,99,100,101};
String s2=new String(bys);
System.out.println("s2:"+s2);
System.out.println("s2.length="+s2.length());
System.out.println("-----------------------");
//public String(byte[] bytes,int index,int length):把字节数组的一部分转成字符串
String s3=new String(bys,0,3); //从0位置开始,3个字符
System.out.println("s3:"+s3);//s3:abc
System.out.println("s3.length="+s3.length());//s3.length=3
System.out.println("-----------------------");
//public String(char[] value):把字符数组转成字符串
char[] chs={'a','b','c','d','e'};
String s4=new String(chs); //从0位置开始,3个字符
System.out.println("s4:"+s4);
System.out.println("s4.length="+s4.length());
System.out.println("-----------------------");
//public String(char[] value,int index,int count):把字符数组的一部分转成字符串
String s5=new String(chs,0,3); //从0位置开始,3个字符
System.out.println("s5:"+s5);
System.out.println("s5.length="+s5.length());
System.out.println("-----------------------");
//public String(String original):把字符串常量值转成字符串
String s6=new String("abcde");
System.out.println("s6:"+s6);
System.out.println("s6.length="+s6.length());
}
}
字符串的特点:一旦被赋值,就不能改变
2.2.2 判断功能
boolean equals(Object obj) //比较字符串的内容是否相同,区分大小写
boolean equalsIgnoreCase(String str) //比较字符串的内容是否相同,忽略大小写
boolean contains(String str) //判断大字符串中是否包含小字符串
boolean startsWith(String str) //判断字符串是否以某个指定的字符串开头
boolean endsWith(String str) //判断字符串是否以某个指定的字符串结尾
boolean isEmpty()// 判断字符串是否为空
2.2.3 获得功能
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) //从指定位置开始到指定位置结束截取字符串。左闭右开
2.2.4 转换功能
byte[] getBytes() //字符串转换为字节数组。
char[] toCharArray() //把字符串转换为字符数组。
static String valueOf(char[] chs) //把字符数组转成字符串。
static String valueOf(int i) //把int类型的数据转成字符串。注意:String类的valueOf方法可以把任意类型的数据转成字符串。
String toLowerCase() //把字符串转成小写。
String toUpperCase() //把字符串转成大写。
String concat(String str) //把字符串拼接。
2.2.5 其他功能
//替换功能:
String replace(char old,char new)
String replace(String old,String new)
//去除字符串两端空格
String trim()
//按字典顺序比较两个字符串
int compareTo(String str)
int compareToIgnoreCase(String str)
3. StringBuffer
3.1 基本介绍
StringBuffer代表可变的字符序列,可以对字符串内容进行增删
3.2 常用方法
//StirngBuffer的构造方法
public StringBuffer() //无参构造方法
public StringBuffer(int capacity) //指定容量的字符串缓冲区对象
public StringBuffer(String str) //指定字符串内容的字符串缓冲区对象
//StringBuffer的方法
public int capacity() //返回当前容量。 理论值
public int length() //返回长度(字符数)。 实际值
3.2.1 添加功能
public StringBuffer append(String str)
//可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身
public StringBuffer insert(int offset,String str)
//在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
3.2.2 删除功能
public StringBuffer deleteCharAt(int index)
//删除指定位置的字符,并返回本身
public StringBuffer delete(int start,int end)
//删除从指定位置开始指定位置结束的内容,并返回本身
3.2.3 替换功能
public StringBuffer replace(int start,int end,String str)
//从start开始到end用str替换
3.2.4 反转功能
public StringBuffer reverse()
3.2.5 截取功能
public String substring(int start) // 注意截取返回的是String,而不是StringBuffer了
public String substring(int start,int end)
4. String, StringBuffer and StringBuilder
4.1 可变性
- String 不可变
- StringBuffer 和 StringBuilder 可变
4.2 线程安全
- String 不可变,因此是线程安全的
- StringBuilder 不是线程安全的
- StringBuffer 是线程安全的,内部使用 synchronized 进行同步
4.3 性能
- Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢
- StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用
- StringBuilder每次都会对StringBuilder对象本身进行操作,而不是生成新的对象并改变对象引用。 相同情况下使用StirngBuilder相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却冒多线程不安全的风险。
4.4 三者使用的总结
- 操作少量的数据,使用String
- 单线程操作字符串缓冲区下操作大量数据,使用StringBuilder
- 多线程操作字符串缓冲区下操作大量数据,使用StringBuffer