JavaSE - 继承

一、继承

1.1 什么是继承?为什么需要继承?

继承(inheritance)是面向对象编程中实现代码复用最重要的机制之一。它允许程序员在保留某个类原有功能和特性的基础上,对其进行扩展增加新能力 ,从而创建一个新的类,这个新类被称为派生类

通过继承,类之间形成由简单到复杂的层次结构,反映了人们对事物从共性到个性、逐步细化的认识过程。

继承主要解决的问题是:抽取共性、减少重复代码,实现更高程度的代码复用

java 复制代码
public class Dog{
    String name;
    int age;
    float weight;
    public void eat() {
        System.out.println(name+ "正在吃饭");
    }

    public void sleep() {
        System.out.println(name+ "正在睡觉");
    }

    public void brak() {
        System.out.println(name+ "汪汪汪~~~");
    }
}

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

通过观察上述代码,可以发现猫类和狗类中有许多重复内容,例如 name、age、weight 这些属性,以及 eat()、sleep() 这些方法。

继承的作用正是为了从中抽取这些共性部分,让它们集中到一个父类中,从而实现代码的复用。

1.2 继承的语法格式

在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+ "正在睡觉");
    }

}
java 复制代码
// Dog.java 
public class Dog extends Animal{
    public Dog() {
        super();
    }

    void bark() {
        System.out.println(name+ "汪汪汪~~~");
    }
}
java 复制代码
// Cat.Java 
public class Cat extends Animal{
    public Cat() {
        super();
    }

    void mew() {
        System.out.println(name+ "喵喵喵~~~");
    }
}

注意:

  1. 子类会自动继承父类中的成员变量和成员方法,使它们在子类中也能直接使用。

  2. 继承之后,子类通常还需要加入自己特有的属性或方法,以体现与父类的区别,否则就没有必要进行继承。

1.3 访问父类成员

1.3.1 子类中访问父类的成员变量

1. 当子类和父类不存在同名变量时
java 复制代码
public class Animal {
    String name;
    int age;
}
java 复制代码
public class Dog extends Animal{
    double weight;
    String color;


    public Dog(String name,int age,double weight,String color) {
        this.name = name;//从父类中继承下来的name
        this.age = age;//从父类中继承下来的age
        this.weight = weight;
        this.color = color;
    }
}
java 复制代码
public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog("阿狗",3,20.5,"黑色");
        //dog类中并没有定义任何成员变量,name和age属性是从父类Animal中继承下来的
        System.out.println(dog.name);
        System.out.println(dog.age);
        System.out.println(dog.weight);
        System.out.println(dog.color);
    }
}
2. 当子类和父类存在同名变量时
java 复制代码
public class Animal {
    String name;
    int age;
    int a = 3;
}
java 复制代码
public class Dog extends Animal{
    double weight;
    String color;
    int a = 0;
    
    public void method() {
        System.out.println(a);//此时a是访问父类继承的a还是子类自己的a呢?
    }
}
java 复制代码
public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.method();//输出结果为0,说明method()中的a是子类自己的
    }
}

在子类的方法中,或通过子类对象访问成员变量时,需要遵循以下规则:

  • 如果子类中定义了该成员变量,则优先访问子类自己的成员。

  • 如果子类中没有该成员变量,则会访问从父类继承下来的成员;若父类也没有,则会在编译阶段报错。

  • 如果子类和父类存在同名成员变量,仍然以子类的成员为准。

总之就是遵循就近原则:先找子类自身的,再查找父类的。

1.3.2子类中访问父类的成员方法

java 复制代码
public class Animal {
    public void methodA() {
        System.out.println("父类中的A方法");
    }
    public void methodB() {
        System.out.println("父类中的B方法");
    }
}
java 复制代码
public class Dog extends Animal{
    double weight;
    String color;
    public void methodA(int a) {
        System.out.println("子类中的A(a)方法");
    }
    public void methodB() {
        System.out.println("子类中的B方法");
    }
    public void methodC() {
        methodA();
        methodA(2);
        methodB();
//输出结果:
父类中的A方法
子类中的A(a)方法
子类中的B方法
    }
}
java 复制代码
public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.methodC();
    }
}

【说明】

当使用子类对象访问方法时,如果父类和子类的方法名称不同,那么会按照以下顺序查找:

先在子类中查找该方法;如果找到了就直接调用;如果子类中不存在,则继续到父类中查找;若父类也没有该方法,则编译器会报错。

当父类与子类存在同名方法,但参数列表不同(即方法重载)时,系统会根据调用时传入的参数来选择最匹配的方法。如果没有任何一个方法匹配,则会报错。

当父类和子类拥有同名的成员变量时,如果想访问父类中的变量,则需要使用super.变量名 的方式进行访问。

1.4 super关键字

如果想在子类方法中访问与子类同名的父类成员,不能直接通过名字访问。为了解决这一问题,Java提供了super关键字,用于在子类的方法中明确调用父类的成员。

java 复制代码
public class Animal {
    String name = "父类";
    int age = 1;
    public void methodA() {
        System.out.println("父类中的A方法");
    }

    public void methodB() {
        System.out.println("父类中的B方法");
    }
}
java 复制代码
public class Dog extends Animal{
    String name = "子类";
    int age = 2;

    public void methodA(int a) {
        System.out.println("子类中的A(a)方法");
    }

    public void methodB() {
        System.out.println("子类中的B方法");
    }

    public void methodC() {
        System.out.println(name);//访问子类的name,
        System.out.println(age);//访问子类的age
        System.out.println(super.name);//访问父类的name
        System.out.println(super.age);//访问父类的age
    }
}
java 复制代码
public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.methodC();
    }
}

在子类的方法中,如果需要明确访问父类中的成员,可以使用super关键字。

【注意事项】

  1. super 只能在非静态方法中使用。

  2. 在子类方法中,可以通过super来访问父类的成员变量成员方法

1.5 子类构造方法

在创建子类对象时,Java 会先调用父类(基类)的构造方法,然后再执行子类自己的构造方法。

java 复制代码
public class Animal {

    public Animal() {
        System.out.println("父类构造方法");
    }

}
public class Dog extends Animal{

    public Dog() {
        System.out.println("子类构造方法");
    }

}
public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();

    }
}
// 输出结果为:父类构造方法
//            子类构造方法
// 说明先调用了基类(父类)构造方法,然后执行子类的构造方法。

注意:

1.如果父类显式定义了无参构造方法(或使用默认构造方法),那么在子类的构造方法中,第一行会隐含地调用super(),也就是自动调用父类的构造方法。

java 复制代码
public Dog() {
    //super();super();没写时,Java在第一行默认有隐含的super();
    System.out.println("子类构造方法");
}

2.如果父类的构造方法带有参数,那么子类必须显式定义自己的构造方法,并在其中选择合适的父类构造方法进行调用,否则编译会失败。

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

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

1.6 super 和 this

super和this都可以在成员方法中用来访问成员变量或调用其他成员方法,也都可以作为构造方法的第一条语句。那么它们之间有什么区别呢?

相同点:

  1. 都是 Java 中的关键字。

  2. 都只能在非静态方法中使用,用于访问非静态成员方法和字段。

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

区别:

  1. 引用对象不同: this 表示当前对象的引用,即调用实例方法的对象;而super表示子类对象中继承自父类的那部分成员的引用。

  2. 访问成员不同 :在非静态成员方法中,this 用于访问本类的方法和属性,super用于访问从父类继承的方法和属性。

  3. 构造方法中调用不同:this(...) 用于调用本类的其他构造方法,**super(...)**用于调用父类的构造方法,两者不能同时出现在同一个构造方法中。

  4. 调用默认行为不同:构造方法中总会存在对super(...)的调用,即使用户没有写,编译器也会自动加上;而this(...)如果用户不写,则不会自动调用。

1.7 初始化

java 复制代码
public class Animal {
    public int age;
    public Animal(int age) {
        this.age = age;
        System.out.println("构造方法");
    }

    {
        System.out.println("实例代码块");
    }

    static{
        System.out.println("静态代码块");
    }
}
java 复制代码
public class TestExtend {
    public static void main(String[] args) {  
Animal animal1 = new Animal(2);
System.out.println("==================");
Animal animal2 = new Animal(2);

    }
}
//输出结果
静态代码块
实例代码块
构造方法
==================
实例代码块
构造方法
  1. 静态代码块 会在类加载阶段执行,并且只执行一次。

  2. 实例代码块 只有在创建对象时才会执行,实例代码块执行完成后,才会执行对应的构造方法。

在继承关系中,这些代码块的执行顺序如下:

java 复制代码
public class Animal {
    public int age;
    public Animal(int age) {
        this.age = age;
        System.out.println("构造方法");
    }

    {
        System.out.println("实例代码块");
    }

    static{
        System.out.println("静态代码块");
    }
}
java 复制代码
public class Dog extends Animal{
    public Dog(int age) {
        super(age);
        System.out.println("子类构造方法");
    }

    {
        System.out.println("子类实例代码块");
    }

    static{
        System.out.println("子类静态代码块");
    }
}
java 复制代码
public class TestExtend {
    public static void main(String[] args) {
        Dog dog1 = new Dog(2);
        System.out.println("==================");
        Dog dog2 = new Dog(2);
    }
}
//输出结果:
静态代码块
子类静态代码块
实例代码块
构造方法
子类实例代码块
子类构造方法
==================
实例代码块
构造方法
子类实例代码块
子类构造方法

结论:

  1. 父类的静态代码块最先执行,优先于子类的静态代码块。

  2. 父类的实例代码块紧接着执行,随后执行父类的构造方法。

  3. 然后执行子类的实例代码块,最后执行子类的构造方法。

  4. 当再次创建子类对象时,父类和子类的静态代码块都不会再执行。

1.8 final关键字

final关键字 可以用来修饰变量、成员方法以及类。

1.当final 修饰变量或字段时,表示该变量是常量,其值在初始化后不可修改。

java 复制代码
final int a = 10;
a = 20; // 编译出错

2.修饰类:表示此类不能被继承

java 复制代码
final public class Animal {
...
}
public class Bird extends Animal {
...
} //语法错误

3.修饰方法:表示该方法不能被重写

相关推荐
后端小张2 小时前
【JAVA 进阶】SpringBoot自动配置机制:从原理到实践的深度解析
java·spring boot·后端·spring·spring cloud·自动配置·注解
毕设源码-赖学姐8 小时前
【开题答辩全过程】以 高校评教评学系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
老华带你飞8 小时前
博物馆展览门户|基于Java博物馆展览门户系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端
liulilittle8 小时前
FileStream C++
开发语言·c++·cocoa
路边草随风8 小时前
iceberg 基于 cosn 构建 catalog
java·大数据
It's now8 小时前
Spring Framework 7.0 原生弹性功能系统讲解
java·后端·spring
点PY9 小时前
C++ 中 std::async 和 std::future 的并发性
java·开发语言·c++
不会代码的小猴9 小时前
C++的第九天笔记
开发语言·c++·笔记
一 乐9 小时前
人事管理系统|基于Springboot+vue的企业人力资源管理系统设计与实现(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·spring boot·后端