🎼 继承
面向对象三大特征之一
一个类的对象可以使用另一个类的对象中的成员,这种关系就称为继承
继承的格式
class A {
}
class B extends A {
}
类B 继承 类A
类A:父类,超类,基类
类B:子类,派生类
继承的好处
- 提高了代码的复用性
- 让类与类之间产生了继承关系,这是构成多态的前提条件
继承的弊端
提高了代码的耦合性
开发原则:高内聚,低耦合
高内聚:是指一个人的能力越强越好
低耦合:是指一个人的关系越少越好
父类出现变化,子类也会发生改变
继承的特点
在Java中,类只支持单继承,不可以多继承,可以多层继承,不可以循环继承。
单继承:
class Fu {
}
class Zi extends Fu {
}
多层继承:
class Ye {
}
class Fu extends Ye {
}
class Zi extends Fu {
}
循环继承:
class Fu extends Zi {
}
class Zi extends Fu {
}
继承的注意事项
子类不能继承父类中被private关键字所修饰的成员变量和成员方法
class Fu {
private String name;
int age;
public void eat() {
}
private void drink() {
}
}
class Zi extends Fu {
public void print() {
System.out.println(name);//编译报错,因为父类的name被私有了
System.out.println(age);
eat();
drink();//编译报错,因为父了drink()被私有了
}
}
子类不能继承父类中的构造方法
什么时候使用继承
当两个类之间存在”is a”的关系的时候,我们就可以使用继承
举例子:
人类
学生类
老师类
狗类
人类 is a 学生类 XXX 所以人类不能继承学生类
人类 is a 老师类 XXX 所以人类不能继承老师类
人类 is a 狗类 XXX 所以人类不能继承狗类
学生类 is a 人类 所以学生类可以继承人类
学生类 is a 老师类 XXX 所以学生类不能继承老师类
学生类 is a 狗类 XXX 所以学生类不能继承狗类
老师类 is a 人类 所以老师类可以继承人类
老师类 is a 学生类 XXX 所以老师类不能继承学生类
老师类 is a 狗类 XXX 所以老师类不可以继承狗类
class Person {}
class Student extends Person {}
class Teacher extends Person {}
class Dog {}
注意:我们不能单单为了获取某个功能而瞎去继承一个类
class Bird {
public void fly() {
}
}
class Person extends Bird {
/*
public void fly() {
}
*/
}
class Demo {
public static void main(String[] args) {
Person p = new Person();
p.fly();
}
}
在子父类继承中成员变量的关系
假如在父类中有一个成员变量,在子类中有一个成员变量,在子类的成员方法中有一个局部变量
如果这三个变量的名字都不同的话:
class Fu {
int i = 1;
}
class Zi extends Fu {
int j = 2;
public void print() {
int a = 3;
System.out.println(a);//3
System.out.println(j);//2
System.out.println(i);//1
//可以一起共存
}
}
class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.print();
}
}
如果这三个变量的名都相同的话:
class Fu {
int i = 1;
}
class Zi extends Fu {
int i = 2;
public void print() {
int i = 3;
System.out.println(i);//3
System.out.println(i);//3 就近原则
System.out.println(i);//3
}
}
class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.print();
}
}
那如果想要分别打印出1,2,3该怎么办?
class Fu {
int i = 1;
}
class Zi extends Fu {
int i = 2;
public void print() {
int i = 3;
System.out.println(i);//3
System.out.println(this.i);//2
System.out.println(super.i);//1
}
}
class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.print();
}
}
在子父类继承中构造方法的关系
子类在中的所有的构造方法的第一行,默认都有一个super() ```java class Fu { String name;
public Fu() {
System.out.pintln("父类的构造方法被调用了");
} }
class Zi extends Fu { public Zi() { //super();//默认调用父类的空参构造 }
public Zi(String name) {
//super();//调用父类的空参构造
}
public Zi(int age) {
//super();//调用父类的空参构造
}
public Zi(String name, int age) {
//super();//调用父类的空参构造
}
}
class Demo { public static void main(String[] args) { Zi zi = new Zi(); System.out.pintln(zi.name);//null 调用了父类的构造方法 } }
为什么会有这样的现象呢?<br />因为有继承,说明子类可以使用父类的成员变量,那也就意味着父类的成员变量一定要有一直才可以被使用,所以在创建类对象的时候,会调用子类的构造方法,而子类的构造方法中会先去调用父类的构造方法,目的就是给父类的额成员变量默认初始化
2. **子类中的所有的构造方法中,第一行要么是super(参数),要么是this(参数),他们不可以共存**
```java
class Fu {
}
class Zi extends Fu {
public Zi() {
this("");//调用了下面的有参构造方法 ,this调用本类
}
public Zi(String name) {
//super(); //没写this默认就是super
}
}
- 子类中所有的构造方法中必须要有至少有一个构造方法里面调用的是super(参数) ```java class Fu { //5 }
class Zi extends Fu { public Zi() { this(“aaa”);//1 }
public Zi(String name) {
this(18);//2
}
public Zi(int age) {
this("aaa", 18);//3
}
public Zi(String name, int age) {
//4
//this("");会重新调用会1,所以要有
super();//要防止进入死循环
}
}
class Demo { public static void main(String[] args) { Zi zi = new Zi(); } }
<a name="DoZtQ"></a>
## 在子父类继承中成员方法的关系
在父类中有一个成员方法,在子类中有一个成员方法
- 如果这两个方法的名字都不同的话:
```java
class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.printFu();//fu
zi.printZi();//zi
}
}
class Fu {
public void printFu() {
System.out.println("fu...");
}
}
class Zi extends Fu {
public void printZi() {
System.out.println("zi...");
}
}
- 如果这两个方法的名字都相同的话:
```java
class Demo {
public static void main(String[] args) {
} }Zi zi = new Zi();
zi.print();//zi
zi.print();//zi
class Fu { public void print() { System.out.println(“fu…”); } }
class Zi extends Fu { public void print() { System.out.println(“zi…”); } } 通过观察,如果两个方法的名字相同的话,结果打印出来的子类中的内容
这里面涉及到一个知识点,叫做方法的重写,也叫作覆盖
<a name="FpasW"></a>
# 🎤 super关键字
> super是父类内存空间的标记,在用法上,我们可以当做父类对象的引用来使用,但是我们不能说super就是父类对象的引用
<a name="NEcWJ"></a>
## 调用构造方法
```java
super(参数);
调用父类的构造方法
this(参数);
调用本类的构造方法
调用成员方法
super.方法名();
调用父类中的成员方法
this.方法名();
调用本类中的成员方法
调用成员变量
super.变量名;
调用父类中的成员变量
this.变量名;
调用本类中的成员变量
🏇 方法的重写(Override)
在子父类继承中,子类中出现与父类方法的声明完全一致的方法,就称为方法的重写(不完全正确)
方法的重写(Overide)和方法的重载(Overload)的区别
- 重载:在同一个类,两个方法的名字相同,参数列表不同
- 重写:在子父类继承中,子类中的方法和父类中方法名是完全一致,参数列表也一致
我们怎么证明该方法是重写方法呢?
只需要在方法的声明上面加上@Override(判断是否方法重写),满足是不报红的,
重写的注意事项
- 子类不能重写父类中的私有(private)的成员方法
- 子类的方法的访问权限修饰符要大于等于父类的方法的访问权限修饰符
public > protected > 默认 > private
class Fu {
protected void print() {
}
}
class Zi extends Fu {
public void print() {
}
}
子类的方法的返回值类型可以和父类重写方法的返回值类不一致的,但是有条件,父类方法的返回值类型是子类方法的返回值类型的父类 ```java class Fu { public Person print() {
} }
class Zi extends Fu { public Student print() {
}
}
```java
class Fu {
public Animal print() {
return new Animal();
}
}
class Zi extends Fu {
@Override
public Dog print() {
return new Dog();
}
}
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
- 静态方法只能重写静态方法
只要和静态相关的,三大特征都不会适用,因为静态超出了面向对象的范围
class Fu{
public static void show() { //
}
}
class Zi extends Fu {
@Override
public static void show(){ //
}
}
🤼♀️ 多态
面向对象三大特征之一
是指一个对象在不同的时刻,表现出来的不同的状态
比如说:
一个水滴
液态,气态,固态
多态的前提条件
- 要有继承关系
- 要有方法的重写(可以没有,但没意义)
- 要有父类引用指向子类对象
多态的代码体现
class Fu {
public void print() {
}
}
class Zi extends Fu {//有继承关系
public void print(){//有方法重写
}
}
class Demo {
public static void main(String[] args) {
Fu fu = new Zi();//父类引用指向子类对象
}
}
多态间子父类之间成员变量的关系
假如父类中有一个成员变量,子类中有一个成员变量
- 如果这两个变量的名字不同的话: ```java class Fu { int i = 1; }
class Zi extends Fu { int j = 2; }
class Demo { public static void main(String[] args) { Fu fu = new Zi(); System.out.println(fu.i);//1 System.out.println(fu.j);//编译报错 } }
**通过结果分析,父类引用不能使用子类中特有的成员变量**![image.png](https://cdn.nlark.com/yuque/0/2022/png/21490994/1647686154762-88d340ba-6797-4ae3-bd4f-1cf226161e8c.png#clientId=u64418b52-a230-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=629&id=ubc3b7de4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1258&originWidth=2800&originalType=binary&ratio=1&rotation=0&showTitle=false&size=832311&status=done&style=none&taskId=u878746f9-1dd1-4de8-a333-dbe38b3cff5&title=&width=1400)
- 如果这两个方法的名字都相同的话
```java
class Fu {
int i = 1;
}
class Zi extends Fu {
int i = 2;
}
class Demo {
public stati c void main(String[] args) {
Fu fu = new Zi();
System.out.println(fu.i);//1
System.out.println(fu.i);//1
}
}
通过结果分析,父类引用只能使用自身带的成员变量,不能使用子类中成员变量
口诀: 多态的前提下,调用成员变量的时候,编译看左边,运行看左边
多态间子父类之间成员方法的关系
父类中有一个成员方法,子类中有一个成员方法
- 如果这两个方法的名字都不同的话:
```java
class Fu {
public void printFu() {
} }System.out.println("fu");
class Zi extends Fu {
//继承父类的成员方法
public void printZi() {
System.out.println(“zi”);
}
}
class Demo { public static void main(String[] args) { Fu fu = new Zi(); fu.printFu();//fu fu.printZi();//编译报错 } }
**通过结果分析,父类引用不能使用子类中特有的成员方法**
- 如果这两个方法的名字都相同的话:
```java
class Demo {
public static void main(String[] args) {
Fu fu = new Zi();
fu.print();//zi
fu.print();//zi
}
}
class Fu {
public void print() {
System.out.println("fu");
}
}
class Zi extends Fu {
public void print() { //方法重写了
System.out.println("zi");
}
}
通过结果分析,父类引用可以使用子类中重写父类的方法
口诀:多态的前提下,调用成员方法,编译看左边,运行看右边
多态间子父类在之间静态成员的关系
class Fu {
static int i = 1;
}
class Zi extends Fu {
static int j = 2;
}
class Demo {
public static void main(String[] args) {
Fu fu = new Zi();
System.out.println(fu.i);
System.out.println(fu.j);
}
}
-----------------------------------------------
class Fu {
public static void print() {
System.out.println("fu");
}
}
class Zi extends Fu {
public static void print() {
System.out.println("zi");
}
}
class Demo {
public static void main(String[] args) {
Fu fu = new Zi();
fu.print();//fu
}
}
口诀:多态的前提下,静态的 成员变量和成员方法,编译看左边,运行看看左边
多态的好处
提高了代码的扩展性
父类引用作为形参,子类对象作为实参
public class Demo02 {
public static void main(String[] args) {
AnimalTool.print(new Cat());
AnimalTool.print(new Dog());
AnimalTool.print(new Pig());
AnimalTool.print(new Bird());
AnimalTool.print(new Sheep());
}
}
//动物工具类
class AnimalTool {
public static void print(Animal animal) {//Animal animal = new Cat()
animal.eat();
}
}
class Animal {
public void eat() {
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Pig extends Animal {
public void eat() {
System.out.println("吃白菜");
}
}
class Bird extends Animal {
public void eat() {
System.out.println("吃虫子");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("吃草");
}
}
例子:
public class Demo02 {
public static void main(String[] args) {
public static void print(Cat cat) { //Cat cat = new Cat()
cat.eat();
}
public static void print(Dog dog) {
dog.eat();
}
public static void print(Pig pig) {
pig.eat();
}
}
}
//动物工具类
class AnimalTool {
public static void print(Cat cat) {
cat.eat();
}
public static void print(Dog dog) {
dog.eat();
}
public static void print(Pig pig) {
pig.eat();
}
}
class Dog{
public void eat() {
System.out.println("吃骨头");
}
}
class Cat{
public void eat() {
System.out.println("吃鱼");
}
}
class Pig{
public void eat() {
System.out.println("吃白菜");
}
}
class Bird{
public void eat() {
System.out.println("吃虫子");
}
}
class Sheep{
public void eat() {
System.out.println("吃草");
}
}
多态的弊端
父类引用不能使用子类中特有的内容
怎么解决?
向下转型来解决这个问题
向上转型:Fu fu = new Zi();
向下转型:Zi zi = (Zi)fu;
class demo {
public static void main(String[] args) {
Person p = new Student();//向上转型
p.eat();//吃肉
//向下转型
Student s = (Student)p;
s.play();
s.eat();
/*Teacher t = (Teacher)p;
t.eat();
t.teach();*/
}
}
class Person {
public void eat() {
System.out. println("吃饭");
}
}
class Student extends Person {
public void eat() {
System.out.println("吃肉");
}
public void play() {
System.out.println("玩游戏");
}
}
class Teacher extends Person {
public void eat() {
System.out.println("吃肉");
}
public void teach() {
System.out.println("教书");
}
}
注意:向下转型转不好,容易出现异常,ClassCastException类型转换异常,是在运行的时候报错
动物 a = new 狗();
狗 d = (狗)a; //正确的向下转型
猫 m = (猫)a; //错误的,狗的堆空间怎么给猫用