Java 语言入门
目标
- 认识 Java 语言中的保留字
- 理解 Java 类与 Java 对象之间的关系。
- 了解 Java 类中的每种结构的功能和语法
- OOP 与对象密切相关。本单元介绍两个与 Java 语言如何处理对象紧密相关的主题:保留字和 Java 类的结构。
保留字
跟任何编程语言一样,Java 语言指定了一些编译器认为具有特殊含义的关键字。出于该原因,不允许您使用它们来命名您的 Java 结构。保留字(也称为关键字)非常少:
- abstract
- assert
- boolean
- break
- byte
- case
- catch
- char
- cla
- const
- continue
- default
- do
- double
- else
- enum
- extend
- final
- finally
- float
- for
- goto
- if
- implement
- import
- instanceof
- int
- interface
- long
- native
- new
- package
- private
- protected
- public
- return
- short
- static
- strictfp
- super
- switch
- synchronized
- thi
- throw
- throw
- transient
- try
- void
- volatile
- while
您也不可以使用 true、false 和 null(技术上讲,它们是 字面常量 而不是关键字)来命名 Java 结构
稍后将会看到,使用 IDE 编程的一个优势是,它可以为保留字使用语法颜色。
Java 类的结构
类是一个包含属性和行为的离散实体(对象)的蓝图。类定义对象的基本结构,在运行时,您的应用程序会创建对象的实例。对象拥有明确定义的边界和状态,它可在被正确请求时执行操作。每种面向对象的语言都拥有有关如何定义类的规则。
在 Java 语言中,类的定义如 清单 1 所示:
清单 1. 类定义
package packageName;
import ClassNameToImport;
accessSpecifier class ClassName {
accessSpecifier dataType variableName [= initialValue];
accessSpecifier ClassName([argumentList]) {
constructorStatement(s)
}
accessSpecifier returnType methodName ([argumentList]) {
methodStatement(s)
}
// This is a comment
/* This is a comment too */
/* This is a
multiline
comment */
}
清单 1 包含各种不同的结构类型,第 1 行是 package,第 2 行是 import,第 3 行是 class。这 3 种结构都属于 保留字,所以它们必须保持清单 1 中的准确格式。我为清单 1 中的其他结构提供的名称描述了它们表达的概念。
备注:在 清单 1 和本节的其他一些代码示例中,方括号表示它们之中的结构不是必需的。方括号(不同于 { 和 })不是 Java 语法的一部分。
可以注意到 清单 1 中的第 11 到 15 行是注释行。在大多数编程语言中,程序员都可添加注释来帮助描述代码。Java 语法允许单行和多行注释。
// This is a comment
/* This is a comment too */
/* This is a
multiline
comment */
单行注释必须包含在一行上,但您可使用邻近的单行注释来形成一个注释组。多行注释以 /* 开头,必须以 */ 终止,而且可以涵盖任意多行。
接下来,我将详细介绍 清单 1 中的结构,首先看看 package。
打包类
在 Java 语言中,您可选择类的名称,比如 Account、Person 或 LizardMan。有时,您可能最终使用了同一个名称来表达两个稍微不同的概念。这种情形称为名称冲突,经常会发生。Java 语言使用包 来解决这些冲突。
Java 包是一种提供命名空间 的机制— 名称在命名空间中是唯一的,但在其外部可能不是唯一的。要唯一地标识一个结构,您必须包含它的命名空间来完全限定它。
包也是利用多个离散功能单元来构造更复杂应用程序的不错方式。
要定义包,可使用 package 关键字,后跟一个合法的包名称,并以一个分号结尾。包名称常常遵循这种事实标准模式:
package orgType.orgName.appName.compName;
这个包定义可分解为:
- orgType 是组织类型,比如 com、org 或 net。
- orgName 是组织的域名称,比如 makotojava、oracle 或 ibm。
- appName 是缩写的应用程序名称。
- compName 是组件的名称。
本课程将使用此约定,而且我推荐坚持使用它来定义包中的所有 Java 类。(Java 语言没有强制要求您遵循这种包约定。您完全不需要指定一个包,但在这种情况下,您的所有类必须具有唯一名称且将位于默认包中。)
导入语句
类定义中的下一部分(返回参考 清单 1)是导入语句。导入语句告诉 Java 编译器在何处查找您在代码中引用的类。任何非简单的类都会使用其他类来实现某种功能,导入语句是您向 Java 编译器告知这些类的方式。
Eclipse 使导入简化:在 Eclipse 编辑器中编写代码时,您可键入想要使用的类的名称,然后按 Ctrl+Shift+O。Eclipse 会确定您需要哪些导入语句并自动添加它们。如果 Eclipse 找到两个具有相同名称的类,它会询问您想为哪个类添加导入语句。
导入语句通常类似于:
import ClassNameToImport;
您指定 import 关键字,后跟您想要导入的类,然后输入一个分号。类名应是完全限定的,意味着名称中应包含它的包。
要导入一个包中的所有类,您可将 .* 放在包名称后。例如,这条语句导入 com.makotojava 包中的每个类:
import com.makotojava.*;
但是,导入整个包会降低代码的可读性,所以我推荐使用完全限定名称来仅导入需要的类。
类声明
要在 Java 语言中定义一个对象,必须声明一个类。可将类视为对象的模板,就像饼干模具一样。
清单 1 包含以下类声明:
accessSpecifier class ClassName {
accessSpecifier dataType variableName [= initialValue];
accessSpecifier ClassName([argumentList]) {
constructorStatement(s)
}
accessSpecifier returnType methodName([argumentList]) {
methodStatement(s)
}
}
类的 accessSpecifier 可拥有多个值,但该值通常为 public。您很快会看到 accessSpecifier 的其他值。
可以将类命名为您想要的任何名称,但约定是使用驼峰式大小写:以一个大写字母开头,但将串联的每个单词的第一个字母大写,将所有其他字母小写。类名应仅包含字母和数字。坚持这些准则,可确保代码更容易供其他遵循相同约定的人使用。
变量和方法
类可拥有两种类型的成员—变量 和方法。
变量
类的变量的值可以区分该类的每个实例并定义它的状态。这些值常常称为实例变量。一个变量有:
- 一个 accessSpecifier
- 一个 dataType
- 一个 variableName
- 一个可选的 initialValue
可能的 accessSpecifier 值包括:
- public:任何包中的任何对象都能看到该变量。(不要使用此值;请参阅公共变量边栏。)
- protected:在同一个包中或(在任何包中定义的)一个子类中定义的任何对象都能看到此变量。
- 没有修饰符(也称为友好 或包私有 访问):仅在同一个包中定义的类的对象能够看到此变量。
- private:只有包含该变量的类能够看到它。
公共变量 :使用公共变量绝不是一个好主意,但在极少的情况下有必要这么做,所以存在这个选项。Java 平台不会限制您的使用情形,所以需要您自己遵守使用良好的编码约定的纪律,即使您被诱惑不这么做。
变量的 dataType 取决于变量的类型— 它可能是一种原语类型或另一种类类型(稍后将更详细地介绍)。
variableName 由您自己决定,但根据约定,变量名使用驼峰式大小写约定,但它们以小写字母开头。(这种样式有时称为小驼峰式大小写。)
现在暂时不要关心 initialValue,只需知道可以在声明一个实例变量时初始化它。(否则,编译器会为您生成一个在实例化该类时设置的默认值。)
示例:Person 的类定义
这个示例总结了您目前所学的知识。清单 2 是 Person 的类定义。
清单 2. Person 的基类定义
package com.makotojava.intro;
public class Person {
private String name;
private int age;
private int height;
private int weight;
private String eyeColor;
private String gender;
}
Person 的这个基类定义目前不是很有用,因为它仅定义了 Person 的属性(而且是私有属性)。要更加完整,Person 类还需要行为 —也就是方法。
方法
类的方法定义它的行为。
方法分为两个主要类别:构造方法;以及所有其他方法,这些方法具有许多类型。构造方法仅用于创建类的实例。其他类型的方法可用于几乎任何应用程序行为。
清单 1 中的类定义给出了定义方法的结构的方式,该结构包括的元素有:
accessSpecifier returnType methodName argumentList 这些结构元素在方法定义中的组合称为方法的签名。
现在仔细看看两种方法类别,首先看看构造方法。
构造方法
可使用构造方法指定如何实例化类。清单 1 以抽象形式给出了构造方法声明语法;这里再次给出了它:
accessSpecifier ClassName([argumentList]) {
constructorStatement(s)
}
构造方法是可选的:如果您不提供构造方法,编译器会为您提供一个,这个方法称为默认(或无参数 或 no-arg)构造方法。如果您使用的构造方法不是 no-arg 构造方法,编译器不会自动为您生成一个。
构造方法的 accessSpecifier 与变量的相同。构造方法的名称必须与类名匹配。所以如果您将类命名为 Person,构造方法的名称必须也是 Person。
对于任何其他不是默认构造方法的构造方法(参见构造方法是可选的边栏),您需要传递一个 argumentList,其中包含一个或多个:
argumentType argumentName
argumentList 中的参数用逗号分隔,而且任何两个参数不能具有相同名称。argumentType 是一种原语类型或另一种类类型(与变量类型相同)。
包含构造方法的类定义
现在来看看在添加了通过两种方式创建 Person 对象的能力后会发生什么:通过使用 no-arg 构造方法和通过初始化一个部分属性列表。
清单 3 展示了如何创建构造方法,以及如何使用 argumentList:
清单 3. 包含构造方法的 Person 类定义
package com.makotojava.intro;
public class Person {
private String name;
private int age;
private int height;
private int weight;
private String eyeColor;
private String gender;
public Person() {
// Nothing to do...
}
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;
}
}
可以注意到,清单 3 中使用了 this 关键字来执行参数赋值。this 关键字是 “this object” 的 Java 速记形式,在引用两个具有相同名称的变量时必须使用它。在本例中,age 既是一个构造方法参数,又是一个类变量,所以 this 关键字可帮助编译器消除引用的歧义。
Person 对象变得越来越有趣了,但它需要有更多行为。为此,您需要更多方法。
其他方法
构造方法是一种具有特定功能的特定方法类型。类似地,其他许多类型的方法执行 Java 程序中的特定功能。从本节到本课程结束,将探索其他方法类型。
返回到 清单 1 中,可以看到方法的声明方式:
accessSpecifier returnType methodName ([argumentList]) {
methodStatement(s)
}
其他方法看起来非常像构造方法,但有两处例外。首先,您可为其他方法提供您喜欢的任何名称(但是,肯定要遵守某些规则)。我推荐以下约定:
- 以小写字母开头。
- 避免使用数字,除非确实有必要使用它们。
- 仅使用字母字符。
第二,不同于构造方法,其他方法拥有一个可选的返回类型。
Person 的其他方法
有了这些基本信息,您就可以在清单 4 中看到在向 Person 对象添加一些方法后发生的情况。(为简洁起见,我省略了构造方法。)
清单 4. 包含一些新方法的 Person
package com.makotojava.intro;
public class Person {
private String name;
private int age;
private int height;
private int weight;
private String eyeColor;
private String gender;
public String getName() { return name; }
public void setName(String value) { name = value; }
// Other getter/setter combinations...
}
请注意 清单 4 中有关 “getter/setter 组合” 的注释。稍后会更多地使用 getter 和 setter。就现在而言,您只需知道 getter 是一个检索属性值的方法,setter 是一个修改该值的方法。清单 4 仅给出了一种 getter/setter 组合(针对 Name 属性),但您可通过类似方式定义更多组合。
在 清单 4 中可以注意到,如果一个方法没有返回值,您必须在该方法的签名中指定 void 返回类型来告诉编译器。
静态和实例方法
通常会使用两种类型的(非构造)方法:实例方法和静态方法。实例方法依赖于特定对象实例的状态来执行其行为。静态方法有时也称为类方法,因为它们的行为不依赖于任何一个对象的状态。静态方法的行为在类级别上发生。
静态方法主要用于实用程序;您可将它们视为全局方法(类似于 C 语言),但将该方法的代码与定义它的类放在一起。
例如,在后面各节中,将使用 JDK Logger 类向控制台输出信息。要创建一个 Logger 类实例,不需要实例化一个 Logger 类;而是调用一个名为 getLogger() 的静态方法。
在类上调用静态方法的语法不同于在对象上调用方法的语法。您还会使用包含该静态方法的类的名称,如这个调用中所示:
Logger l = Logger.getLogger("NewLogger");
在这个示例中,Logger 是类名,getLogger(...) 是方法名。所以要调用静态方法,您不需要对象实例,只需要类名。