1 类
类是构造对象的模板。
由类构造对象的过程称为创建类的实例。
一个源文件中,只能有一个公共类,类名必须与文件名相同。
1.1 类之间的关系
最常见的关系有:
- 依赖(uses-a):一个类的方法需要操纵另一个类的对象
- 聚合(has-a):类 A 的对象包含着类 B 的对象
- 继承(is-a):继承是一种用于表示特殊与一般的关系,父类更一般
应该尽可能地将相互依赖的类减至最少。
1.2 构造器
- 构造器和类名同名,参数不限,没有返回值。
- 构造器中的局部变量会覆盖同名实例域。
- 若未手动编写构造器,会默认提供一个无参构造器,设默认值。手动提供构造器后,不会自动提供无参构造器。
- 构造器不能被继承,因此不能被重写,但可以被重载。
- 父类与子类的构造函数调用次序:若子类构造器没有显式调用父类构造器,不管子类构造器有无参数,都默认调用父类无参构造器。
1.3 finalize 方法
可以为任何一个类添加 finalize 方法,将在垃圾回收器清除对象之前调用。不要依赖,不能保证被调用。
1.4 Object:所有类的超类
如果重新定义 equals 方法,就必须重新定义 hashCode 方法。eauals 与 hashCode 的定义必须一致:如果 x.eauals(y) 返回 true,那么 x.hashCode() 必须与 y.hashCode() 具有相同的值。
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
- 与 null 的比较
- 对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false
x.equals(null); // false;
等价与相等
- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
实现
- 检查是否为同一个对象的引用,如果是直接返回 true;
- 检查是否是同一个类型,如果不是,直接返回 false;
- 将 Object 对象进行转型;
- 判断每个关键域是否相等。
2)hashCode()
hashCode() 返回哈希值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价,这是因为计算哈希值具有随机性,两个值不同的对象可能计算出相同的哈希值。
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象哈希值也相等。
HashSet 和 HashMap 等集合类使用了 hashCode() 方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现 hashCode() 方法。
3)toString()
默认返回 ClassName@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。
4)clone()
clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。
clone() 方法并不是 Cloneable 接口的方法,Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。
使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。
- 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝。
- 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容。
1.5 枚举类
枚举类定义的是一个类,有着指定的几个实例。
比较两个枚举类型的值时,不需要调用 equals 方法,而直接使用"=="就可以了。
2 对象
2.1 创建对象
- 用 new 语句创建对象
- 运用反射
- 调用对象的 clone() 方法
- 运用反序列化手段,调用 java.io.ObjectInputStream 对象的 readObject() 方法
(1) 和 (2) 都会明确的显式的调用构造函数;(3) 是在内存上对已有对象的影印,所以不会调用构造函数;(4) 是从文件中还原类的对象,也不会调用构造函数。
2.2 对象克隆
有两种方式:
- 实现 Cloneable 接口并重写 Object 类中的 clone() 方法;
- 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
2.3 序列化
- 对象序列化(Serializable)是指将对象转换为字节序列的过程,而反序列化则是根据字节序列恢复对象的过程。只有实现了 Serializable 和 Externalizable 接口的类的对象才能被序列化。
- java.io.ObjectOutputStream 代表对象输出流,它的 writeObject(Objectobj) 方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
- java.io.ObjectInputStream 代表对象输入流,它的 readObject() 方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
3 方法
3.1 方法签名
方法签名只有方法名和参数,没有返回值。也就是说,不能有两个名字相同、参数也相同而返回值不同的方法。
3.2 隐式参数与显式参数
隐式参数是出现在方法名前的类对象(this),显式参数位于方法名后面的括号中。
使用 this 可以区分开隐式参数的类对象的实例域和局部变量
3.3 方法参数
Java 方法参数是值传递,不是引用传递。
方法在执行时,先定义了局部变量,这些局部变量指向,传入参数的指向。对局部变量重新指向时,完全不影响原本传入参数的那些指向。
- 值传递:方法接收的是调用者提供的值。
- 引用传递:方法接受的是调用所对应的变量地址。
3.4 参数变量可变的方法
Object… 参数类型与 Object[] 完全一样,省略号表明这个方法可以接收任意数量的的对象。