Java继承分支


在这里插入图片描述

🚀Java继承

🥝为什么需要继承?

Extends01.java中, 我们编写了两个类, 一个是Pupil(小学生), 一个是Graduate(研究生).
问题: 两个类的属性和方法有很多是相同的, 怎么办?

===> 继承(代码复用性)

//小学生->模拟小学生考试 的情况
public class Pupil {
    public String name;
    public int age;
    public double score;//成绩

    public void setScore(double score) {
        this.score = score;
    }

    public void testing() {
        System.out.println("小学生 " + name + " 正在考小学数学");
    }

    public void showInfo() {
        System.out.println("学生名字: " + name + " 年龄 " + age + " 成绩 " + score);
    }
}
//大学生-> 模拟大学生考试的简单情况
public class Graduate {
    public String name;
    public int age;
    public double score;//成绩

    public void setScore(double score) {
        this.score = score;
    }
    public void testing() {//和Pupil不一样
        System.out.println("大学生 " + name + " 正在考大学数学");
    }

    public void showInfo() {
        System.out.println("学生名字: " + name + " 年龄 " + age + " 成绩 " + score);
    }
}
public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "小明";
        pupil.age = 12;
        pupil.testing();
        pupil.setScore(60);
        pupil.showInfo();

        System.out.println("==================");
        Graduate graduate = new Graduate();
        graduate.name = "金角大王";
        graduate.age = 23;
        graduate.testing();
        graduate.setScore(60);
        graduate.showInfo();
    }
}

🥝原理示意图

继承可以解决代码复用, 让我们的编程更加靠近人类思维. 当多个类存在相同的属性(变量)和方法时, 可以从这些类中抽象出父类, 在父类中定义这些相同的属性和方法, 只需要通过extends来声明继承父类即可

· 继承的基本语法
class 子类 extends 父类 { }

1)子类就会拥有父类定义的属性和方法
2)父类又叫 超类, 基类
3)子类又叫派生类
在这里插入图片描述

🥝快速入门

我们对Extends01.java改进, 使用继承的方法, 体会使用继承的好处.

//父类, 是Pupil和Graduate的父类
public class Student {
    //共有的属性
    public String name;
    public int age;
    public double score;//成绩

    //共有的方法
    public void setScore(double score) {
        this.score = score;
    }
    public void showInfo() {
        System.out.println("学生名字: " + name + " 年龄 " + age + " 成绩 " + score);
    }
}
//让Pupil 继承 Student类
public class Pupil extends Student {
    public void testing() {
        System.out.println("小学生 "+ name +" 正在考小学数学");
    }
}
public class Graduate extends Student {
    public void testing() {//和Pupil不一样
        System.out.println("大学生 " + name + " 正在考大学数学");
    }
}
public class Extends01 {
    public static void main(String[] args) {
        com.zzw.extend_.Pupil pupil = new Pupil();
        pupil.name = "小明~";
        pupil.age = 13;
        pupil.testing();
        pupil.setScore(80);
        pupil.showInfo();

        System.out.println("==================");
        com.zzw.extend_.Graduate graduate = new Graduate();
        graduate.name = "金角大王~";
        graduate.age = 22;
        graduate.testing();
        graduate.setScore(70);
        graduate.showInfo();
    }
}

继承给编程带来的便利
1)代码的复用性提高了
2)代码的扩展性和维护性提高了

🥝注意事项和使用细节

1.子类继承了父类所有的属性和方法, 非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问, 要通过父类提供公共的方法去访问
在这里插入图片描述

public class Base { //父类
    //4个属性
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

    public Base() { //无参构造器
        System.out.println("Base()....");
    }

    //父类提供一个public的方法, 返回了n4
    protected int getN4() {
        return n4;
    }
    public void test100() {
        System.out.println("test100");
    }
    protected void test200() {
        System.out.println("test200");
    }
    void test300() {
        System.out.println("test300");
    }
    private void test400() {
        System.out.println("test400");
    }
    //call
    public void callTest400() {
        test400();
    }
}
public class Sub extends Base {
    public Sub() {
        System.out.println("Sub()...");
    }

    public void sayOk() { //子类方法
        //非私有的属性和方法可以在子类直接访问
        //但是私有的属性和方法不能在子类直接访问
        System.out.println(n1 + " " + n2 + " " + n3);
        test100();
        test200();
        test300();
        //test400();错误
        //要通过父类提供公共的方法去访问
        System.out.println("n4=" + getN4());
        callTest400();//
    }
}
public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.sayOk();
    }
}

2.子类必须调用父类的构造器, 完成父类的初始化

3.当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器. 如果父类没有提供无参构造器, 则必须在子类的构造器中用super去指定使用父类的哪个构造器以完成对父类的初始化工作. 否则, 编译不会通过.
案例一:

public class Base { //父类
    public Base() { //无参构造器
        System.out.println("父类Base()构造器被调用....");
    }
}
public class Sub extends Base {
    public Sub() {
        //super();//默认调用父类的无参构造器
        System.out.println("子类Sub()构造器被调用...");
    }

    //当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器
    public Sub(String name) {
        //do nothing
        System.out.println("子类Sub(String name)构造器被调用...");
    }
}
public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();//创建子类对象 sub
        System.out.println("=======第二个对象=======");
        Sub sub2 = new Sub("赵志伟");//创建子类对象 sub2
    }
}

输出结果

父类Base()构造器被调用....
子类Sub()构造器被调用...
=======第二个对象=======
父类Base()构造器被调用....
子类Sub(String name)构造器被调用..

案例二:

public class Base { //父类
    public Base(String name, int age) { //有参构造器
        System.out.println("父类Base(String name, int age)构造器被调用...");
    }
}
public class Sub extends Base {
    public Sub() {
        super("smith", 21);
        System.out.println("子类Sub()构造器被调用...");
    }

    //当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器
    public Sub(String name) {
        super("scott", 23);
        //do nothing
        System.out.println("子类Sub(String name)构造器被调用...");
    }
}

输出结果

=======第一个对象=======
父类Base(String name, int age)构造器被调用...
子类Sub()构造器被调用...
=======第二个对象=======
父类Base(String name, int age)构造器被调用...
子类Sub(String name)构造器被调用...

4.如果希望指定去调用父类的某个构造器, 则显示地调用一下: super(参数列表)

public class Base { //父类
    public Base() { //无参构造器
        System.out.println("父类Base()构造器被调用....");
    }
    public Base(String name, int age) { //有参构造器
        System.out.println("父类Base(String name, int age)构造器被调用...");
    }

    public Base(String name) {//有参构造器
        System.out.println("父类Base(String name)构造器被调用...");
    }
}
public class Sub extends Base {
    public Sub(String name, int age) {
        //1.调用父类的无参构造器, 如下; 或者什么都不写, 默认就是调用super()
        super();//父类的无参构造器
        //2.调用父类的Base(String name) 构造器
        super("scott");
        //3.调用父类的Base(String name, int age) 构造器
        super("scott", 23);
        
        System.out.println("子类Sub()构造器被调用...");
    }
}
@SuppressWarnings({"all"})
public class ExtendsDetail {
    public static void main(String[] args) {
        //System.out.println("=======第一个对象=======");
        //Sub sub = new Sub();//创建子类对象 sub
        //System.out.println("=======第二个对象=======");
        //Sub sub2 = new Sub("赵志伟");//创建子类对象 sub2
        System.out.println("=======第三个对象=======");
        Sub sub3 = new Sub("赵志伟", 23);//创建子类对象 sub3
    }
}

5.super在使用时, 必须放在构造器第一行 (super只能在构造器中使用)

6.super()和this()都只能放在构造器第一行, 因此这两个方法不能共存在一个构造器

7.java所有类都是Object类的子类, Object 是所有类的基类.
在这里插入图片描述
8.父类构造器的调用不限于直接父类. 将一直往上追溯到Object类(顶级父类)

public class TopBase {//父类是Object
    public TopBase() {
        //super(); Object的无参构造器
        System.out.println("构造器TopBase() 被调用...");
    }
}
public class Base extends TopBase{ //父类
    public Base(String name, int age) { //有参构造器
        //默认的super()
        System.out.println("父类Base(String name, int age)构造器被调用...");
    }
}
//输入ctrl+H, 可以看到类的继承关系
public class Sub extends Base {

    public Sub(String name, int age) {

        //调用父类的Base(String name, int age) 构造器
        super("scott", 23);

        System.out.println("子类Sub()构造器被调用...");
    }
}
public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub("赵志伟", 23);//创建子类对象 sub
    }
}

输出

构造器TopBase() 被调用...
父类Base(String name, int age)构造器被调用...
子类Sub()构造器被调用...

9.子类最多只能继承一个父类(指直接继承), 即java中是单继承机制.
思考: 如何让A类继承B类和C类?
答: 让A继承B, B继承C.

10.不能滥用继承, 子类和父类之间必须满足 is-a 的逻辑关系
Person is a Music?
Music extends Person //不合理

Cat is a Animal
Cat extends Animal //合理

🥝继承的本质分析

案例: 子类继承父类. 创建子类对象时, 内存中到底发生了什么?
在这里插入图片描述
讲解继承的本质

public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();//内存的布局
        //这时请注意, 要按照查找关系来返回信息
        //1.首先查看子类是否有该属性
        //2.如果子类有这个属性, 并且可以访问, 则返回信息
        //3.如果子类没有这个属性, 就看父类有没有这个属性(如果父类有该属性,并且可以访问, 则返回信息)
        //4.如果父类没有就按照3的规则, 继续找上级父类, 直到Object
        System.out.println(son.name);//返回的就是大头儿子
        //System.out.println(son.age);//返回地就是39
        System.out.println(son.getAge());//返回地就是39
        System.out.println(son.hobby);//返回的就是旅游
    }
}
class GrandPa {
    String name = "大头爷爷";
    String hobby = "旅游";
}
class Father extends GrandPa {//父类
    String name = "大头爸爸";
    private int age = 39;

    public int getAge() {
        return age;
    }
}
class Son extends Father {//子类
    String name = "大头儿子";
}

🥝课堂练习

案例1: main中: B b = new B(); 会输出什么?
分析: 有默认的super()

public class ExtendsExercise01 {
    public static void main(String[] args) {
        B b = new B();//a, b name, b
    }
}

class A {
    A() {
        System.out.println("a");
    }

    A(String name) {
        System.out.println("a name");
    }
}

class B extends A {
    B() {
        this("abc");
        System.out.println("b");
    }

    B(String name) {
        //super(); 默认有个super()
        System.out.println("b name");
    }
}

输出

a
b name
b

案例2: main中: C c = new C(); 会输出什么?

class A {
    public A() {
        System.out.println("我是A类");
    }
}

class B extends A {
    B() {
        System.out.println("我是B类的无参构造");
    }

    B(String name) {
    	//默认super()
        System.out.println(name + "我是B类的有参构造");
    }
}

class C extends B {
    C() {
        this("hello");
        System.out.println("我是C类的无参构造");
    }

    C(String name) {          
        super("haha");
        System.out.println("我是C类的有参构造");
    }
}

输出

我是A类
haha我是B类的有参构造
我是C类的有参构造
我是C类的无参构造

案例3: 编写Computer类, 包含CPU, 内存, 硬盘等属性, getDetails方法用于返回Computer的详细信息
编写PC类, 继承Computer类, 添加特有属性 品牌brand
编写NotePad子类, 继承Computer类, 添加特有属性 颜色color
编写ExtendsExercise03类, 在main方法中创建PC和NotePad对象, 分别给对象中特有的属性赋值, 以及从Computer类继承的属性赋值, 并使用方法打印输出信息

//编写Computer类, 包含CPU, 内存, 硬盘等属性, getDetails方法用于返回Computer的详细信息
public class Computer {
    private String cpu;
    private int memory;
    private String disk;

    public Computer(String cpu, int memory, String disk) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
    }

    //返回Computer详细信息
    public String getDetails() {
        return "电脑 cpu=" + cpu + " 内存=" + memory + " 硬盘=" + disk;
    }

	//getter, setter方法
}
//编写PC类, 继承Computer类, 添加特有属性 品牌brand
public class PC extends Computer {
    private String brand;

    //这里idea直接根据继承的规则, 自动地把构造器的调用写好
    // 这里也体现 继承设计的基本思想.
    // 父类的构造器完成父类属性初始化, 子类的构造器完成子类属性初始化
    public PC(String cpu, int memory, String disk, String brand) {
        super(cpu, memory, disk);
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public void printInfo() {
        System.out.println("PC信息: ");
        //System.out.println(getCpu() + getMemory() + getDisk());
        //调用父类的getDetails方法, 得到相关属性信息
        System.out.println(getDetails() + " 品牌=" + brand);
    }
}
//编写NotePad子类, 继承Computer类, 添加特有属性 颜色color
public class NotePad extends Computer {

    private String color;

    public NotePad(String cpu, int memory, String disk, String color) {
        super(cpu, memory, disk);
        this.color = color;
    }

    public void printInfo() {
        System.out.println("NotePad信息: ");
        System.out.println(getDetails() + " 颜色=" + color);
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
public class ExtendsExercise03 {
    public static void main(String[] args) {
        PC pc = new PC("intel", 1024, "500G", "戴尔");
        pc.printInfo();
        System.out.println("=================================");
        NotePad notePad = new NotePad("intel", 16, "1024G", "DELL");
        notePad.printInfo();
    }
}

🥝super基本语法

※基本介绍:

  1. super代表父类的引用, 用于访问父类的属性, 方法, 构造器

※基本语法

  1. 访问父类的属性, 但不能访问父类的private属性 super.属性名
  2. 访问父类的方法, 不能访问父类的private方法 super.方法名(参数列表)
  3. 访问父类的构造器, super(参数列表). 只能放在构造器的第一句, 只能出现一句
public class A {
    //4个属性
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

    public A() {}
    public A(String name) {}
    public A(String name, int age) {}

    public void test100() {
        System.out.println("test100");
    }
    protected void test200() {
        System.out.println("test200");
    }
    void test300() {
        System.out.println("test300");
    }
    private void test400() {
        System.out.println("test400");
    }
}
public class B extends A {

    //访问父类的属性, 但不能访问父类的private属性 super.属性名
    public void hi() {
        System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
    }

    //访问父类的方法, 不能访问父类的private方法 super.方法名(参数列表)
    public void ok() {
        super.test100();
        super.test200();
        super.test300();
    }

    B() {
        //super();
        //super("tom");
        super("tom", 23);
    }
}

🥝super细节

※super给编程带来的便利/好处

  1. 调用父类构造器的好处 (分工明确, 父类属性由父类初始化, 子类属性由子类初始化)
  2. 当子类中有和父类中的成员(属性和方法)重名时, 为了访问父类的成员, 必须通过super. 如果没有重名, 使用super, this和 直接访问是一样的效果.
public class A {
    
    public int n1 = 100;

    public void cal() {
        System.out.println("A类的cal()方法...");
    }
}
public class B extends A {

    public int n1 = 333;

    public void sum() {
        System.out.println("B类的sum()方法...");
        //希望调用父类A的cal方法
        //这时, 因为子类B没有cal方法, 因此我们可以使用下面三种方式

        //找cal方法时(cal() 和 this.cal()), 顺序是:
        // (1)先找本类, 如果有, 则调用.
        // (2)如果没有, 则找父类(如果有, 并且可以调用, 则调用)
        // (3)如果父类没有, 则继续找父类的父类. 整个规则, 都是一样的, 直到Object类.
        // 提示: 如果在查找方法的过程中, 找到了, 但不能访问, 则报错, cannot access
        //      如果查找方法的过程中, 没有找到, 则提示该方法不存在, cannot resolve
        //cal();
        //this.cal();//等价 cal()

        //找cal方法(super.cal())的顺序是直接查找父类, 其它的规则一样
        super.cal();

        //演示访问属性的规则
        //n1 和 this.n1 查找的规则是
        //(1)先找本类, 如果有, 则调用.
        //(2)如果没有, 则找父类(如果有, 并且可以调用, 则调用)
        //(3)如果父类没有, 则继续找父类的父类. 整个规则, 都是一样的, 直到Object类.
        // 提示: 如果在查找属性的过程中, 找到了, 但不能访问, 则报错, cannot access
        //      如果查找属性的过程中, 没有找到, 则提示该属性不存在, cannot resolve
        System.out.println(n1);//333
        System.out.println(this.n1);//333

        //找n1(super.n1)的顺序是直接查找父类属性, 其它的规则一样
        System.out.println(super.n1);//100
    }
}
public class Super01 {
    public static void main(String[] args) {
        B b = new B();
        b.sum();
    }
}
  1. super的访问不限于直接父类, 如果爷爷类和本类中有同名的成员, 也可以使用super去访问爷爷类的成员; 如果多个基类(上级类)中都有同名的成员, 使用super访问遵循就近原则. A->B->C, 当然也需要遵守访问权限的相关规则
public class Base { //父类是Object

    public int n1 = 999;

    public void cal() {
        System.out.println("Base类的cal()方法...");
    }
}
public class A extends Base {

    //public int n1 = 100;

    //public void cal() {
    //    System.out.println("A类的cal()方法...");
    //}
}
public class B extends A {
    //编写测试方法
    public void test() {
        //super的访问不限于直接父类, 如果爷爷类和本类中有同名的成员, 也可以使用super去访问爷爷类的成员;
        // 如果多个基类(上级类)中都有同名的成员, 使用super访问遵循就近原则. A->B->C
        System.out.println("super.n1=" + super.n1);
        super.cal();
    }
}
public class Super01 {
    public static void main(String[] args) {
        B b = new B();
        b.test();
    }
}

🥝super和this比较

No.区别点thissuper
1访问属性访问本类中的属性, 如果本类没有此属性, 则从父类中继续查找从父类开始查找属性
2调用方法访问本类中的方法, 如果本类没有此方法, 则从父类继续查找从父类开始查找方法
3调用构造器调用本类构造器, 必须放在构造器的首行调用父类构造器, 必须放在子类构造器的首行
4特殊表示当前对象子类中访问父类对象

🚀方法重写 / 覆盖

🥝基本介绍

简单地说: 方法覆盖(重写) 就是子类有一个方法, 和父类的某个方法的名称, 返回类型, 参数一样, 那么我们就说子类的这个方法覆盖了父类的方法

public class Animal {
    public void cry() {
        System.out.println("动物发出声音...");
    }
}
public class Dog extends Animal{
    //解读
    //1. 因为Dog 是 Animal子类
    //2. Dog的 cry方法 和 Animal的 cry方法 形式一样
    //3. 这时我们就说 Dog的cry方法, 重写了Animal的cry方法
    public void cry() {
        System.out.println("小狗汪汪叫...");
    }
}
public class Override01 {
    public static void main(String[] args) {
        //演示方法重写的情况
        Dog dog = new Dog();
        dog.cry();//ctrl+b
    }
}

🥝注意事项和细节

方法重写也叫方法覆盖, 需要满足下面的条件

  1. 子类方法的形参列表, 方法名称要和父类方法的形参列表, 方法名称完全一样.
  2. 子类方法的返回类型要和父类方法返回类型一样, 或者是父类返回类型的子类. 比如, 父类 返回类型是 Object, 子类方法返回类型是String
public class Animal {
    
    public Object m1() {
        return null;
    }

    public String m2() {
        return "";
    }

    public AAA m3() {
        return null;
    }
}
public class Dog extends Animal{

    //细节: 子类方法的返回类型要和父类方法返回类型一样, 或者是父类返回类型的子类.
    //     比如, 父类 返回类型是 Object, 子类方法返回类型是String
    public String m1() {
        return "";
    }

    //这里Object不是String的子类, 所以编译错误
    //public Object m2() {
    //    return null;
    //}

    public BBB m3() {
        return null;
    }
}
class AAA {}
class BBB extends AAA {}
  1. 子类不能缩小父类方法的访问权限. public > protected > 默认 > private
public class Animal {
    protected void eat() { }
}
public class Dog extends Animal{
    //细节: 子类方法不能缩小父类方法的访问权限
    // public > protected > 默认 > private
    public void eat() { }
}

🥝课堂练习

题1: 请对方法重写和方法重载做一个比较

名称发生范围方法名形参列表返回类型修饰符
重载(overload )本类必须一样类型, 个数或者是顺序至少有一个不同没有要求没有要求
重写(override)父子类必须一样相同子类重写的方法, 返回的类型和父类返回的类型一致, 或者是其子类子类方法不能缩小父类方法的访问范围

提2:
1.编写一个Person类, 包括属性/private (name, age), 构造器, 方法say(返回自我介绍的字符串)
2.编写一个Student类, 继承Person类, 增加属性/private (id, score), 以及构造器, 定义say方法(返回自我介绍的信息)
3.在main中, 分别创建Person类和Student对象, 调用say方法输出自我介绍.

//编写一个Person类, 包括属性/private (name, age), 构造器, 方法say(返回自我介绍的字符串)
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String say() {
        return "名字=" + name + " 年龄=" + age;
    }
}
//编写一个Student类, 继承Person类, 增加属性/private (id, score), 以及构造器, 定义**say**方法(返回自我介绍的信息)
public class Student extends Person {

    private int id;
    private double score;

    public Student(String name, int age, int id, double score) {
        super(name, age);
        this.id = id;
        this.score = score;
    }

    public String say() {// 这里体现super的一个好处. 代码复用
        return "学生" + super.say() + " id=" + id + " score=" + score;
    }
}
public class OverrideExercise {
    public static void main(String[] args) {
        Person person = new Person("李四", 27);
        System.out.println(person.say());
        System.out.println("===========================");
        Student student = new Student("张三", 23, 1, 89);
        System.out.println(student.say());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~ 小团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值