Java 学习记录 第十一天
由于本渣渣是前端转行Java,所以编辑器的选择就直接用 webstorm 同公司的 idea
接下来的知识梳理有一部分会借鉴大佬 廖雪峰的博客
学习目标
实例演示Java中重写和重载从而理解多态的概念
学习内容
- 重写、Override注解
- 方法的重写规则
- Super 关键字在重写中该如何使用
- 重载的定义
- 重写与重载之间的区别
- 多态的概念实例解析
- 虚函数与非虚函数
重写、Override注解
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。
例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
方法的重写规则
- 参数列表与被重写方法的参数列表必须完全相同。
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个类,则不能重写该类的方法。
Super 关键字在重写中该如何使用
当需要在子类中调用父类的被重写方法时,要使用 super 关键字。
重载的定义
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
重写与重载之间的区别
区别点 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
参数列表 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
参数列表 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
class Hero {
private String name;
private String skill;
private Integer hp;
private Integer mp;
public void setName(String name) {
this.name = name;
System.out.println("这是英雄父类的 setName() 方法");
}
public String getName() {
System.out.println("这是英雄父类的 getName() 方法。");
return name;
}
public void setSkill(String skill) {
System.out.println("这是英雄父类的 setSkill() 方法。");
this.skill = skill;
}
public String getSkill() {
System.out.println("这是英雄父类的 getSkill() 方法。");
return skill;
}
public void action(String str) {
System.out.println("重载方法 action() 接收String类型参数:" + this.name + "正在" + str);
}
public void action(int hurt) {
int lastHp = this.hp - hurt;
System.out.println("重载方法 action() 接收int类型参数:" + this.name + "剩余血量:" + lastHp);
}
public Hero(String name) {
setName(name);
System.out.println("这是英雄父类的构造方法。");
}
}
class Houyi extends Hero {
private String name = "后羿";
private String skill = "大招";
private Integer hp = 1000;
@Override
public void setSkill(String skill) {
String skillName = super.getSkill() == null ? "空" : super.getSkill();
super.setSkill(skill);
System.out.println("上面是调用英雄父类的 setSkill() getSkill() 方法,下面是后羿类重写的 setSkill() 方法");
this.skill = skill;
System.out.println("英雄父类的技能为:" + skillName + ",后羿类的技能为:" + this.skill);
}
@Override
public void setName(String name) {
System.out.println("这是后羿类重写的 setName() 方法\n");
this.name = name;
}
@Override
public void action(int hurt) {
System.out.println("\n此处英雄类的 action() 方法被重写。。。。");
int lastHp = this.hp - hurt;
if (lastHp > 0) {
System.out.println("重载方法 action() 接收int类型参数:" + this.name + "剩余血量:" + lastHp);
} else {
System.out.println("重载方法 action() 接收int类型参数:" + this.name + "剩余血量不足,已死亡。");
}
}
public Houyi() {
super("后羿");
System.out.println("这里是后羿类继承了英雄类调用了构造器。\n");
setName(this.name);
setSkill(this.skill);
System.out.println("\n");
System.out.println("英雄类的name:" + super.getName());
System.out.println("后羿类的name:" + name);
}
}
public class Test1 {
public static void main(String[] args) {
Houyi houyi = new Houyi();
houyi.action("做骚操作,好像是大神!!!");
houyi.action(20000);
}
}
总结:方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式,上述代码的输出结果如下图所示,一个简单的演示。
- 方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
- 方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
- 方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
多态的概念实例解析
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
多态的优点:
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态存在的三个必要条件:
- 继承
- 重写
- 父类引用指向子类对象:Parent p = new Child();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
虚函数与非虚函数
虚函数的存在是为了多态。
Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
public class Test1 {
public static void main(String[] args) {
Hero houyi = new Houyi();
Houyi houyi2 = new Houyi();
houyi.action("做骚操作,好像是大神!!!");
houyi.action(20000);
System.out.println("~~~~~~~~~~~~~~~");
houyi2.action("做骚操作,好像是大神!!!");
houyi2.action(20000);
}
}
- 实例化了两个 Houyi 对象:一个使用 Hero 引用 houyi,另一个使用 Houyi 引用 houyi2。
- 当调用 houyi.action() 时,编译器在编译时会在 Hero 类中找到 action(),执行过程 JVM 就调用 Houyi 类的 action()。
- houyi2 是 Houyi 的引用,但引用 houyi2 最终运行的是 Hero 类的 action() 方法。
- 在编译的时候,编译器使用 Hero 类中的 action() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Houyi 类中的 action() 方法。
Java中所有的方法都能以这种方式表现,因此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。
以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法,输出结果如下:
自学不易,点赞鼓励。
谢谢各位看官,如果有哪里写错的还望指出来哈,共同进步。