• 【Java基础篇 | 面向对象】—— 继承


    个人主页兜里有颗棉花糖
    欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
    收录于专栏【JavaSE_primary
    本专栏旨在分享学习JavaSE的一点学习心得,欢迎大家在评论区讨论💌
    在这里插入图片描述

    继承允许一个类继承另一个类的属性和方法,并且可以在其中添加或修改行为。Java引入继承的目的是为了促进代码重用和构建更加模块化和可扩展的软件系统

    一、什么是继承

    继承:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类

    请看代码:

    class Animal{
        public String name;
        public int age;
    
        public void eat(){
            System.out.println(name+"正在吃饭");
        }
    }
    
    class Dog extends Animal{
        public void barks(){
            System.out.println(name+"汪汪叫"+"年龄:"+age);
        }
    }
    
    class Cat extends Animal{
    
        public void catchMouse(){
            System.out.println(name+"正在抓老鼠");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    上述代码中:我们把Animal称为父类/基类,把Cat、Dog称为子类/派生类

    子类继承父类后,会把父类的属性和方法全部继承。所以我们可以把继承理解为共性的抽取,从而达到代码的复用。

    注意:

    • 子类会将父类的成员变量或者成员方法继承子类中。
    • 子类继承父类后,必须添加自己特有的成员来体现出子类与基类的不同,否则就没有必要继承了。
    • 继承最好不要超过3层,否则就会带来可维护的难题。

    二、父类成员访问

    在继承体系中,子类将父类中的方法和字段继承下来了,下面来看看子类是如何直接访问父类中继承下来的成员的。

    2.1子类中如何访问父类的成员变量和成员方法

    请看代码演示:

    
    class Base{
        int a;
        int b;
    }
    
    public class Derived extends Base{
    
        int a;
        int c;
    
        public void method(){
            int a = 10; //这里访问的是子类的a
            int b = 20;
            int c = 20;
            System.out.println(super.a);
            System.out.println(a);
            System.out.println(b);
            System.out.println(c);
        }
    
    	public void methodB(){
    		System.out.println("Derived中的methodB()方法");
    	}
    		public void methodC(){
    		methodB(); // 访问子类自己的methodB()
    		methodA(); // 访问父类继承的methodA()
    		// methodD(); // 编译失败,在整个继承体系中没有发现方法methodD()
    	}
        public static void main(String[] args) {
            Derived derived = new Derived();
            derived.method();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果如下:
    在这里插入图片描述

    2.2小总结

    在子类方法中或者通过子类对象访问成员时,遵循如下规则:

    • 如果访问的成员变量子类中有,优先访问自己的成员变量
    • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
    • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
    • 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错;

    总之,成员变量的访问遵循就近原则,自己有则优先用自己的,否则就去父类中去找。成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时
    再到父类中找,如果父类中也没有则报错。

    现在,如果子类中存在与父类中相同的成员时,那如何在子类中访问父类相同名称的成员呢?下面线

    三、super关键字

    由于某些场景的需要,子类和父类有时会出现相同名称的成员,此时如果我们想要在子类方法中去直接访问父类相同名称的成员是做不到的。所以,java提供了super关键字,以便我们能够访问父类相同成员。

    请看下面代码的举例:

    class Base{
        int a;
        int b;
    
        public void print(){
            System.out.println(a);
            System.out.println(b);
        }
    
        public static void staticfunc(){
            System.out.println("Base::staticfunc");
        }
    }
    
    public class Derived extends Base{
    
        int a = 30;
        int b = 40;
    
        public void print(){
            System.out.println(a);
            System.out.println(b);
        }
    
        public void method(){
            int a = 10; // 为子类中的成员变量a赋值
            int b = 20; // 为子类中的成员变量b赋值
            System.out.println(super.a); // 这里访问的是父类中的成员变量a
            System.out.println(a);       // 这里访问的就是子类中的成员变量a
    
            super.print(); // 子类方法中调用父类的print函数
            print();       // 子类方法中调用子类的print函数
        }
        
        public static void main(String[] args) {
            Derived derived = new Derived();
            derived.method();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行结果如下:
    在这里插入图片描述

    super关键字主要有三种用法:

    • 1.super.data;访问父类的成员变量
    • 2.super.func();访问父类的成员函数
    • 3.super();调用父类的构造函数

    super注意事项

    第一点:使用类名去访问静态成员

    这里我们需要要强调的是,如果我们想要访问父类的静态成员函数的话,我们直接用类名去访问父类中的父类成员方法即可。不建议使用super等引用去访问父类中静态成员函数。
    在这里插入图片描述

    第二点:super、this关键字只能在非静态成员方法中使用。

    在这里插入图片描述
    所有的静态成员都是不依赖于对象存在的,因为静态成员在类加载的时候就已经存在,并且在整个程序运行期间都可以被访问和使用。

    四、子类构造方法

    子类在创建对象时,优先调用父类的构造方法,最后再执行子类的构造方法(我们可以理解为现有父,后有子)。

    首先,我们要知道,对象属性(成员变量)的初始化一定要调用构造函数。在子类对象构造完成之前会先帮助父类完成初始化。

    我们直接看代码举例,请看:

    class person{
        public String name;
        int age;
    
        // 父类构造
        public person(String name, int age) {
            System.out.println("person(String name, int age)");
            this.name = name;
            this.age = age;
        }
    }
    
    public class Student extends person{
        int ID;
        int record;
    
        //子类构造
        public Student(String name,int age,int ID,int record){
            super(name,age); // 先完成父类成员变量的初始化
            // 完成父类成员变量后再完成子类成员变量的初始化
            this.ID = ID;
            this.record = record;
            System.out.println("Student(int ID,int record)");
        }
    
        public static void main(String[] args) {
            Student st1 = new Student("Daming",18,123456,100);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    运行结果如下:
    在这里插入图片描述

    请请看下面这张图:

    在这里插入图片描述

    注意,父类构造函数调用结束后并没有完成父类对象的构造,这里帮助子类完成从父类那里继承过来的属性的初始化。

    小总结

    在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。

    注意点:

    • 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
    • . 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
    • . super(…)只能在子类构造方法中出现一次,并且不能和this同时出现
    • . 在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数中第一条语句。

    五、super和this

    super和this都可以在成员方法中访问成员变量和其它成员函数,同时都可以作为构造函数的第一条语句且不可以同时存在。

    相同点:

    • 只能在类的非静态成员方法中使用,用来访问非静态成员方法和字段。
    • 在构造方法中调用时,必须是构造方法的第一条语句,且不能同时存在。

    不同点:

    • this是当前对象的引用,而super是从子类对象从父类继承下来部分成员的引用。
      在这里插入图片描述
    • 在非静态成员方法中,this用来访问本类的属性,而super用来访问从父类继承下来部分的方法和属性。
    • 在构造方法方面:this()用来调用本类的构造方法,而super()用来调用父类的构造方法。注意二者不能同时出现在构造方法中。
    • 调用方面:this()构造方法我们如果不写的话编译器是一定是不会调用的。但是super()如果我们不写的话,编译器是会自动生成的。

    六、再谈初始化

    我们来看一下下面代码块在继承关系上的执行顺序:

    class Person{
        public String name;
        int age;
    
        static{
            System.out.println("Person::static{}");
        }
    
        {
            System.out.println("Person::{}");
        }
        // 父类构造
        public Person(String name, int age) {
            System.out.println("person(String name, int age)");
            this.name = name;
            this.age = age;
        }
    }
    
    public class Student extends Person{
        int ID;
        int record;
    
        static{
            System.out.println("Student::static{}");
        }
        
        {
            System.out.println("Student::{}");
        }
    
        //子类构造
        public Student(String name,int age,int ID,int record){
            super(name,age); // 先完成父类成员变量的初始化
            // 完成父类成员变量后再完成子类成员变量的初始化
            this.ID = ID;
            this.record = record;
            System.out.println("Student(int ID,int record)");
        }
    
        public static void main(String[] args) {
            Student st1 = new Student("Daming",18,123456,100);
            System.out.println("==========");
            Student st2 = new Student("Daming",21,456789,150);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    执行结果如下:

    在这里插入图片描述

    通过上述代码我们可以得到一些结论:

    • 父类静态代码块优于子类静态代码块执行,静态代码块是最先执行的。
    • 父类实例代码块和父类构造代码块紧接着执行。
    • 子类实例代码块和子类构造代码块紧接着执行。
    • 第二次实例化子类对象的时候,父类和子类的静态代码块都不会执行。

    好了,以上就是本文的全部内容。就到这里,再见啦友友们!!!

  • 相关阅读:
    Langchain-Chatchat-win10本地安装部署成功笔记(CPU)
    Java之MyBatis
    MacOS M1芯片CentOS8部署搭建k8s集群
    Splunk 之 filed 恢复
    opencv入门笔记(二)
    【数据挖掘】2022年京东算法工程师笔试题(23届)
    Dive into Deep Learning笔记——下
    BUUCTF-MISC1
    高防服务器与云防产品都适用哪些情况
    辅助驾驶功能开发-功能对标篇(17)-NGP 城市辅助系统-小鹏
  • 原文地址:https://blog.csdn.net/m0_74352571/article/details/132767447