定义类
导航类和对象主题: ) |
Java 中的每个类都可以由以下元素组成
- 字段、成员变量或实例变量 — 字段是保存特定于每个对象的数据的变量。例如,员工可能有一个 ID 号码。每个类对象都有一个字段。
- 成员方法或实例方法 — 成员方法对对象执行操作。例如,员工可能有一个方法来签发他的工资单或访问他的姓名。
- 静态字段或类字段 — 静态字段对于相同类的任何对象都是通用的。例如,Employee 类中的静态字段可以跟踪发行的最后一个 ID 号码。每个静态字段在类中只存在一次,无论为该类创建了多少对象。
- 静态方法或类方法 — 静态方法是不影响特定对象的方法。
- 内部类 — 有时在另一个类中包含一个类很有用,如果它在类外部没有用或不应该在类外部访问。
- 构造函数 — 生成新对象的特殊方法。
- 参数化类型 — 从 1.5 开始,参数化类型可以在定义期间分配给类。参数化类型将在类实例化时被指定类型替换。这是由编译器完成的。它类似于 C 语言宏“#define”语句,其中预处理器评估宏。
代码清单 4.1:Employee.java
public class Employee { // This defines the Employee class.
// The public modifier indicates that
// it can be accessed by any other class
private static int nextID; // Define a static field. Only one copy of this will exist,
// no matter how many Employees are created.
private int myID; // Define fields that will be stored
private String myName; // for each Employee. The private modifier indicates that
// only code inside the Employee class can access it.
public Employee(String name) { // This is a constructor. You can pass a name to the constructor
// and it will give you a newly created Employee object.
myName = name;
myID = nextID; // Automatically assign an ID to the object
nextID++; // Increment the ID counter
}
public String getName() { // This is a member method that returns the
// Employee object's name.
return myName; // Note how it can access the private field myName.
}
public int getID() { // This is another member method.
return myID;
}
public static int getNextID() { // This is a static method that returns the next ID
// that will be assigned if another Employee is created.
return nextID;
}
}
|
以下 Java 代码将生成此输出
|
|
构造函数在对象分配后立即被调用来初始化对象
代码清单 4.3:Cheese.java
public class Cheese {
// This is a constructor
public Cheese() {
System.out.println("Construct an instance");
}
}
|
通常,构造函数使用new
关键字调用
代码部分 4.1:构造函数调用。
Cheese cheese = new Cheese();
|
构造函数语法与方法语法类似。但是,构造函数的名称与类的名称相同(大小写相同),并且构造函数没有返回类型。第二点是最重要的区别,因为方法也可以与类具有相同的名称,这是不推荐的
代码清单 4.4:Cheese.java
public class Cheese {
// This is a method with the same name as the class
public void Cheese() {
System.out.println("A method execution.");
}
}
|
返回的对象始终是一个有效的、有意义的对象,而不是依赖于单独的初始化方法。构造函数不能是abstract
、final
、native
、static
、strictfp
或synchronized
。但是,构造函数像方法一样可以重载并接受参数。
代码清单 4.5:Cheese.java
public class Cheese {
// This is a constructor
public Cheese() {
doStuff();
}
// This is another constructor
public Cheese(int weight) {
doStuff();
}
// This is yet another constructor
public Cheese(String type, int weight) {
doStuff();
}
}
|
按照惯例,接受其自身类型对象的构造函数并复制数据成员称为复制构造函数。构造函数的一个有趣的特性是,如果你没有在你的类中指定构造函数,那么编译器会为你创建一个。如果写出来,这个默认构造函数看起来像
代码清单 4.6:Cheese.java
public class Cheese {
public Cheese() {
super();
}
}
|
super()
命令调用超类的构造函数。如果没有明确调用super(...)
或this(...)
,那么默认超类构造函数super();
将在构造函数主体执行之前被调用。也就是说,在某些情况下,您需要手动添加调用。例如,如果您编写了一个构造函数,无论它接受什么参数,编译器都不会添加默认构造函数。代码清单 4.8会导致运行时错误
代码清单 4.7:Cheese.java
public class Cheese {
public Cheese(int weight, String type) {
doStuff();
}
}
|
代码清单 4.8:Mouse.java
public class Mouse {
public void eatCheese() {
Cheese c = new Cheese(); // Oops, compile time error!
}
}
|
在扩展现有类时,请牢记这一点。要么创建一个默认构造函数,要么确保每个继承你的类的类都使用正确的构造函数。
初始化器是与字段初始化器同时执行的代码块。
静态初始化器是与静态字段初始化器同时执行的代码块。静态字段初始化器和静态初始化器按声明顺序执行。静态初始化在类加载后执行。
代码部分 4.2:静态初始化器。
static int count = 20;
static int[] squares;
static { // a static initializer
squares = new int[count];
for (int i = 0; i < count; i++)
squares[i] = i * i;
}
static int x = squares[5]; // x is assigned the value 25
|
实例初始化器是代码块,它们在实例(非static
)字段的初始化器执行的同时执行。实例字段初始化器和实例初始化器按声明顺序执行。实例初始化器和实例字段初始化器都在构造函数调用期间执行。初始化器在调用超类构造函数后立即执行,并在构造函数主体之前执行。