《think in java》第四章阅读笔记
第四章 初始化和清除
4.1用构造器自动初始化
1、一旦创建一个对象,就会分配相应的存储空间,并调用构造器。
4.2方法重载
1、方法的参数为基本类型时,只能用小范围的基本类型做参。例如public void test(short i),调用只能用test((short)5),如果用test(5)则会编译报错。如果用public void test(int i),调用则能用test(5),也能用test((short)5)。
public void test(short i){...}
test(1);//程序将编译报错
test((short)1);//程序正常执行
public void test(int i){...}
test(1);//程序将正常执行
test((short)1);//程序将正常执行
2、构造器调用其他构造器,采用this关键字。
public class Test {
public Test(int i){}
public Test(){
this(5);
}
}
4.3清除:收尾和垃圾收集
1、java的垃圾回收器可以回收不再使用的对象占据的内存,但是只能回收用new创建的对象。
2、java提供了finalized()方法,一旦垃圾收集器准备释放对象的存储空间时,会首先调用finalized(),并且只有在下一次垃圾收集过程中,才会真正回收对象内存。它可以帮助我们在垃圾收集期间进行一些重要的清除或清扫工作。主要是用来清除c或c++在java中的固有方法。
3、有时对象的存储空间可能永远都不会释放。因为随着程序的结束,资源会返还给操作系统。而且垃圾收集本身也要消耗一些开销,如永远都不用它,那么永远也不用支出这部分开销。
4、垃圾收集只和内存有关,存在的唯一原因是为了回收程序不再使用的内存。
5、对象里面的对象所占内存也会被垃圾收集一并清除。
6、system.gc()建议虚拟机进行垃圾回收。何时执行,是否执行并不确定。
4.4成员初始化
1、对象初始化的过程
(1)类dog首次创建时,或是类内部的static方法/变量首次访问时,java解释器必须找到dog.class(实现设好的类路径下搜索)。
(2)找到dog.class后,会创建一个class对象,所有的static初始化模块都会运行。
(3)创建一个new Dog()时,dog对象的构建进程首先会在内存堆(heap)里为一个dog对象分配足够的内存。
(4)对dog类中所有基本类型设为他们的默认值。
(5)字段定义时,所有的初始化都会完成。(例如有个参数 Cat cat = new Cat(),则会初始化cat对象)
(6)执行构造器。
简单来说:
第一次new一个子类的执行顺序:(父类静态代码块或父类静态全局变量)→(子类静态代码块或子类静态全局变量)→(父类非静态代码块或父类非静态全局变量)→父类构造函数→(子类非静态代码块或子类非静态全局变量)→子类构造函数
第二次及以后N次new一个子类的执行顺序:(父类非静态代码块或父类非静态全局变量)→父类构造函数→(子类非静态代码块或子类非静态全局变量)→子类构造函数
即优先级:静态>非静态。父类>子类。其余基本按顺序执行。
例子:
class A { static { System.out.println("this is A static"); } public A() { System.out.println("this is A construct"); } public A(int i) { System.out.println("A("+i+")"); } } class Bowl extends A{ static { System.out.println("this is bowl static"); } public Bowl() { System.out.println("this is bowl construct"); } static A a = new A(1); A a1 = new A(2); public Bowl(String i) { System.out.println("bowl("+i+")"); } } class Table extends Bowl{ static { System.out.println("this is table static"); } static Bowl b1 = new Bowl("0"); Bowl b = new Bowl("1"); public Table() { System.out.println("this is table construct"); } }
执行方法
public static void main(String[] args) { System.out.println("--------------------------------"); Table b = new Table(); System.out.println("---------------------------------"); }
执行结果
-------------------------------- this is A static//main方法中执行到new Table(),首先进入顶级父类A类,查找是否有静态代码块,如果有则执行(这里有,所以执行)。然后查找是否有静态全局变量(这里没有,因此A类静态部分执行完毕)。 this is bowl static//然后进入次级父类Bowl类,执行方式同上,首先执行静态代码块 A(1)//然后执行静态全局变量,同时Bowl类静态部分执行完毕 this is table static//然后进入子类Table类,首先执行静态代码块 this is A construct//这里其实是执行静态全局变量Bowl b = new Bowl("0")。需要注意的是,new Bowl("0")是初始化一个Bowl类,因此首先进入Bowl类的顶级父类,即A类,此时A类和Bowl类里的静态部分已经全部被初始化,因此不再重复执行,并且A类中没有全局变量,所以直接执行A类的构造函数 A(2)//A类构造函数执行后,进入Bowl类,该类中的静态变量也已全部初始化,于是初始化非静态变量 bowl(0)//最后调用Bowl类的构造函数 //至此三个父子类中的静态部分已经全部执行完毕,开始执行非静态部分的行为。以后不论执行多少个new Table(),都只会执行以下部分。 this is A construct//每初始化一个类,都是从上往下初始化。顶级父类A类中没有成员变量,所以直接调用A类的构造函数。 A(2)//次级父类Bowl中存在非静态全局变量,每生成一个对象都会初始化一次。 this is bowl construct//Bowl类中的全局变量初始化完毕,执行构造方法 this is A construct//这里其实是执行Bowl b = new Bolw("1")。即初始化一个新的Bowl类,因此需要从Bowl类的父类A类开始执行。A类中没有未初始化的成员变量,直接调用构造函数。 A(2)//Bowl类中存在非静态成员变量,对其初始化 bowl(1)//最后调用构造函数。 this is table construct//以上部分全部完成,最后调用类的构造函数。 ---------------------------------
第一次new一个子类的执行顺序:父类静态代码块→父类静态全局变量→子类静态代码块→子类静态全局变量→父类非静态代码块→父类非静态全局变量→父类构造函数→子类非静态代码块→子类非静态全局变量→子类构造函数
第二次及以后N次new一个子类的执行顺序:父类非静态代码块→父类非静态全局变量→父类构造函数→子类非静态代码块→子类非静态全局变量→子类构造函数
通过main方法执行程序,首先会在入口类(带有main方法的类)中寻找该类中的静态全局变量,然后初始化,由于main方法是静态方法,所以此时非静态全局变量不会被初始化,然后执行main方法。
注意:静态变量初始化一次后,不会再执行第二次。因此可以用作单例模式的线程安全。
2、静态全局变量只会被初始化一次,会在第一次new一个新的对象,或者是第一次被调用的时候初始化。
推荐一个描写类生命周期的文章http://blog.csdn.net/zhengzhb/article/details/7517213
4.5数组初始化
1、数组的定义方法:int[] a和int a[]。推荐使用前一种。它更能直接的表示这是一个int类型的数组。
2、数组的初始化表达式:int[] a = {1,2,3,4};int[] a = new int[5];int[] a = new int[]{1,2,3,4};
3、创建多维数组至少需要确定第一维的长度。int[][][] i = new int[5][][];此时输出int[2],会打印null.