这次探讨的是继承与多态,继承是类的继承,多态是方法的多态。

一、知识块

1、什么是继承

类的继承逻辑还是很好理解的,比如说有一个Car类,表示小汽车,代码是这样的

class Car{
	String brand;		//车的品牌
	int mirror = 3;		//有三个反光镜
	int wheel = 4;		//有四个轮子
}

而在实际生活中,我们需要的信息比这要多得多,我们买车的时候不能说“我要有牌子、有三个反光镜、有四个轮子的车”,因为所有的车都有这些属性,而每一个品牌的车都具有自己的特点,这时我们就可以使用子类了。子类就是说某一个大类中的某一个小类,比如动物类中的狗类,人类中的非洲人类,这里的大类就是父类,而小类就是子类。而子类可以继承父类的属性和行为。

父类用关键字extends表示,这里拿宝马车举例:

class BMW extends Car{
	void go(){
		System.out.println("我是宝马车,我要跑了。");
	}
}

这里的宝马类尽管只有一个方法,但是它同时也具有父类Car中的三个变量,也就是说,这个类定义等价于

class BMW{
	String brand;		//车的品牌
	int mirror = 3;		//有三个反光镜
	int wheel = 4;		//有四个轮子
	void go(){			//定义的go方法
		System.out.println("我是宝马车,我要跑了。");
	}
}

这就叫继承。

2、继承要注意的事项

2.1 方法的重写

方法重写与方法重载是不一样的,方法重写指的是对父类方法的重写。

按照上面的定义,本来我们是原封不动把父类的方法继承过来直接使用的,但是我们也可以进行重写,就是在子类中写上一个同样名称的方法,但是要保证参数数量或类型不同,且不能产生歧义。

继承相当于我们这个类里面已经有了那个方法,但是我们再写一个同样名称的,本质上其实还是重载,但是因为那个方法实际上是继承来的,所以我们给它叫重写。而且当父类方法类型是一个对象时,重写方法的类型可以是父类方法类型的子类型。

2.2 继承中的权限问题

第三回中提到,友好型与protected型属性或行为出了所在的包就不能使用了。但是这两者的区别还是需要了解一下的,而区别就在于继承。

如果子类与父类在同一个包中,那么继承的是父类的非private型数据。

如果子类与父类不在同一个包中,那么继承的是public和友好型数据。

另外,继承来的属性或行为权限不变。

2.3 父子类成员变量大作战

继承中方法可以重写,变量也可以重新定义,当我们在子类中声明了一个成员变量或重写了一个方法,而这个成员变量或方法与父类的某个成员变量或方法名称相同时,子类继承到的父类的成员变量或方法就会被隐藏。

这时,如果子类的某个方法去调用那个同名的变量,调用的就是子类自己声明的变量;而如果是使用继承的方法去调用那个同名的变量,那调用的就是父类的那个变量。简单来说,子类的调用子类的,父类的调用父类的,个人管个人。

在类的定义中我们说过,成员变量会被局部变量隐藏,要在方法中重新使用成员变量就要使用this关键字。这和继承中的成员变量的隐藏很相似,只不过我们要用到的是super关键字。super关键字可以调用父类的属性和行为,这一点和this很像,不再贴代码,我们主要记录一下super的另一个用法,就是去调用父类的构造方法。

继承时,子类不会继承到父类的构造方法。而使用super关键字就可以去使用父类的构造方法。比如,下面这个例子中,People是父类,Asian是子类

class People{
	int legs;
	int eyes;
	int hands;
	People(int legs ,int eyes, int hands){
		this.legs = a;
		this.eyes = b;
		this.hands = c;		
	}
}

class Asian extends People{
	String color;
	Asian(int legs ,int eyes, int hands){
		super(legs,eyes,hands);
		color = "yellow";
	}
}

这里的Asian类就是用了super关键字调用父类的构造方法,让自己继承来的legs、eyes、hands三个变量得以初始化,同时也可以初始化子类自己声明的变量。

2.4 final关键字

之前已经学习了this、super关键字,这里又要来一个重要的关键字final。
final主要就是三句话:

  • final方法不能被重写;
  • final类不能被继承,比如类库中的String类;
  • final变量不能被改变值,即是个常量,所以没有初始值,要给到它初始值。

2.5 对象的上转型对象——继承中的多态

假如我们买了一个动物玩偶盲盒,我们知道这里面一定是个动物,但是我们并不知道是哪种动物,说明书上写着,这种动物盲盒中的动物都会叫,但是只有打开了才知道这种动物怎么叫,每一种动物盲盒开出来的动物叫声都不一样,这就是多态。“每一种会叫的动物”都是一个子类,而“动物”就是一个父类。我们每次拿到盲盒就可以说这个动物可以叫,闭着眼睛打开盲盒,动物发出叫声,我们并不知道它是什么颜色,不知道它的大小,也不知道它别的特征,我们只是闭着眼睛打开它,让它实现自己“叫”的功能。上代码:

class Animal{
	void speak(){};
}
class Cat{
	void speak(){			//重写父类的speak()方法
		System.out.println("我是一只猫,快乐的星猫");
	}
}
class Sheep{
	void speak(){			//重写父类的speak()方法
		System.out.println("别看我只是一只羊,羊儿的聪明难以想象");
	}
}
class Example{
	public static void main(String[] args){
		Animal animal;		//这时我们并不知道这个动物怎么叫,用父类声明出的这个对象就是上转型对象
		animal = new Cat();			//我们把创建的一个猫的对象引用传递给了上转型对象animal
		animal.speak();			//上转型对象只能操作子类创建出来的对象中父类自己有的那部分变量与方法
		animal = new Sheep();
		animal.speak();
	}
}

我们只声明了一个对象变量,但由于创建对象实体的类不同,所以产生了不同的效果,这个就叫继承中的多态

掌握上转型对象,我们就可以更加清楚地理解为什么对象是一种引用型变量了。可能这里的这个例子不太恰当,但是这只是我让自己理解来的,如果有人看的话,希望还是自己想一个例子来理解,这样记忆清楚一些。

2.6 重头戏——abstract与面向抽象编程

abstract是抽象的意思。学习abstract也是记住几句话就行。

  • abstract类只抽象方法,不抽象变量,即不含成员变量;
  • abstract方法只能被声明,不能被实现,只能在子孙类中被重写;
  • 由于abstract类要被重写,abstract类要被继承,所以不能用final类来修饰abstract类或abstract方法。
  • 由于abstract方法要被子孙类对象使用,所以不能用static修饰abstract方法。
  • abstract类中是都是抽象的没有具体意义的东西,所以不能用new创建对象。
  • 如果abstract类被某一个非abstract类继承,那这个非abstract类就必须重写这个abstract类中的abstract方法,即去掉abstract关键字,写出具体方法。

有了这几句理论基础,我们就可以来说说面向抽象编程了。
其实上面的那个动物盲盒已经体现出了面向抽象编程的核心,即上转型对象。
我们声明出一个抽象类,再在里面写上抽象方法,而继承这个抽象类的子类必须重写这个抽象方法。这样,在主程序中,我们就可以使用抽象类声明出(注意是声明而不是创建)一个对象作为上转型对象,然后把其子类创建出来的实例对象的引用赋值给这个上转型对象,那样,这个上转型对象就可以操作子类对象重写的方法了。比如上面的盲盒例子用面向抽象的方法就可以这样写:

abstract class Animal{
	abstract void speak();			//注意,抽象方法没有方法体,只有方法的声明
}
class Cat{
	void speak(){			//非抽象类重写父类的speak()方法
		System.out.println("我是一只猫,快乐的星猫");
	}
}
class Sheep{
	void speak(){			//非抽象类重写父类的speak()方法
		System.out.println("别看我只是一只羊,羊儿的聪明难以想象");
	}
}
class Box{		//一个动物盲盒
	void utter(Animal animal){
		animal.speak();
	}
}
class Example{
	public static void main(String[] args){
		Box box = new Box();
		box.utter(new Cat());
		box.utter(new Sheep());
	}
}

我们写出一个抽象类,这个抽象类可以被很多子类继承。我们再写一个类似于api的东西,即这里的Box类中的utter()方法,这个api可以去调用上转型对象的方法。上转型对象的本质就是存放在这里的一个子类对象的引用,具体来说,我们在这个api中就是用这个引用来调用实例对象的speak()方法的,这就是面向抽象编程的基本逻辑。(是有一点绕)

java继承与多态实验报告 java实验五继承与多态_后端

我们的主程序和Box类就是一个基本框架,而子类Cat与Sheep就是这个程序的扩展,不论最终我们需要多少子类,也就是说这种盲盒不论可以设计出多少种动物模式,我们总体操作的基本框架是不变的,只需要添加这个继承抽象类的子类即可。这就是很经典的“开-闭”原则:对扩展开放,对修改关闭。

二、阶段总结

关键字到现在有了static、final、this、super、abstract
编程思想有了开闭原则、面向抽象编程(主要利用上转型对象)

三、结语

这就是结语。