[size=large][b]第二章 从基础开始[/b][/size]
上一章定义了一个Student类。在学校里,学生都有课程安排,所以需要一个CourseSession类表示课程安排,它存储上课时间和教师信息,同时保存一份这门课程的学生。
CourseSession 需要存储学生,所以需要一个集合来存储学习,大家立刻会想到用ArrayList
private ArrayList<Student> students = new ArrayList<Student>();
注意用import包把java.util.ArrayList包含进来,包提供了一种将相关的类进行分组的机制。
[b]包有几个用途:[/b]
首先,将一组类打成包,给开发者提供了相当的便利,避免开发者必须同一时刻在成十,成百,甚至上千的类中查找。
第二,类被打成包也有发布地目的,可以方便地重用模块或者子系统。
第三,包在java中提供了命名空间。包提供了赋予类一个唯一名字的机制,从而最小化类命名冲突。比如您的类有全称 com.mycompany.studentinfosystem.Student ,第三方API也许会用 com.thirdpartyco.expensivepackages.Student.
习惯:默认的包对于例子或者非商业程序是允许的。但是,对于任何真正的软件开发,所有的类都应该某些包而不是默认包。事实上如果您把类放在 默认包,那么您不可能从其他包使用这些类。
您地开发团队应该就包的命名达成一致。多数公司把他们的域名倒过来,作为包名的开始。例如,一个名叫Minderbinder Enterprise的公司或许用 com.minderbinder作为[b]包名的开始[/b]
声明类参数化类型的一个好处是:限制Java.util.ArrayList只能包含Student对象,从而避免不小心把其他类型的对象添加进去。
重构代码
隐藏CourseSession不必要暴露的细节。您封装了student集合,只可以用get、add、size方法来处理该集合。
例如
封装提供了两个优点:首先,目前在ArrayList中保存student列表。ArrayList是有着特定用法和性能指标地一种数据结构。如果您将该列表直接暴露给客户代码,客户代码将依赖于用ArrayList存储student的事实。这样依赖意味着您无法轻易地改变students的存储形式。第二,暴露完整的集合意味着其他类可以操作该集合——加入新的student对象,删除student对象,诸如此类——而且CourseSession类无法意识到这些改变。CourseSession对象的完整性将遭到破坏。
关键点:
1. 用关键子[b]static[/b]和[b]final[/b]来声明类常量,类常量是成员变量。提醒一下,关键子final表明该成员变量的引用不能改变,即指向不同的值。关键字static意味着在没有创建类的实例的情况下就可以使用该成员变量。
例子:
使用方法:int numberOfSquares = ChessBoard.SQUARES_PER_SIDE;
按照约定,用大写字母定义类常量。当所有的字母都是大写,用“驼峰模式”来标记就不可能,所以标准方法是采用下划线来分割单词。
**********************************************
课程安排需要开始时间和结束时间。用来标记第一天和最后一天,课程一般都是16周(15周课程,在第7周后会用一周的休息时间)
在CourseSession类中定义一个变量startDate;
private Date startDate; //课程安排需要开始时间
然后重载(不是修改原来的构造函数,而是多写一个构造函数)构造函数
[b] 增加对工厂方法的理解[/b]
如果方法足够短,我们就容易提供有意义的、简短的名字命名方法。如果发现为方法命名很困难,请考虑将其拆为几个更小的方法,每个方法只做一个简单的、可以命名的事件。
像上面的代码让人看着糊涂——因为要相对1900年份来指定年份,月份必须指定在0到11,而不是1到12直接。可以通过增加一个生产Date实例的方法来创建时间
[b] 消除所有的警告。[/b]
忽略编译警告就像牙虫的危害---迟早您会为其付出代价,而且为您长期的忽视付出昂贵的代价。
用Calender创建日前而不是用Date创建日期。消除警告(想必大多数人用Date都有警告)。
这样的地方容易让人难以理解。可以通过增加注释的方法帮助理解,
例如 weeks * days per week -3days,
但是错误或容易引起误解的注释是声名狼藉的。上面的注释是无效注释的经典例子
一个好的解决方案是:
这样更富有表现力
上一章定义了一个Student类。在学校里,学生都有课程安排,所以需要一个CourseSession类表示课程安排,它存储上课时间和教师信息,同时保存一份这门课程的学生。
class CourseSession{
private String department; //课程名称
private String number; //编号
CourseSession(String department,String number){
this.department = department;
this.number = number;
}
}
CourseSession 需要存储学生,所以需要一个集合来存储学习,大家立刻会想到用ArrayList
private ArrayList<Student> students = new ArrayList<Student>();
注意用import包把java.util.ArrayList包含进来,包提供了一种将相关的类进行分组的机制。
[b]包有几个用途:[/b]
首先,将一组类打成包,给开发者提供了相当的便利,避免开发者必须同一时刻在成十,成百,甚至上千的类中查找。
第二,类被打成包也有发布地目的,可以方便地重用模块或者子系统。
第三,包在java中提供了命名空间。包提供了赋予类一个唯一名字的机制,从而最小化类命名冲突。比如您的类有全称 com.mycompany.studentinfosystem.Student ,第三方API也许会用 com.thirdpartyco.expensivepackages.Student.
习惯:默认的包对于例子或者非商业程序是允许的。但是,对于任何真正的软件开发,所有的类都应该某些包而不是默认包。事实上如果您把类放在 默认包,那么您不可能从其他包使用这些类。
您地开发团队应该就包的命名达成一致。多数公司把他们的域名倒过来,作为包名的开始。例如,一个名叫Minderbinder Enterprise的公司或许用 com.minderbinder作为[b]包名的开始[/b]
声明类参数化类型的一个好处是:限制Java.util.ArrayList只能包含Student对象,从而避免不小心把其他类型的对象添加进去。
重构代码
隐藏CourseSession不必要暴露的细节。您封装了student集合,只可以用get、add、size方法来处理该集合。
例如
// 暴露了CourseSession的细节
ArrayList<Student> getAllStudents(){
return students;
}
//用get方法根据索引得到student
Student get(int index){
return students.get(index);
}
封装提供了两个优点:首先,目前在ArrayList中保存student列表。ArrayList是有着特定用法和性能指标地一种数据结构。如果您将该列表直接暴露给客户代码,客户代码将依赖于用ArrayList存储student的事实。这样依赖意味着您无法轻易地改变students的存储形式。第二,暴露完整的集合意味着其他类可以操作该集合——加入新的student对象,删除student对象,诸如此类——而且CourseSession类无法意识到这些改变。CourseSession对象的完整性将遭到破坏。
关键点:
1. 用关键子[b]static[/b]和[b]final[/b]来声明类常量,类常量是成员变量。提醒一下,关键子final表明该成员变量的引用不能改变,即指向不同的值。关键字static意味着在没有创建类的实例的情况下就可以使用该成员变量。
例子:
class ChessBoard{
static final int SQUARES_PER_SIDE = 9;
}
使用方法:int numberOfSquares = ChessBoard.SQUARES_PER_SIDE;
按照约定,用大写字母定义类常量。当所有的字母都是大写,用“驼峰模式”来标记就不可能,所以标准方法是采用下划线来分割单词。
**********************************************
课程安排需要开始时间和结束时间。用来标记第一天和最后一天,课程一般都是16周(15周课程,在第7周后会用一周的休息时间)
在CourseSession类中定义一个变量startDate;
private Date startDate; //课程安排需要开始时间
然后重载(不是修改原来的构造函数,而是多写一个构造函数)构造函数
CourseSession(String department,String number,Date startDate){
this.department = department;
this.number = number;
this.startDate = startDate;
}
Date getEndDate(){
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(startDate);
int numberOfDays = 16*7-3;
calendar.add(Calendar.DAY_OF_YEAR, numberOfDays);
Date endDate = calendar.getTime();
return endDate;
}
[b] 增加对工厂方法的理解[/b]
如果方法足够短,我们就容易提供有意义的、简短的名字命名方法。如果发现为方法命名很困难,请考虑将其拆为几个更小的方法,每个方法只做一个简单的、可以命名的事件。
int year = 103;
int month = 0;
int date = 6;
Date startDate = new Date(year,month,date);
像上面的代码让人看着糊涂——因为要相对1900年份来指定年份,月份必须指定在0到11,而不是1到12直接。可以通过增加一个生产Date实例的方法来创建时间
Date createDate(int year,int month,int date){
return new Date(year-1900, month-1, date);
}
用这个方法好比一个工厂生产时间实例,给我年月日,就给你个日期实例,相当简单。这样创建date实例有助于封装对局部变量的理解。
[b] 消除所有的警告。[/b]
忽略编译警告就像牙虫的危害---迟早您会为其付出代价,而且为您长期的忽视付出昂贵的代价。
用Calender创建日前而不是用Date创建日期。消除警告(想必大多数人用Date都有警告)。
Date createDate(int year,int month,int date){
GregorianCalendar calendar = new GregorianCalendar();
calendar.clear();
calendar.set(Calendar.YEAR, year-1900);
calendar.set(Calendar.MONTH, month-1);
calendar.set(Calendar.DAY_OF_MONTH, date);
return calendar.getTime();
}
int numberOfDays = 16*7-3;
这样的地方容易让人难以理解。可以通过增加注释的方法帮助理解,
例如 weeks * days per week -3days,
但是错误或容易引起误解的注释是声名狼藉的。上面的注释是无效注释的经典例子
一个好的解决方案是:
final int sessionLength = 16 ;
final int daysInWeek = 7;
final int daysFromFridayToMonday = 3;
int numberOfday = sessionLength * daysInWeek - daysFromFridayToMonday;
这样更富有表现力