清除与初始化
原文链接 https://jamling.github.io/2007/10/23/Java-Java-%E6%B8%85%E9%99%A4%E4%B8%8E%E5%88%9D%E5%A7%8B%E5%8C%96/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
构造函数构建函数(Constructor)属于一种较特殊的方法类型,因为它没有返回值.这与void返回值存在着明显的区别。对于void返回值,尽管方法本身不会自动返回什么,但仍然可以让它返回另一些东西。构建器则不同,它不仅什么也不会自动返回,而且根本不能有任何选择.若创建一个没有构件器的类,则编译器会自动创建一个默认构件器. <!-- more --> gc()与finalize()gc只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存(当使用JNI技术时,可能会在栈上分配内存,例如java调用c程序,而该c程序使用malloc分配内存时).因此,如果某些对象被分配了栈上的内存区域,那gc就管不着了,对这样的对象进行内存回收就要靠finalize().
举个例子来说,当java 调用非java方法时(这种方法可能是c或是c++的),在非java代码内部也许调用了c的malloc()函数来分配内存,而且除非调用那个了free() 否则不会释放内存(因为free()是c的函数),这个时候要进行释放内存的工作,gc是不起作用的,因而需要在finalize()内部的一个固有方法调用它(free()).
finalize的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存.所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作.
finalize()在什么时候被调用?
- 所有对象被Garbage Collection时自动调用,比如运行System.gc()的时候.
- 程序退出时为每个对象调用一次finalize方法。
- 显式的调用finalize方法
除此以外,正常情况下,当某个对象被系统收集为无用信息的时候,finalize()将被自动调用,但是jvm不保证finalize()一定被调用,也就是说,finalize()的调用是不确定的,这也就是为什么sun不提倡使用finalize()的原因.成员初始化对于类成员,你可以不初始化就直接使用它,JVM会自动对类成员进行初始化,比如,int类型初始化为0,char类型初始化为’’;在书者,作者指的就是类成员的初始化。那么成员函数的成员初始化呢?这就需要自己手动进行了,如果没有初始化而直接使用的话,编译器会给出一个错误:当前变量可能还没有初始化。 如下所示:
int i;
String s;
System.out.println(this.i);//can not be i:The local variable i may not have been initialized
这使我想到了C/C++,在C/C++中是这样介绍的,如果局部变量没有初始化的话,那么编译器将自动赋给它一个随机值。所以在C/C++中,如果没有给局部变量赋初值(初始化)的话,将是一件“危险”的事,而Java则聪明了许多,没有初始化的局部变量,如果直接使用的话,编译不让你通过,以减少程序出现BUG的机率。
构造函数的初始化没看懂,比仙剑的迷宫还难走。搞了半天,没搞懂,惭愧!以下是原文。
static 初始化只有在必要的时候才会进行,...它们才会得到初始化在这以后,static 对象都不会重新初始化。初始化顺序首先是 static(如果它们尚未由前一次对象创建过程初始化)接着是非 static对象。大家可从输出结果中找到相应的证据。 原来是没有看完,根据以上原则,其它一些原则请参考原文。原文中的原例做一下面的分析。
package ch2;
public class Test {
int i;
public void menberInit() {
int i;
String s;
System.out.println(this.i);// can not be i:The local variable i may not
// have been initialized
}
/**
* @param args
*/
public static void main(String[] args) {
// Test t = new Test();
// t.menberInit();
/*static init
System.out.println("Creating new Cupboard() in main");
new Cupboard();// 13
System.out.println("Creating new Cupboard() in main");
new Cupboard();// 14
t2.f2(1);
t3.f3(1);
*/
Mugs x = new Mugs();
}
//static Table t2 = new Table();// 1,初始化静态成员t2.
//static Cupboard t3 = new Cupboard();// 8
}
class Bowl {
Bowl(int marker) {// 3,没有静态成员/方法,执行构造函数//5
System.out.println("Bowl(" + marker + ")");
}
void f(int marker) {
System.out.println("f(" + marker + ")");
}
}
class Table {
static Bowl b1 = new Bowl(1);// 2,初始化静态成员b1
Table() {// 6
System.out.println("Table()");
b2.f(1);// 7
}
void f2(int marker) {
System.out.println("f2(" + marker + ")");
}
static Bowl b2 = new Bowl(2);// 4,初始化静态成员b2
}
class Cupboard {
Bowl b3 = new Bowl(3);// 11最后才是非静态
static Bowl b4 = new Bowl(4);// 9,先静态
Cupboard() {// 12
System.out.println("Cupboard()");
b4.f(2);
}
void f3(int marker) {
System.out.println("f3(" + marker + ")");
}
static Bowl b5 = new Bowl(5);// 10,第二个静态
}
/**
* non-static init
*/
class Mug {
Mug(int marker) {
System.out.println("Mug(" + marker + ")");
}
void f(int marker) {
System.out.println("f(" + marker + ")");
}
}
class Mugs {
Mug c1;
Mug c2;
{
c1 = new Mug(1);
c2 = new Mug(2);
System.out.println("c1 & c2 initialized");
}
Mugs() {
System.out.println("Mugs()");
}
}
请注意程序中注释中的序号,我喜欢用机器的方式思考方式来解这种题。
- 发现静态成员t1,它新建一个Table对象。 在Table对象中,发现静态变量b1,新建一个Bowl对象,初始化Bowl,执行构造 在Table对象中,发现静态变量b2,新建一个Bowl对象,初始化Bowl,执行构造 Table.class没有成员/静态方法了,进行构造方法。
- 发现静态成员t2,它新建一个Cupbord对象。 在Cupboard.class,先初始化静态成员b4,b5,然后才是非静态成员b3,最后执行其构造。
- 程序继续往下执行,new Cupbord(), new Cupbord(),注意到这两次初始化,对已经初始化的静态b4,b5是不会进行第二次初始化的,而b3则每new 一次,则初始化一次。
- 继续执行。 非静态实例的初始化很容易理解。 下面是例子运行的结果: > Mug(1) Mug(2) c1 & c2 initialized Mugs() > 注:如果没有把两条static 实例注释掉的话,将有助于理解上面的静态实例初始化。