第5章 初始化与清理

随着计算机革命的发展,”不安全”的编程方式以逐渐成为编程代价的主因之一.

1. 用构造器确保初始化

Java中构造器与类相同的名称,在初始化期间要自动调用构造器.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package new05;
public class Rock {
public Rock() {
// TODO Auto-generated constructor stub
}
}
package new05;
public class SimpleConstructor {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Rock();
}
}
}

在创建对象时,new Rock();将会为对象分配存储空间,并调用相应的构造器.因为构造器的名称必须与类名完全相同,所以”每个方法首字母小写”的编码风格并不适用于构造器.
不接受任何参数的构造器叫做默认构造器,Java文档中通常使用术语无参构造器.

2. 方法重载

为了让方法名相同而形式参数不同的构造器同时存在,必须用到方法重载.
示例:

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
32
package new05;
class Tree{
int height;
//默认构造器
public Tree() {
System.out.println("Planting a seedling");
height = 0;
}
Tree(int initialHeight){
height = initialHeight;
System.out.println("Creating new Tree that is"+height+"ftte tall");
}
void info(){
System.out.println("Tree is"+height+"feet tall");
}
void info(String s){
System.out.println(s+":Tres is"+height+"feet tall");
}
}
public class OverLoading {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Tree t = new Tree(i);
t.info();
t.info("overloaded method");
}
new Tree();
}
}

创建Tree对象的时候,既可以不含参数,也可以用树的高度为参数.前者表示一颗树苗,后者表示有一定高度的树苗.要支持这种创建方式,得有一个默认构造器和一个采用现有高度作为参数的构造器.

3. 默认构造器

如果你写的类中没有构造器,则编译器会自动帮你创建一个默认的构造器.但如果你创建了一个构造器,编译器就不会帮你自动创建默认构造器.

练习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Dog.java
package new05;
public class Dog {
String name;
Dog(String name){
this.name = name;
}
public void bark(){
System.out.println(this.name+"is barking!");
}
}
DogTest.java
package new05;
public class DogTest {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Dog dog = new Dog("小"+i);
dog.bark();
}
}
}

4. this关键字

this关键字只能在方法内部使用,表示对”调用方法的那个对象”的引用.

5. static的含义

static方法就是没有this的方法,在static方法的内部不能调用非静态方法.而且可以在没有创建对象的前提下,仅仅通过类本身调用static方法.

6. 清理:终结处理和垃圾回收

垃圾回收器只知道释放那些由new分配的内存,但是finalize()能解决.

finalize()的用途?

  • 垃圾回收只与内存有关
  • 对象可能不被垃圾回收
  • 垃圾回收并不等于”析构”

JVM在没有面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的.

7. 成员初始化

Java尽力保证:所有变量在使用前都能得到适当的初始化.对于方法的局部变量,Java以编译时错误的形式来贯彻这种保证.在类定义一个对象引用时,如果不将其初始化.此引用就会获得一个特殊值null.

8. 构造器初始化

无论创建多少个对象,静态数据都只占用一份存储区域.

总结一下对象的创建过程,假设有个名为dog的类:

  • 即是没有显式地使用static关键字,构造器实际上也是静态方法.所以,当首次创建为dog的对象时,或者Dog类的静态方法/静态域首次被访问时,Java解析器必须查找类路径,以定位Dog.class文件.

  • 然后载入Dog.class,有关静态初始化的所有动作都会被执行.所以,静态初始化只在class对象首次加载的时候进行一次.

  • 当用new dog()创建对象的时候,首先将在推上为dog对象分配足够的存储空间.

  • 这块存储空间会被清零,这就自动地将Dog对象中的基本类型数据都设置成了默认值.

  • 执行所有出现于字段定义处的初始化动作.

  • 执行构造器.
    例子:

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
package new05;
class Cup{
Cup(int marker){
System.out.println("Cup("+marker+")");
}
void f(int marker){
System.out.println("f("+marker+")");
}
}
class Cups{
static Cup cup1;
static Cup cup2;
static{
cup1 = new Cup(1);
cup2 = new Cup(2);
}
Cups(){
System.out.println("Cups");
}
}
public class ExplicitStatic {
public static void main(String[] args) {
System.out.println("Inside main()");
Cups.cup1.f(99);
}
}

9. 数组初始化

要定义一个数组,只需要在类型名后加上一对空方括号即可;编译器不允许指定数组的大小,只是对数组的一个引用.

1
int[] a1;

例子:可变参数列表

1
2
3
4
5
6
7
8
9
10
11
12
package new05;
public class AutoboxingVarargs {
public static void f(Integer...args){
for (Integer integer : args) {
System.out.println(integer);
}
}
public static void main(String[] args) {
f(4,5,6,7,8);
}
}