第5章初始化与清理
5.1 用构造器确保初始化
- 当代码执行到new Test()时,将会给对象分配内存,并调用相应的构造器。
5.2方法重载
- 如果想用多种方式创建一个对象,那么就用到方法重载,一个默认构造器,一个有参构造器。
- 方法名相同、参数列表不同(可以顺序不同)。
5.3默认构造器
- 如果类中没有构造器,则编译器会创建一个默认构造器。如果有(无论有参无参),编译器都不会创建。
5.4this关键字
- 在方法内部获得当前对象的引用。this是编译器传入的。直接调用。this可以省略
5.5在构造器中调用构造器
public class ThisTest {
private int code;
private String message;
private T data;
public ThisTest(int code,String message){
this(code,message,null);
}
public ThisTest(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
}
static的含义: static方法是没有this的方法。所以无需创建对象就能使用。
5.5终结处理和垃圾回收
- 当一个对象不是new出来的(比如使用了本地方法里面的c语言中的malloc()函数来分配存储空间),但是在内存中获得了一块区域。垃圾回收器是不能回收的(垃圾回收器只回收new出来的对象)。所以有了finlize()方法。该方法会调用c语言的free()方法释放内存。
5.6成员初始化
- 所有变量在使用前都能得到恰当的初始化。(手动+自动)
- 初始化会自动进行,它会在构造器被调用之前发生
public class ThisTest {
private int i;
public ThisTest(int i){
i = 10;
}
}
i首先会被置为0,然后执行构造方法,然后变成10.
5.7构造器初始化
5.7.1初始化顺序
在类的内部:变量(基本类型和引用类型)会按照定义的顺序进行初始化,没有行的先后顺序,总是先于任何方法(包括构造器)调用之前。
下面是代码示例:
public class Window {
public Window(int marker){System.out.println("window:"+marker);}
}
public class House {
private Window window1 = new Window(1);
public House(){
System.out.println("House()");
w3 = new Window(3);
System.out.println(i);
}
private Window w2 = new Window(2);
void f(){System.out.println("f()");}
int i;
Window w3 = new Window(3);
}
public class Test {
public static void main(String[] args) {
House house = new House();
house.f();
}
}
结果:
window:1
window:2
window:3
House()
window:3
0
f()
5.7.2静态数据的初始化
- 无论创建多少对象,(static)静态数据只占一份内存区域。
- 先初始化静态对象,再初始化非静态对象,静态初始化只有在必要时才会初始化。
下面有个小例子:看完就会明白静态数据是何时初始化的
public class Bowl {
public Bowl(int marker){
System.out.println("marker:"+marker);
}
void f1(int marker){ System.out.println("f1::"+marker);}
}
public class Table {
static Bowl bowl = new Bowl(1);
public Table(){
System.out.println("Table()");
bowl2.f1(1);
}
void f2(int marker){ System.out.println("f2:"+marker); }
static Bowl bowl2 = new Bowl(2);
}
public class Cupboard {
public Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
public Cupboard(){
System.out.println("Cupboard");
bowl4.f1(2);
}
void f3(int marker){ System.out.println("f3:"+marker); }
static Bowl bowl5 = new Bowl(5);
}
public class Test {
public static void main(String[] args) {
System.out.println("creat new cupboard in main");
new Cupboard();
System.out.println("creat new cupboard in main");
new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
}
结果:
marker:1
marker:2
Table()
f1::1
marker:4
marker:5
marker:3
Cupboard
f1::2
creat new cupboard in main
marker:3
Cupboard
f1::2
creat new cupboard in main
marker:3
Cupboard
f1::2
f2:1
f3:1
- 对象的创建过程,以Dog类为例
1.当首次创建Dog对象时,或首次访问Dog类的静态方法或静态域,java解释器先查找类路径,定位Dog.class文件。
2.载入Dog.class,有关静态初始化的所有动作都在此时执行。此时还没有创建对象,因此静态初始化只在Class文件首次加载时进行一次。
3.当用new Dog()创建对象时,首先将在堆上的Dog对象分配足够的存储空间。
4.这块存储空间会被清0、意思就是基本数据类型初始化、引用类型设置为null。
5.执行所有出现于字段定义出的初始化动作。
6.执行构造器。
5.7.3静态代码块
public class Cup {
public Cup(int marker){ System.out.println("Cup:"+marker); }
void f(int marker){ System.out.println("f:"+marker); }
static Cup cup = new Cup(999);
}
public class Cups {
static Cup cup1;
static Cup cup2;
static Cup cup3 = new Cup(3);
static {
cup1 = new Cup(1);
cup2 = new Cup(2);
}
Cups(){ System.out.println("Cups()"); }
static Cup cup4 = new Cup(4);
}
public class Test {
public static void main(String[] args) {
Cups.cup1.f(99);
}
static { System.out.println("test main"); }
}
5.7.4非静态实例初始化
没有static的{}代码块:初始化每一个对象的非静态变量。
public class Mug {
public Mug(int marker){ System.out.println("Mug:"+marker); }
void f(int marker){ System.out.println("f:"+marker); }
}
public class Mugs {
Mug mug1;
Mug mug2;
{
mug1 = new Mug(1);
mug2 = new Mug(2);
}
Mugs(){ System.out.println("Mugs()"); }
Mugs(int marker){ System.out.println("Mugs:"+marker); }
public static void main(String[] args) {
System.out.println("Mugs main");
new Mugs();
System.out.println("new Mugs completed");
new Mugs(1);
System.out.println("new Mugs(1) completed");
}
}
5.8数组初始化
- 一维:int[],二维:int[][]
- 编译器不允许指定数组的大小。
- 初始化:int[] a = new int[10]
- 可变参数列表
public class Test {
public static void main(String[] args) {
f("a","v");
f("bb","cc","dd");
}
public static void f(String... arg){
for (String temp:arg){
System.out.println(temp);
}
}
}
结果:
a
v
bb
cc
dd
5.9枚举类型
- enum
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
public class Test {
public static void main(String[] args) {
Spiciness medium = Spiciness.MEDIUM;
System.out.println(medium);
}
}
结果:
MEDIUM
分析:
上面创建了一个名为Spiciness的枚举类型,有5个具名值。
要想使用枚举,需要创建一个该类型的引用(Spiciness.MEDIUM),并将其赋值给某个实例(Spiciness medium = Spiciness.MEDIUM; )
- 当创建枚举时,会自动添加toString方法,还有ordinal()方法(用来表示某个特定枚举常量的声明顺序,以及static values()的用法)。
public class Test {
public static void main(String[] args) {
for (Spiciness s : Spiciness.values()){
System.out.println(s+":"+s.ordinal());
}
}
}
结果:
NOT:0
MILD:1
MEDIUM:2
HOT:3
FLAMING:4
- enum的新特性:在switch中使用,switch与enum是绝佳的配合。
public class Test {
Spiciness spiciness;
Test(Spiciness spiciness){
this.spiciness = spiciness;
}
void desc(){
switch (spiciness){
case MEDIUM:
System.out.println("medium");
break;
case HOT:
System.out.println("hot");
break;
default:
System.out.println("hello");
}
}
public static void main(String[] args) {
Test test = new Test(Spiciness.MEDIUM);
Test test1 = new Test(Spiciness.HOT);
Test test2 = new Test(Spiciness.FLAMING);
test.desc();
test1.desc();
test2.desc();
}
}