携程Java工程师——一道面向对象面试选择题(转)

 

public class Base
{
    private String baseName = "base";
    public Base()
    {
        callName();
    }

    public void callName()
    {
        System. out. println(baseName);
    }

    static class Sub extends Base
    {
        private String baseName = "sub";
        public void callName()
        {
            System. out. println (baseName) ;
        }
    }
    public static void main(String[] args)
    {
        Base b = new Sub();
    }
}

输出:
null

原因:

在Eclipse中Debug运行一下,一步一步观察,就可以看到程序的执行顺序,就知道为什么是null了。

其它:

看我大师归来:
1. Base b = new Sub();
2. Base b = 直接忽略,从 new Sub();开始
3. 类加载器加载 Base,Sub 类到jvm;
4. 为Base,Sub 类中的两个属性baseName 分配存储空间,但是不初始化
注意:属性的初始化时放在构造器中,按照代码顺序执行的。
5. new Sub会调用Sub的无参构造器,而在这个构造器中会隐式调用父类Base的无参构造器;
6. 父类Base的构造器中代码本质是
public Base()
{
baseName = "base";
callName();
}
即父类的属性baseName 的值为base。但为何输出null,骚年别急。
7. 因为父类构造器方法是在子类中调用的,即大环境是子类。此时,调用的方法callName()当然是指子类的方法。而这个方法打印的属性baseName当然也是子类的。那现在子类的属性baseName的值是多少呢?答案是null.因为此时子类Sub的构造器内代码本质是:
super();
baseName="sub";
此时baseName="sub"还没执行。

因此,左后的值当然是null.

http://group.jobbole.com/7782/#comm-9787

 

属性是绑定类型的,而方法是绑定对象的。

或者说属性是在编译器就确认的,而方法是动态绑定多态

属性没有覆盖的概念,如果同名,这是一种隐藏,访问父类中定义的field,使用super.i

 

类中隐藏了2个引用,一个是this一个是super
在子类中可以用super.访问父类对子类开放的属性和方法
如果子类没有定义与父类中同名的属性,并且子类可以访问父类的这个属性,那么直接用属性名就可调用
如果子类定义与父类同名的属性,而且子类可以访问父类的这个属性,那么用super.调用
如果子类定义了与父类的属性,但子类不对父类的这个属性有访问属性,那么用super.也无法调用

 

当java的子类和父类具有相同名字的属性时,到底java是怎么处理的。

先看代码:

package com.joyfulmath.study.field;

public class Person {
    public String name;

    public String getName() {
        return name;
    }
    
}
package com.joyfulmath.study.field;
public class Student extends Person {
  public String name;
  public Student(String name)
  {
    this.name = name;
    super.name = "Man-Person";//从此处可以看出,java可以通过this,已经super来区分子类和父类。
  }
  @Override
  public String getName() {
    return name;
  }
}
package com.joyfulmath.study.factory;
import com.joyfulmath.study.field.Person;
import com.joyfulmath.study.field.Student;
import com.joyfulmath.study.utils.TraceLog;
public class FieldMethod implements IWorkMethod {
  @Override
  public void startWork() {
    Student st = new Student("Mark-Student");
    Person p = st;
    TraceLog.v(p.name+" "+st.name);
    TraceLog.v(p.getName()+" "+st.getName());
  }
}

其实st.name不用怀疑,肯定是Mark-Student

但是p.name,已经p.getName()呢?

我们先来看p.getName(),虽然P是person,但是它实际代表的地址里面存的是Student,所以

p.getName()实际的调用结果是 st.getName()一样的,也就是student的getname方法。

那p.name呢?

我们在Student里面添加一个属性,level。

public class Student extends Person {
  public String name;
  public int level;
  public Student(String name)
  {
    this.name = name;
    super.name = "Man-Person";
  }
  @Override
  public String getName() {
    return name;
  }
}

然后还是在startwork中,用p.level,结果编译器不认识?

对呀,p是person的对象,它怎么会认识呢。

所以很显然,p只认识Person中的name,而不是Student中的name.

我们看下运行结果:

startWork: Man-Person Mark-Student [at (FieldMethod.java:13)]
startWork: Mark-Student Mark-Student [at (FieldMethod.java:14)]

验证了我们的猜测,可见属性是绑定类型的,而方法是绑定对象的。

或者说属性是在编译器就确认的,而方法是动态绑定(多态)。

http://www.tuicool.com/articles/ZziiIjJ

 

posted @ 2015-10-07 22:07  沧海一滴  阅读(1584)  评论(0编辑  收藏  举报