大数据第十天

继承extends(也叫扩展)

引入

首先写两个类:

    //定义学生类

    class Student {

       //成员变量

       private String name;

       private int age;

       //空构造

       public Student(){}

      

       //getXxx()/setXxx()

   

       public void eat() {

           System.out.println("吃饭");

       }

    }

   

    //定义教师类

    class Teacher {

       //成员变量

       private String name;

       private int age;

       //空构造

       public Teacher(){}

      

       //getXxx()/setXxx()

      

       public void eat() {

           System.out.println("吃饭");

       }

    }

我们观察上面两个类代码:

发现name,age成员变量,以及getXxx()/setXxx(),还有eat()等都是相同的。如果我们后来继续定义类,比如,工人类,运动员类。他们肯定也具备这些内容。那么,我们每一次定义这样的类的时候,都要把这些重复的内容都重新定义一遍。

太麻烦,重复的代码太多,所以,要考虑改进

如何改进呢?

能不能把这些相同的内容给定义到一个独立的类中。然后,让这多个类和这个独立的类产生一个关系,有了这个关系后,这多个类就可以具备这个独立的类的功能。

为了实现这个效果,Java提供了一个技术:继承(也叫“扩展”)

举例:

父亲:4个儿子

继承怎么表示呢?继承的格式是什么样子的呢?

class Father {...}              //定义一个Father类,被继承的类

class Son extends Father {  //定义一个Son类继承(或叫扩展)Father类

}

修改我们的代码:

将Student,Teacher类共同的东西抽取出来,单独放到一个类中,叫Person

    class Person {

       String name;

       int age;

       public Person(){}

      

       //getXxx()/setXxx()

   

       public void eat() {

           System.out.println("吃饭");

       }

    }

    //自定义类继承自抽象出来的Person类,这样,Person中定义的方法就不用再次定义了

    class Student extends Person {

       //空参构造方法,构造方法不能被继承,所以子类的构造还是需要定义

       public Student(){}

       //子类在继承的基础上,再增加自己特有的方法

       public void study(){}

    }

   

    class Teacher extends Person {

       public Teacher(){}

       public void teach(){}

    }

继承概述

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

通过extends关键字可以实现类与类的继承,继承的格式如下:

    class 子类名 extends 父类名 {} 

单独的这个类,也就是被继承的类,称为父类,基类或者超类;这多个类称为子类或者派生类。

有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。

继承的案例和继承的好处

通过一个具体案例来演示代码

案例1:学生类和教师,定义两个功能(吃饭,睡觉)

案例2:加入人类后改进

/*

    继承概述:

       把多个类中相同的内容给提取出来定义到一个类中。

    格式:

       class 子类名 extends 父类名 {}

    好处:

       A:提高了代码的复用性

       B:提高了代码的维护性

       C:让类与类之间产生了关系,是多态的前提

*/

//使用继承前

/*

class Student {

    public void eat() {

       System.out.println("吃饭");

    }

    public void sleep() {

       System.out.println("睡觉");

    }

}

 

class Teacher {

    public void eat() {

       System.out.println("吃饭");

    }

    public void sleep() {

       System.out.println("睡觉");

    }

}

*/

//使用继承后

class Person {

    public void eat() {

       System.out.println("吃饭");

    }

   

    public void sleep() {

       System.out.println("睡觉");

    }

}

 

class Student extends Person {}

 

class Teacher extends Person {}

 

class ExtendsDemo {

    public static void main(String[] args) {

       Student s = new Student();

       s.eat();

       s.sleep();

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

       Teacher t = new Teacher();

       t.eat();

       t.sleep();

    }

}

继承的好处

提高了代码的复用性

   多个类相同的成员可以放到同一个类中

提高了代码的维护性

   如果功能的代码需要修改,修改一处即可,继承的类中自动都被修改了

让类与类之间产生了关系,是多态的前提(后面讲)

Java中继承的特点

  • Java只支持单继承,不支持多继承。

    即:一个类只能有一个直接父类,不可以有多个直接父类。

    class SubDemo extends Demo{}           //ok

    class SubDemo extends Demo1,Demo2...//error不能继承多个类

 

  • Java支持多层继承(继承体系)

    class A{}

    class B extends A{}

    class C extends B{}

/*

class Father {}

class Mother {}

class Son exnteds Father {} //正确的

class Son extends Father,Mother {} // 错误的,不能多继承

*/

class GrandFather {

    public void show() {

       System.out.println("GrandFather");

    }

}

 

class Father extends GrandFather {

    public void method(){

       System.out.println("Father");

    }

}

 

class Son extends Father {}

 

class ExtendsDemo2 {

    public static void main(String[] args) {

       Son s = new Son();

       s.method(); //使用从Father继承的方法

       s.show(); //使用从Father继承的方法

    }

}

Java中继承的注意事项

子类只能继承父类所有非私有的成员(成员方法和成员变量)

子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。

不要为了部分功能而去继承

我们到底在什么时候使用继承呢?

继承中类之间体现的是:"is a"的关系。

/*

    继承的注意事项:

       A:子类只能继承父类所有非私有的成员(成员方法和成员变量)

       B:子类不能继承父类的构造方法,但是可以通过super(马上讲)关键字去访问父类构造方法。

       C:不要为了部分功能而去继承

           class A {

              public void show1(){}

              public void show2(){}

           }

           class B {

              public void show2(){}

              public void show3(){}

           }

           //我们发现B类中出现了和A类一样的show2()方法,所以,我们就用继承来体现

           class B extends A {

              public void show3(){}

           }

           这样其实不好,因为这样你不但有了show2(),还多了show1()。

           有可能show1()不是你想要的。

    那么,我们什么时候考虑使用继承呢?

       继承其实体现的是一种关系:"is a"。

           Person

              Student

              Teacher

           水果

              苹果

              香蕉

              橘子

       采用假设法。

       如果有两个类A,B。只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承。

*/

class Father {

    private int num = 10;

    public int num2 = 20;

    //私有方法,子类不能继承

    private void method() {

       System.out.println(num);

       System.out.println(num2);

    }

    public void show() {

       System.out.println(num);

       System.out.println(num2);

    }

}

 

class Son extends Father {

    public void function() {

       //num可以在Father中访问private

       //System.out.println(num); //子类不能继承父类的私有成员变量

       System.out.println(num2);

    }

}

 

class ExtendsDemo3 {

    public static void main(String[] args) {

       // 创建对象

       Son s = new Son();

       //s.method(); //子类不能继承父类的私有成员方法

       s.show();

       s.function();

    }

}

继承中成员变量的关系

案例演示

子父类中同名和不同名的成员变量

子类方法中寻找变量的顺序

/*

    类的组成:

       成员变量:

       构造方法:

       成员方法:

    而现在我们又讲解了继承,所以,我们就应该来考虑一下,类的组成部分的各自关系。

    继承中成员变量的关系:

       A:子类中的成员变量和父类中的成员变量名称不一样,这个太简单。

       B:子类中的成员变量和父类中的成员变量名称一样,这会出现什么情况呢?

           在子类方法中访问一个变量的查找顺序:

           a:在子类方法的局部范围找,有就使用

           b:在子类的成员范围找,有就使用

           c:在父类的成员范围找,有就使用

           d:如果还找不到,就报错。

*/

class Father {

    public int num = 10;

    public void method() {

       int num = 50;

    }

}

 

class Son extends Father {

    public int num2 = 20;

    public int num = 30;

   

    public void show() {

       int num = 40;

       System.out.println(num);

       System.out.println(num2);

       // 找不到符号

       System.out.println(num3);

    }

}

 

class ExtendsDemo4 {

    public static void main(String[] args) {

       //创建对象

       Son s = new Son();

       s.show();

    }

}

结论

在子类方法中访问一个变量

首先在子类局部范围找,也就是方法内部

然后在子类成员范围找,也就是子类的成员变量

最后在父类成员变量范围找(肯定不能访问到父类局部范围)

如果还是没有就报错。

super关键字

super的用法和this很像

this   代表本类对象的引用

super  代表父类存储空间的标识(可以理解为父类对象的引用)

 

用法(this和super均可如下使用)

  • 访问成员变量

    this.成员变量

    super.成员变量(访问父类的成员变量,不能访问父类的private变量)

    访问静态成员时,也可以用:父类名.静态成员

  • 访问构造方法(子父类的构造方法问题再讲)

this(…)       super(…)          //

  • 访问成员方法(子父类的成员方法问题再讲)

   this.成员方法()      super.成员方法()

   

/*

    问题是:

       我不仅仅要输出局部范围的num,还要输出本类成员范围的num。怎么办呢?

       我还想要输出父类成员范围的num。怎么办呢?

           如果有一个东西和this相似,但是可以直接访问父类的数据就好了。

           恭喜你,这个关键字是存在的:super。

    this和super的区别?

           this代表本类对应的引用。

           super代表父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)

           A:调用成员变量

              this.成员变量 调用本类的成员变量

              super.成员变量 调用父类的成员变量

           B:调用构造方法

              this(...)  调用本类的构造方法

              super(...) 调用父类的构造方法

           C:调用成员方法

              this.成员方法 调用本类的成员方法

              super.成员方法 调用父类的成员方法

*/

class Father {

    public int num = 10;

}

 

class Son extends Father {

    public int num = 20;

    public void show() {

       int num = 30;

       System.out.println(num);    //30

       System.out.println(this.num);   //20

       System.out.println(super.num);  //10

    }

}

 

class ExtendsDemo5 {

    public static void main(String[] args) {

       Son s = new Son();

       s.show();

    }

}

继承中构造方法的关系

1.子类中所有的构造方法默认都会访问父类中空参数的构造方法,除非显式使用super/this调用了父类或者是本类的其他构造方法。

2.在类中对本类或者是父类构造方法的调用,只能是在构造方法中,不能在实例方法中调用构造方法(更不能在类方法中调用构造方法),原因:

  • 实例方法被调用时,说明实例对象已经被创建完了,此时不能再使用this/super去初始化本实例或者是父类实例
  • 类方法是在本类加载的时候就已经加载的,这时实例对象还没有被创建出来,是不能使用this或者super的

class A {

    public A(int i){

       this(1 ,2);//

    }

    public A(int a ,int b){

       System.out.println("hello");//默认在这行之上,调用super();父类的空参构造

    }

}

因为子类会继承父类中的数据(成员变量),可能还会使用父类的数据。

所以,子类初始化之前,一定要先完成父类数据的初始化。

换句话说,一个对象的创建意味着它的所有的父类都会被创建出来。

子类构造方法的第一条语句:

如果是this(...)表明调用的是本类的另一个构造方法,在另一个构造方法中还可以继续使用this(...)调用本类其他的构造方法,如果有多个构造方法的话,可以继续调用下去,但是不能递归调用,最终总会有一个构造方法,第一条语句不是this()了。

这时,可以显式的调用父类的构造方法super(...);或者什么都不写,这时系统默认调用父类的空参构造方法。

总之,当子类对象被创建的时候,总是会先调用父类的构造方法,直到Object这个最上层的对象被创建出来之后,其下的子类对象才会被创建出来。

构造方法不能递归调用

class A {

    public A(int i){

       this(1 ,2);

    }

    public A(int a ,int b){

       this(2);

    }

}

/*

    继承中构造方法的关系

       子类中所有的构造方法默认都会访问父类中空参数的构造方法

       注意:子类每一个构造方法的第一条语句默认都是:super();除非显式this/super

*/

class Father {

    int age;

    public Father() {

       System.out.println("Father的无参构造方法");

    }

   

    public Father(String name) {

       System.out.println("Father的带参构造方法");

    }

}

 

class Son extends Father {

    public Son() {

       //super();

       System.out.println("Son的无参构造方法");

    }

   

    public Son(String name) {

       //super();

       System.out.println("Son的带参构造方法");

    }

}  

 

class ExtendsDemo6 {

    public static void main(String[] args) {

       //创建对象

       Son s = new Son();

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

       Son s2 = new Son("tom");

    }

}

考察父类,子类代码块的执行顺序

从这个案例中能看到有继承关系的类的实例初始化的过程,以及为什么在类方法中不能使用this或者是super关键字

class Father{

    static{

       System.out.println("Father类的静态代码块");

    }

    {

       System.out.println("Father类的构造代码块1");

    }

    public Father(){

       System.out.println("Father的无参构造方法");

    }

    public Father(int x){

       System.out.println("Father的有参构造方法");

    }

    {

       System.out.println("Father类的构造代码块2");

    }

}

class Son extends Father{

    static{

       System.out.println("Son类的静态代码块");

    }

    {

       System.out.println("Son类的构造代码块1");

    }

    public Son(){

       System.out.println("Son的无参构造方法");

    }

    public Son(int x){

       System.out.println("Son的有参构造方法");

    }

    {

       System.out.println("Son类的构造代码块2");

    }

}

class TestBlock{

    public static void main(String[] args){

       Son s = new Son();

    }

}

如果父类中没有空参构造方法,怎么办?

1.子类可以通过super去显式调用父类其他的带参的构造方法

2.子类可以通过this去调用本类的其他构造方法,但是本类其他构造也必须首先用super(...)访问了父类的带参构造(因为父类没有空参构造)

总结

如果父类没有空参构造,子类的构造方法中就必须显式调用父类带参构造super(...);

一定要注意:

super(…)或者this(…)必须出现在构造方法第一条语句上

否则,就会有父类数据的多次初始化

/*

    如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢?

       报错。

    如何解决呢?

       A:在父类中加一个无参构造方法

       B:通过使用super关键字去显示的调用父类的其他带参构造方法

       C:子类通过this去调用本类的其他构造方法

       子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。

    注意事项:

       this(...)或者super(...)必须出现在第一条语句上。

       如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。

*/

class Father {

    /*

    public Father() {

       System.out.println("Father的无参构造方法");

    }

    */

    public Father(String name) {

       System.out.println("Father的带参构造方法");

    }

}

 

class Son extends Father {

    public Son() {

       super("随便给");

       System.out.println("Son的无参构造方法");

       //super("随便给");

    }

   

    public Son(String name) {

       //super("随便给");

       this();

       System.out.println("Son的带参构造方法");

    }

}

 

class ExtendsDemo7 {

    public static void main(String[] args) {

       Son s = new Son();

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

       Son ss = new Son("tom");

    }

}

原文地址:https://www.cnblogs.com/zhaoyongcx/p/6618794.html