JavaSE-继承

发布于:2025-07-09 ⋅ 阅读:(13) ⋅ 点赞:(0)

继承(inheritance)

继承的意义

我们首先来看下面两个类:

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

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

    public void bark(){
        System.out.println(this.name+"正在汪汪叫");
    }
}
public class Cat {
    public String name;
    public int age;

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

    public void miao(){
        System.out.println(this.name+"正在喵喵叫");
    }
}

这两个名为Dog和Cat的类拥有相同的成员变量name、age以及相同的成员方法eat

在主方法中可以正常调用他们的变量和方法。

public class test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name="大黄";
        dog.eat();
        dog.bark();

        System.out.println("============");

        Cat cat = new Cat();
        cat.name="大橘";
        cat.eat();
        cat.miao();
    }
}

 我们可以发现在Dog和Cat中就具有相同的成员,如果每次新建一个动物类时都要加入这些相同成员,未免有些麻烦,于是继承出现了。

如何继承

新建一个Animals类,将动物所具有的共同成员放入其中并删去原来类中的共同成员。

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

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

在原来的类名后加上extends Animals。

格式如下:

修饰符 class 子类名 extends 父类名{

public class Dog extends Animals{


    public void bark(){
        System.out.println(this.name+"正在汪汪叫");
    }
}
public class Cat extends Animals{


    public void miao(){
        System.out.println(this.name+"正在喵喵叫");
    }
}

这样原来的主函数依然可以正常运行。Animals就称为父类/基类/超类,Dog和Cat就叫做子类或者派生类。通过继承,我们实现了代码的复用。

继承中的访问与调用

子类访问父类成员变量

下面三段代码分别是子类,父类和主方法。

package demo2;

public class Derived extends Base{
    public int c=3;

    
    public void func(){
        System.out.println(this.a);
        System.out.println(this.b);
        System.out.println(this.c);
    }
}
package demo2;

public class Base {
    public int a=1;
    public int b=2;
}
package demo2;

public class test {
    public static void main(String[] args) {
        Derived derived =new Derived();
        derived.func();
    }
}

运行结果:

 子类访问了继承自父类的a和b,并访问了自身的成员变量c。

在子类中加入a=100:

package demo2;

public class Derived extends Base{
    public int c=3;

    public int a=100;
    public void func(){
        System.out.println(this.a);
        System.out.println(this.b);
        System.out.println(this.c);
    }
}

运行结果:

可见当父类和子类中同时出现变量a时,this关键字访问的是子类自身的a。

如何在子类中访问父类中的同名变量a?

使用super关键字。

package demo2;

public class Derived extends Base{
    public int c=3;

    public int a=100;
    public void func(){
        System.out.println(super.a);
        System.out.println(this.b);
        System.out.println(this.c);
    }
}

运行结果:

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

1.如果访问的成员变量子类中有,优先访问自己的成员变量。

2.如果访问的成员变量子类中无,则访问父类继承下来的,如果父类中也无,则编译报错。

3.如果访问的成员变量与父类中的成员变量同名,则优先访问自己的。

子类访问父类成员方法

当子类中成员方法与父类中成员方法不同名时,可以直接在子类中调用父类成员方法:

public class Base {
    public int a=1;
    public int b=2;
    public void testBase(){
        System.out.println("testBase---");
    }
}
public class Derived extends Base{
    public int c=3;

    public int a=100;
    public void testDerived(){
        System.out.println("testDerived---");
    }
    public void test(){
        testBase();
        testDerived();
    }
}

public class test {
    public static void main(String[] args) {
        Derived derived =new Derived();
        derived.test();
    }
}

运行结果:

 当子类中方法与父类中方法重名时,优先就近原则调用子类自己的方法,同时可以构成方法的重载和方法的重写:

方法的重载:

public class Base {
    public int a=1;
    public int b=2;
    public void testA(){
        System.out.println("testA()---");
    }
}

public class Derived extends Base{
    public int c=3;

    public int a=100;
    public void testA(char a){
        System.out.println("testA(char a)---");
    }
    public void test(){
        testA();
        testA('a');
    }
}

public class test {
    public static void main(String[] args) {
        Derived derived =new Derived();
        derived.test();
    }
}

 并且可以使用super关键字调用父类方法:

public class Base {
    public int a=1;
    public int b=2;
    public void testA(){
        System.out.println("testA(Base)---");
    }
}

public class Derived extends Base{
    public int c=3;

    public int a=100;
    public void testA(){
        System.out.println("testA(Derived)---");
    }
    public void test(){
        testA();
        super.testA();
    }
}

public class test {
    public static void main(String[] args) {
        Derived derived =new Derived();
        derived.test();
    }
}

运行结果:

但是super只能指代父类,不能指代父类的父类玩套娃。

当子类继承父类之后,在不使用构造方法的情况下,java会自动为子类和父类提供构造方法,但当我们为类添加构造方法时,子类需要显式的调用父类的构造方法,帮助父类完成初始化。

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

    public void eat(){
        System.out.println(this.name+"正在吃饭");
    }
    public Animals(String name,int age){
        this.name=name;
        this.age=age;
    }
}

public class Dog extends Animals {


    public void bark(){
        System.out.println(this.name+"正在汪汪叫");
    }
    public Dog(String name,int age){
        super(name,age);
    }
}

public class test {
    public static void main(String[] args) {
        Dog dog = new Dog("大黄",3);
        System.out.println("name:"+dog.name+" age:"+dog.age);
    }
}

简而言之,当为父类添加构造方法时,需要同时在子类中添加构造方法并在第一行使用super关键字帮助父类初始化。

super和this关键字

1.super()和this()构造方法只能在第一行使用,并且不能同时出现。 

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

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

4.构造方法中一定会有super()的调用,即使用户没有写编译器也会添加,但this()用户不写则没有。

继承中的代码块运行顺序

猜测下列标号代码块的运行顺序:

public class Animals {
    public String name;
    public int age;
    static{
        System.out.println("static::Animal{}");
    }//1
    {
        System.out.println("实例化代码块 Animal");
    }//2
    public Animals(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("Animal()");
    }//3
}

public class Cat extends Animals {
    static{
        System.out.println("static::Cat{}");
    }//4
    {
        System.out.println("实例化代码块 Cat{}");
    }//5

    public void miao(){
        System.out.println(this.name+"正在喵喵叫");
    }
    public Cat(String name,int age){
        super(name,age);
        System.out.println("Cat()");
    }//6
}

public class test {
    public static void main(String[] args) {
        Cat cat=new Cat("大橘",4);

    }
}

运行结果:

运行顺序是:142356

从中可以得出代码块的运行逻辑:先执行父类,子类的静态代码块,再执行父类的实例化和初始化,再执行子类的实例化和初始化。

 继承的权限

private:仅允许本类中调用

default:允许同包中的不同类使用

protected:允许不同包中的子类使用

public:允许不同包的非子类中使用

在继承的权限使用中,我们要尽可能的做到封装:即最大的私密性,隐藏内部细节,只显露出必要的内容给使用者,能够使用private,就尽量不要使用public。有一个暴力的方法,就是将所有的变量设置为private权限,将所有的方法设置为public权限。

final关键字

继承关系不宜有太多层,一般三层为上限。final在java中对变量修饰可以将其变为不可改变的常量,对子类修饰则可以防止这个类被继承,当它被继承时编译器会报错,这个类就被称为密封类。

继承与组合

继承中子类与父类的关系可以表示为子类 is a 父类,而组合可以表示为前者 has a后者,或者后者 is a part of 前者,如汽车和轮胎的关系。

class Student{
    
}

class Teacher{
    
}

public class school {
    public Student[] student=new Student[10];
    
    public Teacher[] teacher=new Teacher[10];
}

学生和老师是学校的一部分,这就是组合关系。


网站公告

今日签到

点亮在社区的每一天
去签到