Java继承

Java继承是面向对象的重要特性,允许子类复用父类的属性和方法,同时进行扩展。本文介绍了继承的概念、语法、父类成员访问、super关键字的使用、子类构造方法、初始化过程以及final关键字的作用,探讨了继承与组合的区别,强调代码复用时应优先考虑组合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、继承的引入及概念

狗的行为有吃饭、睡觉、汪汪叫等,猫的行为有吃饭、睡觉、喵喵叫等;如果将狗和猫分别写成两个类,Dog类中有吃饭、睡觉、汪汪叫三个方法,Cat类中有吃饭、睡觉、喵喵叫三个方法;此时两个类中的大部分代码是重复的,所以引入了继承。新建一个 Animal 类,Dog 类和 Cat 类中都含有 eat() 方法和 sleep() 方法,所以在Animal 类中写入这两个方法,再新建Dog类和Cat类,分别在其中写入 Bark() 汪汪叫的方法和 mew() 喵喵叫的方法。Animal 称为父类/基类/超类,Dog 类和Cat 类都称为子类/派生类

继承机制:是面向对象程序设计使代码可以复用的最重要的手段,允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生的类称为派生类(子类)。继承实现了面向对象程序设计的层次结构。继承主要解决的问题是共性的抽取,实现代码的复用,也通过继承实现多态

二、继承的语法

修饰符 class 子类 extends 父类{
}

eg.

public class Animal {
    String name;
    int age;

    public void eat(){
        System.out.println(name + "在吃饭~");
    }

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

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

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

public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "二哈";
        dog.age = 2;

        System.out.println(dog.name);
        System.out.println(dog.age);

        dog.eat();
        dog.sleep();
        dog.Bark();

    }
}

运行结果

二哈
2
二哈在吃饭~
二哈在睡觉~
二哈汪汪~

子类会将父类中的成员变量或者成员方法继承到子类中。
子类继承父类后,必须要添加自己特有的成员,体现出与父类的不同,否则就没有必要继承了。
父类中的构造方法没有被子类继承。

三、父类成员访问

1、子类中访问父类的成员变量

子类和父类不存在同名的成员变量
子类中没有与父类相同的成员变量,则访问的时候会访问从父类继承下来的成员变量

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
    }
}

子类和父类成员变量同名
若子类中含有与父类相同的成员变量,则优先访问子类自己的成员变量;若子类中没有则会访问从父类继承下来的成员变量;若父类中也没有,则编译报错。即成员变量的访问遵循就近原则

public class Derived extends Base {
    int a;
    char b;
    
    public void method() {
        a = 10;   //访问子类自己的a
        b = 20;   //访问子类自己的b
        //c = 30;   //编译报错,子类和父类中都没有成员变量c
    }
}

2、子类中访问父类的成员方法

成员方法名字不同
子类中没有与父类相同的成员方法名

public class Base {
    public void methodA(){
        System.out.println("父类中的methodA()");
    }
}
public class Derived extends Base {
   public void methodB(){
       System.out.println("子类中的methodB()");
   }

   public void method(){
       methodA();  // 访问从父类继承的methodA()
       methodB();  // 访问子类自己的methodB()
       // methodC();  // 编译失败,子类和父类中都没有该方法
   }

}

成员方法名字相同
通过子类对象访问父类与子类不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,没有找到则报错。
若父类和子类方法名相同,参数不同,即构成重载,则根据调用的方法传递的参数选择适当的方法访问;若父类和子类同名方法的原型一致(重写),则只能访问到子类的,父类的无法访问到。

public class Base {
    public void methodA(){
        System.out.println("父类中的methodA()");
    }

    public void methodB(){
        System.out.println("父类中的methodB()");
    }
}
public class Derived extends Base {
    public void methodA(int a){
        System.out.println("子类中有参数的methodA(int)");
    }

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

   public void method(){
       methodA();  // 访问从父类继承的methodA()
       methodA(10); // 传参,访问子类自己的methodA(int)
       methodB();  // 访问子类自己的methodB()
       // methodC();  // 编译失败,子类和父类中都没有该方法
   }

}

如果子类存在与父类相同的成员,但需要在子类中访问父类相同名称的成员,则需要使用 super 关键字

四、super关键字

作用:在子类方法中访问父类的成员

public class Base {
    int a;
    int b;
    
    public void methodA(){
        System.out.println("父类中的methodA()");
    }

    public void methodB(){
        System.out.println("父类中的methodB()");
    }
}
public class Derived extends Base {
    int a;
    char b;

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

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

   public void method(){
        //访问子类中的a,b
        a = 10;
        b = 20;

        //访问父类中的a,b
       super.a = 30;
       super.b = 40;


       methodA();  // 访问从父类继承的methodA()
       methodA(10); // 传参,访问子类自己的methodA(int)
       methodB();  // 访问子类自己的methodB()
       super.methodB();  //访问父类中的methodB()
       // methodC();  // 编译失败,子类和父类中都没有该方法
   }

}

在子类方法中,访问父类的成员变量和方法。

super() 和 this()的区分

相同点:
1、都是Java关键字 、
2、只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3、在构造方法中调用时,必须是构造方法中第一条语句,且不能同时存在

不同点:
1、this是当前对象(调用实例方法的对象)的引用,super相当于子类对象从父类继承下来部分成员的引用
2、在非静态成员方法中,this 用来访问本类的方法和属性,super 用来访问父类继承下来的方法和属性
3、this 是非静态成员方法的一个隐藏参数,super 不是隐藏参数
4、成员方法中直接访问本类成员时,编译之后会将this 还原(非静态成员都是通过this来访问的);子类中如果通过super 访问父类成员,编译之后在字节码层面super 是不存在的,super只在代码层面体现出来,告诉编译器访问基类的成员
5、构造方法中一定存在super 的调用,即使用户没有写编译器也会自动添加,this 则是用户不写则没有。

五、子类构造方法

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() 不能同时出现

六、初始化

【没有继承时】 代码执行顺序:

public class Test {
    public String name;
    public int age;

    public Test(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("执行构造方法");
    }

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

    static{
        System.out.println("执行静态代码块");
    }

    public static void main(String[] args) {
        Test p1 = new Test("蓝胖子",18);
        System.out.println("*******创建第二个对象*********");
        Test P2 = new Test("胖虎",12);

    }
}

执行静态代码块
执行实例代码块
执行构造方法
*******创建第二个对象*********
执行实例代码块
执行构造方法

静态代码块先执行,并且只执行一次,在类加载阶段执行;
当有对象创建时,才会执行实例代码块,实例代码块执行完成后,构造方法执行。
【有继承关系时】

public class Base {

    public String name;
    public int age;
    public Base(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("父类:构造方法");
    }

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

    static{
        System.out.println("父类:静态代码块");
    }

}
 public class Derived extends Base {
    public Derived(String name,int age){
        super(name, age);
        System.out.println("子类:构造方法");
    }

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

    static{
        System.out.println("子类:静态代码块");
    }

}
public class Test {

    public static void main(String[] args) {
        Derived d1 = new Derived("蓝胖子",18);
        System.out.println("*******创建第二个子类对象*********");
        Derived d2 = new Derived("胖虎",12);

    }

    public static void main1(String[] args) {
        Base d1 = new Base("静香",11);
        System.out.println("*******创建第二个父类对象*********");
        Base d2 = new Base("大雄",12);

    }

}

运行结果

父类:静态代码块
子类:静态代码块
父类:实例代码块
父类:构造方法
子类:实例代码块
子类:构造方法
*******创建第二个子类对象*********
父类:实例代码块
父类:构造方法
子类:实例代码块
子类:构造方法

若执行main1函数,则只执行父类中的方法,如下

父类:静态代码块
父类:实例代码块
父类:构造方法
*******创建第二个父类对象*********
父类:实例代码块
父类:构造方法

一般情况下,将所有字段设为private,将所有方法设为public,不过还是建议写代码的时候想一想,不滥用访问权限

七、继承方式

  1. 单继承:一个父类对应一个子类
  2. 多层继承:一个父类对应一个子类,子类再对应下个子类
  3. 不同类继承同一个类:多个子类继承一个父类
  4. 多继承(Java中不支持):一个子类对应多个父类
    注意:Java中不支持多继承
    一般不希望继承超过三层;如果继承层次太多,则需要考虑对代码的重构。
    如果想要从语法层面进行限制继承,就可以使用 final 关键字。

八、final关键字

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

1、修饰变量或字段,表示常量(不能修改)
2、修饰类:表示此类不能被继承
3、修饰方法:表示该方法不能被重写

九、继承与组合

继承是 is-a 的关系
组合是 has-a 的关系
比如,汽车和轮胎类、发动机、方向盘、车载系统等关系是组合
宝马、大众、奥迪属于汽车,是继承关系
代码复用优先选择组合
原因(继承实现代码复用的缺点):
1、破坏类的封装性
2、子类和父类的耦合性太高(父类某个方法发生改变,子类一定会看到改变;基类某个方法多加了一个参数,子类调用该方法的函数都需要修改)
3、子类除了构造方法没有继承下来外,其他成员都继承到子类中了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值