0%

Java 面向对象(五)—— 初始化与清理

第五章 初始化与清理

一、初始化

1、构造器 constructor

  • 自动调用对应构造器保证初始化

  • 构造器采用与类相同的名称(因此构造器方法首字母无需小写)

  • 绑定初始化和创建

  • 不返回任何值

2、方法重载:不同构造器,参数列表独一无二

3、默认构造器(无参)

  • 创建默认对象

  • this 关键字

    • 表:对调用方法的按个对象的应用
    • 若为同一个类的另一个方法,不使用 this 关键字,精简
    • 若要明确指出对当前对象的引用,使用 this 关键字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 例1
public class Apricot{
void pick();
void pit{ pick() };
// 以下写法没有必要
void pit{ this.pick() };
}
// 例2
public class Leaf{
int i = 0;
Leaf increment() {
i++;
return this; // 返回对当前对象的引用
}
}
void print() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
Leaf x = new Leaf();
x.increment().increment().increment().print();
}
/* Output:
i = 3
*///:~
  • 构造器中调用构造器:仅可用 this 调用一个,且调用位于最起始处

4、成员初始化

  • 基本数据类型有默认初值,自定义的局部变量则必须给出初始化值

  • 注意初始化的顺序

5、构造器初始化

1
2
3
4
5
6
7
8
// i先置为0,后变为7
public class Counter {
int i;
Counter() {
i = 7;
}
...
}
  • 初始化将在构造器被调用前发生

  • 类内部,变量定义顺序决定了初始化顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Window {
Window(int marker) {
print("Windows(" + marker + ")"); // 初始化时打印
}
}
class House {
Window w1 = new Window(1); // 调用构造器前
House() {
print("House()");
w3 = new Window(33);
}
Window w2 = new Window(2); // 调用构造器后
void finished() {
print("finished()");
}
Window w3 = new Window(3); // 末尾
}
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.finished();
}
}
/* Output:
Window(1)
Window(2)
Window(3)
House()
Window(33)
finished()
*///~

上例说明,即使对象散布在 Window 的不同部分,仍会在调用构造器或其他方法前得到初始化。

  • 先初始化静态对象,后其他对象

  • 静态块:显式的静态初始化

1
2
3
4
5
static {
代码块1
代码块2
...
}

静态初始化动作只进行一次

6、数组的初始化

以下两种均可(前者更合理,后者符合 C、C++ 习惯):

1
2
3
4
5
6
7
8
int[] a;
int a[];
// 尽量在定义时初始化
int[] a = new int[rand.nextInt(20)];
// 产生一维数组的可打印版本
import java.util.*;
print(Arrays.toString(a));
// 注:toString默认打印 类名 和 对象的地址(@+16进制数字)
  • 不允许指定数组的大小,分配空间必须写初始化表达式

  • 使用花括号括起来的列表初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
// 形式1
Interger[] a = {
new Interger(1),
new Interger(2),
3,
};
// 形式2
public class DynamicArray {
public static void main(String[] args) {
Other.main(new String[]){ "fiddle", "de", "dum"};
// 在方法调用处创建数组,可在调用时提供可替换的参数
}
}
class Other {
public static void main(String[] args) {
for(String s : args)
System.out.print(s + " ");
}
}
/* Output:
fiddle de dum
*///~

7、可变参数列表

  • 将 0 个参数传递给可变参数列表是可行的

  • 不依赖于自动包装机制,使用基本类型

  • 使得重载变得复杂,编译器在各个情况均要使用自动包装机制来匹配重载

    • 在不适用参数调用时,则无法确定了。此时应添加一个非可变参数

二、清理

1、finalize ():清理不是 new 创建的内存

  • 使用原因:回收程序不再使用的内存

  • 终结条件的验证

2、辨析:并非所有对象都会被垃圾回收

3、回收

  • 引用计数:释放引用计数为 9 的空间

    • 简单,速度很慢
    • 缺陷:对循环引用不适用,应被回收的值,其引用计数不为 0
  • 停止 - 复制(自适应)技术:动作发生时,程序暂停

  • JIT 即时编译器技术:翻译程序为本地机器码以提升速度

    • 即时编译
      • 缺陷:累加时长长,且会增加可执行代码的长度,导致页面调度
    • 惰性评估:尽在必要时编译

三、枚举类型 enum

1、toString

2、ordinal:用于表示特定枚举常量的声明顺序

3、可把 enum 视作类,具有自己的方法

  • 可在 switch 语句中使用,配合在有限可能值集合中选择