Java面向对象基础之多态

本文总结了Java面向对象中的多态概念,包括子类如何覆写父类方法,final关键字的使用,以及如何通过super调用父类方法。重点探讨了多态的特性,即运行时动态调用实际类型的方法,允许代码扩展而无需修改原有基于父类的部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小结

  • 子类可以覆写父类的方法(Override),覆写在子类中改变了父类方法的行为
  • Java的方法调用总是作用于运行时期对象的实际类型,这种行为称为多态
  • final修饰符有多重作用:
  1. final修饰的方法可以阻止被覆写
  2. final修饰的class可以阻止被继承
  3. final修饰的field必须在创建对象时初始化,随后不可修改

覆写(Override):在继承关系中,子类如果定义了一个与方法签名(方法的名称和参数类型)完全相同的方法

class Person{//例如我们在父类中定义了run()方法
    public void run(){
        System.out.println("Person.run");
    }
}
class Student extedns Person{//在子类Student中,覆写了这个run()方法
    public void run(){
        System.out.println("Student.run");
    }
}

Override和Overload不同的是,如果方法签名不同,就是Overload,Overload方法是一个新方法;如果方法签名相同,并且返回值也相同,就是Override。注意:方法名相同,方法参数相同,但方法返回值不同,也是不同方法。在java程序中,出现这种情况,编译器会报错。

如果子类覆写了父类的方法,一个实际类型为Student,引用类型为Person的变量,调用其run()方法,实际上调用的方法是Student的run()方法,因此可得出结论:Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型,这个非常重要的特性在面向对象编程中称之为多态(Polymorphic)

//多态
public class Main {
    public static void main(String[] args){
        Person p = new Student();
        p.run();//输出的是Student.run
    }
}
class Person{//例如我们在父类中定义了run()方法
    public void run(){
        System.out.println("Person.run");
    }
}
class Student extends Person{//在子类Student中,覆写了这个run()方法
    @Override//让编译器帮助检查是否进行了正确的覆写。不小心写错了方法签名,编译器会报错
    public void run(){
        System.out.println("Student.run");
    }
}

多态

针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法

多态的特性就是,运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。这种不确定性的方法调用,作用是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码

//多态
public class Main {
    public static void main(String[] args){
        //给一个有普通收入、工资收入和享受国务院特殊津贴的小伙伴算税
        Income incomes[] = new Income[]{
                new Income(3000),//普通收入
                new Salary(7500),//工资
                new Allowance(15000)//津贴
        };
        //利用多态,totalTax()方法只需要和Income打交道,不需要知道salary和Allowance的存在
        //如果需要新增稿费收入,只需要从Income派生,然后正确覆写getTax()方法即可
        System.out.println(totalTax(incomes));
    }
    public static double totalTax(Income...incomes){//定义了一个可变参数incomes,相当于数组类型
        double total = 0;
        for(Income income:incomes){//遍历数组incomes
            total+=income.getTax();//访问引用类型变量income指向的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 Salary extends Income{//继承
    public Salary(double income){
        super(income);//调用父类的构造方法Income(double)
    }
    @Override//让编译器检查是否进行了正确的覆写
    public double getTax(){//覆写父类的方法
        if(income <= 5000){//此处income==this.income
            return 0;
        }
        return (income-5000)*0.2;
    }
}
class Allowance extends Income{//继承
    public Allowance(double income){
        super(income);//调用父类的构造方法Income(double)
    }
    @Override//让编译器检查是否进行了正确的覆写
    public double getTax(){//覆写父类的方法
        return 0;
    }
}

覆写Object方法

因为所有的class最终都继承自Object,而Object定义了几个重要的方法:

  • toString():把instance输出为String
  • equals():判断两个instance是否逻辑相等
  • hashCode():计算一个instance的哈希值

覆写这几个方法

class Person{
    protected String name;
    @Override
    public String toString(){
        return "Person:name="+name;
    }
    //比较是否相等
    public boolean equals(Object o){
        //当且仅当o为Person类型
        if(o instanceof Person){
            Person p = (Person)o;//强制类型转换
            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(){
        //调用父类的hello()方法
        return super.hello()+"!";
    }
}

final

继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override:

class Person{
    protected String name;
    public final String hello(){//不允许被子类覆写
        return"hello,"+name;//构造方法
    }
}
class Student extends Person{
    @Override//compile error:不允许覆写
    public String hello(){
    }
}

对一个类的实例字段,同样可以用final修饰。在构造方法中初始化final字段,对final字段重新赋值会报错

//覆写Object方法
public class Main {
    public static void main(String[] args){
        Person p = new Person("cheng");
        p.name = "cheng";//当试图修改时,compile error!
    }
}
class Person{
    public final String name;//用final修饰的字段在初始化后不能被修改
    public Person(String name){
        this.name = name;
    }
}

public class Test {

    public static void main(String[] args){
        show(new Cat());
        show(new Dog());
        Animal a = new Cat();//向上转型
        a.eat();
        Cat c = (Cat)a;//向下转型
        c.work();

    }
    public static void show(Animal a){
        a.eat();
        if(a instanceof Cat){//instanceof用来判断一个对象是否是一个类的实例
            Cat c = (Cat)a;
            c.work();
        }
        else if(a instanceof Dog){
            Dog c = (Dog)a;
            c.work();
        }
    }
}
abstract class Animal{
    abstract void eat();
}
class Cat extends Animal{
    public void eat(){
        System.out.println("吃鱼");
    }
    public void work(){
        System.out.println("抓老鼠");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("吃肉");
    }
    public void work(){
        System.out.println("看家");
    }
}
//Employee.java
public class Employee {
    private String name;
    private String address;
    private int number;
    public Employee(String name,String address,int number){
        System.out.println("构造函数");
        this.address = address;
        this.name = name;
        this.number = number;
    }
    public void mailCheck(){
        System.out.println("把支票寄给: "+this.name+" "+this.address);
        //当类中的成员变量和局部变量重名时,使用this表示成员变量
    }
    public String toString(){
        return name+" "+address+" "+number;
    }
    public String getName(){
        return name;
    }
    public String getAddress(){
        return address;
    }
    public int getNumber(){
        return number;
    }
    public void setAddress(String newAddress){
        address = newAddress;
    }
}
//Salary.java
public class Salary extends Employee{
    private double salary;//全年工资
    public Salary(String name,String address,int number,double salary){
        super(name,address,number);
        setSalary(salary);
    }
    public void mailCheck(){
        System.out.println("Salary的mailCheck方法");
        System.out.println("邮寄支票给: "+getName()+", 工资为:"+salary);
    }
    public void setSalary(double newSalary){
        if(newSalary>=0.0)
            salary = newSalary;
    }
    public double getSalary(){
        return salary;
    }
    public double computeSalary(){
        System.out.println("计算工资付给: "+getName());
        return salary/52;
    }
}
//VirtualDemo.java
public class VirtualDemo {
    public static void main(String[] args){
        Salary a = new Salary("成希乐","北京",3,500000.00);
        Employee b = new Salary("lele","成都",4,30000.00);
        System.out.println("使用Salary的引用调用mailCheck----------");
        a.mailCheck();;
        System.out.println("使用Employee的引用调用mailCheck--------");
        b.mailCheck();
    }
}
/*
构造函数
构造函数
使用Salary的引用调用mailCheck----------
Salary的mailCheck方法
邮寄支票给: 成希乐, 工资为:500000.0
使用Employee的引用调用mailCheck--------
Salary的mailCheck方法
邮寄支票给: lele, 工资为:30000.0
 */

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值