一、对象无处不在
一、对象无处不在
1.1 数据保存在哪里
寄存器(register)
。这是速度最快的数据存储方式,因为它保存数据的位置不同于其他方式:数据会直接保存在中央处理器(central processing unit, CPU)里2。然而寄存器的数量是有限的,所以只能按需分配。此外,你不能直接控制寄存器的分配,甚至你在程序中都找不到寄存器存在过的证据( C 和 C++ 是例外,它们允许你向编译器申请分配寄存器)。栈(stack)
。数据存储在随机存取存储器(random-access memory, RAM)里,处理器可以通过栈指针(stack pointer)直接操作该数据。具体来说,栈指针向下移动将申请一块新的内存,向上移动则会释放这块内存。这是一种极其迅速和高效的内存分配方式,其效率仅次于寄存器。只不过 Java 系统在创建应用程序时就必须明确栈上所有对象的生命周期。这种限制约束了程序的灵活性,因此虽然有一些数据会保存在栈上(尤其是对象引用),对象本身却并非如此。堆(heap)
。这是一个通用的内存池(使用的也是RAM空间),用于存放所有 Java 对象。与栈不同的是,编译器并不关心位于堆上的对象需要存在多久。因此,堆的使用是非常灵活的。比如,当你需要一个对象时,可以随时使用new来创建这个对象,那么当这段代码被执行时,Java 会在堆上为该对象分配内存空间。然而这种灵活性是有代价的:分配和清理堆存储要比栈存储花费更多的时间(如果你可以像 C++ 那样在栈上创建对象的话)。好消息是,随着时间的推移,Java的堆内存分配机制已经变得非常高效了,所以你并不需要太过关注此类问题。常量存储(constant storage)
。常量通常会直接保存在程序代码中,因为它们的值不会改变,所以这样做是安全的。有时候常量会与其他代码隔离开来,于是在某些嵌入系统里,这些常量就可以保存在只读存储器(read-only memory, ROM)中3。非RAM存储(non-RAM storage)
。如果一段数据没有保存在应用程序里,那么该数据的生命周期既不依赖于应用程序是否运行,也不受应用程序的管制。其中最典型的例子之一是“序列化对象”(serialized object),它指的是转换为字节流(叫作“序列化”)并可以发送至其他机器的对象。另一个例子则是“持久化对象”(persistent object),它指的是保存在磁盘上的对象,而这些对象即便在程序结束运行之后也依然能够保持其状态。这些数据存储类型的特点在于,它们会将对象转换成其他形式以保存于其他媒介中,然后在需要的时候重新转换回常规的RAM对象。Java 支持轻量级的持久化对象存储,而 JDBC 以及Hibernate 等库则提供了更为成熟的解决方案,即支持使用数据库存取对象信息。”
1.2 使用class关键字创建新类型
如果一切皆为对象,那么具体到某一类对象的外观和行为是如何确定的呢?换句话说,我们如何创建对象的类型?说到这里,你可能会想当然地认为会有一个名为“type”的关键字,然而由于历史原因,大多数面向对象编程语言会使用“class”关键字来描述新的对象种类。其用法是class关键字后面跟着新的类名:
class ATypeName {
// 类的具体实现放在这里
}
这段代码创建了一个新的类,只不过其内容只包含了一行注释,因此目前也没有什么实际的作用。话虽如此,你还是可以通过new关键字创建一个该类的对象:
ATypeName a = new ATypeName();
1.3 字段
当定义一个类时,你可以为其定义两种元素:字段(有时叫作“数据成员”)和方法(有时叫作“成员函数”)。
当一个类的字段是基本类型时,即便你没有初始化这些字段,它们也会拥有默认值:
基本类型 | 默认值 |
---|---|
boolean | false |
char | \u0000(null) |
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
1.4 static关键字
创建一个类即描述了其对象的外观和行为。直到使用new关键字时,你才会真正创建一个对象,以及为该对象分配内存,并使得其方法可以被调用。
然而在两种情况下,这种方式会显得不合时宜。第一种情况是,有时候我们需要一小块共享空间来保存某个特定的字段,而并不关心创建多少个对象,甚至有没有创建对象。第二种情况是,你需要使用一个类的某个方法,而该方法和具体的对象无关;换句话说,你希望即便没有生成任何该类的对象,依然可以调用其方法。
static关键字(源自 C++)为以上两个问题提供了解决方案。如果你使用了static关键字,则意味着使用static的字段或方法不依赖于任何对象。也就是说,即便你没有为一个类创建任何对象,依然可以调用该类的static方法或static字段。另外,由于非static的字段和方法必须基于指定的对象,因此对于非static的字段和方法来说,你必须创建一个对象才可以使用非static的字段或方法。
创建一个static字段或方法,只需要把static关键字放置于字段或方法定义的前面就可以了。以下是创建并初始化一个static字段的例子:
class StaticTest {
static int i = 47;
}
即便你创建了两个StaticTest类的对象,StaticTest.i依然只会占用同一块内存空间。也就是说,字段i会被两个对象所共享。例如:
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
st1.i和st2.i的值都是47,这是因为两者使用的内存空间是相同的。
我们较为推荐通过类名调用static变量
,因为这种方法突出了变量的static特质。
StaticTest.i++;