Java之继承

继承

作者简介: zoro-1,目前大一,正在学习Java,数据结构等

作者主页: zoro-1的主页

欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖

继承

为什么使用继承

Java中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是

现实世界错综复杂,事物之间可能会存在一些关联,那在设计程序是就需要考虑。

比如:猫和狗我们分别用一个类来表示
狗类

java 复制代码
// Dog.java

public class Dog{
    string name;
    int age;
    float weight;
    
    public void eat(){
        System.out.println(name + "正在吃饭");
   }
    
    public void sleep(){
        System.out.println(name + "正在睡觉");
   }
    
    void Bark(){
    System.out.println(name + "汪汪汪~~~");
   }
}
 

猫类

java 复制代码
// Cat.Java

public class Cat{
    string name;
    int age;
    float weight;
    
    public void eat(){
        System.out.println(name + "正在吃饭");
   }
    
    public void sleep()
   {
        System.out.println(name + "正在睡觉");
   }
    
    void mew(){
        System.out.println(name + "喵喵喵~~~");
   }
}

他们类中会有很多相同的属性,就造成了代码冗杂,
面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。

继承是什么

共性的抽取,实现代码复用

上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。

继承的语法

在Java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下:

java 复制代码
修饰符 class 子类 extends 父类 {
   // ...  

}
java 复制代码
// Animal.java

public class Animal{
    String name;
    int age;
    
    public void eat(){
        System.out.println(name + "正在吃饭");
   }
    
    public void sleep(){
        System.out.println(name + "正在睡觉");
   }
}
 

// Dog.java

public class Dog extends Animal{    
    void bark(){
        System.out.println(name + "汪汪汪~~~");
   }
}
// Cat.Java

public class Cat extends Animal{   
    void mew(){
        System.out.println(name + "喵喵喵~~~");
   }
}
 

// TestExtend.java

public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();
        // dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的

        System.out.println(dog.name);
        System.out.println(dog.age);
 
        // dog访问的eat()和sleep()方法也是从Animal中继承下来的

        dog.eat();
        dog.sleep();
        dog.bark();
   }
}

注意:

  1. 子类会将父类中的成员变量或者成员方法继承到子类中了

  2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

访问父类成员

访问父类成员变量

  1. 成员变量名字不同
java 复制代码
public class Base {
    int a;
    int b;
}
 

public class Derived extends Base{
    int c;
    public void method(){
        a = 10;    // 访问从父类中继承下来的a

        b = 20;    // 访问从父类中继承下来的b

        c = 30;    // 访问子类自己的c

   }
}
  1. 成员变量名字相同
java 复制代码
public class Base {
    int a;
    int b;
    int c;
}
 

/

public class Derived extends Base{
    int a;              // 与父类中成员a同名,且类型相同

    char b;             // 与父类中成员b同名,但类型不同

 
    public void method(){
        a = 100;        // 访问父类继承的a,还是子类自己新增的a?

        b = 101;        // 访问父类继承的b,还是子类自己新增的b?

        c = 102;        // 子类没有c,访问的肯定是从父类继承下来的c

        // d = 103;     // 编译失败,因为父类和子类都没有定义成员变量b

   }
}

在子类方法中 或者 通过子类对象访问成员时:

  • 如果访问的成员变量子类中有,优先访问自己的成员变量。
  • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
  • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。

访问父类成员方法

  1. 成员方法名字不同
java 复制代码
public class Base {
     public void methodA(){
         System.out.println("Base中的methodA()");
     }
}
 

public class Derived extends Base{
    public void methodB(){
        System.out.println("Derived中的methodB()方法");
   }
    
    public void methodC(){
        methodB();         // 访问子类自己的methodB()

        methodA();         // 访问父类继承的methodA()

        // methodD();     // 编译失败,在整个继承体系中没有发现方法methodD()

   }
}
  1. 成员方法名字相同
java 复制代码
public class Base {
     public void methodA(){
         System.out.println("Base中的methodA()");
     }
 
     public void methodB(){
         System.out.println("Base中的methodB()");
     }
}
 

public class Derived extends Base{
    public void methodA(int a) {
        System.out.println("Derived中的method(int)方法");
   }
 
    public void methodB(){
        System.out.println("Derived中的methodB()方法");
   }
 
    public void methodC(){
        methodA();      // 没有传参,访问父类中的methodA()

        methodA(20);    // 传递int参数,访问子类中的methodA(int)

        methodB();      // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到

   }
}
  • 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
  • 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;

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

super关键字

由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员。

java 复制代码
public class Base {
     public void methodA(){
         System.out.println("Base中的methodA()");
     }
 
     public void methodB(){
         System.out.println("Base中的methodB()");
     }
}
 

public class Derived extends Base{
    public void methodA(int a) {
        System.out.println("Derived中的method(int)方法");
   }
 
    public void methodB(){
        System.out.println("Derived中的methodB()方法");
   }
 
    public void methodC(){
        methodA();      // 没有传参,访问父类中的methodA()

        methodA(20);    // 传递int参数,访问子类中的methodA(int)

        methodB();      // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到

   }
}

public class Base {
    int a;
    int b;
 public void methodA(){
     System.out.println("Base中的methodA()");
 }
 
 public void methodB(){
     System.out.println("Base中的methodB()");
 }
}
public class Derived extends Base{
    int a;    // 与父类中成员变量同名且类型相同

    char b;   // 与父类中成员变量同名但类型不同

    // 与父类中methodA()构成重载

    public void methodA(int a) {
        System.out.println("Derived中的method()方法");
   }
 
    // 与基类中methodB()构成重写(即原型一致,重写后序详细介绍)

    public void methodB(){
        System.out.println("Derived中的methodB()方法");
   }
 
    public void methodC(){
        // 对于同名的成员变量,直接访问时,访问的都是子类的

        a = 100;   // 等价于: this.a = 100;

        b = 101;   // 等价于: this.b = 101;

        // 注意:this是当前对象的引用

 
        // 访问父类的成员变量时,需要借助super关键字

        // super是获取到子类对象中从基类继承下来的部分

        super.a = 200;
        super.b = 201;
 
 
        // 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法

        methodA();      // 没有传参,访问父类中的methodA()

        methodA(20);    // 传递int参数,访问子类中的methodA(int)

 
        // 如果在子类中要访问重写的基类方法,则需要借助super关键字

        methodB();      // 直接访问,则永远访问到的都是子类中的methodA(),基类的无法访问到

        super.methodB(); // 访问基类的methodB()

   }
}

在子类方法中,如果想要明确访问父类中成员 时,借助super关键字即可。

子类构造方法

java 复制代码
public class Base {
    public Base(){
        System.out.println("Base()");
 }
 }
 

public class Derived extends Base{
   public Derived(){
       // super();   // 注意子类构造方法中默认会调用基类的无参构造方法:super(),

       // 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,

       // 并且只能出现一次

       System.out.println("Derived()");
   }
}
 

public class Test {
    public static void main(String[] args) {
        Derived d = new Derived();
   }
}
 

结果打印:

Base()

Derived()

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

注意

  1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构

    造方法

  2. 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的

    父类构造方法调用,否则编译失败。

  3. 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。

  4. super(...)只能在子类构造方法中出现一次,并且不能和this同时出现

super和this

异同

【相同点】

  1. 都是Java中的关键字

  2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段

  3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

【不同点】

  1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用

  2. 在非静态成员方法中,(非静态方法参数列表有this)this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性

  3. 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现

  4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

分别的使用方法

继承的方式

时刻牢记, 我们写的类是现实事物的抽象. 而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多. 类之间的关系也会更加复杂.但是即使如此, 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了.
如果想从语法上进行限制继承, 就可以使用 final 关键字

final关键字

final是Java中的一个关键字,用于修饰类、方法和变量,具体作用如下:

  1. final修饰的类无法被继承;
  2. final修饰的方法无法被子类重写;
  3. final修饰的变量为常量,值不可改变;
  4. final修饰的引用变量,其引用地址不可改变,但是其所引用的对象的状态可以改变。

使用final关键字可以增强代码的安全性和可读性,避免一些不必要的错误操作。

相关推荐
考虑考虑2 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261352 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊3 小时前
Java学习第22天 - 云原生与容器化
java
渣哥5 小时前
原来 Java 里线程安全集合有这么多种
java
间彧5 小时前
Spring Boot集成Spring Security完整指南
java
间彧6 小时前
Spring Secutiy基本原理及工作流程
java
Java水解7 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆9 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学9 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole9 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端