Java面向对象的三大特征之继承

简介: Java面向对象的三大特征之继承

铁汁们,好久不见😁。我们前面聊了聊Java中封装那点事,那么今天就让我们看看Java中的继承到底是个什么东东😉


初识继承

😎我们先不说是继承的概念是什么?那概念太抽象了,让我们用例子说话😊

class Person {         // Person类
    public String name;    
    public int age;        // 修饰限定符是public,可任意访问
    public String sex2;
    public Person(String name, int age, String sex) {  // 使用构造函数传参进行初始化成员变量
        this.name = name;
        this.age = age;
        this.sex2 = sex;
    }
    public void eat () {
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep () {
        System.out.println(this.name + "正在休息");
    }
}
class Student {         // 学生类
    public String name;
    public int age;
    public String sex;
    public String school;
    // 用构造函数给Student类中的成员变量初始化
    public Student(String name, int age, String sex, String school) {
        this.name = name; 
        this.age = age;
        this.sex = sex;
        this.school = school;
    }
    public void eat () {
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep () {
        System.out.println(this.name + "正在休息");
    }
    public void homework() {
        System.out.println(this.age + "岁的" + this.name + "正在写他的家庭作业");
    }
}


🍑大家看上面的代码是不是有很多重复的,Person类中的很多属性在Student类当中也有,那我们能不能有一种办法,让Student类也能使用Person类中的成员属性呢🤔,这样Student类不就不用再重复写Person类当中有的方法了吗?

0010a671c3d74c52b877fc7175752d2c.gif

📝继承就可以实现这种功能,你看:学生属不属于人类,属于吧!那么Person类中有的name、age、eat()等这些方法再Student类当中肯定也有,Student类和Person类的区别就在于Student类扩展了一些学生专有的属性和方法。


比如通过继承上面的代码就可以缩短为:

1.class Person {
    public String name;
    public int age;        // 修饰限定符是public,可任意访问
    public String sex2;
    public Person(String name, int age, String sex) {  // 使用构造函数传参进行初始化成员变量
        this.name = name;     // 之后再子类中要先调用父类的构造方法
        this.age = age;
        this.sex2 = sex;
    }
    public void eat () {
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep () {
        System.out.println(this.name + "正在休息");
    }
}
Java 的继承通过 extends 关键字来实现
//Student extends Person 就代表子类Student继承了父类Person
class Student extends Person{
    public String school;
    public Student(String name, int age, String sex, String school) {
        super(name, age, sex);  // 必须调用父类的构造方法,然后才能进行子类的构造
        this.school = school;   // 你想父类还没完成构造初始化,子类也不能,先有父再有子呀!
    }
    public void homework() {    // 子类可以调用父类中的age、name等属性和方法
        System.out.println(this.age + "岁的" + this.name + "正在写他的家庭作业");
    }
}
public class test2 {
    public static void main(String[] args) {
        Student student = new Student("张三", 14, "男", "茶啊二中");
        student.eat();
        student.homework();
    }
}


😎来看一下运行结果:

c8da22f0bb514b15a33df2b5c43874cb.png

📝看来当子类继承父类后,还真的就像继承遗产一样-->拥有了父类的成员变量和成员方法

🍑但如果子类中自己的变量名或方法和父类中的相同怎么办,当访问该方法或变量时:访问的到底是子类的还是父类的呢🤔?

d4a825245ec74cb5ae3a191d8fab1189.gif

🌰让我们用例子来测试一下

class Base {  // 父类
    int a = 3;
    int b = 99;
    public void method() {
        System.out.println("这是父类的普通方法");
    }
}
class Derived extends Base {  // 子类通过extends关键字继承父类
    int a = 777;
    int c = 100000;
    public void test () {
        System.out.println("当调用子类和父类同名的成员变量a时,打印的是:" + this.a);
        System.out.println("当调用只有子类中有的成员变量c时,打印的是:" + this.c);
        System.out.println("当调用只有父类中有的成员变量b时,打印的是:" + this.b);
    }
}
public class test2 {
    public static void main(String[] args) {
        Derived derived = new Derived();
        derived.test();
    }
}

65f1eb93da0b4672b3e358e6f6253ed2.png

🍑可以看到,当子类和父类的成员变量有重名的情况时,优先调用父类的,只有当子类中没有这个变量时才会考虑父类。(看来子类也希望独立自主,也不希望全靠父亲帮助😁)


📝那么问题又来了,如果当重名时(也可以理解为子类重写父类的变量),我们就想调用未重写的父类的怎么办?其实也行:用一下super关键字就可以了。

class Base {  // 父类
    int a = 3;
    int b = 99;
    public void method() {
        System.out.println("这是父类的普通方法");
    }
}
class Derived extends Base {  // 子类通过extends关键字继承父类
    int a = 777;
    int c = 100000;
    public void test () {
        System.out.println("当调用子类和父类同名的成员变量a时,默认调用的是子类的a:" + this.a);
        System.out.println("当调用子类和父类同名的成员变量a时,用super关键字可以调用父类的a:" + super.a);
    }
}
public class test2 {
    public static void main(String[] args) {
        Derived derived = new Derived();
        derived.test();
    }
}


4990b12f38124de6b04ae8615d22fdab.png

🤔 那这是为啥呢?让我们来看看内存中成员变量a的位置就知道了

5c83c909a07f4c158d88663963a8c353.png

📝从图中我们也可以看到,通过this引用访问(即对当前对象访问),我们可以访问子类和父类中所有的成员变量和方法(但是默认优先访问子类中有的)


📝那要是子类中的变量名、方法名和父类一样怎么办?或者说子类对父类的方法发生了重写怎么办?很简单,用super 关键字可以在方法重写(或者说方法名字相同)的情况下访问到父类的方法。

总结一下

super关键字的作用就是:在子类方法中访问父类的成员变量或成员方法,但要注意我们通过super是不能访问父类private修饰的变量和方法的,因为这个只属于父类的内部成员(我们只能通过公共接口getter和setter来进行访问


继承中的几个注意点

一、在java只支持以下几种继承方法

206db7e0d86840328e65c3f041fcacec.png


🐟为什么多继承不支持呢?一个子类难道就不能有多个父类吗?

a09976f255c24569848afd1f64c13f16.gif

😂好好好,接下来咱们举一个例子来说明:

🌰如果有两个类共同继承(extends)一个父类,那么父类的方法可以被两个子类重写(只要符合重写的条件就可以)。然后,如果有一个新类同时继承了这两个子类,那么在调用该重写方法(或者说方法名相同的方法)的时候,编译器就不能识别要调用哪个类的方法了。这也正是著名的菱形问题,见下图。


362a26b08cc8ca9dea85e47cf67b601f.png

🍑 ClassC 同时继承了 ClassA 和 ClassB,ClassC 的对象在调用 ClassA 和 ClassB 中重写的方法时,就不知道该调用 ClassA 的方法,还是 ClassB 的方法。


📝所以为了避免这种情况的发生,在Java中是不支持多继承的,如果想要实现所谓的 " 多继承 ",就需要用到接口了,我们下篇会讲到。


二、在继承中,如果要实例化子类对象,必须先要调用父类的构造。

🍑来看一段代码

class Person {
    public Person() {   // 父类构造
        System.out.println("这是父类的构造方法");
    }
}
class Student extends Person {
    //编译器会自动在子类构造函数的第一句加上 super(); 来调用父类的无参构造器
    //此时可以省略不写。如果想写上的话必须在子类构造函数的第一句,
    public Student() {
        System.out.println("这是子类的构造方法");
    }
}
public class test2 {
    public static void main(String[] args) {
        Student student = new Student();
    }
}


3e7957fbe881476eb83840a33e9072a8.png

🤔上面默认调用的是父类的无参构造,那如果父类的构造方法含有参数呢?

🌰那就是下面这种情形:

class Person {
    String name;
    public Person(String name) {   // 父类构造
        System.out.println("这是父类的含一个参数的构造方法,姓名是:" + name);
    }
}
class Student extends Person {
    public Student(String name) {
        super(name); // 必须防止子类构造方法的第一行
        System.out.println("这是子类带一个参数的构造方法");
    }
}
public class test2 {
    public static void main(String[] args) {
        Student student = new Student("小鱼儿");
    }
}
输出:
这是父类的含一个参数的构造方法,姓名是:小鱼儿
这是子类带一个参数的构造方法

好了,今天我们的继承就先说到这😎,下篇让我们聊聊抽象类和接口中的那些恩怨情仇😉

😊新的一天,让我们一起加油!b6709ea493a64fdeb5a36cfba01a1ddc.jpg



相关文章
|
10天前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
24天前
|
Java 编译器
Java——类与对象(继承和多态)
本文介绍了面向对象编程中的继承概念,包括如何避免重复代码、构造方法的调用规则、成员变量的访问以及权限修饰符的使用。文中详细解释了继承与组合的区别,并探讨了多态的概念,包括向上转型、向下转型和方法的重写。此外,还讨论了静态绑定和动态绑定的区别,以及多态带来的优势和弊端。
27 9
Java——类与对象(继承和多态)
|
8天前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
18 4
|
8天前
|
SQL 安全 Java
JAVA代码审计SAST工具使用与漏洞特征
JAVA代码审计SAST工具使用与漏洞特征
24 1
|
10天前
|
Java
接口和抽象类【Java面向对象知识回顾②】
本文讨论了Java中抽象类和接口的概念与区别。抽象类是不能被实例化的类,可以包含抽象和非抽象方法,常用作其他类的基类。接口是一种纯抽象类型,只包含抽象方法和常量,不能被实例化,且实现接口的类必须实现接口中定义的所有方法。文章还比较了抽象类和接口在实现方式、方法类型、成员变量、构造方法和访问修饰符等方面的不同,并探讨了它们的使用场景。
接口和抽象类【Java面向对象知识回顾②】
|
13天前
|
Java
Java 的继承
在一个森林中,各种动物共存,如狗和猫。为了管理和组织这些动物,我们采用面向对象的方法设计模型。首先创建 `Animal` 超类,包含 `name` 和 `age` 属性及 `makeSound()` 和 `displayInfo()` 方法。接着,通过继承 `Animal` 创建子类 `Dog` 和 `Cat`,重写 `makeSound()` 方法以发出不同的声音。实例化这些子类并使用它们,展示了继承带来的代码重用、扩展性和多态性等优点。这种方式不仅简化了代码,还体现了现实世界的层次结构。
|
25天前
|
安全 Java Go
面向对象程序设计语言:Java
Java语言语法和C语言和C++语言很接近,很容易学习和使用,Java丢弃了C++中很少使用的、很难理解的、令人迷惑的特性,Java语言不使用指针,而是引用,并提供了自动分配和回收内存空间,使得程序员不必为内存管理而担忧
37 2
|
28天前
|
Java
java的继承详解
在 Java 中,继承是一个核心概念,它允许子类 `extends` 父类来重用和扩展其属性与方法。子类可以覆盖父类的方法以改变行为,同时使用 `super` 关键字调用父类的构造方法或方法。虽然 Java 不支持多继承,但可以利用抽象类与接口实现多层继承。这种方式极大地增强了代码的复用性和维护性。
|
14天前
|
Java 开发者
Java 面向对象
Java 是一种面向对象的编程语言,通过对象与类的概念组织代码和数据。面向对象编程的核心包括类、对象、继承、多态、封装和抽象。类是对象的蓝图,定义了属性和行为;对象则是类的实例。继承允许子类继承父类的属性和方法,增强代码复用性;多态则支持通过相同接口调用不同类型对象的行为,包括方法重载和重写。封装通过公共方法隐藏对象细节,提高安全性;抽象则对对象特征进行提炼,通过抽象类和接口实现。理解这些概念有助于设计高效、可维护的 Java 应用程序。
|
21天前
|
Java 开发者
Java编程之旅:探索面向对象的力量
【9月更文挑战第16天】在编程的世界中,Java以其强大的面向对象编程特性而闻名。本文将带你走进Java的世界,一起探索类与对象的奥秘,学习如何通过封装、继承和多态性构建健壮的软件系统。无论你是初学者还是有经验的开发者,本文都旨在提供实用的代码示例,帮助你提升Java技能。准备好开始这段旅程了吗?让我们启程吧!
下一篇
无影云桌面