首先说经过Java的初步学习,我已经能体会Java完完全全是针对生活的一种高度抽象,其面向对象的思想核心让程序编写的中心由考虑程序运行的顺序和步骤转向了研究分析各个不同组件(对象和类)之间的关系与相互作用,这一点是面向过程的编程思想不能达到的。反观之前自己写的Python程序,完全没有发挥出面向对象编程的精髓,整个方法都有偏离。由于Java这种高度抽象性,我们只需通过生活中的具体实例,就能理解Java中类的继承概念和应用。
一、什么是继承
我们之前已经知道,类是对对象的一种高度概括,具有相同或相近属性和方法的对象可归为一类,通过类,我们可以对对象进行实例化。而此处继承的概念,则是针对类与类之间的关系,对这一观点的理解必不可少,或者说继承是通过一套模板去生成另一套模板。
二、Java继承的语法格式
Java继承的关键字为extends。其格式为:
Public class 类名(亦称子类、派生类、超类) extends 类名(父类、基类),如我们现在首先定义一个学生类,在学生类中定义好“name”属性、“setname”方法和“study”方法。代码如下:
public class Student{
public String name;
public void study(){
System.out.println("学生"+name+"正在学习");
}
public void setname(String name){
this.name = name;
}
}
注意,其中由于setname中的参数名与此类中的一个属性名一致,需要用this去指代区分,this在此相当于“学生类”,可以理解为调用了学生类中的姓名属性,并把参数值赋给该姓名属性。也就是说,如果两者名称不一致,则不需要使用this。
然后我们通过继承生成一个大学生类,代码如下:
public class UNstudent extends class Student {
}
在此注意,子类中必须调用父类的一个构造方法,否则程序编译会报错。关于此处知识点,详细说明如下:
1. 关于为何会报错:根据父类创建子类时,首先会创建父类,而如果子类中未调用父类方法,则父类中无参数传入,因此报错。
2. 若父类中的方法无参数传入,则子类中不必写。因为子类中默认会调用一个无参数的父类构造方法,此时恰好与父类情况一致。
3. 调用父类构造方法的关键字是super:
Super.function(参数)
4. 使用super调用父类构造方法必须放在可执行代码的第一位。
三、子类继承父类的哪些内容
子类将继承父类所有的属性和方法
对于不同访问修饰符在不同情况下的使用,详见下表
访问修饰符 | 同类中 | 同包中 | 不同包中 | 不同包但有继承关系的子类中 |
Private | 可以 | 不可以 | 不可以 | 不可以 |
默认 | 可以 | 可以 | 不可以 | 不可以 |
Protected | 可以 | 可以 | 不可以 | 可以 |
Public | 可以 | 可以 | 可以 | 可以 |
子类可以定义父类中没有的属性和方法。
四、方法重写
1. 方法重写使用的情况:存在继承关系,但子类要求对父类中继承的方法进行重写,同时保留原有返回值类型和参数。
2. 方法重写的条件:
a) 必须存在继承关系
b) 子类重写方法时,方法的访问修饰符可以大于或等于父类方法的访问修饰符
c) 子类重写方法时,方法的返回值类型、方法名、参数必须和父类保持一致
五、自动转型
自动转型可理解为让一个对象拥有子类的方法,但却有归于父类中,在调用方法时会优先调用子类的方法,若子类中无方法,则会去父类中寻找相应方法。若需要执行父类的方法,需使用关键字super。
自动转型可应用于以下场景:当我们需要让一个父类以下的拥有不同方法的各个子类对象作为父类对象都去实现父类中的方法时。
六、多态
多态是由方法重载、继承、方法重写、自动转型技术组合的特性。
面向对象编程的三大特性即:继承、多态、封装
七、为什么要继承
继承有两点好处:第一,可以提高代码的重用性。也就是说,我们通过对一个抽象而概括的父类的定义,可以在以后修改需求或功能时,甚至在进行其他项目时,能够运用上已经完成的框架,而不需要全盘重写,这可以大大减轻我们的工作量。第二,可以提高程序的扩展性。我们如果有新的需求,需要添加更改新的功能,可以直接修改父类,而不需要对已经实例化的对象进行逐一修改,这可以让我们程序能够轻而易举的增加删减功能。
八、练习:奥特曼A和小怪兽A进行PK,直到一方的血量为0时结束战斗,输出谁胜利了。要求仅定义一个类,运用继承的方法。代码如下:
首先定义一个NPC类
//定义NPC父类,通过此类再继承生成奥特曼类和小怪兽类
public class NPC{
//定义字符串姓名属性
public String name;
//定义整数的生命值属性
public int HP;
//定义设定姓名的方法
public void setname(String name){
//注意此处由于参数值和属性名相同,用this来区分。this表示当前类
this.name=name;
}
//定义设置生命值的方法
public void setHP(int HP){
this.HP=HP;
}
//定义攻击的方法
public void fight(NPC npc){
//生命值减少
npc.HP--;
//输出
System.out.println(name+"攻击了"+npc.name+",当前"+npc.name+"的生命值为"+npc.HP+";"+name+"的生命值为"+HP);
//判定生命值,并输出获胜情况
if(npc.HP<=0){
System.out.println("战斗结束,获胜者是"+this.name);
}
}
}
然后再根据此父类去生成两个子类
//根据NPC类来继承生成一个小怪兽类
public class Monster extends NPC{
}
//根据NPC类继承生成一个奥特曼类
public class Outman extends NPC{
}
再来定义入口主函数,并实例化
//定义入口主函数
public class Manager{
public static void main(String [] args){
//根据奥特曼类来实例化奥特曼A
Outman OutA = new Outman();
//设定姓名
OutA.setname("奥特曼A");
//设定生命值
OutA.setHP(30);
//根据小怪兽类实例化小怪兽A
Monster MonA = new Monster();
//设定姓名
MonA.setname("小怪兽A");
//设定生命值
MonA.setHP(30);
//判定当前生命值并决定是否继续战斗
while(OutA.HP>0 && MonA.HP>0){
OutA.fight(MonA);
if(MonA.HP<=0){
break;
}
MonA.fight(OutA);
}
}
}