读Java编程思想随笔の一切都是对象
2.1用引用操纵对象
每种编程语言都有自己的操纵内存中元素的方式。有时候,程序员必须要注意将要处理的数据是什么类型。你是直接操作元素,还是用某种基于特殊语法的间接表示来操纵对象?
所有这一切在Java中都得到了简化。一切都被视为对象,因此可采用单一固定的语法。尽管一切都看作对象,但操纵的标识符实际上是对象的一个引用。引用好比遥控器,电视机好比是对象,我们可以通过操纵遥控器(引用)来间接操纵电视机(对象),另外,没有电视机(对象),遥控器(引用)也可单独存在。
2.2由你创建所有对象
2.2.1对象存储
程序运行时,对象是怎么进行放置安排的?特别是内存是怎样分配的?有五个不同的地方可以存储数据。
1)寄存器
这是最快的存储区,因为它位于不同于其他存储区的地方---处理器内部。但是寄存器的数量极其有限,所以寄存器根据需求进行分配。你不能直接控制,也不能感觉到寄存器存在的任何迹象。
2)堆栈
位于通用RAM(随机访问存储器)中,但通过堆栈指针可以从处理器那里获得直接支持。堆栈指针若向下移动,则分配新的内存;若向上移动,则释放内存,这是一种快速有效的存储方法。仅次于寄存器。创建程序时,Java系统必须知道存储在堆栈内所有项的确切生命周期,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些Java数据存储于堆栈中--特别是对象引用,但是Java对象并不存在于其中。
3)堆
一种通用的内存池(也位于RAM中),用于存放所有的Java对象,堆不同于堆栈的好处是,编译器不需要知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当需要一个对象时,只需用new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代价:用堆进行存储分配和清理可能比堆栈进行存储分配需要更多的时间。
4)常量存储
常量值通常直接放在程序代码内部,这样做是安全的,因为它们永远不会被改变。
5)非RAM存储
如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序没有运行时也可以存在。其中两个基本的例子是流对象和持久化对象。在流对象中,对象转化成字节流,通常被发送给另一台机器。在持久化对象中,对象被存放于磁盘上,因此即使程序终止,它们仍可以保持自己的状态。
2.3永远不要销毁对象
2.3.1作用域
2.3.2对象的作用域
事实证明,由new创建的对象,只要你需要,就会一直保留下去。这样,许多C++编程问题,就会从Java中完全消失。在C++中,你不仅要确保对象的保留时间与你需要这些对象的时间一样长,而且还需要你在使用完它们之后,将其销毁。
这样便带来一个有趣的问题。如果Java让对象继续存在,那么靠什么才能防止这些对象填满内存空间,进而阻塞你的程序呢?这正是C++可能会发生的问题,这也是Java的神奇之所在。Java有一个垃圾回收器,用来监视用new创建的所有对象,并辨别那些不会再被使用的对象。随后,释放这些对象的内存空间,以便供其他新的对象使用。也就是说,你根本不必担心内存回收问题。只需要你创建对象,一旦不需要,它们会自动消失。这样做就消除了这类编程问题(内存泄漏),这是由于程序员忘记释放内存而产生的问题。
2.6构建一个Java程序
2.6.3 static关键字
通常来说,当创建类时,就是在描述那个类的对象的外观和行为。除非用new创建那个对象,否则,实际上并未获得任何对象,甚至根本就不创建任何对象。另一种情形是,希望某个方法不与包含它的类的任何对象关联在一起。也就是说,即使没有创建对象,也能够调用这个方法。
通过static关键字可以满足这两方面的需要。当声明一个事物是static时,就意味着这个域或方法不与包含它的类的任何对象关联在一起。所以,即使从未创建某个类的任何对象,也可以调用static方法或访问static域。通常你必须创建一个对象,并用它来访问数据和方法。因为非static域或方法必须知道它们一起运作的特定对象。
class StaticTest{ static int i =47; } public class Test{ StaticTest t1 = new StaticTest(); StaticTest t2 = new StaticTest(); //t1.i与t2.i指向的是同一份存储空间 }
引用static变量有两种方式,如前例所示,可以通过一个对象去定位它,也可以通过类名直接引用。
使用类名引用static变量是首选方式,这不仅因为它强调了static变量结构,而且在某些情况下,它还为编译器进行优化提供了更好的机会。