答群友问:Java静态内部类、普通内部类等的理解

来源:互联网 发布:中国p2p网贷行业数据 编辑:程序博客网 时间:2024/06/06 00:59

        昨日,某群友在某群里发了一个问题,内容如下:

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(Sub.this.baseName);}}public static void main(String[] args){Base b = new Sub();}}
        以上Base类的main()方法,到底输出内容是什么?

        有的同学猜base,理由为构造Sub时会调用super的构造方法,而Base的构造方法会调用其callName()方法,所以输出的是父类的baseName,即内容为base,而有的同学猜是sub,理由是构造Sub时虽然会调用super的构造方法,而Base的构造方法会调用其callName()方法,但是子类Sub重写了父类Base的callName()方法,所以输出的应该是子类的baseName,即内容为sub。

        而我的分析,却应该是null,为什么呢?把这个类稍微改造如下:

public class Base {static {System.out.println("父类Base的静态代码块被调用了!");}private String baseName = "base";public Base() {System.out.println("父类Base的构造方法被调用了!");System.out.println("父类Base的成员变量baseName被初始化为" + baseName);callName();}public void callName() {System.out.println("父类Base的callName()方法被调用了!");System.out.println(baseName);} static class Sub extends Base {static{System.out.println("子类Sub的静态代码块被调用了!");}public Sub(){System.out.println("子类Sub的构造方法被调用了!");}private String baseName = "sub";public void callName() {System.out.println("子类Sub的callName()方法被调用了!");System.out.println(baseName);}}public static void main(String[] args) {Base b = new Sub();}}
        执行下Base的main()方法,结果会是什么呢?答案如下:

父类Base的静态代码块被调用了!子类Sub的静态代码块被调用了!父类Base的构造方法被调用了!父类Base的成员变量baseName被初始化为base子类Sub的callName()方法被调用了!null子类Sub的构造方法被调用了!
        为什么会是这个样子呢?我们简单分析它的大体流程应该如下:

        1、首先,执行Base的main()方法中的Base b = new Sub();语句时,遇到Sub类,JVM会先加载Sub类,但是加载它时遇到了extends关键字,所以会在加载Sub类前先加载其父类Base,而第一行输出的“父类Base的静态代码块被调用了!”正好验证了这一点;

        2、其次,该轮到加载Sub类了,故第二行输出为“子类Sub的静态代码块被调用了!”;

        3、然后,类加载完了,该执行new操作了,众所周知,new操作是实例化一个对象,那么,子类被实例化时,是不是会调用父类的构造方法呢?答案是肯定的,而第三行的输出结果也确实是“父类Base的构造方法被调用了!”;

        4、在父类Base的构造方法中,我们输出了父类Base的成员变量baseName的值,输出的值也确实是base;

        5、接下来,该调用callName()方法了,那么这个callName()是应该调用父类的还是子类的呢?由于是初始化子类,而子类也恰好重写了父类的callName()方法,那么结果应该肯定是调用子类的callName()方法了,而第五行输出结果也确实是“子类Sub的callName()方法被调用了!”;
        6、既然调用子类的callName()方法,那么就应该输出子类的baseName的值了,是不是就应该是sub呢?先卖个关子,再看下一步输出;

        7、第七行输出结果为“子类Sub的构造方法被调用了!”,说明了什么?在调用子类callName()方法时,子类Sub还没有被实例化,那么它成员变量baseName的值就不应该是“sub”,而只能是null了,所以回过头来,第6行结果输出null也就不足为奇了!

        特别需要说的,如果我们把子类的成员变量baseName定义为static,那么结果就不一样了,它会输出为sub,因为虽然在Sub构造方法执行前就调用子类的callName()方法,但是由于子类成员变量baseName被定义为static,在子类被JVM加载时,baseName的初始化就会被执行了,输出结果如下:

父类Base的静态代码块被调用了!子类Sub的静态代码块被调用了!父类Base的构造方法被调用了!父类Base的成员变量baseName被初始化为base子类Sub的callName()方法被调用了!sub子类Sub的构造方法被调用了!

        以上就是针对那位群友问题的全部解答。

        下面,简要回顾下Java静态内部类、普通内部类等的区别:
        1、构造形式:静态内部类有两种构造方式,第一种是直接new 内部类名(),比如上面的Base b = new Sub(),而第二种则是根据外部类的类名.内部类名()进行new操作,比如Base b = new Base.Sub();普通内部类则只能先显示构造外部类实例,再通过外部类实例.new 内部类名()来构造内部类实例,比如Base b = new Base().new Sub();

        2、静态内部类可以有静态成员,而非静态内部类则不能有静态成员;

        3、静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;

        4、非静态内部类的非静态成员可以访问外部类的非静态变量。

        未完待续!


6 0
原创粉丝点击