【Java】super关键字的理解与使用(包含隐藏想象的理解)

super关键字的引用

需求:在子类中某一个方法中,去调用父类被覆盖的方法
此时,就需要使用到super关键字
什么是super:
this:当前对象,谁调用this所在的方法,this就是哪一个对象;
super:当前对象的父类对象;

class Bird
{
	public void fly(){
		System.out.println("fly");
	}
}
class Penguin extends Bird
{
	
	public void fly(){
		System.out.println("Can't fly");
	}
	public void say(){
		System.out.println("I am singing");
		super.fly(); //调用父类中的fly方法
	}
}

内存分析

【Java】super关键字的理解与使用(包含隐藏想象的理解)_第1张图片

子类初始化过程

子类初始化过程:创建对象子类对象的过程。

(1)在创建子类对象之前,会首先创建父类对象。

class Person
{
	private String name;
	private int age;
	Person(){
		System.out.println("父类构造器");
	}
}

class Student extends Person
{
	private int studentID;
	Student(){
		//这里会存在一个隐式的super(),表示调用了父类构造器 
		System.out.println("子类构造器");
	}
}
class SubClassInitDemo 
{
	public static void main(String[] args) 
	{
		Student s = new Student();	//创建一个学生对象
	}
}
---------- 运行java ----------
父类构造器
子类构造器

输出完成 (耗时 0) - 正常终止

注: 子类Student是无法继承到父类Person中私有的name和age字段的,但是有的书籍记载:private成员是可以继承的,只不是不能呗外界访问,我们承认该观点,但是因为访问不到,我们可以简单地认为private成员不能被子类继承。

因此,在创建字类对象的时执行属性是:先进入子类构造器,然后在构造器中会先调用父类构造器(创建父类对象),再执行子类构造器代码。

调用子类构造器之前,在子类构造器中会先调用父类构造器,默认调用的是父类的无参数构造器。

class Person
{
	private String name;
	private int age;
	Person(String name){
		System.out.println("父类构造器");
	}
}
class Student extends Person
{
	private int studentID;
	//这里会报错,因为在子类构造器中会先调用父类构造器,默认调用的是父类的无参数构造器
	// 因为父类中,不存在无参数构造器
	Student(){
		System.out.println("子类构造器");
	}
}
---------- 编译java ----------
SubClassInitDemo.java:13: 错误: 无法将类 Person中的构造器 Person应用到给定类型;
	Student(){
	         ^
  需要: String
  找到: 没有参数
  原因: 实际参数列表和形式参数列表长度不同
1 个错误

(2)如果父类不存在可以被子类访问的构造器,则不能存在子类。
也是就说父类中的所有构造器都被私有化了,就不存在继承它的子类了;

class Person
{
	private String name;
	private int age;
	private Person(){ //构造器被私有化
		System.out.println("父类构造器1");
	}

}
class Student extends Person
{
	private int studentID;
	Student(){ // 编译报错,因为父类构造器被私有化了
		System.out.println("子类构造器");
	}
}

---------- 编译java ----------
SubClassInitDemo.java:14: 错误: Person() 在 Person 中是 private 访问控制
	Student(){
	         ^
1 个错误

输出完成 (耗时 0) - 正常终止

(3)如果父类没有提供无参数构造器,此时子类必须显示通过super语句去调用父类带参数的构造器。

class Person
{
	private String name;
	private int age;
	Person(String name){
		this.name = name;
		System.out.println("父类构造器");
	}
	
}
class Student extends Person
{
	private int studentID;
	Student(){
		super("Liming");
		System.out.println("子类构造器");
	}
}

在子类中如何获取父类的私有(private修饰)成员变量,代码如下:

class Person
{
	private String name;
	private int age;
	Person(String name,int age){
		this.name = name;
		this.age = age;
	}
		
	public String getName(){
		return name;
	}
	
	public int getAge(){
		return age;
	}
}

class Student extends Person
{
	private int studentID;
	Student(String name, int age, int studentID){
		//调用父类构造器这句话必须作为子类构造器的第一句话
		super(name,age);
		this.studentID = studentID;
	}
	public int getStudentID(){	
		return studentID;
	}

	public void print(){
	//其实,下面的super关键字可以不写,因为子类中没有getName和getName方法,
	// 在子类中找不到的话,会自动去父类中寻找这两个方法
		System.out.println("name="+super.getName()+",age="+super.getName()+",studentID="+this.getStudentID());
	}
}
class SubClassInitDemo 
{
	public static void main(String[] args) {
		Student s = new Student("Liming",18,10475);	//创建一个学生对象
		s.print();
	}
}
---------- 运行java ----------
name=Liming,age=18,studentID=10475

输出完成 (耗时 0) - 正常终止

super关键字使用场景和隐藏现象

super关键字的使用场景:

(1)可以使用super解决子类隐藏了父类的字段情况,如下代码。但是,该情况我们一般不讨论,因为破坏了封装。

class Person{
	public String name = "will";
}

class Student extends Person{
	public int name = 18; //隐藏了父类中的name字段
	public void printName(){
		System.out.println(name); // 18
		Sytem.out.println(super.name);// will
	}
}

(2)在子类方法中,调用父类被覆盖的方法,如引出super的例子代码,此时必须使用super。

(3)在子类构造中,调用父类构造器,此时必须使用super语句:super(实参)。如上上述子类初始化过程的(3)代码。

什么是“隐藏现象 ” :

所谓“隐藏”就是“遮蔽”的意思

(1)满足继承的访问权限下,隐藏父类静态方法:若子类定义的静态方法的签名和父类中的静态方法的签名相同,那么此时就是隐藏父类的方法。注意:仅仅是静态方法。

如下代码:在父类和子类中都存在了一个static修饰的dowork方法,这里的dowork是隐藏的概念,而不是覆盖 !在多态中会再次提到。

class Person{
	public String name = "will";
	public static void dowork(){}
}

class Student extends Person{
	public int name = 18; //隐藏了父类中的name字段
	public void printName(){
		System.out.println(name); // 18
		Sytsem.out.println(super.name);// will
	}
	public static void dowork(){}
}

(2)满足继承的访问权限下,隐藏父类字段:若子类中定义的字段和父类中的字段名相同(不管类型),此时就是隐藏父类字段,此时只能通过super关键字访问被隐藏的字段。

代码示例如super关键字的使用场景(1)中的代码。

(3)隐藏本类字段:若同类中某局部变量名和字段名相同,此时就是隐藏本类字段,此时只能通过this访问被隐藏的字段。

如下代码中,父类中存在String 类型name字段,子类中int类型的name字段,在字段的printName方法中又存在了一个boolean类型的局部变量name,那么根据程序的就近原则打印出来的接错依次是false,18,will

class Person{
	public String name = "will";
}

class Student extends Person{

	public int name = 18; //隐藏了父类中的name字段
	public void printName(){
		boolean name = false;
		System.out.println(name); // false
		System.out.println(this.name); // 18
		System.out.println(super.name);// will
	}
}
class SuperDemo2{

	public static void main(String[] agrs){
		Student s = new Student();
		s.printName();
	}
}
---------- 运行java ----------
false
18
will

输出完成 (耗时 0) - 正常终止

注意:
static不能与super和this关键字共存,这是因为super和this都是对象级别的,而static修饰的是类级别

你可能感兴趣的:(#,面向对象)