【从零开始学习JAVA | 第十四篇】继承

目录

前言: 

引入:

继承:

小拓展:

优点:

 成员方法的继承问题:

总结:


前言: 

 继承是面向对象三大特性之一,它是在封装之后我们讲解的一个重要的性质,继承是类与类之间的关系,子类会继承父类中对子类开放的成员函数以及成员方法。这大大优化了我们在写代码时候的复用性,因此我们要学好继承,这样才可以学好JAVA

引入:

如果此时我们要创建一个学生类,它的成员变量包括姓名,年龄,性别,成员行为包括吃饭,玩游戏,睡觉,学习,要创建一个教师类,它的成员变量包括姓名,年龄,性别,成员行为包括吃饭,玩游戏,睡觉,教学,放到以前我们会这样建立:

但是我们会发现,其实这两个类的行为都是高度相似的,那么我们能否精简一下呢?
这里就用到我们之前说的继承,我们抽象出来一个父类概括这两个类之间的共同点,然后然这两个类继承父类,这样不就降低了代码的复用性。

 其实这就是继承的思想:把类之间的相似点抽象出来,建立一个可以继承的父类,以此来降低代码的复用性。

继承:

Java中,继承是面向对象编程中的重要概念之一,它是指一个类可以继承另一个类的属性和方法,同时可以添加或覆盖父类的功能,从而创建一个新的类。通过继承,子类可以获得父类的全部或部分特性,从而减少代码的重复,简化程序的设计,提高代码的可重用性。

在Java中,一个子类通过  extends  关键字继承一个父类。子类可以访问父类中的公有和受保护的成员变量和方法,但不能访问父类的私有成员变量和方法。子类可以覆盖父类的方法,实现自己特定的行为,或者添加新的方法和属性。

以下是一个简单的Java类继承的示例:

class Animal {
    String name;
    int age;
    void eat() {
        System.out.println("Animal is eating");
    }
}

class Cat extends Animal {
    void meow() {
        System.out.println("Cat is meowing");
    }
}

public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.name = "Tom";
        cat.age = 2;
        cat.eat();
        cat.meow();
    }
}

在上述示例中,Cat类继承了Animal类,并添加了一个自己的方法meow。在主程序中,我们创建一个Cat类的对象,并可以访问Animal类的属性和方法,并且可以调用Cat类自己的方法。

小拓展:

JAVA和C++的继承方式非常相似,但是在java中,所有的继承都是公共继承,不存在保护继承和私有继承

在 Java 中,继承可以分为公有继承受保护继承

公有继承(public inheritance)是指子类可以继承父类的公有成员和受保护成员,而不能继承父类的私有成员。

受保护继承(protected inheritance)是指子类可以继承父类的公有成员和受保护成员,但不能直接访问父类的私有成员。(只是不能直接访问而已,在后面我们会介绍方式访问)

实际上,在 Java 中不需要特别声明继承方式,默认使用公有继承。Java 中所有成员变量和方法都有一个访问修饰符,如public、private和protected等,通过使用不同的访问修饰符可以限制子类的访问范围。同时,Java提供了一些关键字,如final和static等,可以在继承时进行限制或定义。

*受保护继承(JAVA)   不是      保护继承(C++)

受保护继承    会把父类的私有成员继承给子类,子类不能直接访问,可以间接访问

保护继承    会把父类的所有成员都继承给子类(私有成员也继承),只不过子类不会提供调                    用的函数,也就相当于没有继承。

如果不相信我们可以调用 vs 2022 管理员后台,进行验证,这里篇幅有限,就不进行验证了。

如何在子类中访问父类中private的变量呢?

如果需要在子类中访问父类的私有变量,可以使用Java中提供的getter和setter方法,通过调用父类的公有方法来实现。

比如,我们在父类中声明了一个私有变量:

public class Parent {
    private int x;
    public Parent(int x) {
        this.x = x;
    }
    private int getX() {
        return x;
    }
}

可以在父类中添加一个公有的getter方法getX(),用于获取私有变量x的值:

public class Parent {
    private int x;
    public Parent(int x) {
        this.x = x;
    }
    private int getX() {
        return x;
    }
    public int getPrivateVariableX() {
        return this.getX();
    }
}

子类可以通过调用父类的公有方法getPrivateVariableX()来获取父类的私有变量x的值:

public class Child extends Parent {
    public Child(int x) {
        super(x);
    }
    public int getPrivateVariableX() {
        return super.getPrivateVariableX();
    }
}

这样就可以在子类中获取并使用父类中的私有变量x了。但是需要注意的是,私有变量在父类中是被限制了访问权限的,而在子类中通过公有方法来访问,可能会造成访问权限的漏洞,因此需要特别注意。

优点:

继承是面向对象程序设计中的一个重要特性,具有以下优点:

1. 代码重用:通过继承,可以将现有的类的代码和功能复用在新的类中,避免了重复编写代码造成资源浪费的问题,提高了代码的复用性。

2. 继承关系体现了程序中的真实的关系:继承关系可以清晰地表示父类与子类之间的关系,从而提高程序的可读性和可维护性。

3. 扩展性好:通过继承可以很方便地扩展一个类并增加新的特性,同时又不会影响到现有的代码和已有的功能。

4. 多态性:通过继承和子类的多态特性,可以使代码更加灵活和可扩展。

5. 代码封装:继承提供了一种封装的方式,父类中的成员可以在子类中被重用,并且对外不可见,保证了代码的安全性和隐私性。

总之,通过继承可以相对简单地实现代码的重用和扩展,使代码更加灵活、可读性更高,并且可以提高代码的可维护性。

介绍到这里,很多小伙伴都会陷入到一个误区既然继承这么便洁,我们以后遇到创建多个类就进行抽象父类,再把父类继承给子类。

这种思想是错误的!这是因为虽然继承很方便,但是我们的父类应该是与子类有一定的相连关系的,这样做是为了提高代码的可读性,例如我们把动物抽象出来,继承给小猫,小狗这样的子类,把手机抽象出来,继承给小米,华为,苹果这样的子类。而不可以随意抽象父类。

JAVA中只支持单继承,不支持多继承,但支持多层继承。

翻译一下:A不可以同时继承B,C(不支持多继承).但是可以让C继承B,A再继承C(多层继承)。

其实这个特性是为了解决C++中的多继承带来的缺陷:如果两个父类的方法名称相似,那么我们的子类如果继承了这两个父类,在调用这个方法的时候,我们到底调用的是哪一个父类中的方法呢?
c++给出的答案是 虚继承

虚继承是指在继承的时候使用关键字  virtual  ,这样的继承方式可以避免派生类继承多个基类中的同名成员(数据和函数)造成的歧义。这是因为虚继承会让派生类只在其继承树中保存一个唯一的基类子对象,从而消除了重复继承的成员。

例如,假设有两个基类A和B,它们都有一个同名函数f(),如果一个类C继承了A和B,那么在调用C::f()时,会发生二义性问题。但如果A和B被虚继承,那么C类只继承一个A子对象和一个B子对象,这样C::f()就不会被重复继承。如果需要在C中调用A或B中的f()函数,可以通过作用域解析符(::)指定调用某个基类的函数,在调用时指定基类A或B即可。

以下是一个示例代码:

class A {
public:
    virtual void f() {
        cout << "A::f()" << endl;
    }
};

class B {
public:
    virtual void f() {
        cout << "B::f()" << endl;
    }
};

class C: public virtual A, public virtual B {
public:
    virtual void f() {
        A::f(); // 调用A的f()函数
        B::f(); // 调用B的f()函数
    }
};

int main() {
    C c;
    c.f(); // 输出 A::f() B::f()
    return 0;
}

而我们的JAVA为了避免这一个繁琐的步骤,就直接禁止了子类进行多父类继承的能力。

 其实每一个类都会直接或间接的继承Object。

在Java中,Object是所有类的根类。Java语言是一门面向对象的语言,所有的类都直接或间接继承了Object类,因此Object类在Java程序中是非常重要和基础的。

Object类定义了一些通用的方法,如:

  • toString()方法:用于返回对象的字符串表示,通常被子类重写。
  • equals()方法:用于比较两个对象是否相等。
  • hashCode()方法:用于返回对象的哈希码,通常被重写。

此外,Object类还定义了一些其他常用的方法,如getClass()方法用于获取对象的类,notify()、notifyAll()和wait()等方法用于线程同步等。

需要注意的是,Object类型是Java中的顶级类型,不是基本类型。在Java中,有八种基本数据类型:byte、short、int、long、float、double、boolean和char。尽管Object类是Java中非常重要的类,但也不要滥用Object类型,尽量使用具体的子类,以便程序具有更好的类型安全性和可读性。

 成员方法的继承问题:

 如果 A 到 D 逐层继承,那么如果我们在A中调用方法C,他真的会逐级父类去找方法D嘛?

实际上是不会这样的,我们有一个虚方法表,各个父类会将自己的方法添加到虚函数表中,供调用的时候快速查询。

虚方法表中的方法必须是:

  • 非private:private关键字修饰的方法只能在本类中访问,无法被子类继承,因此不可能成为虚方法,也不会在虚方法表中存储。

  • 非static:静态方法是属于类而不是对象的,不能被子类继承,因此也不可能成为虚方法,也不会在虚方法表中存储。

  • 非final:final关键字修饰的方法是不能被子类重写的,因此也不可能成为虚方法,也不会在虚方法表中存储。

需要注意的是,在Java中,虚方法表是在运行时动态生成的,基于实际的对象类型,而不是变量的声明类型。这是Java实现多态性的一种高效的方式,可以使程序实现运行时动态绑定,提高代码的可维护性和可扩展性。

2.子类继承父类构造方法的问题:

1.子类不能继承父类的构造方法,但是可以通过super调用

2.子类构造方法的第一行,一定是一个super();这是因为子类的构造中可能会用到父类的成员变量,如果父类不先进行构造,直接运用在子类的构造中,可能会出错。

3.要想super调用父类的有参构造,必须手动书写

总结:

继承是类与类之间的关系,而继承隶属于面向对象的三大特性之一,是一个很重要的性质。因此我们要学好这一个知识点,这样才可以玩转JAVA

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

猜你喜欢

转载自blog.csdn.net/fckbb/article/details/131343600