Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

1 类

类是构造对象的模板。

由类构造对象的过程称为创建类的实例。

一个源文件中,只能有一个公共类,类名必须与文件名相同。

1.1 类之间的关系

最常见的关系有:

  1. 依赖(uses-a):一个类的方法需要操纵另一个类的对象
  2. 聚合(has-a):类 A 的对象包含着类 B 的对象
  3. 继承(is-a):继承是一种用于表示特殊与一般的关系,父类更一般

应该尽可能地将相互依赖的类减至最少。

1.2 构造器

  1. 构造器和类名同名,参数不限,没有返回值。
  2. 构造器中的局部变量会覆盖同名实例域。
  3. 若未手动编写构造器,会默认提供一个无参构造器,设默认值。手动提供构造器后,不会自动提供无参构造器。
  4. 构造器不能被继承,因此不能被重写,但可以被重载。
  5. 父类与子类的构造函数调用次序:若子类构造器没有显式调用父类构造器,不管子类构造器有无参数,都默认调用父类无参构造器。

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 创建对象

  1. 用 new 语句创建对象
  2. 运用反射
  3. 调用对象的 clone() 方法
  4. 运用反序列化手段,调用 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[] 完全一样,省略号表明这个方法可以接收任意数量的的对象。