莫度编程网

技术文章干货、编程学习教程与开发工具分享

JAVA面试|重载和重写的区别(重载和重写的区别,面向对象的三个特性)

我们来把重载(Overloading)和重写(Overriding)这两个面向对象编程的核心概念掰开揉碎,用大白话和例子讲清楚它们的区别。

一、核心比喻(先记住这个感觉)

重载(Overloading):就像是同一个名字的工具箱,但里面装了功能相似但适用场景不同的工具。比如“扳手工具箱”里可能有:拧小螺丝的小扳手、拧大螺丝的大扳手、拧水管的长柄扳手。它们都叫“扳手”(方法名相同),但根据你要拧的东西不同(参数不同),你选用不同的扳手。

重写(Overriding):就像是儿子继承了老爸的某项手艺,但儿子觉得老爸的做法不够好或者不适合新时代,于是他自己重新定义了一套更好的做法。比如老爸有个“修自行车”的方法(父类方法),儿子继承了,但儿子觉得老爸修得慢,他发明了一套更快的方法。当别人叫“修自行车”时(调用同名同参数的方法),如果对象是儿子,就用儿子的新方法(重写后的方法)。


二、重载(Overloading)

发生在哪里?同一个类里面。

是什么?在同一个类中,允许存在多个同名的方法。

关键要求:这些同名方法的参数列表必须不同。怎么个不同法?

参数个数不同:比如add(int a, int b)和add(int a, int b, int c)

参数类型不同:比如add(int a, int b)和add(double a, double b)

参数顺序不同(通常指类型顺序):比如print(String name, int age)和print(int age, String name)

注意:仅仅返回值类型不同、方法修饰符不同(如static)或者抛出的异常不同,不足以构成重载! 编译器区分重载方法只看“方法签名”,而方法签名 = 方法名 + 参数列表(参数类型、个数、顺序)。

为什么叫“重载”?就是给同一个方法名“装载”了多种不同的实现方式(参数组合),让它能处理更多样的情况。

调用时怎么知道用哪个?在编译时,编译器根据你调用方法时传递的实际参数的类型、个数和顺序,就能精确地确定应该调用哪个重载版本。这叫静态绑定或早绑定。

目的:提高方法的灵活性和可读性。使用者只需要记住一个方法名(比如print或calculate),就能根据不同的需求传入不同的参数,不用记一堆不同名字的方法(如printString, printInt, calculateSum, calculateAverage)。

通俗例子:

你开了一家奶茶店,有个方法叫makeDrink()。

重载1:makeDrink(String teaType) // 做指定类型的纯茶

重载2:makeDrink(String teaType, String milkType) // 做指定茶底+指定奶的奶茶

重载3:makeDrink(String teaType, String milkType, int sugarLevel) // 做指定茶底+指定奶+指定糖度的奶茶

顾客点单时,你说“做杯奶茶”(方法名 makeDrink),店员(编译器)会根据顾客具体说了什么(参数:只要茶?茶+奶?茶+奶+半糖?)来决定执行哪个具体流程。

public class Calculator {

// 重载1:两个整数相加

public int add(int a, int b) {

return a + b;

}


// 重载2:三个整数相加 (参数个数不同)

public int add(int a, int b, int c) {

return a + b + c;

}


// 重载3:两个浮点数相加 (参数类型不同)

public double add(double a, double b) {

return a + b;

}


// 重载4:整数和浮点数相加 (参数类型不同)

public double add(int a, double b) {

return a + b;

}


// 重载5:浮点数和整数相加 (参数顺序不同)

public double add(double a, int b) {

return a + b;

}


// 错误示例:仅返回值不同,不是重载!编译器会报错

// public double add(int a, int b) {

// return (double)(a + b);

// }

}


三、重写(Overriding)

发生在哪里?具有继承关系的父子类之间(子类继承父类)。

是什么?子类觉得从父类继承来的某个方法的实现不适合自己(或者想扩展功能),于是自己重新写一个实现来替代父类的那个方法。

关键要求:

方法名必须相同

参数列表必须完全相同(个数、类型、顺序)

返回值类型:

在Java 5+中,子类方法的返回值类型可以是父类方法返回值类型的子类型(协变返回类型)。

否则,必须和父类方法返回值类型相同。

访问权限:子类方法的访问权限不能比父类方法更严格。例如,父类方法是public,子类重写时不能是protected或private(可以保持 public)。父类方法是protected,子类可以是protected或 public(不能是private)。

异常:子类方法抛出的受检异常(Checked Exception)范围不能比父类方法抛出的更广(可以更少或相同,或者不抛)。

static, final, private方法不能被重写:static方法属于类,与对象无关;final方法禁止修改;private方法在子类不可见。

@Override注解:强烈建议在子类重写方法上加上@Override注解。这会让编译器帮你检查是否真的满足了重写的所有条件(方法签名匹配、访问权限等),避免因拼写错误等原因导致你以为重写了其实没有。

为什么叫“重写”?就是子类把父类的方法实现覆盖掉、重新写了一遍。

调用时怎么知道用哪个?在运行时,JVM根据实际创建的对象类型(而不是引用变量的类型)来决定调用哪个版本的方法。这叫动态绑定或晚绑定。这是实现多态性(Polymorphism)的关键机制。

目的:实现多态。允许子类根据自身需要定制特定的行为,同时对外提供统一的接口(父类定义的方法名)。这是面向对象“一个接口,多种实现”思想的核心体现。

通俗例子

父类Animal有个方法makeSound() { 输出 "动物叫" }。

子类 Dog 重写了makeSound() { 输出 "汪汪汪!" }。

子类 Cat 重写了makeSound() { 输出 "喵喵喵~" }。

当你写代码:

Animal myAnimal = new Dog(); // 引用是Animal类型,实际对象是Dog

myAnimal.makeSound(); // 输出 "汪汪汪!" (运行时根据实际对象Dog决定)

Animal anotherAnimal = new Cat(); // 引用是Animal类型,实际对象是Cat

anotherAnimal.makeSound(); // 输出 "喵喵喵~" (运行时根据实际对象Cat决定)

虽然myAnimal和anotherAnimal的引用类型都是Animal,但调用makeSound() 时,JVM看它们实际指向的对象是Dog还是Cat,然后调用相应子类重写的方法。这就是多态的魅力!

class Animal {

public void makeSound() {

System.out.println("动物叫");

}


public Animal getOffspring() {

return new Animal();

}

}


class Dog extends Animal {

// 重写 makeSound() 方法

@Override // 使用注解,让编译器检查

public void makeSound() {

System.out.println("汪汪汪!");

}


// 重写 getOffspring() 方法,使用协变返回类型 (返回Dog, 是Animal的子类)

@Override

public Dog getOffspring() {

return new Dog();

}

}


class Cat extends Animal {

// 重写 makeSound() 方法

@Override

public void makeSound() {

System.out.println("喵喵喵~");

}

}


四、总结

重载:同类名同,参数不同。(发生在同类,方法名一样,参数必须不一样)

重写:子承父业,改头换面。(发生在父子类,方法名和参数必须一模一样,子类重写实现)

希望这个详细又通俗的解释能帮你彻底分清重载和重写!是不是豁然开朗了?

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言

    Powered By Z-BlogPHP 1.7.4

    蜀ICP备2024111239号-43