现在是时候将您在上一节中学到的东西结合起来,并开始编写一些代码。本节将引导您声明一个类,并使用 Eclipse Package Explorer 向其添加变量和方法。您将学习如何使用 Logger
类来监视应用程序的行为,以及如何将 main()
方法用作测试工具(test harness)。 测试工具是通过在各种条件下运行程序单元并监视其行为和输出来测试程序单元的软件和测试数据的集合。
创建一个包
如果未在(Java 透视图中的)Package Explorer 视图中,可在 Eclipse 中通过 Window > Perspective > Open Perspective 进入该视图。您将通过设置来创建您的第一个 Java 类。第一步是创建一个放置该类的位置。包是名称空间构造,而且它们可方便地直接映射到文件系统的目录结构。
您可以不使用默认包(这通常是一个坏主意),而是专门为您正在编写的代码创建一个包。单击 File > New > Package 启动 Java Package 向导,如图 1 所示。
图 1. Eclipse Java Package 向导
将 com.nowjava.intro
键入到 Name 文本框中并单击 Finish。可以在 Package Explorer 中看到创建的新包。
声明类
可以从 Package Explorer 中通过多种方式创建类,但最简单的方法是右键单击刚创建的包并选择 New > Class… 。New Class 对话框将会打开。
在 Name 文本框中,键入 Person
,然后单击 Finish。
新类将显示在编辑窗口中。建议关闭一些在您首次打开 Java 透视图时其中默认打开的视图(Problems、Javadoc 等),以便更容易查看您的源代码。(在下次打开 Eclipse 并转到 Java 透视图时,Eclipse 会记住您不想看到这些视图。)图 2 显示了一个打开了必要视图的工作区。
图 2. 一个井然有序的工作区
Eclipse 为您生成了一个 shell 类,并在顶部包含 package
语句。您现在只需充实该类。可以通过 Window > Preferences > Java > Code Style > Code Templates 配置 Eclipse 如何生成新类。为了简便起见,我们使用了 Eclipse 的开箱即用的代码生成功能。
在图 2 中,请注意新的源代码文件名称旁边的星号 (*
),它表示我已进行了修改。另请注意,该代码尚未保存。接下来请注意我在声明 Name
属性时犯了一个错误:我将 Name
的类型声明为了 Strin
。编译器无法找到对这样一个类的引用,所以将它标记为一个编译错误(在 Strin
下添加了红色的波浪线)。当然,我在 Strin
的末尾添加一个 g
来修复我的错误。这是对使用 IDE 而不是使用命令行工具进行软件开发的一个小示范。可通过将类型更改为 String
来更正该错误。
添加类变量
在清单 3 中,已经开始充实 Person
类,但我没有过多地解释语法。现在,我将正式定义如何添加类变量。
回想一下,一个变量有一个 accessSpecifier
、一个 dataType
、一个 variableName
和一个可选的 initialValue
。之前,您简单了解了如何定义 accessSpecifier
和 variableName
。现在,您已看到一个变量可以拥有的 dataType
。
dataType
可以是原语类型或对另一个对象的引用。例如,可以注意到 Age
是一个 int
(原语类型),Name
是一个 String
(一个对象)。JDK 提供了大量有用的类,比如 java.lang.String
,而且不需要导入 java.lang
包中的类(Java 编译器采用的一种简写形式)。但无论 dataType
是 JDK 类(比如 String
),还是用户定义的类,语法都基本相同。
表 1 给出了您可能经常看到的 8 种原语数据类型,包括没有显式初始化某个成员变量的值时这些原语采用的默认值。
表 1. 原语数据类型
类型 | 大小 | 默认值 | 值范围 |
---|---|---|---|
boolean | 不适用 | false | true 或 false |
byte | 8 位 | 0 | -128 到 127 |
char | 16 位 | (无符号) | \u0000′ \u0000′ 到 \uffff’ 或 0 到 65535 |
short | 16 位 | 0 | -32768 到 32767 |
int | 32 位 | 0 | -2147483648 到 2147483647 |
long | 64 位 | 0 | -9223372036854775808 到 9223372036854775807 |
float | 32 位 | 0.0 | 1.17549435e-38 到 3.4028235e+38 |
double | 64 位 | 0.0 | 4.9e-324 到 1.7976931348623157e+308 |
内置日志功能
在进一步学习编码之前,需要知道您的程序如何告诉您它们在做什么。
Java 平台包含 java.util.logging
包,这是一种以可读形式收集程序信息的内置记录机制。Logger 是您通过对 Logger
类执行静态方法调用所创建的命名实体:
import java.util.logging.Logger;
//...
Logger l = Logger.getLogger(getClass().getName());
在调用 getLogger()
方法时,向它传递了一个 String
。就现在而言,只需养成传递您正编写的代码所在类的名称的习惯。在任何常规(即非静态)方法中,前面的代码始终引用该类的名称并将它传递给 Logger
。
如果要在一个静态方法内调用一次 Logger
,可引用您所在的类的名称:
Logger l = Logger.getLogger(Person.class.getName());
在这个示例中,您所在的类是 Person
类,所以您引用了一个称为 class
的特殊文字,它检索 Class
对象(将在以后更详细介绍)并获取其 Name
属性。
在编写良好的 Java 代码教程中包含一个如何 不 进行日志记录的技巧。
开始执行测试之前,首先进入 Person
的 Eclipse 源代码编辑器,并将此代码添加到清单 3 中的 public class Person {
后,类似于这样:
package com.nowjava.intro;
public class Person {
private String name;
private int age;
private int height;
private int weight;
private String eyeColor;
private String gender;
}
Eclipse 拥有一个方便的代码生成器来生成 getter 和 setter(以及其他内容)。要尝试使用代码生成器,请将鼠标光标放在 Person
类定义上(即放在类定义中的单词 Person
上),然后单击 Source > Generate Getters and Setters… 。对话框打开时,单击 Select All ,如图 3 所示。
图 3. 生成 getter 和 setter 的 Eclipse
对于插入点,请选择 Last member 并单击 OK。
现在,将一个构造方法添加到 Person
中,方法是将清单 5 中的代码键入到您的源代码窗口中,放在类定义顶部部分下方(public class Person ()
下方紧接的一行)。
清单 5. Person
构造方法
public Person(String name, int age, int height, int weight, String eyeColor, String gender) {
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
this.eyeColor = eyeColor;
this.gender = gender;
}
确保没有看到表示编译错误的波浪线。
使用 main()
作为测试平台
main()
是一个特殊方法,可以将它包含在任何类中,以便 JRE 可执行其代码。类不是必须拥有 main()
方法 事实上,大部分类从没拥有该方法 而且一个类最多可拥有一个 main()
方法。 main()
是一种值得拥有的方便方法,因为它为类提供了一个快速测试平台。在企业开发中,将会使用测试库,比如 JUnit,但使用 main()
作为测试平台可以快速直接地创建测试平台。
生成一个 JUnit 测试案例
现在生成一个 JUnit 测试案例,在该案例中,使用了清单 5 中的构造方法来实例化一个 Person
,然后将该对象的状态输出到控制台。从这种意义上讲,“测试”可确保构造方法调用采用的属性顺序是正确的(也就是说,为它们设置了正确的属性)。
在 Package Explorer 中,右键单击您的 Person
类,然后单击 New > JUnit Test Case。New JUnit Test Case 向导的第一页将会打开,如图 4 所示。
图 4. 创建一个 JUnit 测试案例
单击 Next 接受默认值。您会看到 Test Methods 对话框,如图 5 所示。
图 5. 选择方法(向导将为其生成测试案例)
在这个对话框中,选择您想让向导为其构建测试的一个或多个方法。在本例中,仅选择构造方法,如图 5 所示。单击 Finish,Eclipse 会生成 JUnit 测试案例。
接下来,打开 PersonTest
,进入 testPerson()
方法,使它看起来类似于清单 6。
清单 6. testPerson()
方法
@Test
public void testPerson() {
Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
Logger l = Logger.getLogger(Person.class.getName());
l.info("Name: " + p.getName());
l.info("Age:"+ p.getAge());
l.info("Height (cm):"+ p.getHeight());
l.info("Weight (kg):"+ p.getWeight());
l.info("Eye Color:"+ p.getEyeColor());
l.info("Gender:"+ p.getGender());
assertEquals("Joe Q Author", p.getName());
assertEquals(42, p.getAge());
assertEquals(173, p.getHeight());
assertEquals(82, p.getWeight());
assertEquals("Brown", p.getEyeColor());
assertEquals("MALE", p.getGender());
}
暂时不用担心 Logger
类。只需输入您在清单 6 中看到的代码即可。您现在已准备好运行您的第一个 Java 程序和 JUnit 测试案例。
在 Eclipse 中运行单元测试
在 Eclipse 中,右键单击 Package Explore 中的 PersonTest.java 并选择 Run As > JUnit Test。图 6 显示了发生的情况。
图 6. 查看 Person
的运行状况
控制台视图将会自动打开,以显示 Logger
输出,JUnit 视图表明测试运行正常。
向 Java 类添加行为
Person
目前看起来还不错,但可以使用一些额外的行为让它变得更有趣。创建行为意味着添加方法。本节会更详细地介绍 访问器方法 — 即您已看到过的 getter 和 setter。
访问器方法
您在上一节末尾处实际看到的 getter 和 setter 称为 访问器方法 。(快速回顾:getter 是检索属性值的方法;setter 是修改该值的方法。)要封装一个类中来自其他对象的数据,可以将它的变量声明为 private
,然后提供访问器方法。
访问器的命名遵循称为 JavaBeans 模式 的严格约定。在此模式中,任何 Foo
属性都有一个称为 getFoo()
的 getter 方法和一个称为 setFoo()
的 setter 方法。JavaBeans 模式很常见,所以 Eclipse IDE 中内置了对它的支持,您在生成 Person
的 getter 和 setter 时已看到。
访问器遵循以下准则:
- 属性始终使用
private
访问级别来声明。 - getter 和 setter 的访问说明符是
public
。 - getter 不接受任何参数,而且返回一个与它访问的属性同类型的值。
- setter 仅接受一个与该属性同类型的参数,而且不返回值。
声明访问器
目前为止,声明访问器的最简单方法是让 Eclipse 为您声明它。但您还需要知道如何手动编写一个 getter 和 setter 对。
假设我有一个属性 Foo
,它的类型为 java.lang.String
。我的完整 Foo
声明(依照访问器准则)是:
private String foo;
public String getFoo() {
return foo;
}
public void setFoo(String value) {
foo = value;
}
注意,传递给 setter 的参数值的命名方式与通过 Eclipse 生成它时所用的命名方式不同(其中参数名称与属性名称相同 — 例如 public void setFoo(String **foo**)
)。在我手动编写 setter 的极少数情况下,我始终使用 value
作为 setter 的参数值名称。这个吸引眼球的词汇(我自己的规范,建议其他开发人员也这么做)提醒我,我已经手动编写了 setter。如果我没有使用 Eclipse 为我生成 getter 和 setter,那么我一定拥有合理的理由。使用 value
作为 setter 的参数值,这会提醒我这个 setter 很特殊。(代码注释可满足同样的用途。)
调用方法
调用方法很简单。例如,清单 6 中的 testPerson
方法调用 Person
的各种 getter 来返回它们的值。现在,我将正式介绍执行方法调用的机制。
包含参数和不含参数的方法调用
要在对象上调用某个方法,需要使用对该对象的引用。方法调用语法包含: