目录
在之前类和对象的博客中我介绍了封装,今天我来介绍一下继承和多态
继承
在了解继承前我们先看一段代码
class Cat {
String name;
int age;
String Sex;
public void eat() {
System.out.println(this.name+"eat");
}
}
class Dog {
String name;
int age;
String Sex;
public void eat() {
System.out.println(this.name+"eat");
}
}
我们定义了两个类,但是类的变量和方法有着很多共性的地方,我们能不能将共性的代码提取出来呢,答案是肯定的
class Animal {
String name;
int age;
String Sex;
public void eat() {
System.out.println(this.name+"eat");
}
}
class Cat extends Animal {
public void mew() {
System.out.println(this.name+"cat()::mew");
}
}
class Dog extends Animal {
public void bark() {
System.out.println(this.name+"dog()::bark");
}
}
继承的概念
继承就是对共性的抽取从而达到代码的复用
继承机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,继承主要解决的问题是:共性的抽取,实现代码复用
继承的语法
在Java中如果要表示类之间的继承关系,需要借助extends关键字
修饰符 class 子类 extends 父类 {
}
注意:
1.子类会将父类中的成员变量或者成员方法继承到子类中了
2.子类继承父类之后,要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了3继承是 is-a 的关系
父类成员变量访问
子类和父类成员变量不同名
class Base {
public int a=1;
public int b=2;
}
class Derived extends Base {
public int c=3;
public int d=4;
public void test() {
System.out.println(a);//1
System.out.println(b);//2
System.out.println(c);//3
System.out.println(d);//4
}
}
子类和父类成员变量同名
class Base {
public int a=1;
public int b=2;
}
class Derived extends Base {
public int a=3;
public int d=4;
public void test() {
System.out.println(a);//3
System.out.println(b);//2
System.out.println(d);//4
}
}
可以看到子类成员方法在访问同名成员变量时优先子类成员变量,那如果想访问父类成员变量要如何修改呢,答案是要使用super关键字
总结:
如果访问的成员变量子类中有,优先访问自己的成员变量
如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错
如果访问的成员变量与父类中成员变量同名,则优先访问自己的,即:子类将父类同名成员隐藏了
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找
super:准确来说就是一个普通的关键字,让程序或人能够知道,易读
子类中访问父类中的成员方法
总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错
此时子类test与父类test方法构成重载,所以在有继承关系的类中依旧可以重载
总结:
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错
通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法的原型一致(重写),则只能访问到子类的,父类的无法通过派生类对象直接访问到
super关键字
super.data
super.func()
注意
1.只能在非静态方法中使用
2. 在子类方法中,访问父类的成员变量和方法
super()
要了解super()就要先了解子类构造方法
父子父子,先有父再有子,即:子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(this.name+"正在吃");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Cat extends Animal {
public String hair;
public void mew() {
System.out.println(this.name+"正在叫");
}
public Cat(String name,int age,String hair) {
super(name,age);//显示调用父类构造方法,来初始化子类从父类继承过来的东西
this.hair=hair;
}
}
public class Test {
public static void main(String[] args) {
Cat cat1=new Cat("家家",12,"白色");
cat1.eat();
cat1.mew();
}
}
使用super()先执行父类构造在执行子类构造,若父类无参或默认构造编译器默认提供隐式的super()调用
注意:
1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
2. 如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败
3. 在子类构造方法中,super()调用父类构造时,必须是子类构造函数中第一条语句
4. super()只能在子类构造方法中出现一次,并且不能和this()同时出现
super与this对比
super与this有很多相同点但区别还是很明显的,我来总结一下
相同点
1. 都是Java中的关键字
2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在,要注意是this()与super()不能同时存在
不同点
1.this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3.this是非静态成员方法的一个隐藏参数,super不是隐藏的参数
4.在构造方法中:this()用于调用本类构造方法,super()用于调用父类构造方法,两种调用不能同时在构造方法中出现
5.构造方法中一定会存在super()的调用,用户没有写编译器也会增加,但是this()用户不写则没有
初始化顺序
总结:
1. 静态代码块先执行,并且只执行一次,在类加载阶段执行
2. 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
3.父类静态代码块优先于子类静态代码块执行,且是最早执行
4.父类实例代码块和父类构造方法紧接着执行
5.子类的实例代码块和子类构造方法紧接着再执行
6.第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
protected关键字
protected 继承权限 可以被不同包中的子类访问到
注意:父类必须是public修饰的
继承方式
Java支持:单继承,多层继承,不同类继承同一个类
在Java中不支持多继承
如果我们不想让类被其他类继承时我们可以使用final来修饰类
被finl修饰的类也叫密封类
final修饰变量使其成为常量
final修饰方法使其不会被重写
继承与组合
继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车
class Student {
}
class Teacher {
}
class School {
public Student[] student;
public Teacher[] teacher;
}
将其他类的类型作为类的成员称为组合,一般建议使用组合
多态
通俗来说就是多种形态,具体就是去完成某个行为,不同对象去完成产生不同状态,要想了解多态就要了解向上转型和方法重写
向上转型
向上转型就是父类引用引用子类对象,我来举个例子
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(this.name+"吃饭");
}
}
class Cat extends Animal {
public String hair;
public void mew() {
System.out.println(this.name+"正在叫");
}
}
public class testDemo3 {
public static void main(String[] args) {
Animal animal =new Cat();//向上转型 父类引用引用子类对象
animal.eat();
}
}
三种常见向上转型
1.直接赋值
Animal animal =new Cat();
2.方法的参数
public static void func(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Cat cat=new Cat();
func(cat);//func(new cat());
}
3.方法的返回值
public static Animal func2() {
//return new Animal();
return new Cat();
}
向上转型的优点:让代码实现更简单灵活
向上转型的缺陷:不能调用到子类特有的方法
重写
重写也称为覆盖,覆写,是实现多态的重要条件,返回值和形参都不能改变即外壳不变,核心重写
看一下结果
可以看到打印的并不是父类方法内容,而是子类方法内容这就涉及到方法的重写了
重写方法的特点
1.方法名称相同
2.方法放回值相同
3.方法的参数列表相同
此时产生了动态绑定也是多态的基础
可以看到在编译的时候还是Animal.eat(),但是在运行的时候变为了子类自己的eat(),在运行时才确定了要调用谁,我们将它称为运行时绑定
静态绑定:在编译时就确定的方法,比如 重载
理解多态
有了上述知识的掌握我们就可以理解多态了
多态的前提条件
1.必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法
重写总结
1.静态方法不能重写
2.private修饰的方法不能重写
3.子类的访问修饰限定符要大于父类的
4.被重写的方法返回值类型可以不同,但是必须是具有父子关系的
协变类型 返回值构成父子关系
5.被final修饰的不能重写
向下转型
public static void main(String[] args) {
Animal animal = new Bird();
Bird bird=(Bird)animal;//向下转型
bird.fly();
}
父类引用赋值给子类引用叫向上转型,但是向下转型不安全举个例子
public static void main(String[] args) {
Animal animal = new Cat();
Bird bird=(Bird)animal;//向下转型
bird.fly();
}
此时代码会报错,如何修改就需要使用instanceof来判断
public static void main(String[] args) {
Animal animal = new Cat();
//判断animal这个引用的对象是否是Bird的对象
if(animal instanceof Bird) {
Bird bird=(Bird)animal;//向下转型
bird.fly();
}
}
多态的优缺点
优点
1.能够降低代码的 "圈复杂度", 避免使用大量的 if - else
圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂
2.可扩展能力更强
使用多态修改代码成本降低
缺点
代码的运行效率降低
避免在构造方法中调用重写的方法
看一段代码
我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func此时是执行B的func(),还是执行D的func()就不容易看出来,看下结果
为什么呢?
构造 D 对象的同时, 会调用 B 的构造方法
B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func()
此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0
所以不要在构造方法中调用重写的方法避免写出错误的代码
以上就是有关Java中继承+多态的总结,希望对你有帮助还请点赞评论会持续更新的!Po累Po累