浅理解Java中的继承

概念

继承是Java中面向对编程第二个重要特性,它主要解决的是共性抽取问题,同时继承还是多态的前提!那么什么是共性抽取问题呢?我们以生活中的例子感性的理解一下,学校里所有的职工都可以作为了一个Employee类,他们有工号(ID),姓名(name)……,对应的成员方法有工作(work)……。职工中又有老师、助教……,不同类型的职工都有工号、姓名和自己的工作,如果每类职工都编写一个类,代码如下:

class Teacher extends Employee{
    int ID;
    String name;
	// 其他成员变量
    
    public void work(){
        System.out.println("teacher...");
    }
    
    //其他成员方法

}
class Assistant {
    int ID;
    String name;
    // 其他成员变量

    public void work(){
        System.out.println("assistant...");
    }
     //其他成员方法
   
}

// 其他职工类

我们可以看到,不同的职工类的成员变量和成员方法有很多相似的东西,如果要写的类很多时,代码不免就显得有些冗余。因此,我们可以抽取出其中共性的东西构建一个父类,将所有的职工类看作子类,子类可以继承父类的一些成员变量和成员方法,同时它们又有自己独特的内容。

class Employee {
    String name;
    int ID;

    public void showInfo(){
        System.out.println("ID is: " + this.ID + " and name is: " + this.name);
    }

    public void work(){
        System.out.println("employee...");
    }
}

class Teacher extends Employee{
    @Override
    public void work() {
        System.out.println("teacher...");
    }
}

class Assistant extends Employee{
    @Override
    public void work() {
        System.out.println("assistant...");
    }
}


public class ExtendsTest {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        t.ID = 001;
        t.name = "Forlogen";
        t.work();  // teacher...
        t.showInfo(); // ID is: 1 and name is: Forlogen

        Assistant assistant = new Assistant();
        assistant.ID = 110;
        assistant.name = "kobe";
        assistant.work();  // assistant...
        assistant.showInfo();  // ID is: 110 and name is: kobe
    }
}

其中继承中包含有几个重要概念:

  • 父类、基类、超类,如上面的Employee类,代码实现上它就是一个普通的类
  • 子类、派生类,如上面的Teacher类、Assistant类,继承关系通过extends关键字实现
public class 子类名称 extends 父类名称{}

同时继承关系中还有如下特点:

  • 子类可以拥有父类的“内容”,如上面的ID、name、work()、showInfo()
  • 子类还可以拥有自己专有的内容,即子类中还可以实现自己独有的逻辑

成员变量的访问

在父子类的继承关系中,如果成员变量重名,则创建子类对象时,访问有两种方式

  • 直接通过子类对象访问成员变量:等号左边是谁就优先用谁,没有则向上找

    如何理解上面的两句话呢?我们同样通过例子来看一下,假设现在Employee类如下,其中多加了一个成员变量school表示是哪一所院校:

    class Employee {
        String name;
        int ID = 111;
        String school = "STU";
    
        public void showInfo(){
            System.out.println("ID is: " + this.ID + " and name is: " + this.name);
        }
    
        public void work(){
            System.out.println("employee...");
        }
    }
    

    Teacher类如下,它只有自己的ID,并没有school:

    class Teacher extends Employee{
        int ID = 100;
    
        @Override
        public void work() {
            System.out.println("teacher...");
        }
    }
    

    那么,我们实例化一个Teacher类的对象,并访问其中的成员变量:

    public class ExtendsTest1 {
        public static void main(String[] args) {
            Teacher t = new Teacher();
            System.out.println(t.ID);  // 100
            System.out.println(t.school); // STU
        }
    }
    

    现在等号左边的时Teacher,所以优先使用Teacher中的成员变量,因此ID为100;而school在Teacher中没有,因此要继续向它的父类中寻找,最后输出为STU。

  • 间接通过成员方法访问成员变量:该方法属于谁就优先用谁,没有则向上找

    class Employee {
        String name;
        int ID = 111;
        String school = "STU";
        
    	// 获取ID
        public void getID(){
            System.out.println(ID);
        }
    }
    
    class Teacher extends Employee{
        int ID = 100;
    
        public void getTeacherID(){
            System.out.println(ID);
        }
    }
    
    public class ExtendsTest1 {
        public static void main(String[] args) {
            Teacher t = new Teacher();
    
            t.getTeacherID(); // 100
            t.getID();  // 111
        }
    }
    

    如上所示,Employee类中有成员方法getID来访问成员变量ID,Teacher中同样有getTeacherID来访问ID。当我们使用Teacher的对象访问ID时,使用的方法属于谁就优先用谁。getTeacherID属于Teacher,因此优先用它,相应的输出为100,而getID在Teacher中并没有,而是在它的父类中存在,因此这里访问的ID就是Employee的ID,输出111。


重名变量的使用

在子类的实现中,可能会出现继承的父类的成员变量、自己的成员变量以及自己成员方法中的局部变量有重名的情况发生,那么在使用重名变量时如何区分呢?针对不同的变量,可以做如下区分:

  • 局部变量:直接写
  • 本类变量:使用this关键字
  • 父类变量:使用super关键字

在父子类的继承关系中,创建子类对象,访问成员方法的规则:创建的对象是谁就优先用谁,如果没有则向上找

class Employee {
    int ID = 111;
}


class Teacher extends Employee{
    int ID = 100;

    public void showID(){
        int ID = 222;
        System.out.println(ID);
        System.out.println(this.ID);
        System.out.println(super.ID);
    }
}

public class ExtendsTest1 {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        t.showID(); // 222  100  111
    }
}

成员方法的使用

在继承关系中,成员方法的使用和成员变量的使用类似:创建的对象是谁就优先用谁,如果没有则向上找

class Employee {
    String name;
    int ID = 111;
    
    public void showInfo(){
        System.out.println("ID is: " + this.ID + " and name is: " + this.name);  
    }
}

class Teacher extends Employee{
	int ID = 100;
}

public class ExtendsTest2 {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        t.showInfo();  // ID is: 111 and name is: null
    }
}

如上所示,在使用Teacher对象的showInfo()时,如果它有则直接调用,由于它没有就向上找他父类中的方法。


重写(Override)

重写是指在继承关系中方法的名称一样参数的列表也一样

重写(Override)和重载(Overload)的区别:

  • Override:方法的名称一样,参数列表也一样,也称覆盖、覆写
  • Overload:方法的名称一样,参数列表不一样

方法覆盖重写的注意事项:

  • 必须保证父子类之间的方法名称相同,参数列表也相同

@Override只写在方法前面,用来检测是不是有效的方法覆盖

  • 子类方法的返回值必须小于等于父类方法的返回值范围
  • 子类方法的权限必须大于等于父类方法的权限修饰符: public > protected > (default) > private
class Employee {
    String name;
    int ID = 111;

    public void showInfo(){
        System.out.println("ID is: " + ID);
        System.out.println("name is: " + name);
    }
}

class Teacher extends Employee{
    int ID = 100;
    String classroom = "CS";
    int salary = 10000;

    @Override
    public void showInfo() {
        System.out.println("ID is: " + ID);
        System.out.println("name is: " + name);
        System.out.println("classroom is: " + classroom);
        System.out.println("salary is: " + salary);
    }
}

public class OverrideTest {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        t.showInfo();
        /*
        ID is: 100
        name is: null
        classroom is: CS
        salary is: 10000
         */
    }
}

构造方法的访问

继承关系中,父子类构造方法的访问特点:

  • 子类构造方法中有一个默认的"super()"调用,所以一定是先调用父类构造,后执行子类构造
  • 子类调用可以通过super关键字调用父类构造方法
  • super的父类构造调用,必须是子类构造方法的第一个语句,一个子类构造不能调用多次super构造
  • 子类必须调用父类构造方法,不写则使用默认赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个
public class Employee {
    String name;
    int ID = 111;
    String school = "STU";

    public Employee(){
        System.out.println("Employee constructor...");
    }
}

class Teacher extends Employee{
    public Teacher(){
        super();
        System.out.println("Teacher constructor...");
    }
}

public class ExtendsTest3 {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        // Employee constructor...
        //Teacher constructor...
}


super关键字的用法

  • 在子类的成员方法中访问父类的成员变量
  • 在子类的成员方法中访问父类的成员方法
  • 在子类的构造方法中访问父类的构造方法
class Employee {
    String name;
    int ID = 111;
    String school = "STU";

    public void showInfo(){
        System.out.println("ID is: " + ID);
        System.out.println("name is: " + name);
    }
}

class Teacher extends Employee{
    public Teacher(){
        super();
        System.out.println("Teacher constructor...");
    }
    
    public void getFatherInfo(){
        System.out.println(super.school);
        super.showInfo();
    }
}

public class ExtendsTest3 {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        t.getFatherInfo();
        /*
        STU
        ID is: 111
        name is: null
         */
    }
}

this关键字用法

  • 在本类的成员方法中访问本类的成员变量
  • 在本类的成员方法中访问本类的另一个成员方法
  • 在本类的构造方法中访问另一个构造方法,
    • this(…)调用必须是构造方法的第一个语句,唯一一个
    • super和this两种构造不能同时使用

Java中的this关键字


重要特征

  • Java是单继承的,一个类的直接父类只能有一个

    class A{}
    class B extends A{} // 正确
    class C{}
    class D extends A, C{} //错误
    
  • Java语言可以多级继承

    class A{}
    class B extends A{} //正确
    class C extends B{} //正确
    
  • 一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类

    class A{}
    class B extends A{} //正确
    class C extends A{} // 正确
    
发布了461 篇原创文章 · 获赞 122 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/Forlogen/article/details/105640863