面向对象基础
学习资料来自多态 - Java教程 - 廖雪峰的官方网站
目录
面向对象基础
Override
多态
举个例子
覆写Object方法
调用super
final
练习
小结
Override
在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)
举个例子,在Person类中定义run()方法,在子类Student类中,覆写这个方法
class Person {
public void run() {
System.out.println("Person.run");
}
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}
Override和Overload的区别:
如果方法签名不同,就是Overload,Overload方法是一个新方法;
如果签名方法相同,并且返回值也相同,就是Override
注意:
方法名相同,方法参数相同,但方法返回值不同,也是不同的方法,在Java程序中,出现这种情况,编译器会报错。
class Person {
public void run() { ... }
}
class Student extends Person {
// 不是Override,因为参数不同
public void run(String s) { ... }
// 不是Override,因为返回值不同
public int run() { ... }
}
加上Override可以让编译器帮助检查是否进行了正确的覆写,但他不是必须的。
引用变量的声明类型可能与其实际类型不符,比如:
Person p = new Student();
如果子类覆写了父类的方法,那么一个实际类型为Student,引用类型为Person的变量,调用其run()方法,调用的是Student的run()方法。
所以,Java实施例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。这个特性在面向对象编程中称之多态(Polymorphic)。
多态
多态是指针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
运行期才能动态决定调用的子类方法,对于某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。
举个例子
定义一种收入,需要报税,对于工资收入可以减去一个基数,如果享受国务院特殊津贴,可以全部免税。
public class Main {
public static void main(String[] args) {
Income[] incomes = new Income[] {
new Income(3000),
new Income(7500),
new StateCouncilSpecialAllowance(15000)
};
System.out.println(totalTax(incomes));
}
public static double totalTax(Income... incomes) {
double total = 0;
for (Income income : incomes) {
total += total + income.getTax();
}
return total;
}
}
class Income {
protected double income;
public Income(double income) {
this.income = income;
}
public double getTax() {
return income * 0.1;
}
}
class Salary extends Income {
public Salary(double income) {
super(income);
}
@Override
public double getTax() {
if(income <= 5000) {
return 0;
}
return (income - 5000) * 0.2;
}
}
class StateCouncilSpecialAllowance extends Income {
public StateCouncilSpecialAllowance(double income) {
super(income);
}
@Override
public double getTax() {
return 0;
}
}
利用多态,totalTax()只需要知道Income,不需要知道Salary和StateCouncilSpecialAllowance就能正确计算出总的税,
如果要新增一种收入,只需要从Income派生,然后正确覆写getTax()方法就行,把新的类型传入totalTax()不需要修改任何代码。
覆写Object方法
所有的class最终都继承自Object,而Object定义了几个重要方法:
- toString():把instance输出为String
- equals():判断两个instance是否逻辑相等
- hashCode():计算一个instance的哈希值
在必要情况下,可以覆写这几个方法,例如:
class Person {
...
// 显示更有意义的字符串:
@Override
public String toString() {
return "Person:name=" + name;
}
// 比较是否相等:
@Override
public boolean equals(Object o) {
// 当且仅当o为Person类型:
if (o instanceof Person) {
Person p = (Person) o;
// 并且name字段相同时,返回true:
return this.name.equals(p.name);
}
return false;
}
// 计算hash:
@Override
public int hashCode() {
return this.name.hashCode();
}
}
调用super
在子类的覆写方法中,如果要调用父类的被覆写方法,可以通过super调用。
class Person {
protected String name;
public String hello() {
return "Hello," + name;
}
}
class Student extends Person {
@Override
public String hello() {
return super.hello() + "!";
}
}
final
- 用final修饰的方法不能被Override:父类的方法用final修饰就可以不允许子类他的某个方法进行覆写
- 用final修饰的类不能被继承。
- 用final修饰的字段在初始化之后不能被修改。
练习
public class Main {
public static void main(String[] args) {
// TODO: 给一个有工资收入和稿费收入的小伙伴算税:
Income[] incomes = new Income[]{
new Income(3000),
new SalaryIncome(7500),
new RoyaltyIncome(12000)
};
System.out.println(totalTax(incomes));
}
public static double totalTax(Income... incomes) {
double total = 0;
for (Income income : incomes) {
total += total + income.getTax();
}
return total;
}
}
class Income {
protected double income;
public Income(double income) {
this.income = income;
}
public double getTax() {
return income * 0.1; // 税率10%
}
}
class SalaryIncome extends Income {
public SalaryIncome(double income) {
super(income);
}
@Override
public double getTax() {
if (income <= 5000) {
return 0;
}
return (income - 5000) * 0.2;
}
}
class RoyaltyIncome extends Income {
public RoyaltyIncome(double income) {
super(income);
}
}
小结
子类可以覆写父类的方法(Override),覆写在子类中改变了父类方法的行为;
Java的方法调用总是作用于运行期对象的实际类型,这种行为称为多态;