zoukankan      html  css  js  c++  java
  • javase学习第八天(继承和多态)

    继承和多态

    继承

    定义

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

    通过extends关键字可以实现类与类的继承:

    格式: class 子类名 extends 父类名{}

    被继承的类称为父类或基类、超类。

    好处

    提高代码的复用性:多个类相同的成员可以放到同一个类中;

    提高代码的可维护性:如果功能的代码需要修改,修改一处即可,继承的类中自动被修改。

    特点

    1、只能单继承,不支持多继承:即一个类只能有一个父类,不能有多个父类;

    2、可以多层继承: class A{};--> cass B extends A();-->class C extends B{}.

    注意事项

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

    2、子类不能继承父类的构造方法,但可通过super关键字去访问父类的构造方法;

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

    案例:

     1 class Father{
     2     private int num1=10;
     3     public int num2=20;
     4     public void show(){
     5         System.out.println(num2);
     6         System.out.println(num1);
     7     }
     8     private void method(){
     9         System.out.println(num2);
    10         System.out.println(num1);
    11         
    12     }
    13 }
    14 class Son extends Father{
    15     public void function(){
    16         // System.out.println(num1);//不能继承私有的num1
    17         System.out.println(num2);
    18     }
    19 }
    20 class Extends1Demo{
    21     public static void main(String[]args){
    22         Son s=new Son();
    23         s.show();
    24         // s.method();//不能继承私有方法
    25         s.function();
    26     }
    27 }

    继承中成员变量关系

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

    1、首先在子类局部范围找,即方法内部;

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

    3、最后在父类的成员变量范围找(不能访问父类的局部范围) ;

    4、如果还没有就报错。

     1 class Father {
     2     public int num = 10;
     3 }
     4 
     5 class Son extends Father {
     6     public int num = 20;
     7     public void show() {
     8         int num = 30;
     9         System.out.println(num);        //30(就近原则)
    10         System.out.println(this.num);    //20(this修饰访问成员变量)
    11         System.out.println(super.num);    //10
    12     }
    13 }

    super关键字

    和this使用相识, this 代表本类当前对象的引用 ,super 代表父类存储空间的标识(也可理解为父类对象的引用)

    使用方式:

    访问成员变量:

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

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

    访问成员方法,(注意没有"."): this(...)访问本类的构造方法,根据括号内参数来区分访问哪个;

    super(...)访问父类的构造方法,根据括号内参数来区分访问哪个。

    访问成员方法: this.成员方法(),super.成员方法()

    继承中构造方法的关系

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

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

    -->1、实例方法被调用时,说明实例对象已经被创建完了,此时不能再使用this、super去初始化本地实例或是父类实例

    -->2、类方法是在本类加载的时候就已经加载了,这时候实例对象还没有被创建出来。

    子类初始化之前,一定要先完成父类数据的初始化(一个对象的创建意味着它的所有的父类都会被创建出来);

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

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

    构造方法不能递归调用:如下错误

    1 class A {
    2     public A(int i){
    3         this(1 ,2);
    4     }
    5     public A(int a ,int b){
    6         this(2);
    7     }
    8 }

    子类中所有的构造方法默认都会访问父类中空参数的构造方法(注意:子类每一个构造方法的第一条语句默认都是:super();除非显示this、super);

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

    且super(...)或者this(...)必须出现在构造方法第一条语句上,否则,会出现父类数据的多次初始化。

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

    构造代码块顺序:

    父类的静态代码块-->子类的静态代码块-->父类的构造代码块-->父类构造方法-->子类构造代码块-->子类的构造方法

     1 class Father{
     2     int age;
     3     static{
     4         System.out.println("static code block");
     5     }
     6     {
     7         System.out.println("code block 1");
     8     }
     9     public Father(){
    10         System.out.println("no arguments" );
    11     }
    12     public Father(int age){
    13         System.out.println("hava arguments");
    14     }
    15     {
    16         System.out.println("code block 2");
    17     }
    18 }
    19 class Son extends Father{
    20     int age;
    21     static{
    22         System.out.println("son static code block");
    23     }
    24     {
    25         System.out.println("son code block 1");
    26     }
    27     public Son(){
    28         System.out.println("son -no arguments" );
    29     }
    30     public Son(int age){
    31         System.out.println("son hava arguments");
    32     }
    33     {
    34         System.out.println("son code block 2");
    35     }
    36 }
    37 class FatherDemo{
    38     public static void main(String[]args){
    39         
    40     Son s=new Son();
    41     }
    42 }

    结果:

    类加载过程和对象初始化过程

    类加载的时候执行的是类的初始化(静态代码块、有继承关系的类,从上到下) 对象初始化过程

    1、先执行的是父类成员变量的初始化(如果父类成员是另一个对象的引用的话,则先对引用的类加载,然后成员变量初始化,然后成员变量初始化,然后构造代码块、然后构造方法;完成后,将引用赋给成员变量),再父类构造代码块,再父类构造方法。

    2、然后子类成员变量的初始化(如果是另一个对象的引用的话,过程如上),子类构造代码块,子类构造方法

    一个类中不可包含自身实例对象的引用。 当此类对象初始化的时候,先加载类,然后对成员变量赋初始值,然后是显式赋值,在显式赋值的时候发现是本类的构造方法的调用,再去调构造方法; 而本类的构造方法还是对成员变量赋值,在赋值的时候还去调用构造方法,造成了构造方法的迭代

    继承中成员方法的使用

    子父类中存在同名和不同名的成员方法 通过子类对象去访问一个实例方法:

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

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

    方法的重写

    定义:子类中出现和父类一模一样的方法声明,称为方法覆盖(override)/重写(overwrite).

    应用条件: 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样既沿袭父类的功能,又有子类特有的内容;

    方法的重写时多态实现的条件。

    重写注意事项:

    1、父类中私有方法不能被重写,编译报错

    2、子类重写父类方法时,访问权限不能更低(后面讲),否则编译报错

    3、子类重写父类方法,返回值类型可以相同,或者是父类返回值类型的子类型

    4、父类的实例方法(非静态方法),子类不能重新定义为静态方法

    5、父类静态方法(类方法),子类也必须通过静态方法进行“重写”(虽然编译和使用都不报错,但其实这个算不上方法重写)

    6、子类中重写父类方法,按照重写的原则(访问权限不能变小,返回值同类或者子类,方法名相同,形参列表相同);否则子类中定义的同名方法就是方法的重载(继承而来的方法和子类定义的方法构成重载),重载就必须让参数列表不同。如果子类方法只与继承自父类的方法返回值不同,不能构成重载

    7、子类如果想重写父类的方法,最好是让方法的签名一模一样

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

    注意

    父类的私有变量子类不可继承但在子类的构造方法中,使用super调用父类的构造方法那么,对成员变量的赋值,到底是赋给了父类的成员变量?还是子类的成员变量?

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

    final关键字

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

    特点:

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

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

    修饰局部变量: 在方法内部,该变量不可以被改变;

    在方法声明上,分基本类型和引用类型两种:

    基本类型:这个参数的值不能被改变;

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

    1 public void testFinalVar(final int i){
    2     //i = 1; // error,不能再对final变量赋值
    3 }
    4 public void testFinalVar(final String str){
    5     //str = null; //error不能再对final变量赋值
    6 }

    赋值情况:

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

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

    经过验证: 若类中的final成员变量在声明时没有赋值,并且存在多个构造方法的话,则在每个构造方法中都应该显示的为这个final变量赋值(可以不同),或者可以抽取到构造代码块中进行赋值; 多个构造方法中,不能互相调用。

    多态(polymorphism)

    定义

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

    事物的引用分为两种类型:编译时类型,声明时指定的类型;运行时类型,实际赋给这个变量的对象的类型。

    如: Student s = new Student();

    等号左边就是编译时的类型,s在编译时指定的类型是Student,等号右边是运行时实际赋值给这个变量的值。 如果等号左右两边的类型不一致,就有可能出现了多态。

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

    多态的前提条件

    1、有继承关系(没有继承关系的话,不同类型的变量是不能赋值的)

    2、有方法重写(没有方法的重写的话,始终调用的是父类的方法)

    3、有父类引用指向子类对象(不使用父类的引用的话,始终调用的是子类的方法)

    成员访问特点:

    成员变量

    1、编译看左边,运行看左边(父类的引用始终访问的是父类的变量,不论是不是static);

    2、即使子类有同名的变量,访问的也是父类的变量

    成员方法

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

    静态方法

    1、编译看左边,运行看左边(父类的引用始终访问的是父类的静态方法)

    2、所以前面说静态方法不能算方法的重写

    总结:

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

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

  • 相关阅读:
    redis的5种数据结构和基本操作
    kafka架构、基本术语、消息存储结构
    kafka基本概念和hello world搭建
    kafka安装
    【Android-NetWork】 判断是否连接网络,判断网络连接方式
    【计算机-虚拟wifi】Win7虚拟wifi设置
    【Andriod-AlertDialog控件】 弹出对话框AlertDialog用法
    【Winform-自定义控件】ImageButton 支持鼠标正常、悬停、按下更改图片,支持文本
    【Wince-自定义控件】ImageButton 带图片、文字
    【Winform-自定义控件】一个自定义的进度条
  • 原文地址:https://www.cnblogs.com/yihaifutai/p/6616840.html
Copyright © 2011-2022 走看看