Java初始化之二

this关键字

假设有同一类型的两个对象,分别是ab.

       class Banana{ void peel( int i ){ /*……*/} }

      

       public class BananaPeel{

              public static void main(String[] args){

                     Banana a=new Banana(),

b=new Banana();

a.peel(1);

b.peel(2);

}

}

 

如果只有一个peel()方法,它是如何知道是被a调用的还是被b调用的呢?

实际上编译器做了一些幕后工作。它暗自把所操作对象的引用作为第一个参数传递给了peel(),所以上述两个方法改写一下,可变为:

 

Banana.peel(a,1);

Banana.peel(b,2);

 

这只是内部表现形式,我们并不能这样书写,那这种写法可以帮助你了解实际发生的事。

假设你希望在方法内部获得当前对象的引用。由于这个引用是由编译器偷偷传入的,所以没有标识符可用。但是,为此有个专门的关键词:thisthis关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。但如果想在当前方法中调用同类中的其他方法就没必要使用this,直接调用即可。

 

public class Apricot{

       void pick();

       void pit(){ pick(); }

}

 

pit()内部,你可以写this.pick();但没有必要。编译器能自动帮你添加。只有在明确指出当前对象的引用时才会使用this.

 

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();

}

}

输出

i = 3

 

由于increment()通过this关键字反悔了对当前对象的引用,所以很容易在一条与距离对同一个对象进行多次操作。

 

class Person{

       public void eat(Apple apple){

              Apple peeled = apple.getPeeled();

              System.out.println(“Yummy”);

}

}

class Peeler{

       static Apple peel(Apple apple){

              return apple;

}

}

class Apple{

       Apple getPeeled(){return Peeler.peel(this);}

}

public class PassingThis{

       public static void main(String [] args){

              new Person().eat(new Apple());

}

}

输出

Yummy

 

由这个例子可以看出来,this还可以将自身引用传递给外部方法。

在构造器中调用构造器

       可能会为一个类编写多个构造器,有时候可能想在一个构造器中调用另一个构造器,以免重复代码,用this可以避免这一点。

       通常写this的时候,都指的是“这个对象”或者“当前对象”,而且他本身表示对当前对象的引用。在构造器中,如果为this添加了参数列表,那么就有了不同的含义。这将产生对符合此参数里表的某个构造器的明确调用;这样,调用构造器就有了直接的途径:

       public class Flower{

       int petalCount=0;

       String s=”initial value”;

       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 onlt, s =”+ss);

       s=ss;

}

Flower(String s, int petals){

       this(petals);

       //! this(s);//不能两个一起调用

       this.s=s;

System.out.println(“String & int args”);

}

Flower(){

       this(“hi”,47);

       System.out.println(“default constructor (on args)”);

}

void printPetalCount(){

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

       System.out.println(“petalCount = ” + petalCount + “ s = “ + s);

}

public static void main(String [] args){

       Flower x = new Flower();

       x printPetalCount();

}

}

输出

Constructor w/ int arg only, petalCount = 47

String & int args

default constructor (no args)

petalCount = 47 s = hi

 

从上面的例子可以看出来,尽管可以用this调用一个构造器,但却不能调用两个,此外,必须将构造器调用至于最起始处,否则编译器会报错。

这个例子还展示了this的另一种用法,使用this.s来区分了由于参数和成员名称相同所带来的问题。

此外在printPetalCount()方法中可以看出,除了构造器之外,严谨在其他任何地方调用构造器。

成员初始化

       Java会尽力保证所有变量在使用之前都能得到恰当的初始化,对于方法的局部变量,Java以编译是错误的形式来贯彻这种保证。

      

       void f(){

              int i;

              i++;//error i未被初始化

}

 

实际上编译器可以为i赋一个默认值,但是局部未初始化的变量更有可能是程序员的疏忽,所以采用默认值反而会掩盖这种失误,因此强制程序员提供一个初始值。

但如果类的数据成员(字段)是基本类型,Java都会保证其会有一个初始值

 

public class InitialValues{

       boolean t;

       char c;

       byte b;

       short s;

       int i;

       long l;

       float f;

       double d;

       InitialValues reference;

       void printInitialValues(){

              System.out.println(“Data type     Initial value”);

              System.out.println(“boolean      “+t);

              System.out.println(“char         “+c);

              System.out.println(“byte         “+b);

              System.out.println(“short         “+s);

              System.out.println(“int           “+i);

              System.out.println(“long         “+l);

              System.out.println(“float         “+f);

              System.out.println(“double       “+d);

              System.out.println(“reference     ”+reference);

}

public static void main(String [] args){

       InitialValues iv=new InitialValues();

       iv.printInitialValues();

}

}

输出

Data type     Initial value

boolean      false

char         [ ]

byte         0

short        0

int          0

long         0

float         0.0

double       0.0

       reference     null

 

       char的值为0,所以显示为空白,尽管我们没有给予变量初始值,但他们确实被赋予了初始值,对于类里的对象引用,如果不将其初始化,此引用就会获得一个null.

指定初始化

       如果想为类中某个字段赋值,直接在其被定义的地方赋值即可

 

       public class InitialValues2{

              boolean bool=true;

              char c=’x’;

              byte b=47;

              short s=0xff;

              int i=999;

              long lng=1;

              float f=3.14f;

              double d=3.141582;

}

      

也可以用相同的方法来初始化非基本类型对象。

 

class Depth{}

 

public class Measurement{

       Depth d=new Depth();

}

 

也可以通过方法返回值来初始化

 

public class MethodInit{

       int i=f();

       int f(){return 11;}

}

方法中也可带有参数,参数必须是已经初始化的变量

 

public class MethodInit2{

       int i=f();

       int j=g(i);

       int f(){return 11;}

       int g(int n){reutn n*10;}

}

 

但下面的写法就是错误的

public class MethodInit3{

       //! int j=g(i); //Illegal forward reference

       int i=f();

       int f(){ return 11;}

       int g(int n){ return n*10;}

}

从上面的例子可以看出正确的初始化取决于初始化顺序,而与编译方式无关。

构造器初始化

       也可以使用构造器来进行初始化,在运行时刻,可以调用方法或执行某些动作来确定初始值。但要记住:无法阻止自动初始化的进行,例如。

      

       public class Counter{

       int i;

       Counter(){i = 7;}

}

在这里i首先被赋值为0,而后再被赋值为7.

初始化顺序

       在类的内部,变量定义的先后顺序据定了初始化的顺序。即使变量的定义散布于方法定义之间。它仍旧会在任何方法被调用之前得到初始化。

 

       class Window{

       Window(int marker){System.out.println(“Window(”+ marker + “)”);}

}

class House{

       Window w1=new Window(1);

       House(){

              System.out.println(“House()”);

              w3=new Window(33);

}

Window w2=new Window(2);

              void f(){System.out.println(“f()”);}

              Window w3=new Window(3);

}

 

public class OrderOfInitialization{

       public static void main(String[] args){

              House h=new House();

              h.f();

}

}

输出

Window(1)

Window(2)

Window(3)

House()

Window(33)

f()

 

Window对象的3次实例化分别被安排在了House内部的各个角落,以足以证明它们全部在调用构造器或其他方法之前得到初始化。此外w3被引用2次。

由输出可见,w3这个引用会被初始化两次;一次在调用构造器前,一次在调用构造器后(第一次引用的对象将被丢弃,并作为垃圾回收)

posted on 2011-02-24 14:18  柠檬哥  阅读(302)  评论(0编辑  收藏  举报

导航