Java继承和多态
-
继承的介绍与使用
继承:让类与类之间产生关系(子父类关系),子类可以直接使用父类中非私有的成员
场景:现在需要编写三个类,描述程序员,项目经理,人事,属性为(姓名,年龄,工资)
可以发现,三个类中的属性完全一样,重复编写就非常麻烦,这时我们可以抽取一个共同的父类 Employee,将共性内容定义在父类中,再让三个子类继承 Employee,复用性就提高了。
只继承属性,不影响父子类各自属性的值。
继承的是【属性定义】,不是【属性值】
-
子类不用重复写String name;int age...
-
子类可以直接setName,此时设置的是子类自己的,跟父类无关
-
父类可以直接setName,此时设置的是父类自己的,跟子类无关
我只是有了我爸的房子,我、我爸在这个房子做什么,互不干扰
格式
public class Employee {
String name; // 姓名
int age; // 年龄
double salary; // 工资
}
public class Coder extends Employee {
}
public class Manager extends Employee {
}
public class Hr extends Employee {
}
public class Test {
public static void main (String[] args){
Coder c = new Coder();
c.name = "张三";
c.age = 23;
c.salary = 10000;
Manager a = new Manager();
a.name = "李四";
a.age = 24;
a.salary = 15000;
Hr h = new Hr();
h.name = "王五";
h.age = 25;
h.salary = 7000;
}
}
可以发现父类中非私有的属性,子类是可以直接使用的。
问题:按照标准的 JavaBean 要求,成员变量是需要私有的,那子类怎么使用呢?
回答:私有成员变量后,我们还会提供对应的 setXxx 和 getXxx 方法,这些方法是 public 公共的,子类可以调用这些方法解决问题。
package com.itheima.mextends;
public class ExtendsDemo1 {
public static void main(String[] args) {
Coder c = new Coder();
c.setName("张三");
c.setAge(23);
c.setSalary(12000);
System.out.println(c.getName() + "---" + c.getAge() + "---" + c.getSalary());
}
}
class Employee {
private String name;
private int age;
private double salary;
public void work() {
System.out.println("员工工作");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
class Coder extends Employee {
}
class Manager extends Employee {
}
使用场景
好处与弊端
-
好处:提高了代码的复用性,维护性
-
弊端:类和类之间的耦合性增强了
继承的学习路径
-
继承中成员变量的访问特点
思考:子父类中,如果出现了重名的成员变量,使用的时候会优先使用??
this:调用本类成员
super:调用父类成员
-
方法重写
思考:子类继承了父类之后,如果编写的方法结构和父类相同,但是方法内逻辑不相同,创建子类对象,调用方法的时候,执行的是父类方法逻辑,还是子类方法逻辑?
答案:子类方法逻辑,但这不是就近原则,是子类的方法,对父类方法进行了重写。
介绍
在继承体系中,子类可以继承到父类的方法
但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改
这就需要采用方法的重写,方法重写又称方法覆盖
要求
子类重写父类方法,需要保证方法声明完全一致(方法名,参数,返回值类型需要保持一致)
举例
package com.itheima.mextends;
public class ExtendsDemo3 {
/*
方法重写的使用场景:
子类继承了父类的方法, 但是子类不想原封不动的继承父类的方法逻辑
想要修改或者是增强, 就可以重写父类方法.
方法重载(Overload): 在同一个类中, 方法名相同, 参数不同, 与返回值无关
参数不同: 个数不同, 类型不同, 顺序不同.
方法重写(Override): 在子父类中, 出现了方法声明完全一致的方法
(方法名, 参数, 返回值都需要和父类保持一致)
*/
public static void main(String[] args) {
Son s = new Son();
s.love();
}
}
class Father {
void love() {
System.out.println("送花");
System.out.println("送自行车");
System.out.println("送冰箱");
}
}
class Son extends Father {
@Override // Override注解: 校验当前方法, 是否是重写的方法
public void love() {
super.love();
System.out.println("送口红");
System.out.println("送包");
}
}
使用 @Override 注解可以校验当前方法, 是否是重写的方法
重写方法的使用可以使用 Ctrl + O 的快捷键,查看父类中有哪些方法可以重写
注意事项
-
Java中继承的特点
Java只支持单继承,不支持多继承,但支持多层继承
-
问题:为什么不支持多继承?
-
回答:如果支持多继承,假设多个父类中有相同的方法结构,逻辑却不同,子类不知道该使用谁的逻辑
-
问题:为啥多层继承可以?
-
回答:多层继承的话,子类会知道使用重写后的方法逻辑
-
继承成员访问特点-构造方法
构造方法不能继承,子类需要手动编写自己的构造方法!
原因:构造方法要求方法名与类名相同,如果继承了父类的构造方法,就无法与类名相同了
问题:子类在初始化之前,是否需要先完成父类的初始化?
回答:需要!因为子类在初始化的时候,很有可能要用到父类中的数据,如果父类没有提前完成初始化,子类将无法使用父类数据
问题:子类是如何完成父类初始化的呢?
线索:什么方法是用于对象初始化的?
回答:构造方法,所以子类只要有办法调用到父类的构造方法,就能完成父类初始化
在所有的构造方法中,都默认隐藏了一句话 super();
通过这句代码,来访问父类的空参构造方法
super(); 可以调用父类的空参数构造方法
super(name, age); 我们可以传入参数,手动调用父类的带参数构造方法,从而完成数据的初始化
-
多态
同一个行为具有多个不同表现形式或形态的能力
这一概念有些晦涩,先不用纠结,理解:接口就是多态的一种
我们先掌握多态的使用前提,还有成员访问特点之后,再看这一概念
多态的前提条件
package com.itheima.polymorphism;
public class PolymorphismDemo1 {
/*
多态的前提:
1. 继承 | 实现关系
2. 方法重写
3. 父类引用指向子类对象
*/
public static void main(String[] args) {
// 子类引用, 指向子类对象
Zi z = new Zi();
System.out.println(z.num); // 20
z.show(); // Zi....show...
// 父类引用, 指向子类对象 (以多态的形式创建对象)
Fu f = new Zi();
System.out.println(f.num); // 10--成员变量看父类
f.show(); // Zi....show... --成员方法看子类
}
}
class Fu {
int num = 10;
public void show() {
System.out.println("Fu...show...");
}
}
// 继承关系
class Zi extends Fu {
int num = 20;
// 方法重写
@Override
public void show() {
System.out.println("Zi....show...");
}
}
多态的成员访问特点
了解即可,面试不会问
-
编译报错:Idea会提醒
-
运行报错:大家后面都会做测试,能看出具体的问题
public class PolymorphismDemo1 {
/*
成员变量: 编译看左边(父类), 运行看左边(父类)
因为是父类的引用, 所以访问存在局限性, 只能访问super空间中的数据.
*/
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num); // 10
}
}
class Fu {
int num = 10;
}
class Zi extends Fu {
int num = 20;
}
public class PolymorphismDemo1 {
/*
成员方法: 编译看左边(父类), 运行看右边(子类)
编译时检查方法在父类中是否存在
不存在: 编译出错
存在: 编译通过, 但运行的时候, 一定会执行子类的方法逻辑
*/
public static void main(String[] args) {
Fu f = new Zi();
f.show(); // Zi...show...
}
}
class Fu {
public void show() {
System.out.println("Fu...show...");
}
}
class Zi extends Fu {
public void show() {
System.out.println("Zi....show...");
}
}
执行子类方法逻辑,这种设计可以保证方法的调用是有意义的,因为父类中的方法有可能是抽象方法
package com.itheima.polymorphism;
public class PolymorphismDemo2 {
public static void main(String[] args) {
// 接口类型变量, 指向了实现类对象 (多态的形式创建对象)
Inter i = new InterImpl();
i.show();
}
}
interface Inter {
void show();
}
class InterImpl implements Inter {
@Override
public void show() {
System.out.println("实现类重写后的show方法...");
}
}
多态创建对象,调用静态成员需要单独注意
package com.itheima.polymorphism;
public class PolymorphismDemo1 {
/*
静态成员: 编译看左边(父类), 运行看左边(父类)
static修饰的成员, 推荐使用类名调用
f.show(); ---> 字节码文件中 ---> Fu.show();
*/
public static void main(String[] args) {
Fu f = new Zi();
f.show(); // Fu....show...
}
}
class Fu {
public static void show() {
System.out.println("Fu...show...");
}
}
class Zi extends Fu {
public static void show() {
System.out.println("Zi....show...");
}
}
-
多态的好处和弊端
好处
多态的好处:提高了程序的扩展性
abstract class Animal {
public abstract void eat();
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome() {
System.out.println("狗看家...");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void catchMouse() {
System.out.println("猫捉老鼠...");
}
}
多态分类两种
-
对象多态
将方法的形参定义为父类类型, 这个方法可以接收该父类的任意子类对象
Animal a = new Dog();
Animal a = new Cat();
-
行为多态
同一个行为, 具有多个不同表现形式或形态的能力
useAnimal方法中,调用 eat() 方法,因为接收到的对象不同
该方法的表现形式也不同
如果接收到 Cat 对象,输出的就是猫吃鱼
如果接收到 Dog 对象,输出的就是狗吃肉
弊端
多态的弊端 :不能直接使用子类的特有成员,想要使用的话,需要向下转型。
向上转型:从子到父(父类引用指向子类对象)
Fu f = new Zi();
向下转型:从父到子(将父类引用所指向的对象, 转交给子类类型)
Zi z = (Zi)f;

浙公网安备 33010602011771号