对象与类

4.3.7 基于类的访问权限

从前面已经知道,方法可以访问所调用对象的私有数据。一个方法可以访问所属类的所有
对象的私有数据,这令很多人感到奇怪!例如,下面看一下用来比较两个雇员的 equals 方法。

典型的调用方式是 if (harry,equals(boss)) . . .
这个方法访问 harry 的私有域, 这点并不会让人奇怪,然而, 它还访问了 boss 的私有域。这
是合法的, 其原因是 boss 是 Employee 类对象, 而 Employee 类的方法可以访问 Employee 类
的任何一个对象的私有域

class Employee
{
	// instance fields
	private String name;
	private double salary;
	private Local Date hireDay;
	// constructor
	public Employee(String n , double s, int year , int month, int day)
	{
		name = n;
		salary = s;
		hireDay = Local Date.of(year, month, day);
	}
}

public String getNameO
{
	return name;
}
class Employee
{
	public boolean equals(Employee other)
	{
	return name.equals(other.name);
	}
}

4.4.3 静态方法

在下面几种情况下使用静态方法:

  1. 一 方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如: Math.pow )。
  2. 一个方法只需要访问类的静态域(例如:Employee.getNextld)。

4.4.4 工厂方法

静态方法还有另外一种常见的用途。类似 LocalDate 和 NumberFormat 的类使用静态工
厂方法 (factory methocO 来构造对象。 你已经见过工厂方法 LocalDate.now 和 LocalDate.of。

NumberFormat 类如下使用工厂方法生成不同风格的格式化对象:
NumberFormat currencyFormatter = NumberFormat.getCurrencylnstance();
NumberFormat percentFormatter = NumberFormat.getPercentlnstance()double x = 0.1;
System.out.println(currencyFormatter.format(x)); // prints SO.10
System.out.println(percentFomatter.format(x)); // prints 10%

为什么 NumberFormat 类不利用构造器完成这些操作呢? 这主要有两个原因:无法命名构造器。构造器的名字必须与类名相同。但是, 这里希望将得到的货币实例和百分比实例采用不用的名字。当使用构造器时,无法改变所构造的对象类型。而 Factory方法将返回一个 DecimalFormat
类对象。

4.10 类设计技巧

我们不会面面俱到, 也不希望过于沉闷, 所以这一章结束之前, 简单地介绍几点技巧。
应用这些技巧可以使得设计出来的类更具有 OOP 的专业水准。
1. 一定要保证数据私有
这是最重要的;绝对不要破坏封装性。有时候, 需要编写一个访问器方法或更改器方法,
但是最好还是保持实例域的私有性。很多惨痛的经验告诉我们, 数据的表示形式很可能会改
变, 但它们的使用方式却不会经常发生变化。当数据保持私有时, 它们的表示形式的变化不
会对类的使用者产生影响, 即使出现 bug 也易于检测。
2. 一定要对数据初始化
Java 不对局部变量进行初始化, 但是会对对象的实例域进行初始化。最好不要依赖于系
统的默认值, 而是应该显式地初始化所有的数据, 具体的初始化方式可以是提供默认值, 也
可以是在所有构造器中设置默认值。
3. 不要在类中使用过多的基本类型
就是说,用其他的类代替多个相关的基本类型的使用。这样会使类更加易于理解且易于
修改。例如, 用一个称为 Address 的新的类替换一个 Customer 类中以下的实例域:

private String street;
private String city;
private String state;
private int zip;

这样, 可以很容易处理地址的变化, 例如, 需要增加对国际地址的处理。
4. 不是所有的域都需要独立的域访问器和域更改器
或许, 需要获得或设置雇员的薪金。 而一旦构造了雇员对象, 就应该禁止更改雇用日
期,并且在对象中,常常包含一些不希望别人获得或设置的实例域, 例如, 在 Address 类中,
存放州缩写的数组。
5. 将职责过多的类进行分解
这样说似乎有点含糊不清, 究竟多少算是“ 过多” ? 每个人的看法不同。但是, 如果明
显地可以将一个复杂的类分解成两个更为简单的类,就应该将其分解(但另一方面, 也不要
走极端。设计 10 个类,每个类只有一个方法,显然有些矫枉过正了)。
下面是一个反面的设计示例。

public class CardDeck // bad design
{
	private int口 value;
	private int[] suit;
	public CardDeck() {...}
	public void shuffle() {... }
	public int getTopValue() {...}
	public int getTopSuit() {...}
	public void draw(){...}
}

实际上,这个类实现了两个独立的概念:一副牌(含有 shuffle 方法和 draw 方法)和一张牌(含有查看面值和花色的方法)。另外, 引入一个表示单张牌的 Card 类。现在有两个类,
每个类完成自己的职责:

public class CardDeck
{
	private Card[] cards;
	public CardDeck() { .. . }
	public void shuffle() { . . . }
	public Card getTop() { . . . }
	public void draw() { . . . }
}
public class Card
{
	private int value;
	private int suit;
	public Card(int aValue, int aSuit) { . . . }
	public int getValue() { . . . }
	public int getSuit() { . . . }
} 

6.类名和方法名要能够体现它们的职责
与变量应该有一个能够反映其含义的名字一样, 类也应该如此(在标准类库中, 也存在
着一些含义不明确的例子, 如: Date 类实际上是一个用于描述时间的类 )。
命名类名的良好习惯是采用一个名词( Order )、 前面有形容词修饰的名词( RushOrder)
或动名词(有“ -ing” 后缀)修饰名词(例如, BillingAddress )。对于方法来说,习惯是访问
器方法用小写 get 开头 ( getSalary ), 更改器方法用小写的 set 开头(setSalary )
7.优先使用不可变的类
LocalDate 类以及 java.time 包中的其他类是不可变的——没有方法能修改对象的状态。
类似 plusDays 的方法并不是更改对象,而是返回状态已修改的新对象。
更改对象的问题在于, 如果多个线程试图同时更新一个对象, 就会发生并发更改。其结
果是不可预料的。 如果类是不可变的,就可以安全地在多个线程间共享其对象。
因此, 要尽可能让类是不可变的, 这是一个很好的想法。对于表示值的类, 如一个字符
串或一个时间点,这尤其容易。计算会生成新值, 而不是更新原来的值。
当然,并不是所有类都应当是不可变的。如果员工加薪时让 raiseSalary 方法返回一个新
的 Employee 对象, 这会很奇怪。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值