第四章:对象与类
在类之间,最常见的关系有:
(1)依赖(“users-a”)
(2)聚合(“has-a”)
(3)继承(“is-a”)
依赖(dependence):如果一个类的方法操作另一个类的对象,我们就说一个类依赖于另一个类。应该尽可能地将相互依赖减至最少。如果A类不知道B的存在,它就不会关心B的任何改变(这意味着B的改变不会导致A产生任何Bug)。用软件工程的术语来说,就是让类之间的耦合度最小。
聚合(aggregation):意味着类A的对象包含类B的对象。(也可以叫“关联”)
继承(inheritance):是一种用于表示特殊与一般关系的。如果类A扩展类B,类A不但包含从类B继承的方法,还会拥有一些额外的功能。
对象与对象变量:
在对象与对象变量之间存在着一个重要的区别。例如:
Date deadline; //deadline doesn’t refer to any object
定义了一个对象变量deadline,它可以引用Date类型的对象。但是一定要认识到:变量deadline不是一个对象,实际上也没有引用对象。此时,不能将任何Date方法应用于这个变量上。
s = deadline.toString(); // not yet
将产生编译错误。
必须首先初始化变量deadline,这里有两个选择。当然,可以用新构造的对象初始化这个变量: deadline = new Date();
也可以让这个变量引用一个已存在的对象:deadline = birthday;
// 现在两个对象变量引用同一个对象。
一定要认识到:一个对象变量并没有实际包含一个对象,而仅仅是引用一个对象!
在Java中,任何对象变量的值(栈)都是对存储在另外一个地方(堆)的一个对象的引用。new操作符的返回值也是一个引用。
Date deadline = new Date(); 有两个部分。表达式new Date()构造了一个Date类型的对象,并且它的值是对新创建对象的引用。这个引用存储在变量deadline中。
静态变量与静态方法:
静态方法时一种不能向对象实施操作的方法。
可以认为静态方法时没有this参数的方法(在一个非静态的方法中,this参数表示这个方法的隐式参数)。
因为静态方法不能操作对象,所以不能在静态方法中访问实例域。但是,静态方法可以访问自身类中的静态域。
使用静态方法的两种情况:
(1)一个方法不需要访问对象状态,其所需参数都是通过显示参数提供。
(2)一个方法只需要访问类的静态域。
工厂方法:静态方法的一种常见的用途。
Main方法:不需要使用对象调用静态方法。main方法不对任何对象进行操作。事实上,在启动程序时还没有任何一个对象。静态的main方法将执行并创建程序所需要的对象。
方法参数:按值调用(call by value) 按引用调用(callby reference)
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给他的任何参数变量的内容。
然而,方法参数共有两种类型:
(1)基本数据类型(数字、布尔值)
(2)对象引用。
一个方法不可能修改一个基本数据类型的参数。而对象引用作为参数传递给方法后,方法得到的是对象引用的拷贝,而对象引用与它的拷贝同时引用同一个对象。所以,以对象引用作为参数能够改变对象内容。
(1)一个方法不能修改一个基本数据类型的参数。
(2)一个方法可以改变一个对象参数的状态。(对象引用)
(3)一个方法不能让对象参数引用一个新的对象。(不能让对象引用指向一个新的对象)
对象构造:
如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值:数值为0、布尔值为false、对象引用为null。然而,只有缺少程序设计竟然的人才会这样做。的确,如果不明确地对域进行初始化,就会影响程序代码的可读性。
注释:这是域与局部变量的主要不同点。必须明确地初始化方法中的局部变量。但是,如果没有初始化类中的域,将会被初始化为默认值(0、false、null)。
仅当类没有提供任何构造器的时候,系统才会提供一个默认的构造器。如果在编写类的时候,给出了一个构造器,哪怕是很简单的,要想让这个类的用户能够采用 new ClassName()构造实例的话,就必须提供一个默认的构造器(即不带参数的构造器)。当然,如果希望所有域被赋予默认值,可以采用下列格式:
public ClassName() { ); // 如:单例模式
类设计技巧:
(1)一定要保证数据私有!绝对不要破坏封装性。数据的表示形式很可能会改变,但它们的使用方式却不会经常发生变化。当数据保持私有时,它们的表示形式的变化不会对类的使用者产生影响,即使出现Bug也易于检测。
(2)一定要对数据初始化!Java不对局部变量进行初始化,但是会对对象的实例域进行初始化。最好不要依赖于系统的默认值,而是应该显示地初始化所有的对象,具体的初始化方法可以是提供默认值,也可以是在所有构造器中设置默认值。
(3)不要在类中过多的使用基本类型!
(4)不是所有的域都需要独立的域访问器和域更改器!
(5)将职责过多的类进行分解!
(6)类名和方法名要能够体现他们的职责!命名类名的良好习惯是采用一个名词(Order)、前面有形容词修饰的名词(RushOrder)或动名词(ing)修饰名词(BillingAddress)。