zoukankan      html  css  js  c++  java
  • Java学习笔记——第六章 继承和多态

    第六章 继承和多态

    6.1 何为继承

    6.1.1 继承共同行为

    继承基本上是为避免多个重复定义共同行为(注意是基本上,滥用继承会导致程序维护上有问题,可结合随后的第七章接口和多态了解,加以区别)
    我们把相同的程序代码提升为父类。然后在子类中将重复的代码复制过来,即使用新的关键词 extends,表示一种扩充行为,即子类 继承父类。例如:

    public class Role{	//定义一个父类Role,包含子类的共同程序代码
    	private String name;
    	private int level;
    	
    	public int getLevel(){
    		return level;
    	}
     	...(省略)
    }
    public class SwordMan extends Role(){	//定义一个子类SwordMan继承父类
    	public void fight(){	//定义父类中没有的方法,即子类特有的方法
    		System.out.println("攻击");
    	}
    }
    

    通常我们会使用以上类图,第一格Role表示类名,第二格中name,level,blood表示数据成员,:之后是成员类型,-为private,第三格表示方法名称,+表示public,:之后表示返回类型,继承则以空心箭头表示(此处用普通箭头代替)
    注:private成员会被继承,只是子类无法直接进行存取,必须通过父类提供的方法存取。

    6.1.2 多态和is-a

    在Java中,子类只能继承一个父类,继承除了可避免类间重复的行为定义外,还有子类和父类间是is-a的关系,即子类“是一种”父类的关系。例如,以下程序是可以编译通过的:

    Role role1 = new SwordsMan();//SwordsMan是不是一种Role
    Role role2 = new Magician();//Magician是不是一种Role
    

    对于以上程序,要检查自己的语法逻辑是否正确,方式是 从=号右边往左读:右边是不是一种左边(右边类是不是左边类的子类)?但是,SwordsMan swordsman = new Role();//Role是一种SwordsMan,这是错误的。
    再如:

    Role role1 = new SwordsMan();//SwordsMan是一种Role是正确的
    SwordsMan swordsman = role1;//role1不一定是SwordsMan
    

    针对以上程序,可以使用扮演通过编译:

    Role role2 = new SwordsMan();
    SwordsMan swordsman = (SwordsMan)role2;
    

    但是,以上叙说的扮演并可能会存在问题,只是强制告诉编译器让这段代码通过编译,不过后果自负。扮演也有一定的规则要求,前后的类是一个类型,譬如如下扮演将会编译失败:

    Role role3 = new Magician();//role3拥有的是Magician的属性和方法值
    SwordsMan swordsman = (SwordsMan)role3;//Role扮演的是Magician,强制转换为	
    										   //SwordsMan在执行上会报错
    

    通过以上讲述,我们引出多态的概念,抽象的讲就是使用单一接口操作多种类型的对象
    总结:
    结合上述案例,简单的说就是(以游戏为例),当我们拥有了多种角色,但是每种角色都拥有很多相似的属性和方法,我们首先将相同的属性和方法封装为一个父类,让子类继承父类的这些属性和方法(如果是public或者提供存取的方法,在子类中可以使用),子类继承父类,可以在子类中定义父类中没有的方法或者对父类中的方法进行重载。当然这里有一个问题,当我们需要的对象(即游戏中的角色)很多时,每个子类继承父类后都要重写方法(这种方式显然不可取),对于这种问题,
    即当所有的子类继承父类都要重写一个方法时,我们可以将其重新定义一个方法,传入的参数就是父类的类型运用上面讲到的即可进行调用。这样当我们创建的对象较多时,只要他们都是继承父类,都可以使用这个方法,就不需要使用重载的方式,对每个不同的子类都进行方法的重写,这样的方式有利于进行程序的维护,只需要对一个方法进行修改而不需要对每个重写的方法都进行修改。

    ...(省略子类继承父类的程序代码)
    public class RPG{
    	public static void main(String[] args){
    		SwardsMan swardsman = new SwardsMan();
    		swardsman.setBlood(200);
    		Magician magician = new Magician();
    		magician.setBlood(100);
    		showBlood(swardsman);//SwordsMan是一种Role,可以调用showBlood方法
    		showBlood(Magician);//Magician是一种Role,可以调用showBlood方法
    	}
    	static void showBlood(Role role){	//声明为Role类型
    		System.out.println("血量为:%d",role.getBlood());
    	}
    }
    

    6.1.3 重新定义行为

    假设现在我们要定义一个static()方法,我们考虑使用多态,但是父类中并没有定义子类中特有的方法,那我们将子类特有的方法在父类中进行声明,至于方法具体如何实现,交给各子类对此方法的重写。
    重新定义(Override):在继承父类方法后,定义与父类中相同的方法部署,但是执行的内容不同。
    注:
    1、在重新定义父类中的某个方法时,子类必须撰写父类方法中相同的签署。
    2、在JDK5之后支持标注(Annotation),如果加入Override标注,在重写方法时如果方法名字写错(包括大小写),会引发编译器错误。

    6.1.4 抽象方法、抽象类

    如果某方法块中没有任何程序代码操作,可以使用abstract标注该方法为抽象方法。该方法不用撰写{}区块,直接以“;”结束即可。
    注:
    类中如果有方法没有操作(例如含抽象方法),并且标示为abstract,表示这个类是不完整类,那么这个类就不能用来创建实例。
    Java中规定,内含抽象方法的类,一定要在class前标示abstract。

    public abstract class Role{
    	...(省略)
    	public abstract void fight();
    }
    

    对于抽象方法的两种处理方式:
    1、继续标示该方法为abstract(该子类因此也是个抽象类,必须在class前标示abstract);
    2、操作该抽象方法(重写该抽象方法)。
    如果没有以上两种操作,将引发编译错误。

    6.2 编译语法细节

    6.2.1 protected成员

    如果只想让子类直接获取类的属性值,可以将属性值定义为protected。被声明为protected的成员,相同包中的类可以直接存取,不同包中的类可以在继承后的子类直接存取。
    若没有定义权限关键字,就默认为是包范围。

    6.2.2 重新定义的细节

    在Java中,如果想获取父类中的方法定义,可以在调用方法前,加上super关键词。
    可以使用super关键词调用的父类方法,不能定义为private(因为这样就限定只能在类内部使用)。
    对于父类中的方法权限,只能扩大但不能缩小。若原来成员public,子类中重新进行定义时不可为private或protected。

    6.2.3 再看构造函数

    在创建子类实例后,会先进行父类定义的初始流程,在进行子类中定义的初始流程,也就是创建子类实例后,会先执行父类构造函数定义的流程,在执行子类构造函数定义的流程。
    如果在子类构造函数中没有指定执行父类中哪个构造函数,默认会调用父类中无参数构造函数。如果想执行父类中某个构造函数,可以使用super()指定。
    注:
    1、this()和super()只能择一使用,而且一定要在构造函数第一行执行。
    2、如果没有撰写任何构造函数时,自动会加入没有参数的默认构造函数,如果自行定义了构造函数,则不会自动加入任何构造函数了。
    3、如果定义了有参数的构造函数,也可以加入无参数构造函数,即使内容为空也可以。

    6.2.4 再看final关键词

    在构造函数中,一定要对定义为final关键词的数据成员指定初始值,否则会编译出错。
    如果在class前使用了final关键词,那么表示该类是最后一个,不会再有子类继承该类。同样,如果某一个方法限定为final,表示最后一次定义方法,子类不会重新定义final方法。

    6.2.5 java.lang.Object

    在java中,子类只能继承一个父类,如果在定义类时没有使用关键词extends指定继承任何类,那一定是继承java.lang.Object,即,如下写法是等价的:

    public class Some{
    	...
    }
    //相当于
    public class Some extends Object{
    	...
    }
    

    所以任何类追溯到最上层的父类,一定就是java.lang.Object,即java所有对象都是一种Object。
    任何类型的对象,都可以使用Object声明的名称来参考。

    6.2.5.1 重新定义toString()

    Object的toString()默认定义为:

    public String toString(){
    	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    
    6.2.5.2 重新定义equals()

    在java中要比较两个对象的实质相等性,要通过equals()方法进行比较。
    注:
    instanceof运算符:用来判断对象是否由某个类创建,左操作数是对象,右操作数是类。

    //判断other是否为Cat创造出来的
    if(!(other instanceof Cat)){
    	return false;
    }
    ...
    

    6.2.6 关于垃圾回收

    对于不再有用的对象,JVM有垃圾收集(GC)机制,收集到的垃圾对象所占据的内存空间会被垃圾收集器释放。在执行流程中,无法通过变量参考的对象,就会被GC认定为垃圾对象。
    GC在进行回收对象前,对调用对象的finalize()方法,在Object上定义的方法。如果在对象被回收前有些事情想做,可以重新定义finalize()方法。

  • 相关阅读:
    MongoDB+模板引擎 项目实例-学生档案管理
    MongoDB 增删改查命令速查
    MongoDB 数据库概述及环境搭建
    Flutter 升级
    TypeScript 快速上手及学习笔记
    Android ContentProvider 启动分析
    HTTP 报文格式简介
    深入浅出 HTTPS (详解版)
    从你输入网址,到看到网页——详解中间发生的过程
    TCP 三次握手和四次挥手图解(有限状态机)
  • 原文地址:https://www.cnblogs.com/albertman/p/8718147.html
Copyright © 2011-2022 走看看