一、继承

1.1继承的概念

  • 继承是Java面向对象编程的基石,允许创建分等级层次的类
  • 继承就是子类继承父类的行为和特征,使得子类对象有父类的实例域或方法
  • 子类从父类继承方法,使子类具有父类相同的行为
  • 继承是从已有的类中派生出新的类,新的类能吸收已有的

举例:

  • 兔子和山羊的父类是食草动物,老虎和狮子的父类是食肉动物
  • 食草动物和食肉动物的父类是动物
  • 所以继承要符合的条件是is-a
  • 父类比子类的概括性更强,子类比父类更加具体化
  • 虽然食草动物和食肉动物都属于动物的子类,但是两者的行为和特征都有所差别,所以子类会有父类的一般特征但也有自身的特征 

1.2类的继承方式

在Java中可以使用extends关键字申明一个类是从另一个类继承下来的,格式如下

class 父类 {

}

class 子类 extends 父类 {

}

代码形式如下:

//子类Student继承父类People
public class Student extends People
  • Student称之为子类(派生类),People称之为父类(基类或者超类)

作用:当子类继承父类后,就可以使用父类的方法和属性了

  • 举例一:子类继承了父类,就可以调用父类的方法

父类:

//父类
public class People {
    public void say(){
        System.out.println("Hello World");
    }
}

子类:利用关键字“extends"进行继承


//子类
public class Men extends People {
}

启动项:

public class DemoApplication {

    public static void main(String[] args) {


        //利用new关键字创建一个子类的对象
        Men man = new Men();
        //通过子类对象调用父类的方法
        man.say();
    }
}

结论:子类man可以通过继承父类People从而调用父类中的方法say()

  • 举例2:有一个Dog类,有一个Cat类

狗类:

//狗类
public class Dog {
    public String name;
    public int age;

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

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

猫类:

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

    public void dogCall(){
        System.out.println(name + "喵喵喵");
    }

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

观察发现Dog类和Cat类有相同的成员变量成员方法,为了避免写重复代码,将其中相同的部分提取出来,组成一个父类Animal:

父类Animal:包含有Dog类和Cat类公共的属性和方法

public class Animal {
    
    //Dog类和Cat类公共的属性
    public String name;
    public int age;

    //Dog类和Cat类公共的方法
    public void eat() {
        System.out.println(name + "吃饭");
    }
}

将Dog类和Cat类的相同属性和方法都放到一个类中,组成了父类动物类,父类动物类在原来的特性上面加以拓展,增加Dog的新功能(例如这里Dog类的功能是"汪汪叫“),就有了Dog类,那么Dog就需要继承动物类

  • Dog和Cat类都需要继承Animal类
  • Animal类就称为 父类/ 基类 /超类
  • Dog类 / Cat类 就称为 子类 / 派生类
  • 继承之后子类可以复用父类的成员,子类只需要关心自己独有的属性或行为
  • 子类可以继承父类的属性和行为,但不能继承父类的构造器
  • 子类拥有父类非private的属性、方法
  • 子类继承父类之后,必须添加属于自己的新成员变量或者属性,否则继承就没有意义了

增加父类Animal之后的Dog类:


//Dog通过extends关键字继承父类Animal
public class Dog extends Animal {

    //子类Dog独有的方法
    public void dogCall(){
        System.out.println(name + "汪汪汪");
    }

}

增加父类Animal之后的Cat类:


//Cat类通过extends关键字继承父类Animal
public class Cat extends Animal {

    //子类Cat独有的方法
    public void catCall(){
        System.out.println(name + "喵喵喵");
    }


}

二、父类成员访问

子类将父类的成员继承了下来,包括成员属性和成员方法,下面探讨如何访问

2.1 子类访问父类的成员变量

  • 访问父类与子类不同名的成员变量:直接访问
//父类
public class Animal {

    //Dog类和Cat类公共的属性
    public String name;
    public int age;

    //Dog类和Cat类公共的方法
    public void eat() {
        System.out.println(name + "吃饭");
    }
}


//子类
public class Dog extends Animal {

    //这里的colour就是子类与父类不同名的成员变量
    public String colour;


    //子类Dog独有的方法
    public void dogCall(){
        System.out.println(name + "汪汪汪");
    }

}
  • 访问父类成员的属性被private修饰时,前面介绍过被private修饰的变量只能在同一个包同一个文件的同一个类下使用,那么此时如何访问:可以利用Generate进行封装

例如:在父类中的age属性使用private修饰,即使DOG继承了父类,也不能对这个属性进行访问,解决办法是对这个属性进行封装,

父类Animal:利用右键快捷键选择Generate对属性进行封装

//父类
public class Animal {

    //Dog类和Cat类公共的属性
    public String name;
    private int age;


    //右键选择Generate进行封装
    public int getAge() {
        return age;
    }
    //右键选择Generate进行封装
    public void setAge(int age) {
        this.age = age;
    }

    //Dog类和Cat类公共的方法
    public void eat() {
        System.out.println(name + "吃饭");
    }
}

子类Dog类:

//子类
public class Dog extends Animal {

    //这里的colour就是子类与父类不同名的成员变量
    public String colour;


    //子类Dog独有的方法
    public void dogCall(){

        //将从父类继承的name属性赋值
        String name = "大黄";

        //通过getAge访问父类的私有属性age
        System.out.println(name + getAge() + "汪汪汪");
    }

}

启动项:


public class DemoApplication {

    public static void main(String[] args) {

        //通过New关键字进行创建dog对象
        Dog dog = new Dog();

        //通过子类对象访问父类的私有属性age并进行赋值
        dog.setAge(1);

        //通过对象dog调用子类方法
        dog.dogCall();
        
    }
}

打印输出结果:

大黄1汪汪汪

  • 访问父类与子类同名的成员变量
     

父类:


//父类
public class Animal {

    String name = "阿黄";
    
    //Dog类和Cat类公共的方法
    public void eat() {
        System.out.println(name + "吃饭");
    }
}

子类:

//子类
public class Dog extends Animal {

    //子类Dog独有的方法
    public void dogCall(){

        //将从父类继承的name属性赋值
        String name = "阿福";
        System.out.println(name  + "汪汪汪");
    }

}

启动项:

//启动项
public class DemoApplication {

    public static void main(String[] args) {

        //通过New关键字进行创建dog对象
        Dog dog = new Dog();

        //通过对象dog调用子类方法
        dog.dogCall();
    }
}

打印输出:打印输出的为子类中的赋值

阿福汪汪汪

父类中有属性名为name,子类中也有属性名name,且他们的类型相同,我们通过打印可以看到打印输出的为子类name成员变量的值

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

  • 如果访问的成员变量子类有,优先访问子类
  • 如果访问的成员变量子类没有,访问从父类继承下来的成员变量,如果父类也没有,则编译报错
  • 如果访问的成员变量子类和父类同名,根据就近原则优先访问子类的成员变量,
  • 也可以通过关键字类访问成员变量(访问子类本类的用this,访问父类的用super)

2.2 子类访问父类的成员方法

  • 访问父类中与子类不同名的方法:直接访问

父类:父类方法eat()

//父类
public class Animal {

    String name = "阿黄";

    //父类的方法eat()
    public void eat() {
        System.out.println(name + "吃饭");
    }
}

子类:子类方法dogCall()

//子类
public class Dog extends Animal {

    //子类Dog独有的方法dogCall()
    public void dogCall(){
        //将父类的方法一起添加进来,方便调用
        eat();

        //将从父类继承的name属性赋值
        String name = "阿福";
        System.out.println(name  + "汪汪汪");
    }

}

启动项:

//启动项
public class DemoApplication {

    public static void main(String[] args) {

        //通过New关键字进行创建dog对象
        Dog dog = new Dog();

        //通过对象dog调用子类方法
        dog.dogCall();
    }
}

打印输出:

阿黄吃饭
阿福汪汪汪
  • 访问父类与子类中同名的方法:优先访问子类

父类:eat()方法

//父类
public class Animal {

    String name = "阿黄";

    //父类的方法eat()
    public void eat() {
        System.out.println(name + "吃饭");
    }
}

子类:eat()方法

//子类
public class Dog extends Animal {

    //子类Dog的方法eat()
    public void eat(){
        //将父类的方法一起添加进来,方便调用

        //将从父类继承的name属性赋值
        String name = "阿福";
        System.out.println(name  + "汪汪汪");
    }

}

启动项:

//启动项
public class DemoApplication {j

    public static void main(String[] args) {

        //通过New关键字进行创建dog对象
        Dog dog = new Dog();

        //通过对象dog调用子类方法
        dog.eat();
    }
}

打印输出:

阿福汪汪汪

结论:当访问父类和子类同名的方法时,优先访问子类的成员方法

那么现在存在这样一个问题:

如果父类和子类的成员变量和成员方法相同,访问时优先级肯定时子类更高,如何通过子类的方法去访问父类的成员变量或者方法呢

三、Super关键字详解

上面验证了如果子类和父类的变量或者方法相同,那么通过子类的方法去访问,一定会访问到子类的方法,那么如何才能访问到父类中相同的方法和变量呢,就是使用super关键字

super.成员变量 :表示访问父类的成员变量

super.成员方法 : 表示访问父类的成员方法

super(): 表示访问父类的构造方法

super关键字的作用是:在子类中访问父类的成员方法或者变量

如果我们在子类中看到了super关键字,那么一定是访问了父类的成员方法或者变量

父类:


//父类
public class Animal {

    String name = "阿黄";

    //父类的方法eat()
    public void eat() {
        System.out.println(name + "吃饭");
    }
}

子类:利用super关键字访问父类中同名变量name

//子类
public class Dog extends Animal {

    //子类Dog的方法eat()
    public void eat(){

        //将从父类继承的name属性赋值
        String name = "阿福";
        //利用super关键字访问父类中同名的成员变量name
        System.out.println(super.name  + "汪汪汪");
    }

}

启动项:

public class DemoApplication {

    public static void main(String[] args) {

        //通过New关键字进行创建dog对象
        Dog dog = new Dog();

        //通过对象dog调用子类方法
        dog.eat();
    }
}

打印输出:成功利用super关键字调用了父类的name

阿黄汪汪汪

在上述代码中,我们在Dog类的eat方法中,使用了super.name,即调用了父类的“name=阿黄”,所以打印的name是父类中的name,通过运行结果来看也确实如此

  • super关键字只能在非静态方法中使用
  • super在子类方法中,访问父类的成员方法和变量

三、子类的构造器

子类之所以叫子类,因为它继承了父类,但子类是不继承父类的构造器的,他只是进行一个调用,

  • 如果父类的构造器没有参数,则子类的构造器不需要使用super关键字调用父类的构造器,系统会自动调用父类的无参构造
  • 如果父类的构造器带有参数,则必须在子类构造器中通过super关键字调用父类构造器并赋予相应的形参列表
  • super(实参列表)必须在构造器的第一行

父类:

//父类
public class Animal {

    private int age;

    //父类的无参构造
    public Animal() {
        System.out.println("Animal()的无参构造");
    }


    //父类的有参构造
    public Animal(int age) {
        System.out.println("Animal()的有参构造");
        this.age = age;
    }
}

Dog类:利用super调用父类中的有参构造器

//子类
public class Dog extends Animal {

    private int age;

    //自动调用父类的无参构造
    public Dog() {
        System.out.println("Dog的无参构造");
    }

    public Dog(int age) {

        //利用super调用父类中的有参构造器
        super(200);
        System.out.println("Dog的有参构造"+age);

        this.age = age;
    }
}

Cat类利用super调用父类中的有参构造器

//Cat类通过extends关键字继承父类Animal
public class Cat extends Animal {

    private int age;


    //调用的无参构造
    public Cat() {
        System.out.println("Cat的无参构造");
    }


    public Cat(int age) {
        //利用super调用父类中的有参构造
        super(100);
        System.out.println("Cat的有参构造"+age);

        this.age = age;
    }
}

启动项:


public class DemoApplication {

    public static void main(String[] args) {

        System.out.println("Dog类继承-----------");
        Dog dog = new Dog();
        Dog dog1 = new Dog(300);

        System.out.println("Cat类继承------------");
        Cat cat = new Cat();
        new Cat(400);


    }
}

控制台打印输出:

Dog类继承-----------
Animal()的无参构造
Dog的无参构造
Animal()的有参构造
Dog的有参构造300
Cat类继承------------
Animal()的无参构造
Cat的无参构造
Animal()的有参构造
Cat的有参构造400


Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐