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

简介:         昨日,某群友在某群里发了一个问题,内容如下: public class Base { private String baseName = "base"; public Base(){ callName(); } public void callName(){ System.

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

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、非静态内部类的非静态成员可以访问外部类的非静态变量。

        未完待续!


相关文章
|
2月前
|
Java 编译器
【Java】内部类
【Java】内部类
22 0
|
4月前
|
Java
【Java基础面试二】、个Java文件里可以有多个类吗(不含内部类)?
这篇文章讨论了Java文件中类的定义规则,指出一个Java文件可以包含多个类(不包含内部类),但其中最多只能有一个public类,且如果有public类,它的名称必须与文件名一致。
|
4月前
|
算法 Java
12 Java常用类(一)(内部类+object类+包装类)
12 Java常用类(一)(内部类+object类+包装类)
38 5
|
5月前
|
Java
Java进阶之内部类
【7月更文挑战第13天】Java内部类增进代码组织与封装,允许直接访问外部类成员,包括私有成员。主要有四种类型:成员、静态、局部和匿名内部类。匿名内部类常用于一次性实现接口或扩展类。内部类可隐藏实现细节,减少命名冲突,并在特定上下文中定义辅助类。示例展示了静态和非静态内部类如何在Shape类中封装Circle和Rectangle。使用内部类能提升代码可读性,但可能增加复杂性。
39 6
|
4月前
|
Java
【Java】内部类、枚举、泛型
【Java】内部类、枚举、泛型
|
6月前
|
Java
一篇文章讲明白Java中内部类详解—匿名内部类
一篇文章讲明白Java中内部类详解—匿名内部类
95 2
|
6月前
|
设计模式 Java 编译器
Java中的内部类(如果想知道Java中有关内部类的知识点,那么只看这一篇就足够了!)
Java中的内部类(如果想知道Java中有关内部类的知识点,那么只看这一篇就足够了!)
|
6月前
|
Java 编译器
24. 【Java教程】内部类
24. 【Java教程】内部类
30 2
|
7月前
|
Java 程序员
Java的内部类与匿名类的技术性文章
Java的内部类与匿名类的技术性文章
44 1
|
6月前
|
设计模式 安全 Java
Java设计模式:代理模式的静态和动态之分(八)
Java设计模式:代理模式的静态和动态之分(八)