Thinking in Java ---ch04笔记

Ch04 Initiation and Clean-up

4.2.2 主类型的过载

主(数据)类型能从一个较小的类型自动转变成一个较大的类型。

方法采用了容量更小、范围更窄的主类型值。若我们的自变量范围比它宽,就必须用括号中的类型名将其转为适当的类型。如果不这样做,编译器会报告出错。
大家可注意到这是一种缩小转换。也就是说,在造型或转型过程中可能丢失一些信息。这正是编译器强迫我们明确定义的原因——我们需明确表达想要转型的愿望。

4.2.3返回值过载

java 不允许通过返回之类型重载,因为

void f() {}
int f() {}
若编译器可根据上下文(语境)明确判断出含义,比如在int x=f()中,那么这样做完全没有问题。然而,我们也可能调用一个方法,同时忽略返回值;我们通常把这称为为它的副作用去调用一个方法,因为我们关心的不是返回值,而是方法调用的其他效果。所以假如我们象下面这样调用方法:
f();
Java怎样判断f()的具体调用方式呢?而且别人如何识别并理解代码呢?由于存在这一类的问题,所以不能根据返回值类型来区分过载的方法。

424默认构建器

默认构建器是没有自变量的。它们的作用是创建一个空对象。若创建一个没有构建器的类,则编译程序会帮我们自动创建一个默认构建器。例如:

如果已经定义了一个构建器(无论是否有自变量),编译程序都不会帮我们自动合成一个默认构建器。

425 This

 

this关键字(注意只能在方法内部使用可为已调用了其方法的那个对象生成相应的句柄。可象对待其他任何对象句柄一样对待这个句柄。但要注意,假若准备从自己某个类的另一个方法内部调用一个类方法,就不必使用this。只需简单地调用那个方法即可。当前的this句柄会自动应用于其他方法。

 

1. 在构建器里调用构建器
若为一个类写了多个构建器,那么经常都需要在一个构建器里调用另一个构建器,以避免写重复的代码。可用this关键字做到这一点。
通常,当我们说this的时候,都是指这个对象或者当前对象。而且它本身会产生当前对象的一个句柄。在一个构建器中,若为其赋予一个自变量列表,那么this关键字会具有不同的含义:它会对与那个自变量列表相符的构建器进行明确的调用。这样一来,我们就可通过一条直接的途径来调用其他构建器。如下所示:

//: Flower.java

// Calling constructors with "this"

 

public class Flower {

  private int petalCount = 0;

  private String s = new String("null");

  Flower(int petals) {

    petalCount = petals;

    System.out.println(

      "Constructor w/ int arg only, petalCount= "

      + petalCount);

  }

  Flower(String ss) {

    System.out.println(

      "Constructor w/ String arg only, s=" + ss);

    s = ss;

  }

  Flower(String s, int petals) {

    this(petals);

//!    this(s); // Can't call two!

    this.s = s; // Another use of "this"

    System.out.println("String & int args");

  }

  Flower() {

    this("hi", 47);

    System.out.println(

      "default constructor (no args)");

  }

  void print() {

//!    this(11); // Not inside non-constructor!

    System.out.println(

      "petalCount = " + petalCount + " s = "+ s);

  }

  public static void main(String[] args) {

    Flower x = new Flower();

    x.print();

  }

} ///:~


其中,构建器Flower(String s,int petals)向我们揭示出这样一个问题:尽管可用this调用一个构建器,但不可调用两个。除此以外,构建器调用必须是我们做的第一件事情,否则会收到编译程序的报错信息。
这个例子也向大家展示了this的另一项用途。由于自变量s的名字以及成员数据s的名字是相同的,所以会出现混淆。为解决这个问题,可用this.s来引用成员数据。

2. static的含义
理解了this关键字后,我们可更完整地理解static(静态)方法的含义。它意味着一个特定的方法没有this。我们不可从一个static方法内部发出对非static方法的调用(注释
),尽管反过来说是可以的。而且在没有任何对象的前提下,我们可针对类本身发出对一个static方法的调用。事实上,那正是static方法最基本的意义。它就好象我们创建一个全局函数的等价物(在C语言中)。除了全局函数不允许在Java中使用以外,若将一个static方法置入一个类的内部,它就可以访问其他static方法以及static字段。

:有可能发出这类调用的一种情况是我们将一个对象句柄传到static方法内部。随后,通过句柄(此时实际是this),我们可调用非static方法,并访问非static字段。但一般地,如果真的想要这样做,只要制作一个普通的、非static方法即可。

有些人抱怨static方法并不是面向对象的,因为它们具有全局函数的某些特点;利用static方法,我们不必向对象发送一条消息,因为不存在this。这可能是一个清楚的自变量,若您发现自己使用了大量静态方法,就应重新思考自己的策略。然而,static的概念是非常实用的,许多时候都需要用到它。所以至于它们是否真的面向对象,应该留给理论家去讨论。事实上,即使Smalltalk在自己的类方法里也有类似于static的东西。

4.3 清除:收尾和垃圾收集

4.3.1 finalize()用途何在

4.3.2 必须执行清除

4.4 成员初始化

1. 初始化顺序
在一个类里,初始化的顺序是由变量在类内的定义顺序决定的。即使变量定义大量遍布于方法定义的中间,那些变量仍会在调用任何方法之前得到初始化——甚至在构建器调用之前。例如:

//: OrderOfInitialization.java

// Demonstrates initialization order.

 

// When the constructor is called, to create a

// Tag object, you'll see a message:

class Tag {

  Tag(int marker) {

    System.out.println("Tag(" + marker + ")");

  }

}

 

class Card {

  Tag t1 = new Tag(1); // Before constructor

  Card() {

    // Indicate we're in the constructor:

    System.out.println("Card()");

    t3 = new Tag(33); // Re-initialize t3

  }

  Tag t2 = new Tag(2); // After constructor

  void f() {

    System.out.println("f()");

  }

  Tag t3 = new Tag(3); // At end

}

 

public class OrderOfInitialization {

  public static void main(String[] args) {

    Card t = new Card();

    t.f(); // Shows that construction is done

  }

} ///:~


Card中,Tag对象的定义故意到处散布,以证明它们全都会在构建器进入或者发生其他任何事情之前得到初始化。除此之外,t3在构建器内部得到了重新初始化。它的输入结果如下:(这个例子很清楚的表明变量的初始化顺序)

Tag(1)

Tag(2)

Tag(3)

Card()

Tag(33)

f()

 

2. 静态数据的初始化
若数据是静态的(static),那么同样的事情就会发生;如果它属于一个基本类型(主类型),而且未对其初始化,就会自动获得自己的标准基本类型初始值;如果它是指向一个对象的句柄,那么除非新建一个对象,并将句柄同它连接起来,否则就会得到一个空值(NULL)。
如果想在定义的同时进行初始化,采取的方法与非静态值表面看起来是相同的。但由于static值只有一个存储区域,所以无论创建多少个对象,都必然会遇到何时对那个存储区域进行初始化的问题。下面这个例子可将这个问题说更清楚一些:

//: StaticInitialization.java

// Specifying initial values in a

// class definition.

 

class Bowl {

  Bowl(int marker) {

    System.out.println("Bowl(" + marker + ")");

  }

  void f(int marker) {

    System.out.println("f(" + marker + ")");

  }

}

 

class Table {

  static Bowl b1 = new Bowl(1);

  Table() {

    System.out.println("Table()");

    b2.f(1);

  }

  void f2(int marker) {

    System.out.println("f2(" + marker + ")");

  }

  static Bowl b2 = new Bowl(2);

}

 

class Cupboard {

  Bowl b3 = new Bowl(3);

  static Bowl b4 = new Bowl(4);

  Cupboard() {

    System.out.println("Cupboard()");

    b4.f(2);

  }

  void f3(int marker) {

    System.out.println("f3(" + marker + ")");

  }

  static Bowl b5 = new Bowl(5);

}

 

public class StaticInitialization {

  public static void main(String[] args) {

    System.out.println(

      "Creating new Cupboard() in main");

    new Cupboard();

    System.out.println(

      "Creating new Cupboard() in main");

    new Cupboard();

    t2.f2(1);

    t3.f3(1);

  }

  static Table t2 = new Table();

  static Cupboard t3 = new Cupboard();

} ///:~


Bowl允许我们检查一个类的创建过程,而TableCupboard能创建散布于类定义中的Bowlstatic成员。注意在static定义之前,Cupboard先创建了一个非staticBowl b3。它的输出结果如下:(注意
System.out.println( "Creating new Cupboard() in main");要在static 初始化后在执行)

Bowl(1)

Bowl(2)

Table()

f(1)

Bowl(4)

Bowl(5)

Bowl(3)

Cupboard()

f(2)

Creating new Cupboard() in main

Bowl(3)

Cupboard()

f(2)

Creating new Cupboard() in main

Bowl(3)

Cupboard()

f(2)

f2(1)

f3(1)

 

 

1)static初始化只有在必要的时候才会进行。 

2)如果不创建一个Table对象,而且永远都不引用Table.b1Table.b2,那么static Bowl b1b2永远都不会创建。

3)只有在创建了第一个Table对象之后(或者发生了第一次static访问),它们才会创建。在那以后,static对象不会重新初始化
4)初始化的顺序是

a)首先static如果它们尚未由前一次对象创建过程初始化),

b)接着是非static对象。

在这里有必要总结一下对象的创建过程。请考虑一个名为Dog的类:
(1) 类型为Dog的一个对象首次创建时,或者Dog类的static方法/static字段首次访问时,Java解释器必须找到Dog.class(在事先设好的类路径里搜索)。
(2) 找到Dog.class后(它会创建一个Class对象,这将在后面学到),它的所有static初始化模块都会运行。因此,static初始化仅发生一次——Class对象首次载入的时候。
(3) 创建一个new Dog()时,Dog对象的构建进程首先会在内存堆(Heap里为一个Dog对象分配足够多的存储空间。
(4) 这种存储空间会清为零,将Dog中的所有基本类型设为它们的默认值(零用于数字,以及booleanchar的等价设定)。
(5) 进行字段定义时发生的所有初始化都会执行。
(6) 执行构建器。

 

4.5 数组初始化

为定义一个数组,只需在类型名后简单地跟随一对空方括号即可:
int[] al;
也可以将方括号置于标识符后面,获得完全一致的结果:
int al[];

 

由于数组的大小是随机决定的(使用早先定义的pRand()方法),所以非常明显,数组的创建实际是在运行期间进行的。不可以定义数组的大小(无初始化时),但是初始化是可以做到 int[] a = new int[4]

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值