java学习笔记(三)——初始化与清理

3.1 用构造器确保初始化

Java中,通过提供构造器,类的设计者可确保每个对象都会得到初始化。创建对象时,如果其类有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造器,从而保证初始化。

构造器的名称必须与类名完全相同。

不接受任何参数的构造器叫默认构造器,也叫无参构造器。

构造器是一种特殊类型的方法,因为它没有返回值。这与void不同。对于void,尽管方法本身不会自动返回什么,但扔可选择让他返回别的东西,比如在其中使用了response。


3.2 方法重载

3.2.1 区分重载方法

每个重载方法都必须有一个独一无二的参数类型列表系统。

参数顺序不同也可以区分两个方法,不过最好不要如此,这会使代码难以维护。


3.3 默认构造器

默认构造器(无参构造器)是没有形式参数的——它的作用是创建一个默认对象。如果类中没有构造器,则编译器会帮你创建一个默认构造器。


3.4 this

this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。this的用法和其他对象引用并无不同。但是如果在方法内部调用同一个类的另一个方法,不必使用this,直接调用即可。当前方法中的this会自动应用于同一个类中的其他方法。

3.4.1 在构造器中调用构造器

this都是指“这个对象”或“当前对象”,而且它本身表示对当前对象的引用。在构造器中,如果为this添加了参数列表,那就有了不同的含义。这将产生对符合此参数列表的某个构造器的明确调用。

3.4.2 static

static方法就是没有this的方法。在static方法的内部不能调用非静态方法,反过来可以。而且可以在没有创建对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static的主要用途。它很像全局方法。Java中禁止使用全局方法,但你在类中置如static方法就可以访问其他static方法和static域。


3.5 垃圾回收

Java有垃圾回收器负责回收无用对象占据的内存资源。但当对象(并非使用new)获得了一块特殊的内存区域,由于垃圾回收器只回收new分配的内存,这种特殊内存无法释放。为了应对这种情况,Java允许在类中定义一个名为finalize()方法。它的工作原理为:一旦垃圾回收器准备好释放对象占用的存储空间,首先调用finalize()方法,并在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。

对象可能不被垃圾回收;垃圾回收不等于“析构”。

只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统。因为垃圾回收本身也有开销,要是不使用它,就不用支付这部分。

3.5.1 finalize()用途

垃圾回收只与内存有关。使用垃圾回收器的唯一原因是为了回收程序不再使用的内存。

3.5.2 必须清理

Java不允许创建局部对象,必须使用new创建对象。简单地想,正是由于垃圾收集机制的存在,使Java没有析构函数。然而垃圾回收器并不能完全替代析构函数(而且决不能直接调用finalize)。如果希望进行除释放空间之外的清理工作,还是要明确调用某个恰当的Java方法。这就等于使用析构函数了。

垃圾回收并不能保证一定发生。如果JVM并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的。

3.5.3 垃圾回收器

垃圾回收器工作时,一面回收空间,一面使堆中的对象紧凑排列,这样“堆指针”就可以很容易移动到更靠近传送带的开始处,也就尽量避免了页面错误。通过垃圾回收器对对象进行重排,实现了一种高速的、有无限空间可供分配的堆模型。

引用计数是一种简单但很慢的垃圾回收技术。每个对象有一个引用计数器,当右引用连接至对象时,引用计数加1。当引用离开作用域或被置为null时,计数器减1。虽然管理引用计数的开销不大,但这项开销在整个程序生命周期中将持续发生。垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象引用计数为0时,就释放其占用的空间。这种方法有个缺陷,如果对象之间存在循环引用,就可能会出现对象应被回收,但引用计数却不为0的情况。对垃圾回收器而言,定位这样的交互自引用的对象组工作量极大。引用计数长用来说明垃圾收集的工作方式,但似乎从未被应用于任何一种Java虚拟机中。

在更快的模式中,垃圾回收器的思想是:对任何“活的对象”,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。这个引用链条可能会穿过数个对象层次。由此,如果从堆栈和静态存储区开始,遍历所有引用,就能找到所有“活“”的对象。对于发现的每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用,如此反复进行,直到“根源于堆栈和静态存储区的引用”所形成的网络全部被访问为止。你所访问过的对象必须都是“活”的。这样就解决了“交互自引用对象组”的问题。

在这种方式下,JVM将采用一种自适应的垃圾回收技术。至于如何处理找到的存活对象,取决于不同JVM实现。有一种叫停止-复制。这就是先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆,没复制的全是垃圾。当对象被复制到新堆时,他们是一个挨着一个的,所以新堆保持紧凑排列,然后就可以简单直接地分配新空间了。

对于这种复制式回收器,效率会降低。首先,要有两个堆,然后需要在这两个堆上来回复制,从而维护比实际多一倍的空间。第二个问题在于复制。程序进入稳定状态后,可能只会产生少量垃圾。复制式回收器仍然会将所有内存自一处复制到另一处,这很浪费。为了避免这种情形,一些虚拟机会进行检查:要是没有新垃圾产生,就会换到另一种工作模式,就是“标记-清除”。

“标记-清除”所依据的思路同样是从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象。每当它找到一个存活的对象,就会给对象设一个标记,这个过程中不会回收任何对象。只有全部标记工作完成的时候,清理动作才会开始。在清理过程中,没有标记的对象将被释放,不会发生任何复制动作。所以剩下的堆空间是不连续的,垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。


3.6 成员初始化

Java尽力保证,所有变量在使用前都能得到恰当的初始化。对于方法的局部变量,Java以编译时错误的形式来贯彻这种保证。

但类的每个基本类型数据成员保证都会有一个初始值。


3.7 构造器初始化

可以用构造器来进行初始化。在运行时刻,可以调用方法或执行某些动作来确定初始值,为编程带来了灵活性。但无法阻止自动初始化的进行,它将在构造器被调用之前发生。

3.7.1 初始化顺序

在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法被调用之前得到初始化。

3.7.2 静态数据初始化

无论创建多少个对象,静态数据都只占一份存储区域。static关键字不能应用于局部变量,它只作用于域。如果一个域的静态是基本类型域,且没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始化值就是null。


3.8 数组初始化

数组只是相同类型,用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。数组是通过方括号下标操作符[ ]来定义和使用的。要定义一个数组,只需在类型名后面加上一对空方括号即可,阿里Java编程手册规定,以这种方式为定义数组的标准格式,因为它清晰表明了数组类型。

编译器不允许指定数组大小。如果只拥有对数组的一个引用(引用已经分配了足够的存储),而没有给数组对象本身分配空间。为了给数组创建相应的存储空间,必须写初始化表达式。对于数组,初始化动作可以出现在任何地方,但也可以使用一种特殊的初始化表达式,它必须在创建数组的地方出现。这种特殊的初始化是由一对花括号括起来的值组成。在这种情况下,存储空间的分配(等价new)由编译器负责。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值