0 0

java解惑 谜题53 的不解5

我是java初学者。
在看到谜题53时有一些疑问,描述如下:
有一库类:
public class Thing {
    public Thing(int i){
     ....
    }
}


public class MyThing extends Thing {
  private final int arg;
  public MyThing(){
     super(arg = Math.random());   //为什么编译不过去。书上说线程安全,没太理解。请指教.. 从网上搜了 下,构造方                                        //法不是线程安全的啊。
}

}


public class MyThing extends Thing {
    private final int arg;
      public MyThing(){
     this(Math.random());   
}
    private MyThing(int i){
     super(i);
     arg = i;
}

}



第二个子类可以,为什么它可以呢?谢谢指教。
2009年12月26日 12:32

5个答案 按时间排序 按投票排序

0 0

咳,完全是逻辑上问题,一激动,说了这么多。

2009年12月29日 10:56
0 0

全都没讲到点子上。报错这和final没有任何关系。举个列子
public int i=1;
public MyThing(){
   super(i);
}
上面这段代码也会编译不通过的:Cannot refer to an instance field arg while explicitly invoking a constructor(在明确调用一个构造器的时候不能引用类的一个实例域)

原因很简单,我们假如可以通过编译器。我问一个问题,调用构造器是干什么的?
大家都知道,创建一个对象并初始化对象中的数据域。红色字体很重要,在上面的列子中new MyThing()的作用就是为了初始化这个对象的数据域i,试问在没有通过构造器初始化之前,i的值是不确定的。我们怎么能使用一个不确定的i值来初始化对象。

打个比方:我们去银行取5元钱,但是必须首先交5块钱手续费(也就是首先明确初始化)。我能不能给银行的人说:你把5元先取给我,然后我再把这钱给你交手续费呢???

当然例子不是很恰当,但说的意思差不多。总之,必须要先明确初始化实例域,才能创建好对象,进而才能使用对象的数据域。在没有实例化之前就想使用他,就像没有取出5元钱就想先用这钱一样,不可能。
public int i=1;
public MyThing(int j){
   super(j);
}
上面的代码就可以,为什么,因为编译器知道,你如果想构造这个对象,必须传过来一个确定的值来初始化。

2009年12月29日 10:53
0 0

楼上说得不太对,final变量是可以被赋值的,但必须保证只被赋一次。像这种成员变量,就可以在构造函数中赋值。
问题是java对语法结构是很严格的,a = b这样的语句被认为不是值(既不是左值也不是右值,不能再用),不能foo(a = b),也不能c = (a = b)或者(a = b) = c。所以编译不过去的。

考虑到super必须在第一句话,只能写成你下面的那种形式了。
实际上,在构造函数里调用外面的方法,像random,是不太合适的。比较好的办法是写个静态方法createMyThing来产生一个对象。

2009年12月29日 08:20
0 0

super(arg = Math.random());

就算arg不是final的,这种调用方法在java里可以吗? 如果可以话,这种情况传的参数值是什么?

2009年12月26日 22:27
0 0

private final int arg;
arg = Math.random();
为什么编译不过去?当然编译不过去。arg是final的变量,给一个final类型的变量赋值当然会报错了。
你在Eclipse等ide自己写个例子,它会提示你不能被赋值。

2009年12月26日 16:04

相关推荐

Global site tag (gtag.js) - Google Analytics