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

        未完待续!


相关文章
|
1月前
|
Java
java中,剩下的这两个内部类不太好理解!
java中,剩下的这两个内部类不太好理解!
12 0
|
1月前
|
Java 编译器
java中常见的几种内部类,你会几个?(未完)
java中常见的几种内部类,你会几个?(未完)
14 1
|
4月前
|
Java
Java语言特性:什么是静态关键字(static)?
Java语言特性:什么是静态关键字(static)?
151 0
|
4月前
|
Java 数据安全/隐私保护
【零基础学Java】—内部类的概念与分类(三十)
【零基础学Java】—内部类的概念与分类(三十)
|
12天前
|
安全 Java 编译器
接口之美,内部之妙:深入解析Java的接口与内部类
接口之美,内部之妙:深入解析Java的接口与内部类
34 0
接口之美,内部之妙:深入解析Java的接口与内部类
|
14天前
|
Java API
Java基础—笔记—内部类、枚举、泛型篇
本文介绍了Java编程中的内部类、枚举和泛型概念。匿名内部类用于简化类的创建,常作为方法参数,其原理是生成一个隐含的子类。枚举用于表示有限的固定数量的值,常用于系统配置或switch语句中。泛型则用来在编译时增强类型安全性,接收特定数据类型,包括泛型类、泛型接口和泛型方法。
9 0
|
14天前
|
存储 Java
java接口和内部类
java接口和内部类
|
30天前
|
Java 程序员 C#
静态构造方法解析,Java新手必看技能
静态构造方法解析,Java新手必看技能
9 0
|
1月前
|
Java 编译器
详解java各种内部类
详解java各种内部类
|
1月前
|
设计模式 Java
JAVA内部类
JAVA内部类
10 1