1 实例化对象
在Java中,new关键字用于实例化一个对象。new运算符在内存中创建一个对象,并返回对新创建的对象的一个引用。只要我们的程序保留对该对象的一个引用,那么这个对象将一直保留在内存中。
下面的语句声明了一个Employee引用,并使用new关键字将该引用赋值给一个新的Employee对象:
| Employee e; e = new Employee(); |
引用e指向内存中的Employee对象。运算符new为该对象分配内存,然后将该对象的所有成员变量赋以初始值,这样,这些成员变量就不会包含垃圾数据。
表列出了各种数据类型的成员变量的初始值。
表4-1一个对象的成员变量的初始值 | |
成员变量的数据类型 | 初始值 |
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0 |
double | 0.0 |
char | 空字符 |
boolean | false |
任何类型的引用 | null |
实例化对象的两条语句可以合并为一条语句来实现。例如:
| Employee e = new Employee(); |
这里我们之所以要分开为两条语句,是为了重点强调在实例化时实际上在内存中创建了两个实体:一个引用和一个对象。在两条语句的第一条语句中,e被声明为对一个Employee对象的引用,意思是e可以引用任何Employee对象。而第二条语句中,e被赋值了一个新的Employee对象。
2 理解引用
一个引用通常是一个包含它引用的对象的内存地址的32位整型值。这里说“通常”,是因为Java语言规范中没有严格定义一个引用的大小。在将来,引用可能会是64位或更大。同样,如果用在更小的电子设备中的操作系统中,引用可能比32位小。
引用本质上是整型。那么,为什么要将引用声明为一个特定的数据类型呢?这是因为Java中数据类型是严格强制的,一个引用必须是某种特殊的类数据类型。
例如,通过下面的语句,我们在内存中分配了两个Employee引用和一个String引用:
| Employee e1, e2; String s; |
三个引用消耗相同的内存容量,并且实际上都是整型数据类型。但是,引用e1和e2只能引用Employee对象。引用s只能引用String对象。如果我们试图用下面的语句打破这个规则,是不能通过编译的:
| s = new Employee(); //不能通过编译 e1 = "张三"; //不能通过编译 |
也许有人会认为使用强制类型转换运算符可以解决问题:
| e1 = new Employee(); //有效 s = e1; //不能通过编译 s = (Employee) e1; //还是不能通过编译 |
但是,编译器知道String对象和Employee对象是不兼容的,于是上面的语句中会发生编译错误。
而引用e1和e2是相同的数据类型,因此可以相互赋值。例如:
| e1 = new Employee(); e2 = e1; //有效 |
引用e1被赋值了一个新的Employee对象,引用e2被赋值为e1。这是有效的,因为e1和e2都是Employee引用,是相同的数据类型。这个新的Employee对象现在有两个引用指向它,如图4.1所示。注意,在内存中只有一个Employee对象,因为我们只能用new关键字一次。将e1赋值给e2不会创建一个新对象。
图4.1 引用相互赋值
我们还可以用一条语句来声明引用e,并初始化Employee对象:
| Employee e = new Employee(); |
这条语句在内存中创建了两个分离的元素:引用e和Employee对象。引用e不是一个对象。对象本身并没有变量名,我们要访问和使用对象的唯一方式是使用对该对象的一个引用。
3 方法必须与某个对象关联
初学者最常见的一个错误是试图不使用一个引用就调用一个方法。在下面的代码片断中,第二条语句不能通过编译:
| Employee e1 = new Employee(), e2 = new Employee(); //有效 mailCheck(); //错误。邮寄支票给谁? |
在上面的代码行中,我们实例化了两个Employee对象,然后试图不使用引用就调用mailCheck()。首先,Java中这样做是不行的。在Java中要调用一个方法必须使用一个引用,除非该方法被声明为静态的(static)。在这种情况下,我们可以直接在方法前面用类名加上一个点运算符来调用方法。
其次,因为有两个Employee对象,所以就有两个mailCheck()方法。如果不指定恰当的引用和点运算符,那么编译器根本不知道我们到底要调用哪一个对象的方法。
与每个Employee对象有自己的name、address、salary等属性一样,每个Employee对象也有自己的mailCheck()和computePay()方法。要调用mailCheck(),我们就必须使用点运算符指定要调用哪个对象的mailCheck()方法。例如,下面的语句调用e1代表的Employee对象的mailCheck()方法:
| e1.mailCheck(); |
下面的语句调用e2代表的Employee的mailCheck()方法:
| e2.mailCheck(); |