清理:终结与垃圾回收
这里要明白这么几点
1、垃圾回收器只能回收由new产生的对象,如果你使用或产生了非new产生的对象,垃圾回收器是不知道如何把他清理掉的 。这个时候就要使用到finalize()。
2、垃圾回收器的运做方式是这样的,当垃圾回收器打算开始释放你的对象所占用的资源时,会先调用finalize(),并且在下一次垃圾回收动作放生的时候才回收该对象所占用的资源,如果使用finalize(),他便会让你得以在垃圾回收的时候执行你自己的清理动作
3、finalize()是不会在对象销毁的时候自动唤起的。假设有一个对象在产生的过程中将自己绘制在屏幕之上。你要是没有手动的把他清理掉,那么他永远会在那里,而如果你将清理屏幕的功能放在finalize()之中,那么当这个对象被垃圾回收器回收之前,他在屏幕上的影象会先被清除
4、你的对象永远可能都不会被回收,因为你的程序并没有把系统资源占用到需要垃圾回收器出马的状态,你所占用的系统资源会在程序结束运行的时候全部释放,那么你就不用付出垃圾回收器运行的额外系统开支。
finalize()存在是为了什么?
要知道,finalize()不是用于一般的清理工作,垃圾回收器回清理那些使用new产生的对象,而那些非常规方法(c/c++原生函数)产生对象才会需要
finalize()来进行清理工作。既然这样,我们就不应该大量的使用finalize(),因为他不是摆放常规清理动作的执行场所。
你必须执行清理动作
那么那里才是正常清理动作的执行场所呢?
在c++中,当对象以stack中创建得时候,那么有一个叫析构的函数会自动的在该对象产生地点所在的大括号范围结束前自动调用,如果这个对象是以new的方式从heap中创建的话,那么就必须调用delete运算符才能清理对象。而java不允许对象从stack中创建,你一定得用new从heap中创建对象才行,但是java并没有delete运算符,而是靠的是垃圾回收器来清理对象,所以,我们可以说,既然java中有了垃圾回收器,也就不需要析构函数。但是垃圾回收器毕竟不是析构函数,他无法代替析构函数的作用。如果你除了释放空间之外还要执行其他的清理动作,那么你就要自行调用类似于析构函数的函数
死亡条件
只要程序不依靠于调用finalize(),那么这个函数还有一个用途,就是对对象的死亡条件的检查。
那么,什么是死亡条件呢?在对象被清理的时候,该对象一定是要处于某种状态下才能会被正常的清理,也就是假如有一个数据流已经不再使用了,需要清理掉,那么你再清理之前,先要被关闭掉,这就是一种死亡条件
class myclass
{
boolean b = false;
myclass(boolean b)
{
this.b=b;
}
void death()
{
b=false;
}
public void finalize()
{
if(b) //死亡条件就是b必须要是false
System.out.println("Error : b is true ,class can't be clean");
else
System.out.println("OK ! CleanUp");
}
}
class test
{
public static void main(String args[])
{
myclass mc = new myclass(true);
mc.death();
new myclass(true);
System.gc(); //System.gc()被用来强迫终结动作的发生,不过即使没有使用他,只要你能把系统资源占用到必须垃圾回收器出马的时候,finalize()还是会被执行的
}
}
垃圾回收器的运做方式
学过程序的人应该都知道,从heap中创建对象(基本数据类型除外)的做法,会大幅度的影响系统的速度,然而垃圾回收器的竟然能大幅度的提高从heap中创建对象的效率,你也许会很惊奇,垃圾回收器的储存空间的释放竟然能影响到储存空间的分配,还有更惊奇的,这种从heap中创建对象的方式的速度已经逼近其他语言从stack中创建的速度了!
为什么会这样子呢?想象一下,一个倒霉的家伙(对象)站一个公交车(c++的heap)上,而其他的人都有座位,他需要不停的环视车内,希望能有没人做的座位(高的代价),一单某个人(对象)离开座位(被释放),他就会冲上去坐好。而在java的jvm中,公交车的座位变成了传送带,当有空的座位的时候,后面的人补上前面的座位,依次类推,而那个倒霉的人只要直接的前往最后一个被置空的座位就行了,而不需要他在不停的环视周围。垃圾回收器会重新排列heap中的所有对象,使他们更紧密的排列在一起,这样就能使heap指针移至更靠近传送带前段的位置,避免内存分页置换动作,效率大大提高。
gc为了取得更快的垃圾回收速度,于是他会在static和stack中查找每个对象的句柄是否指向一个heap来判断该对象是否被引用,是否该清理,清理之后会把更改的句柄重新映射排列。因为gc的这种特殊性,当gc启动的时候,执行中的程序会暂时停止。
成员初始化
在java中,class的基本数据类型的数据成员变量会被系统自动初始化,而当变量被定义在函数之中的时候,他是不会被系统自动初始化的
class test
{
byte b;
char c;
short s;
int i;
long l;
float f;
double d;
boolean bl;
void go()
{
int ii;
//System.out.println(ii);当变量被定义在函数之中的时候,他是不会被系统自动初始化的
ii=10;
System.out.println(ii);
System.out.print(
"byte : "+b+"\n"+
"char : "+c+"\n"+
"short : "+s+"\n"+
"int : "+i+"\n"+
"long : "+l+"\n"+
"float : "+f+"\n"+
"double : "+d+"\n"+
"boolean : "+bl+"\n");
}
public static void main(String args[])
{
test t = new test();
t.go();
}
}
结果
10
byte : 0
char : char的值为0,显示空白
short : 0
int : 0
long : 0
float : 0.0
double : 0.0
boolean : false
指定初值
怎么样指定初指,这个我想我不用再写了吧~这个大家肯定知道
以构造函数进行初始化动作
构造函数可以用来执行初始化动作,因而你有更大的弹性,但是构造函数的初始化动作是发生再自动初始化之后的
class test
{
int i;
test()
{
i=10;
}
}
那么,i先会被自动初始化为0,然后才会被构造函数初始化为10,就连定义变量的时候就给定初始值的时候也是一样的,这点需要注意
初始化次序
变量的初始化顺序取决于class中的定义变量的次序,变量也许会散落各处,穿插在函数之中,但是所有的变量一定在任何函数,哪怕是构造函数被调用之前完成初始化
静态数据的初始化
static的基本数据类型的数据初始化情况和non-static的基本数据类型没什么不同的,但是假如他是某个对象的句柄,那么初始值就是null。static的初始化动作只会在必要的时候发生,如果你没有产生class对象,也没有调用静态的数据,则static的数据永远不会被初始化。static的初始化只会发生在第一个static的访问动作发生的时候,自此之后,static对象便不会再被初始化
如果static并没有在对象生成的时候初始化,那么变量的初始化顺序就变为static、non-static、method
static明确初始化
java中允许你将多个static的变量组织起来,放在static 块中
static
{
int i =10;
byte b= 20;
}
当你首次产生class对象,或者首次调用该类的static成员,static 块就被初始化
non-static实体初始化动作
java中也为非静态的变量初始化提供了类似于static的方法
{
int i=10;
byte b=10;
}
如果我们想产生无名的内隐类,这个方法是必须的(第8章会讲到)
下一节我们会讲到本章的最后一段,Array,请大家继续关注
大家有什么问题,可以这样联系我
E-mail molmd@163.net /163.com QQ:31349283 我们可以共同学习进步!
欢迎访问我的blog,http://blog.csdn.com/maoerzuozuo 里面有更多学习内容!