> 文档中心 > 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类当中有的方法了吗?

 

 

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

😁而继承就是在已经存在类(Person)的基础上进行扩展,从而产生新的类(Student)。已经存在的类称为父类、基类或超类,而新产生的类称为子类或派生类。

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

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继承了父类Personclass 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();    }}

😎来看一下运行结果: 

🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟

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

 

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

 

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

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();    }}

 

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

📝那么问题又来了,如果当重名时(也可以理解为子类重写父类的变量),我们就想调用未重写的父类的怎么办?其实也行:用一下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();    }}

 

🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟

 

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

 

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

 

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

 

总结一下

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


 

继承中的几个注意点

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

 

 

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

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

 

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

 

🍑 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();    }}

 

 

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

🌰那就是下面这种情形:

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("小鱼儿");    }}输出:这是父类的含一个参数的构造方法,姓名是:小鱼儿这是子类带一个参数的构造方法

 

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

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