zoukankan      html  css  js  c++  java
  • 大数据第十天

    继承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");

        }

    }

  • 相关阅读:
    移动端布局方案汇总&&原理解析
    Javascript运行机制
    git 使用
    async await详解
    vue使用axios调用豆瓣API跨域问题
    hash和history的区别
    http状态码
    XSS 和 CSRF简述及预防措施
    【pytorch】pytorch基础学习
    [源码解读] ResNet源码解读(pytorch)
  • 原文地址:https://www.cnblogs.com/zhaoyongcx/p/6618794.html
Copyright © 2011-2022 走看看