Java面向对象之继承(extends)

5 阅读7分钟

extends继承

继承是面向对象里面很重要的概念,继承允许我们创建分等级层次的类。

引入

  • 比如我们有一个学生类
import java.util.HashMap;  
  
public class Student {  
    int number;  
    int age;  
    String name;  
    int gender;  
  
    public Student(int number, int age, String name, int gender) {  
        this.number = number;  
        this.age = age;  
        this.name = name;  
        this.gender = gender;  
    }  
  
    public Student(int number, String name) {  
        this.number = number;  
        this.name = name;  
    }  
  
    public Student() {  
  
    }  
  
    @Override  
    public String toString() {  
        return "com.ls.Student{" +  
                "number=" + number +  
                ", age=" + age +  
                ", name='" + name + ''' +  
                ", gender=" + gender +  
                '}';  
    }  
}
  • 有一个教师类
public class Teacher {  
    int age;  
    String name;  
    int gender;  
    int experience; // 教龄  
  
    @Override  
    public String toString() {  
        return "Teacher{" +  
                "age=" + age +  
                ", name='" + name + ''' +  
                ", gender=" + gender +  
                ", experience=" + experience +  
                '}';  
    }  
  
    public Teacher(int experience, int age, String name, int gender) {  
        this.age = age;  
        this.name = name;  
        this.gender = gender;  
        this.experience = experience;  
    }  
  
    public Teacher(){}  
  
    public static void main(String[] args) {  
  
    }  
}
  • 现在我们用一个School类来管理这两个类,如果不使用继承,我们发现会重复写了很多方法的重载如add方法,这会大大减慢我们的编程效率
import java.util.ArrayList;  
  
public class School {  
    //因为Student使用public修饰,所以我们可以在任何地方访问student类中的内容,并创建Student对象  
    ArrayList<Student> students = new ArrayList<>();  
    ArrayList<Teacher> teachers = new ArrayList<>();  
    //添加学生方法  
    public void add(Student s) {  
        students.add(s);  
    }  
  
    public void add(Teacher t) {  
        teachers.add(t);  
    }  
  
    public void query() {  
        for (Student student : students){  
            System.out.println("查询学生信息" + students);  
        }  
  
        for (Teacher teacher : teachers){  
            System.out.println("查询老师信息" + teachers);  
        }  
    }  
  
    public static void main(String[] args) {  
        School school = new School();  
        Student laosun = new Student(10, 18, "老孙", 1);  
        Student laowang = new Student(11, 19, "老王", 1);  
        Student laowu = new Student(12, 20, "老吴", 1);  
        Student laoli = new Student(13, 21, "老李", 0);  
  
        Teacher laozhang = new Teacher(5,30,"老张",1);  
        Teacher laoyu = new Teacher(10,35,"老余",1);  
        Teacher laoliu = new Teacher(15,40,"老刘",0);  
        //添加学生  
        school.add(laosun);  
        school.add(laowang);  
        school.add(laowu);  
        school.add(laoli);  
        //添加老师  
        school.add(laozhang);  
        school.add(laoyu);  
        school.add(laoliu);  
  
        school.query();  
  
    }  
}
  • 有没有什么方法可以提高我们的编程效率呢,当然有,我们可以为Student类和Teacher类共同创建一个父类People,并在School中使用该父类People对Student和Teacher进行统一管理

    • 首先创建一个People类将Student和Teacher都有的属性和方法进行统一管理
    //People类
    package com.ls;  
    public class People {  
        //protected修饰的变量,一个包下面可以访问,不同包下面的子类可访问
        protected int gender;  
        protected int age;  
        private String name;  
      
        public People(){  
      
        }  
        public People(String name) {  
            this.name = name;  
        }  
        public String  getName() {  
            return this.name;  
        }  
        public String setName(String value) {  
            return this.name = value;  
        }  
    }
    
    • 之后我们精简Student类和Teacher类

      • Student类
    	import java.util.HashMap;  
    	  
    	public class Student extends People{  
    	    int number;  
    	  
    	  
    	    public Student(int number, int age, String name, int gender) {  
    	        super(name);
    	        this.number = number;  
    	        this.age = age;  
               //this.name = name; 
               //因为在父类种给name设置了private,所以该成员变量只能在people类中访问
    	        this.gender = gender;  
    	    }  
    	  
    	    public Student(int number, String name) {  
    	        super(name);  
    	        this.number = number;  
               //this.name = name;  
    	    }  
    	  
    	    public Student() {  
    	    //   super("无");//父类的构造方法会先加载  
    	    //我们重载了父类的构造方法,创建了一个不需要传值的父类构造方法,这样就不用在使用super向父类的构造方法传入值了  
    	  
    	    }  
    	  
    	    @Override  
    	    public String toString() {  
    	        String name = getName();  
    	        return "com.ls.Student{" +  
    	                "number=" + number +  
    	                ", age=" + age +  
    	                ", name='" + name + ''' +  
    	                ", gender=" + gender +  
    	                '}';  
    	    }  
    	  
    	    public static void main(String[] args) {  
    }
    
    • Teacher类
public class Teacher extends People{
    //    int gender;  
    //    int age;  
    //    String name;  
    int experience; // 教龄  

    @Override
    public String toString() {
        String name = getName();
        return "Teacher{" +
                "age=" + age +
                ", name='" + name + ''' +  
        ", gender=" + gender +
                ", experience=" + experience +
                '}';
    }

    public Teacher(int experience, int age, String name, int gender) {
        // this.age = age;  
        // super.age = age;  
        // this.name = name;  
        setName(name);
        this.gender = gender;
        this.experience = experience;
    }

    public Teacher(){}

    public static void main(String[] args) {

    }
}
  • 最后我们在School类中使用People对人员进行管理,而不是Teacher和Student,比如将之前的Arraylist改为People类的Arrylist
import javax.lang.model.element.PackageElement;
import java.util.ArrayList;

public class School {
    //因为Student使用public修饰,所以我们可以在任何地方访问student类中的内容,并创建Student对象  
    //ArrayList<Student> students = new ArrayList<>();  
    //ArrayList<Teacher> teachers = new ArrayList<>();  
    //添加学生方法  
   /*public void add(Student s) {  
        students.add(s);    
     }    
     public void add(Teacher t) {
        teachers.add(t);
     }*/
    ArrayList<People> peoples = new ArrayList<>();
    public void add(People people){
        peoples.add(people);
    }

    /*public void query() {  
        for (Student student : students){
            System.out.println("查询学生信息" + students);  
        }  
        for (Teacher teacher : teachers){
            System.out.println("查询老师信息" + teachers);  
        }    
    }*/
    // 父类可以持有子类的对象
    public void query(){
        for (People people : peoples){
            System.out.println("信息查询:" + people);
        }
    }



    public static void main(String[] args) {
        School school = new School();
        Student laosun = new Student(10, 18, "老孙", 1);
        Student laowang = new Student(11, 19, "老王", 1);
        Student laowu = new Student(12, 20, "老吴", 1);
        Student laoli = new Student(13, 21, "老李", 0);

        Teacher laozhang = new Teacher(5,30,"老张",1);
        Teacher laoyu = new Teacher(10,35,"老余",1);
        Teacher laoliu = new Teacher(15,40,"老刘",0);
        //添加学生  
        school.add(laosun);
        school.add(laowang);
        school.add(laowu);
        school.add(laoli);
        //添加老师  
        school.add(laozhang);
        school.add(laoyu);
        school.add(laoliu);
        //People.add()  
        school.query();
        People p1 = new Student(10,10,"老孙",1);
        System.out.println(p1);  
       /*Student stu1 = (Student)p1;  
       int number = stu1.number;*/
        int number = ((Student)p1).number;
        System.out.println("学号是:" + number);
    }
}

概念

  • 在 Java 中,继承是一种面向对象编程的基本机制,它允许一个类(称为子类)继承另一个类(称为父类或基类),子类可以直接使用继承自父类的属性和方法,除法使用private声明。
  • 父类不能直接使用子类的属性和方法,要使用子类示例化的对象进行访问,或者使用持有子类的父类对象,进行访问。
  • 使用private修饰的属性和方法是无法继承的,或者更仔细的说是属性可以继承,但或权限隔开子类无法拿到private修饰的属性,方法则是直接无法继承。

super

  • super关键字用来对该对象父类中被子类隐藏的属性和方法
父类构造方法对子类的影响
  • 每次创建某子类的时候,java会默认将父类的构造方法加载进来,这就意味着,我们在创建子类的构造方法的时候父类的构造方法会自动加载到子类的构造方法中。super(...)即可访问到父类的构造方法,所以这里我们可以利用super关键字来选择所需的构造方法。

    • 比如如果直接使用super()无参数,那么就是使用父类中无参数的构造方法,这实际上可以不写
    • 同理,super(String value)则是使用了父类中有String类型的参数的方法
    • 这里实际上利用的是父类构造方法的重载,其原理是创建子类时java会默认将父类的构造方法加载进来
    • 注意的是,如果父类中没有无参数的构造方法,那么不使用super(value)向父类构造方法传参并调用,是会报错的。
什么时候使用super
  • 如果子类中没有重写父类的方法,是可以直接调用父类的方法,不需要使用 super
  • 如果子类重写了父类的方法,并且希望在重写的方法中调用父类的实现,这时就需要使用 super 关键字
  • 如果父类构造方法带有参数,那么子类在创建构造方法时必须要使用super(Class value)向父类传参,因为创建子类时java会默认将父类的构造方法加载进来
super使用的是什么算法
  • java中的super不像python的super遵循C3算法,而是遵循线性规则,因为java是单继承语言一个类只有一个父类,因此不会出现钻石继承,继承链是树状的,不需要进行图的遍历(C3算法也是一种图遍历算法)

子类对象当父类对象使用

  • 注意:以下示例中父类为People子类为Student和Teacher
  • 子类对象可以被当作父类对象来使用,就像之前的例子,我们可以直接将Student类的对象、Teacher类的对象传入People类的Arraylist中
//1.
People p1 = new Student(10,10,"老孙",1);
//2.
ArrayList<People> peoples = new ArrayList<>();
public void add(People people){
    peoples.add(people);  
}
Student laosun = new Student(10, 18, "老孙", 1);
//子类对象
school.add(laosun);
//调用了add方法父类变量peoples持有了子类对象
//3.
ArrayList<People> peoples = new ArrayList<>();
public void add(People people){  
    peoples.add(people);  
}
People laosun = new Student(10, 18, "老孙", 1);
//直接让laosun父类变量持有
school.add(laosun);
//调用了add方法父类变量peoples持有了laosun

向上转型

  • 注意:以下示例中父类为People子类为Student和Teacher
  • 父类可以直接持有子类(子类对象可以直接被当作父类对象使用)
People p1 = new Student(10,10,"老孙",1);
  • 可以看到虽然我们让父类变量p1持有了子类对象(向上转型),但编译器仍然可以发现这是一个Student对象
    image.png

向下转型

  • java创建对象是不能向下转型,即使是强制类型转换也不行 Student stu1 = (Student) new People();//这行代码是无法运行的
  • 但是我们可以将向上转型的对象重新向下转型,以便于访问该对象原本类中的属性和方法,比如,当我们想要获得向上转型的对象p1的属性值时,要将它强制转换为子类型,如下方代码所示:
//number属性属于Student类
People p1 = new Student (10, 10, "老孙", 1);
//p1.number;//这里会报错,因为编译器会看不懂,会认为p1就是一个people类型,而People类中没有number属性
Student stu1 = (Student) p1; //将P1强制转换为Student类,这里代码就不会报错
int number = stu1.number;
System.out.println("sut1 number = " + number)