Think in Java -- Initialization & Cleanup

在这一章,讲Java的初始化和清除。


  • 不同的初始化位置
public class Constructor_ex2 {
    String s = "123";
    String ss;
    private static String static_s;
    private static String static_ss = "static_ss_ss";
    private static final String satic_ss_final = "static_ss_final_ss";

    static {
        static_s = "q2";
    }

    public Constructor_ex2() {
        this.ss = "another";
    }
    public Constructor_ex2(int a) {
        this.ss = "another + s";
    }

    public static void main() {
        Constructor_ex2 instance = new Constructor_ex2();
    }
}

例如在上面这段代码中,我们可以看到这个类中有五个变量。这里主要介绍前面四个变量初始化的位置。
s和ss都是在构造函数中初始化的,每一个构造函数都会初始化一次。这里我们可以使用javap -verbose方式查看。

这里我们可以看到,在两个构造函数中,都有对s和ss的初始化。

static_s和static_ss的初始化是在一个static方法里面的。我们同样可以使用javap -verbose查看。

  • 对于基础类型在重载函数中的转化问题
这个问题很简单,就是在我们使用重构函数的时候,如果我们传入的参数没有和这个函数有完全匹配的,怎么处理?一般都是线上转型,就是一直往大的转,转到最接近的。
大小顺序如下:
char byte short int long float double.
但是在这里注意一下,char是特殊的,如果没有完全符合char的函数,就会直接跳到int去找,而不会找byte和short.

  • 为什么不能使用返回值来进行重载
在面试的时候,我们经常会被问道,Java中根据什么进行重载的,这里是根据参数表。那为什么不能根据返回值呢?因为我们在调用有返回值的函数时,我们也可以不用变量接否这个函数的返回值。如下:
public void fi() {

    }

    public int f1() {
        return 1;
    }

    f1();

想上面这样,编译器就不能知道我们具体要调用哪个函数了。

  • finalize() 和 System.gc()
在Java中,没有析构函数的概念。当对象被JVM消除的时候,会自动调用finalize()的方法。我们也可以使用System.gc()强制调用一些可以被消除对象的finalize()方法。
public class temp {
    int a;

    @Override
    protected void finalize() {
        System.out.println("clean up");
    }

    public static void main(String args[]) {
        temp instance = new temp();
        instance=null;
        System.gc();
    }
}

  • Java内存回收机制 Reference Counting
Reference Counting是每一个对象都有一个Reference Counting,当有新的reference指向这个对象的时候,Reference Counting就会增加。当这个对象的引用退出了作用域,或者被置为null后,Reference Counting就会减少。当Reference Counting是0的时候,这个对象就会被回收掉。但是如果存在对象之间的循环引用,那这样即使Reference Counting不为0,某些对象仍然要被消除。这就需要GC做一些额外的工作了。这种机制似乎没有被使用到任何的JVM中。

  • Java内存回收机制 寻找能到达的对象。
这种方法是,我们从stack或者在stack storage中开始,因为对象的引用都是放在这里。然后我们顺着引用,就能找出所有通着这些引用能达到的对象。那这些能达到的对象都是不能被回收的,其他的就是可以的。所以就算是循环引用,也能通过这种方法gc掉。

有关JVM中对内存分配更加详细的内容,在可以看看我的另外的一篇文章。
  • 一个类中的field的初始化
field的初始化可以在定义的时候初始化,也可以使用默认的初始值,或者在构造函数中进行初始化。但是这里,我们在构造函数进行初始化的前,会先进行前两部的初始化。下面的例子会让我们看到这样的过程。
public class temp {
    int a = 2;
    int b;
    public temp() {
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        this.a = 1;
    }

    public static void main(String args[]) {
        temp instance = new temp();
    }
}

  • 初始化的整理过程
  1. 这类使用书上的方式,我们使用Dog作为例子进行说明。当我们第一次创建Dog或者使用到Dog类的时候,我们需要找到Dog.class。
  2. 在我们找到找到Dog.class后,我们载入Dog.class,在这过程中,这个类中的全部的静态变量将会被初始化。
  3. 当我们使用new来创建一个Dog对象的时候,我们会在Heap上找到一块空间来创建这个对象。
  4. 我们选择的这个空间会被初始化为0,所以我们在这个空间上创建Dog对象,初始值都是为0的。
  5. 然手是执行我们在field定义时做的初始化。
  6. 执行构造函数。

  • 初始化的顺序
  1. 静态变量初始化,包括static模块,执行的循序和定义的顺序是一样的
  2. 非晶态的初始化,包括 instance 模块,顺序和定义顺序一样。
  3. 构造函数。
  • 使用...来定义变长变量
当我们不知道一个函数接收的变量有多少个的时候,我们可以使用数组作为参数,我们也可以使用...。如下:
 void cal(int... b) {
        for (int i=0 ; i<b.length ; i++) {
            System.out.println(b[i]);
        }
    }


    public static void main(String args[]) {
        temp instance = new temp();
        instance.cal(1 , 2 , 3);
    }
但是我们不能利用这个进行函数的重载,会发生错误。如:
    void cal(Character... c) {
        for (int i=0 ; i<c.length ; i++) {
            System.out.println(c[i]);
        }
    }
    void cal(int... b) {
        for (int i=0 ; i<b.length ; i++) {
            System.out.println(b[i]);
        }
    }


    public static void main(String args[]) {
        temp instance = new temp();
        instance.cal(1 , 2 , 3);
        instance.cal('1' , '2' , '3');
    }
下面是显示的错误:

如果我们需要两个变长参数的函数,我们需要在两个函数中每个函数中添加一个特有的变量,一定要在每一个函数中都添加,如下:
 void cal(char c , char... b) {
        for (int i=0 ; i<b.length ; i++) {
            System.out.println(b[i]);
        }
    }
    void cal(int a , char...b) {
        for (int i=0 ; i<b.length ; i++) {
            System.out.println(b[i]);
        }
    }


    public static void main(String args[]) {
        temp instance = new temp();
        instance.cal(1 , '2' , '3');
        instance.cal('1' , '2' , '3');
    }

但是下面的程序中,会发生编译错误,但是我不知道怎么解释,请大神们帮忙看看为什么会出错。。。 微笑
    void cal(char c , char... b) {
        for (int i=0 ; i<b.length ; i++) {
            System.out.println(b[i]);
        }
    }
    void cal(int a , int...b) {
        for (int i=0 ; i<b.length ; i++) {
            System.out.println(b[i]);
        }
    }


    public static void main(String args[]) {
        temp instance = new temp();
        instance.cal(1 , 2 , 3);
        instance.cal('1' , '2' , '3');
    }
上面的程序会出现编译错误,如下:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值