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

    继承中成员方法的关系

    案例演示

    子父类中存在同名和不同名的成员方法

    结论

    通过子类对象去访问一个实例方法

    首先在子类中找(是否子类进行了重写,或者是子类特有的方法)

    然后在父类中找(子类没重写,而是从父类继承而来的)

    /*

        继承中成员方法的关系:

           A:子类中的方法和父类中的方法声明不一样,这个太简单。

           B:子类中的方法和父类中的方法声明一样,这个该怎么?

               通过子类对象调用方法:

                  a:先找子类中,看有没有这个方法,有就使用

                  b:再看父类中,有没有这个方法,有就使用

                  c:如果没有就报错。

    */

    class Father {

        public void show() {

           System.out.println("show Father");

        }

    }

     

    class Son extends Father {

        public void method() {

           System.out.println("method Son");

        }

       

        //子类中重写父类的show方法

        public void show() {

           System.out.println("show Son");

        }

    }

     

    class ExtendsDemo8 {

        public static void main(String[] args) {

           //创建对象

           Son s = new Son();

           s.show();

           s.method();

           //s.fucntion(); //找不到符号,子类中没有,父类中也没有

        }

    }

    方法重写概述

    方法重写概述

    • 子类中出现了和父类中一模一样的方法声明

    称为方法覆盖Override/重写OverWrite

    使用特点:

    • 如果方法名不同,就调用对应的方法
    • 如果方法名相同,最终使用的是子类自己的(使用子类的引用的时候,在多态情况下,使用父类的引用,则有可能调用的是父类的静态方法)

    方法重写的应用:

    • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
    • 方法的重写是多态实现的条件

    /*

        方法重写:子类中出现了和父类中方法声明一模一样的方法。

        子类对象调用方法的时候:

           先找子类本身,再找父类。

        方法重写的应用:

           当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。

           这样,即沿袭了父类的功能,又定义了子类特有的内容。

        案例:

           A:定义一个手机类。

           B:通过研究,发明了一个新手机,这个手机的作用是在打完电话后,可以听天气预报。

    */

    class Phone {

        public void call(String name) {

           System.out.println("给"+name+"打电话");

        }

    }

     

    class NewPhone extends Phone {

        public void call(String name) {

           //System.out.println("给"+name+"打电话");

           super.call(name);

           System.out.println("可以听天气预报了");

        }

    }

     

    class ExtendsDemo9 {

        public static void main(String[] args) {

           NewPhone np = new NewPhone();

           np.call("tom");

        }

    }

    方法重写的注意事项

    • 父类中私有方法不能被重写,编译报错
    • 子类重写父类方法时,访问权限不能更低(后面讲),否则编译报错
    • 子类重写父类方法,返回值类型可以相同,或者是父类返回值类型的子类型
    • 父类的实例方法(非静态方法),子类不能重新定义为静态方法
    • 父类静态方法(类方法),子类也必须通过静态方法进行“重写”(虽然编译和使用都不报错,但其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中会讲解)
    • 子类中重写父类方法,按照重写的原则(访问权限不能变小,返回值同类或者子类,方法名相同,形参列表相同);否则子类中定义的同名方法就是方法的重载(继承而来的方法和子类定义的方法构成重载),重载就必须让参数列表不同。如果子类方法只与继承自父类的方法返回值不同,不能构成重载
    • 子类如果想重写父类的方法,最好是让方法的签名一模一样

    (重写方法的一个重要用途就是:父类的引用能够指向子类的方法,但是静态方法的“重写”,在多态中依然调用的是父类的方法,所以,从这个角度上来讲,子类对父类的静态方法的重写不能算是真正方法的重写)

    /*

        方法重写的注意事项

           A:父类中私有方法不能被重写

               因为父类私有方法子类根本就无法继承

           B:子类重写父类方法时,访问权限不能更低,最好就一致

           C:父类静态方法,子类也必须通过静态方法进行重写

               其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中讲解

              

        子类重写父类方法的时候,最好声明一模一样。

        否则同名的方法就是方法的重载了,就必须让形参列表不同

    */

    class Father {

        //private void show() {}

        /*

        public void show() {

           System.out.println("show Father");

        }

        */

        //默认的访问权限

        void show() {

           System.out.println("show Father");

        }

        /*

        public static void method() {

        }

        */

        public void method() {

        }

    }

     

    class Son extends Father {

        //private void show() {}

        //子类中对父类方法的重写

        /*

        public void show() {

           System.out.println("show Son");

        }

        */

        //public访问权限大于父类中默认的访问权限,ok

        public void show() {

           System.out.println("show Son");

        }

       

        //error,父类的method是实例方法,子类中重写变成了static类方法

        //从多态的角度看,这个方法不能使用父类的引用来动态绑定,使用范围变小了

        //public static void method() {

        //}

        //以下子类中和父类同样的方法签名,才是对方法的重写

        public void method() {

        }

    }

     

    class ExtendsDemo10 {

        public static void main(String[] args) {

           Son s = new Son();

           s.show();

        }

    }

    面试题

    1:方法重写和方法重载的区别?方法重载能改变返回值类型吗?

    方法重写:

        在子类中,出现和父类中一模一样的方法声明的现象。(访问权限可以放大,返回值可以同类型,可以是父类返回值类型的子类)

    方法重载:

        同一个类中,出现的方法名相同,参数列表不同的现象。

    方法重载能改变返回值类型,因为它和返回值类型无关。

    Override/OverWrite:方法重写

    Overload:方法重载

    2:this关键字和super关键字分别代表什么?以及他们各自的使用场景和作用。

    this:代表当前类的对象引用

    super:代表父类实例存储空间的标识。

        (可以理解为父类实例的引用,通过super可以访问父类实例的非私有成员)

    场景:

        成员变量:

           this.成员变量

           super.成员变量

        构造方法:

           this(...)

           super(...)

        成员方法:

           this.成员方法

           super.成员方法

    继承练习

    学生和教师案例

    父类Person中成员private修饰,子类如何访问呢?不能用super,使用封装的方法

    /*

        学生案例和教师案例讲解

        学生:

           成员变量;姓名,年龄

           构造方法:无参,带参

           成员方法:getXxx()/setXxx()

        教师:

           成员变量;姓名,年龄

           构造方法:无参,带参

           成员方法:getXxx()/setXxx()

        看上面两个类的成员,发现了很多相同的东西,所以我们就考虑抽取一个共性的类:

        人:

           成员变量;姓名,年龄

           构造方法:无参,带参

           成员方法:getXxx()/setXxx()

           学生 继承 人

           教师 继承 人

    */

    //定义人类

    class Person {

        //姓名

        private String name;

        //年龄

        private int age;

        public Person() {

        }

        public Person(String name,int age) { //"tom",27

           this.name = name;

           this.age = age;

        }

        public String getName() {

           return name;

        }

        public void setName(String name) {

           this.name = name;

        }

        public int getAge() {

           return age;

        }

        public void setAge(int age) {

           this.age = age;

        }

    }

     

    //定义学生类,不能继承父类的private变量,使用公有的方法进行访问

    class Student extends Person {

        public Student() {}

        public Student(String name,int age) { //"tom",27

           //this.name = name; //error不能继承private变量

           //this.age = age;

           super(name,age);

        }

    }

     

    //定义教师类,同样调用父类的构造方法进行教师对象的初始化

    class Teacher extends Person {}

     

    class ExtendsTest4 {

        public static void main(String[] args) {

           //创建学生对象并测试

           //方式1,学生类继承了get/set方法,可以对父类的私有变量进行操作

           Student s1 = new Student();

           s1.setName("tom");

           s1.setAge(27);

           System.out.println(s1.getName()+"---"+s1.getAge());

          

           //方式2,通过调用父类的构造方法进行私有变量的赋值

           Student s2 = new Student("tom",27);

           System.out.println(s2.getName()+"---"+s2.getAge());

          

           //补全教师类中的代码并进行测试。

           //创建教师对象,并访问其中的变量

        }

    }

    思考:

    上面代码中,子类的构造方法中,使用的是super调用父类的构造方法,那么,对成员变量的赋值,到底是赋给了父类的成员变量?还是子类的成员变量?

    可以理解为:整个父类的对象都在子类对象中,值确实是赋给了父类的成员变量,但是子类通过方法可以使用,和自己的变量没有区别。这就是继承的好处。

    从上面的例子可以看出,其实父类的private成员变量也被子类“继承”了,只不过是不让直接访问而已。

    猫狗类抽象出动物类过程

    从猫狗的共性抽取出一个动物类,让猫狗继承这个父类

    案例练习的目的:

        熟练继承关系中子类的方法重写,this,super关键字的使用,以及变量的访问等

    /*

        先找到具体的事物,然后发现具体的事物有共性,才提取出一个父类。

        猫:

           成员变量:姓名,年龄,颜色

           构造方法:无参,带参

           成员方法:

               getXxx()/setXxx()

               eat()

               palyGame()

        狗:

           成员变量:姓名,年龄,颜色

           构造方法:无参,带参

           成员方法:

               getXxx()/setXxx()

               eat()

               lookDoor()

        共性:

           成员变量:姓名,年龄,颜色

           构造方法:无参,带参

           成员方法:

               getXxx()/setXxx()

               eat()

        把共性定义到一个类中,这个类的名字叫:动物。

        动物类:

           成员变量:姓名,年龄,颜色

           构造方法:无参,带参

           成员方法:

               getXxx()/setXxx()

               eat()

           猫:   

               构造方法:无参,带参

               成员方法:palyGame()

           狗:

               构造方法:无参,带参

               成员方法:lookDoor()

    */

    //定义动物类

    class Animal {

        //姓名

        private String name;

        //年龄

        private int age;

        //颜色

        private String color;

        public Animal() {}//空参构造

        //带参构造

        public Animal(String name,int age,String color) {

           this.name = name;

           this.age = age;

           this.color = color;

        }

        public String getName() {

           return name;

        }

        public void setName(String name) {

           this.name = name;

        }

        public int getAge() {

           return age;

        }

        public void setAge(int age) {

           this.age = age;

        }

        public String getColor() {

           return color;

        }

        public void setColor(String color) {

           this.color = color;

        }

        //公有方法

        public void eat() {

           System.out.println("吃");

        }

    }

     

    //定义猫类继承动物类

    class Cat extends Animal {

        public Cat() {}

        public Cat(String name,int age,String color) {

           super(name,age,color);//调用父类的构造方法对变量进行赋值

        }

        //定义猫类自己的方法

        public void playGame() {

           System.out.println("猫玩");

        }

    }

     

    //定义狗类继承动物类

    class Dog extends Animal {

        public Dog() {}

       

        public Dog(String name,int age,String color) {

           super(name,age,color);

        }

        //定义狗类自己的方法

        public void watchDoor() {

           System.out.println("狗看门");

        }

    }

     

    //测试类

    class ExtendsTest5 {

        public static void main(String[] args) {

           //测试猫

           //方式1,通过set方法对变量进行赋值

           Cat c1 = new Cat();

           c1.setName("Tom");

           c1.setAge(3);

           c1.setColor("白色");

           System.out.println("猫的名字是:"+c1.getName()+";年龄是:"+c1.getAge()+";颜色是:"+c1.getColor());

           c1.eat();

           c1.playGame();

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

          

           //方式2,通过父类构造方法对变量赋值

           Cat c2 = new Cat("杰瑞",5,"土豪金");

           System.out.println("猫的名字是:"+c2.getName()+";年龄是:"+c2.getAge()+";颜色是:"+c2.getColor());

           c2.eat();

           c2.playGame();

          

           //作业:两种方式完成狗类的初始化,并访问其变量和方法

        }

    }

    final关键字

    final关键字是最终的意思,可以修饰类,成员变量,成员方法。

    它的特点是:

    • 修饰类,类不能被继承(不能放在extends后)
    • 修饰变量,变量就变成了常量,只能被赋值一次,不论子类还是本类中,都不能修改

    (常量一般是大写字母表示,final int ONE = 1;)

    • 修饰方法,方法不能被重写(子类只有使用权,没有修改权)

     

    /*

        继承的代码体现

        由于继承中方法有一个现象:方法重写。

        所以,父类的功能,就会被子类给覆盖掉。

        有些时候,我们不想让子类去覆盖掉父类的功能,只能让他使用。(只有使用权,没有修改权)

        这个时候,针对这种情况,Java就提供了一个关键字:final

        final:最终的意思。常见的是它可以修饰类,方法,变量。

    */

    class Fu {

        public final void show() {

           System.out.println("这是绝密资源,任何人都不能修改");

        }

    }

     

    class Zi extends Fu {

        // Zi中的show()无法重写Fu中final方法show(),编译报错

        //public void show() {

           //System.out.println("出错");

        //}

    }

     

    class ZiDemo {

        public static void main(String[] args) {

           Zi z = new Zi();

           z.show();

        }

    }

    final特性

    /*

        final可以修饰类,方法,变量

        特点:

           final可以修饰类,该类不能被继承。

           final可以修饰方法,该方法不能被重写。

           final可以修饰变量,该变量不能被重新赋值。因为这个变量其实是常量。

        常量:

           A:字面值常量

               "hello",10,true

           B:自定义常量

               final int x = 10;

    */

     

    //final class Fu //无法从最终Fu进行继承

     

    class Fu {

        public int num = 10;

        public final int num2 = 20;

        //final方法不能被重写

        public final void show() {

       

        }

    }

    class Zi extends Fu {

        // Zi中的show()无法覆盖Fu中的show()

        //public void show() {

           //num = 100;

           //System.out.println(num);

           //无法为最终变量num2再次分配值

           //num2 = 200;

           //System.out.println(num2);

        //}

    }

    class FinalDemo {

        public static void main(String[] args) {

           Zi z = new Zi();

           z.show();//调用的是继承自父类的final方法

        }

    }

    final关键字面试题

    final修饰局部变量

    • 在方法内部,该变量不可以被改变
    • 在方法声明上,分别演示基本类型和引用类型作为参数的情况

        基本类型,是这个参数的值不能被改变

        引用类型,是这个参数指向的地址值不能被改变

    /*

        面试题:final修饰局部变量的问题

        基本类型:基本类型的值不能发生改变。

        引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。

    */

    //以下演示的是局部变量使用final修饰的情况

    class Student {

        int age = 10;

    }

     

    class FinalTest {

        public static void main(String[] args) {

           //局部变量是基本数据类型

           int x = 10;

           x = 100;      //非final变量值可以被改变

           System.out.println(x);

           final int y = 10;

           //无法为最终变量y再赋值,即使是相同的值也不行

           //y = 100;

           System.out.println(y);

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

          

           //局部变量是引用数据类型

           Student s = new Student();

           System.out.println(s.age);

           s.age = 100;

           System.out.println(s.age);//100,改变了引用对象的属性值

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

          

           final Student ss = new Student();//ss只能指向这个new出来的对象

           System.out.println(ss.age);

           ss.age = 100;        //可以改变所指对象的属性值

           System.out.println(ss.age);

           //无法为最终变量ss分配值,error

           //ss = new Student();

        }

    }

    以下演示的是形参使用final修饰的情况

    public void testFinalVar(final int i){

        //i = 1; // error,不能再对final变量赋值

    }

    public void testFinalVar(final String str){

        //str = null; //error不能再对final变量赋值

    }

     

     

    final修饰变量的初始化时机

    在对象构造完毕前即可(非静态的常量)

     

     

    /*

        final修饰变量的初始化时机

           A:被final修饰的变量只能赋值一次。

           B:在构造方法完毕前。(非静态的常量)

    */

    class Demo {

        //int num = 10;

        //final int num2 = 20;

        //一旦在声明的时候赋值的话,就不能再任何地方修改了

       

        int num;

        final int num2;//声明的时候不赋值,可以在构造代码块或者构造方法中赋值

       

        {

           //num2 = 10;

        }

       

        public Demo() {

           num = 100;//普通变量使用之前赋值即可

           //由于声明时没有赋值,构造方法中可以为最终变量num2赋值

           num2 = 200;

        }

    }

     

    class FinalTest2 {

        public static void main(String[] args) {

           Demo d = new Demo();

           System.out.println(d.num);

           System.out.println(d.num2);

        }

    }

    总结:

    类中非static的final变量(实例final变量)可以在声明的时候赋值,如果声明的时候没有赋值的话,就必须在以下两个地方赋值,一个是构造代码块中,一个是构造方法中。如果这两个地方也没有赋值的话,编译报错。

    如果是static修饰的final变量(类变量)的话,则只能在两个地方赋值:声明的时候,或者是在静态代码块中。

    经过验证:

    若类中的final成员变量在声明时没有赋值,并且存在多个构造方法的话,则在每个构造方法中都应该显示的为这个final变量赋值,或者可以抽取到构造代码块中进行赋值

    多个构造方法中,不能互相调用

    多态概述(Polymorphism)

    多态:某一个事物,在不同时刻表现出来的不同状态。

    在Java中,对一个事物的引用可以分成两种类型,一种是编译时的类型,一种是运行时的类型。

    编译时的类型指的是声明这个变量时指定的类型;运行时类型指的是实际赋给这个变量的对象的类型。

    举例:

    之前写的例子全都是将某类对象的引用赋值给这个类的一个变量,这个变量的值就是这个对象的地址值:

        Student s = new Student();

    等号左边就是编译时的类型,s在编译时指定的类型是Student,等号右边是运行时实际赋值给这个变量的值,恰好也是一个Student类型的对象的引用。

    如果等号左右两边的类型不一致,就有可能出现了多态。

    举例:若Student类继承自Person类,则下面的写法是正确的

        Person p = new Student();

    Java语言是强类型的,基本数据类型之间存在自动向上转型的特性,在引用数据类型中,同样存在这样的情况,Student类继承了Person类,在语义范围上来看,一个Student类的对象确实是一个Person类的对象,两者是“is a”的关系。

    举例:

        猫可以是猫的类型。猫 m = new 猫();

        同时猫也是动物的一种,从动物继承而来,也可以把猫称为动物。

        动物 d = new 猫();

     

    看一个多态的例子

    class Father{

        int age = 50;

        public void speak(){

           System.out.println("father speak()");

        }

    }

    class Son extends Father{

        int age = 30;

        public void speak(){

           System.out.println("son speak()");

        }

        //子类特有的方法talk

        public void talk(){

           System.out.println("son talk()");

        }

    }

    class PolymTest{

        public static void main(String[] args){

           Father fa = new Father();

           fa.speak();          //没有体现多态,只是通过父类引用调用父类方法

           Father f = new Son();    //父类引用指向子类对象,体现多态

           f.speak();           //调用方法,如果子类重写了该方法,则调用子类的

           //f.talk();       //不能调用子类特有的方法

           System.out.println(f.age);//50,成员变量没有多态性

           Son s = new Son();

           s.speak();          

        }

    }

    父类引用不能调用子类特有的方法,因为使用了父类的引用,就代表站在父类的角度来看待当前的子类对象,只能看到从父类继承而来的特性,或者是子类重写的父类的方法。成员变量没有多态性,只能看到父类的成员变量。

    多态案例及成员访问特点

    /*

        多态:同一个对象(事物),在不同时刻体现出来的不同状态。

          

        多态的前提:

           A:要有继承关系。

           B:要有方法重写。

               其实没有也是可以的,但是如果没有这个就没有意义。

                  动物 d = new 猫();

                  d.show();

                  动物 d = new 狗();

                  d.show();

           C:要有父类引用指向子类对象。

               父 f =  new 子();

        多态中的成员访问特点:

           A:成员变量

               编译看左边,运行看左边。

           B:构造方法

               创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。

           C:成员方法

               编译看左边,运行看右边。

           D:静态方法

               编译看左边,运行看左边。

               (静态和类相关,算不上重写,所以,访问还是左边的)

           由于成员方法存在方法重写,所以它运行看右边。

    */

    class Fu {

        public int num = 100;

     

        public void show() {

           System.out.println("show Fu");

        }

       

        public static void function() {

           System.out.println("function Fu");

        }

    }

     

    class Zi extends Fu {

        public int num = 1000;

        public int num2 = 200;

     

        public void show() {

           System.out.println("show Zi");

        }

       

        public void method() {

           System.out.println("method zi");

        }

       

        public static void function() {

           System.out.println("function Zi");

        }

    }

     

    class DuoTaiDemo {

        public static void main(String[] args) {

           //要有父类引用指向子类对象。

           Fu f = new Zi();

           System.out.println(f.num);//成员属性没有多态性,这里访问的是父类的实例变量,100

           //由于父类没有num2这个实例变量,所以编译报错,找不到符号

           //System.out.println(f.num2);

           f.show();//由于子类重写了父类的show方法,所以这里调用的是子类的show方法

           //找不到符号,父类引用不能调用子类特有的方法

           //f.method();

           f.function();//调用的是父类的静态方法,虽然子类也有同样的静态方法

        }

    }

    多态的前提条件

    • 有继承关系(没有继承关系的话,不同类型的变量是不能赋值的)
    • 有方法重写(没有方法的重写的话,始终调用的是父类的方法)
    • 有父类引用指向子类对象(不使用父类的引用的话,始终调用的是子类的方法)

    多态中成员访问特点

    成员变量

    • 编译看左边,运行看左边(父类的引用始终访问的是父类的变量,不论是不是static)
    • 即使子类有同名的变量,访问的也是父类的变量

    成员方法

    • 编译看左边,运行看右边(父类的引用运行时访问的是子类重写的方法)

    静态方法

    • 编译看左边,运行看左边(父类的引用始终访问的是父类的静态方法)
    • 所以前面说静态方法不能算方法的重写

    总结:

        成员变量和静态方法没有多态性

        只有被子类重写的成员方法才有多态性

  • 相关阅读:
    第 4 章 MySQL 安全管理
    第 3 章 MySQL 存储引擎简介
    第 2 章 MySQL 架构组成
    道教的【五行】学说
    【matlab】MTATLAB解线性方程组
    RPolar项目进度记录
    numpy和TensorFlow的函数
    卷积
    腾讯QQ空间超分辨率技术TSR
    混元桩
  • 原文地址:https://www.cnblogs.com/zhaoyongcx/p/6618805.html
Copyright © 2011-2022 走看看