11Java基础之继承

继承

  • java中提供了一个extends的关键字,意思是“拓展”。用这个关键字可以让一个类和另一个类建立父子关系。

继承的特点

  • 子类能直接继承父类的非私有成员(成员变量,成员方法)

  • JAVA中类只有单继承,没有多继承。一个儿子只能有一个爸爸,一个爸爸可以有多个儿子。

  • 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。

  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extend来表示。

继承后对象的创建

  • 子类的对象是由子类和父类 以及多张设计图一起创建的。
  • 对象能直接访问什么成员,是由父类这多张设计图共同决定的,多张设计图在外暴露了什么成员,对象就可以访问什么成员。

注意

  • 子类继承父类,就会拥有父类的全部方法。
  • 父类中私有的东西,子类无法继承。
  • 在IDEA工具中,父类或者继承的子类,在类名上按Ctrl+H快捷键可以看到类的继承关系。
  父类:
  public class A {
    public int i;

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

    private int j;

    private void print1(){
        System.out.println("我是A类的私有方法");
    }
}

子类:
public class B extends A{
    private int k;

    public void print2(){
        // 继承特点:子类只能继承父类的非私有成员
        System.out.println(i);
        print();

        // 继承特点:子类不能继承父类的私有成员
//        System.out.println(j); // 报错
//        print1();              // 报错
    }
}

main方法:
public static void main(String[] args) {
        // 子类对象的创建特点:会由子类和父类等多张设计图共同创建出子类对象,但是能访问什么看权限。
        B b = new B();

        System.out.println(b.i);
//        System.out.println(b.j); // 报错
//        System.out.println(b.k); // 报错

        b.print();
//        b.print1(); // 报错
        b.print2();
    }
}
  • 继承的好处就是可以减少重复代码的编写
    案例
父类:
public class People {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

子类:
public class Teacher extends People{
    private String skill;

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }
}

main方法:
public static void main(String[] args) {
        Teacher t = new Teacher();

        t.setName("波妞");
        t.setSkill("程序员鼓励师,哄程序员加班~");
        System.out.println(t.getName());
        System.out.println(t.getSkill());
    }

输出结果:
波妞
程序员鼓励师,哄程序员加班~
  • teacher类继承了people类,所以它可以调用父类中的方法,也可以调用自身的方法。

继承的相关注意事项

  1. 权限修饰符
  • 权限修饰符,是用来限制类中的成员(成员变量、成员方法、构造器、代码块……)能够被访问的范围。
  • 权限修饰符有如下几种:
    • private:只能本类
    • 缺省:本类、同一个包中的类
    • protected:本类、同一个包中的类、子孙类中
    • public:任意位置

案例

父类:
public class Father {
    // 1. private,私有的,只能在本类中访问
    private void privateMethod() {
        System.out.println("private");
    }

    // 2. 缺省,只能在本类,同一包下的其他类中访问
    void defaultMethod() {
        System.out.println("default:缺省");
    }

    // 3. prodected,子类权限,只能在本类,同一个包下的类,子孙类中访问
    protected void protectedMedhod() {
        System.out.println("protected");
    }

    // 4. public,公共权限,可以在任意类中访问
    public void publicMedhod() {
        System.out.println("public");
    }

    public static void main(String[] args) {
        Father f = new Father();
        f.privateMethod();
        f.defaultMethod();
        f.protectedMedhod();
        f.publicMedhod();
    }
}

**同一个类中,所有的修饰的方法都可以调用。**

同一个包,不同的类调用
public class Demo01 {
    public static void main(String[] args) {
        Father f = new Father();
        //f.privateMethod(); // 报错
        f.defaultMethod();
        f.protectedMedhod();
        f.publicMedhod();
    }
}
**除了私有修饰的方法不能调用,剩下的都可以调用。**
-------------------------------------------------
不同包的类
public class Demo02 {
    public static void main(String[] args) {
        Father f = new Father();
//        f.privateMethod();  // 报错
//        f.defaultMethod();  // 报错
//        f.protectedMedhod();// 报错
        f.publicMedhod();
    }
}
**除了公共修饰的方法,别的修饰的方法都不能调用。**
-----------------------------------------------
不同包的子类
public class Son extends Father {
    public void print(){
//        privateMethod();  // 报错
//        defaultMethod();  // 报错
        protectedMedhod();
        publicMedhod();
    }

    public static void main(String[] args) {
        Son s = new Son();
//        s.privateMethod();  // 报错
//        s.defaultMethod();  // 报错
        s.protectedMedhod();
        s.publicMedhod();
    }
}
**除了私有和缺省修饰的方法,别的都可以调用。**

单继承

  • Java是单继承的,Java中的类不支持多继承,但是支持多层继承。

案例:

// 目标:搞清楚继承的特点
public class Test {
    public static void main(String[] args) {

    }
}

//1. Java是单继承的,一个类只能有一个父类
//2. Java不支持多继承
class A{ }
class B{ }
//class C extends A,B{} //报错

//3. Java支持多层继承
class C extends A{}
class D extends C{}
  • 为何Java中类不支持多继承?
    • 如果C类同时继承了A、B类,但是A、B类中都有同名方法时,C类在调用方法的时候是无法区分调用的是哪个类中的方法。
    • 多层继承则不存在这个问题,假设C类继承A,D类继承C,如果A类和C类中都有同名方法,D类调用的时候只会选择C类中的方法,Java会采取就近原则。

object类

  • object类是java中所有类的祖宗。我们写的任意类,其实都是object的子类或者子孙类。

方法重写

  • 什么是方法重写?
    1. 当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是重写。
    2. 注意:重写后,方法的访问,Java会遵循就近原则。

方法重写的其他注意事项

  1. 重写小技巧:使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
  2. 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public > protected > 缺省)。
  3. 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
  4. 私有方法、静态方法不能被重写,如果重写会报错的。
    案例:
父类:
public class Animal {
    public void run(){
        System.out.println("每个动物都会跑!");
    }
}
子类:
public class Tiger extends Animal{
    // 方法重写:名称相同,参数列表相同,返回值类型相同
    @Override  //重写的校验注解:安全,优雅
    public void run(){
        System.out.println("老虎跑的贼快!");
    }
}
main方法:
public static void main(String[] args) {
    Tiger t = new Tiger();
    t.run();
}
输出结果:
老虎跑的贼快!

方法重写的应用场景

  • 子类重写Object类的toString()方法,以便返回对象内容。
    案例
创建一个学生类:
public class Student {
    private String name;
    private char sex;
    private double height;
    private String desc;

    public Student() {
    }

    public Student(String nanme, char sex, double height, String desc) {
        this.name = nanme;
        this.sex = sex;
        this.height = height;
        this.desc = desc;
    }

    public String getNanme() {
        return name;
    }

    public void setNanme(String nanme) {
        this.name = nanme;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
    @Override
    public String toString(){
        return "Student {name = " + name + ", sex = " + sex + ", height = " + height + ", desc = " + desc + "}";
    }
}

main方法:
// 目标:搞清楚方法重写在实际开发类中的应用场景。
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("王小二", '男', 1.78, "是个好学生");
        System.out.println(s1);
    }
}
输出结果:
Student {name = 王小二, sex = 男, height = 1.78, desc = 是个好学生}

在打印对象时,实际是调用了objcet类中的toString方法,输出的结果就是对象的内存地址。如果toString被重写后,在打印对象时,则调用的是重写后的toString方法。

子类中访问其他成员的特点

  • 在子类方法中,访问其他成员(成员变量、成员方法),是依据就近原则

    1. 先子类局部范围找。
    2. 然后子类成员范围找。
  • 如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的成员怎么办?

    • 可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法

VS this:

  1. 代表的对象不同:
    this:本身调用者的这个对象
    super:代表父类对象的应用
  2. 调用前提:
    this:没有继承也可以使用。
    super:只能在继承后使用。
  3. 构造方法:
    this(),调用的是本类的构造。
    super(),调用的是父类中的构造。
    案例:
// 目标:继承过后,子类访问成员的特点,就近原则
public class Test {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.showName();
    }
}

class Zi extends Fu{
    String name = "子类名称";

    public void showName(){
        String name = "局部名称";
        System.out.println(name);
        System.out.println(this.name);// 访问子类成员name
    }
}
class Fu{
    String name = "父类名称";
}
输出结果:
局部名称
子类名称
-------------------------------------------------------------
public class Test2 {
    public static void main(String[] args) {
        Zi2 z = new Zi2();
        z.run();
        z.go();
    }
}

class Zi2 extends Fu2{
    @Override
    public void run(){
        System.out.println("子类跑");
    }

    public void go(){
        run(); // 调用子类的run方法
        super.run();// 调用父类的run方法
    }

}
class Fu2{
    public void run(){
        System.out.println("父类跑");
    }
}
输出结果:
子类跑
子类跑
父类跑

子类构造器的特点

  • 子类的全部构造器,都会先调用父类的构造器,再执行自己。
    案例:
父类:
public class Animal {
    public Animal(){
        System.out.println("父类无参构造器");
    }

    public Animal(String n){
        System.out.println("父类有参构造器");
    }
}
子类:
public class Wolf extends Animal{
    public Wolf() {
        System.out.println("子类Wolf无参构造器");
    }

    public Wolf(String n){
        System.out.println("子类Wolf有参构造器");
    }
}
main方法:
//目标:搞清楚子类构造器的特点:子类全部构造器会优先调用父类的构造器,再执行子类的构造器
public class Test {
    public static void main(String[] args) {
        // 子类的全部构造器都会先调用父类的构造器,在执行子类的构造器
        Wolf w = new Wolf();
        Wolf w2 = new Wolf("wolf");
    }
}
输出结果:
父类无参构造器
子类Wolf无参构造器
父类无参构造器
子类Wolf有参构造器

子类构造器是如何实现父类构造器的?

  1. 默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),它会调用父类的无参构造器。
  2. 如果父类没有无参构造器,则我们必须在子类构造器的第一行手写super(……),指定去调用父类的有参构造器。

为什么子类的全部构造器会先调用父类的构造器?
案例:

// 父类
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    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 class Teacher extends Person{
    private String skill;

    public Teacher() {

    }
    public Teacher(String name, int age, String skill) {
        super(name,age); //子类调用父类构造器:初始化继承自父类的那部分数据
        this.skill = skill;
    }


    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }
}
main方法:
public static void main(String[] args) {
   Teacher t1 = new Teacher();
      Teacher t2 = new Teacher("蜘蛛侠", 30, "教语文");

      t1.setName("钢铁侠");
      t1.setAge(20);
      t1.setSkill("教数学");

      System.out.println(t1.getName());
      System.out.println(t1.getAge());
      System.out.println(t1.getSkill());
      System.out.println(t2.getName());
      System.out.println(t2.getAge());
      System.out.println(t2.getSkill());

}
输出结果:
钢铁侠
20
教数学
蜘蛛侠
30
教语文
  • 子类构造器可以通过父类构造器,把对象中包含父类这部分的数据先初始化赋值,再回来把对象利包含子类这部分的数据也进行初始化赋值。
    这样既调用了父类的构造器又调用了子类的构造器,这样就保证了数据的完整性。

this(……)调用兄弟构造器

  • 任意类的构造器中,是可以通过this(……)去调用该类的其他构造器的。
    案例:
学生类:
public class Student {
    private String name;
    private int age;
    private String school;

    public Student() {
    }

    public Student(String name, int age){
        this(name, age, "霍格沃兹");
    }
    public Student(String name, int age, String school) {
        this.name = name;
        this.age = age;
        this.school = school;
    }

    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 String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }
}
main方法:
public static void main(String[] args) {
        // 1. 创建对象封装数据
    Student s1 = new Student("孙悟空", 500, "斜月三星洞");
    System.out.println(s1.getName());
    System.out.println(s1.getAge());
    System.out.println(s1.getSchool());

    // 不需要给学校赋值,默认值是霍格沃兹
    // 注意事项:this(...),super(...)不能同时出现,且必须在构造器的第一行。
    Student s2 = new Student("蜘蛛精", 300);
    System.out.println(s2.getName());
    System.out.println(s2.getAge());
    System.out.println(s2.getSchool());
}
输出结果:
孙悟空
500
斜月三星洞
蜘蛛精
300
霍格沃兹
  • 注意事项:this(...),super(...)不能同时出现,且必须在构造器的第一行。
    • 如果同时出现,this(...)会调用有参构造器,有参构造器会调用父类的有参构造器,这是如果在调用super(……)就会出现重复调用父类构造器的问题。
posted @ 2025-07-23 17:42  狂风将军  阅读(29)  评论(0)    收藏  举报