今天研究了一下两个问题。
1、static变量的生命周期是什么样的?
我们知道static变量是静态的,那么它是不是程序一开始运行就被初始化了的呢?
class Bowl{
private int num;
public Bowl(int a) {
num = a;
System.out.println("Create Bowl " + num);
}
public void use() {
System.out.println("Use Bowl " + num);
}
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
System.out.println("finalize Bowl " + num);
super.finalize();
}
}
class Table{
private int num;
static Bowl b1 = new Bowl(1);
public Table(int a) {
num = a;
System.out.println("Create Table " + num);
b2.use();
}
public void use() {
System.out.println("Use Table " + num);
}
static Bowl b2 = new Bowl(2);
}
class CupBoard{
private int num;
Bowl b3 = new Bowl(3);
static Bowl b4 = new Bowl(4);
public CupBoard(int a) {
num = a;
System.out.println("Create Cupboard " + num);
b3.use();
}
public void use() {
System.out.println("Use Cupboard " + num);
b3.use();
}
}
public class InitializationTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
new CupBoard(1);
System.gc(); //finalize nothing 静态变量的生命周期是从第一次类被调用 到程序结束
System.out.println("phase1");
new CupBoard(2).use(); //由于Cupboard内部的Bowl4是static的,只会创建一次;但是Bowl3就不是了
System.out.println("phase2");
}
static Table t1 = new Table(1); //可以把这里的static去掉,以及把这句话去掉试试看结果如何?
/*
* 变量初始化顺序
* 首先找到public类,这个类的static变量首先初始化,包括其内部的所有成员。
* (因为main是程序的入口,调用了main方法相当于调用了public类里的一个静态方法,就会将这个类里所有静态成员初始化。)
* 初始化顺序按照(静态变量)-(成员变量)顺序进行,同种变量先声明的先初始化。
* 假如有一个类内部有静态变量,如果不创建这个类的对象,内部的静态变量也不会初始化(即:用到才初始化)
* 疑问:就算没有初始化,static成员变量的地址是否是固定的?
*/
}
通过这段代码我们可以得知static变量的生命周期:
从这个类中任意一个属性或方法被调用或者创建了这个类的对象的那一刻开始,
到整个程序运行结束才结束。
具体的细节写在上面的注释里了。
2、java的垃圾回收器怎么分辨一个对象需不需要回收?
关于垃圾回收器的具体实现我现在并不清楚,但是通过finalize方法我们可以一窥什么样的对象是可以被回收的。
finalize方法实际上就是C++的析构函数,它们唯一的区别就在于:C++中我们必须显式地调用delete去释放对象(delphi里是free),而java里这个工作交给了垃圾回收器来做,也就是说不能保证一定被调用。当我们需要在这个对象被释放时释放其他什么东西时(一般不需要这么做,对象里包含的其他所有对象都会被垃圾回收器自动处理掉,除非你在里面嵌入了c的malloc这类方法),我们需要在程序中手工去释放它(利用try-final块)
class Son{
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
System.out.println("finish Son:" + Thread.currentThread());
super.finalize();
}
}
class Test1{
private Son son;
public Test1() {
}
public Test1(Son s) {
// TODO Auto-generated constructor stub
son = s;
}
public Test1(Son s, boolean b) {
}
public void getSon(Son s) {
son = s;
}
public void getSon(Son s, boolean b) {
System.gc();
}
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
System.out.println("finish Test1:" + Thread.currentThread());
super.finalize();
}
}
public class FinalizationTest {
public static void newSon() {
Son s2 = new Son();
Test1 t1 = new Test1(s2);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Test1();
System.gc(); //finalize Test1
new Test1(new Son()); //finalize Son Test1
System.gc();
Test1 t2 =new Test1(new Son());
System.gc(); //finalize nothing
Son s = new Son();
new Test1(s);
System.gc(); //finalize Test1
newSon();
System.gc(); //finalize Test1 Son 因为对象是的作用域在方法内部
for(Son s2 = new Son();;) {
break;
}
System.gc(); //finalize nothing
try {
Test1 t = new Test1();
}finally {
}
System.gc(); //finalize nothing 疑惑 在for和try块中声明的对象,虽然作用域只有块内,但仍不会被认为是可以回收的
// Son s3 = new Son();
Test1 t3 = new Test1();
t3.getSon(new Son(),true); //finalize nothing new Son()作为参数的生命周期是整个getSon方法,因此方法内部gc不会回收
System.gc(); //finalize Son
Test1 t4 = new Test1(new Son(), true);
System.gc(); //finalize Son 因为Son并没有实际存入t4中
Test1 t5 = new Test1();
t5 = null;
System.gc(); //finalize Test 因为此时对象已经没有了引用 就是可回收的
/*
* 这段代码中Finalize会执行几次呢?观察结果不难发现,如果不声明引用,仅仅是new一个对象,那么在这个对象不再被使用到的时候
* 就处于可以被回收的状态。但是如果声明了一个对象的引用,在引用的生命周期结束之前(也就是在这个引用的作用域中),此对象
* 都不会被认为是可以回收的。
* 如果一个对象a是对象b的成员,要看两个引用(如果有)生命周期的共同作用:两个引用的生命周期都结束,它才是可回收的。
* 疑惑:try和for块内部声明的引用并不会因为生命周期到期而导致对象变为可回收状态
*/
}
}
这仅仅是管中窥豹而已。关于垃圾回收器,要学的还很多。