重载发生在同一类中,靠参数列表区分,是编译期行为;重写发生在父子类间,要求签名一致,是运行期行为;构造方法可重载不可重写;泛型擦除和varargs易引发重载冲突,桥接方法由编译器自动生成。

重载(Overload)发生在同一个类里,靠参数列表区分
方法重载是编译期行为,JVM只看调用时传入的实参类型和个数,不看返回值或修饰符。哪怕两个方法仅返回值不同,javac 直接报错:method xxx() is already defined in class。
- 参数个数不同:如
print(String)和print(String, String) - 参数类型不同:如
process(int)和process(double)(注意基本类型与包装类不算自动重载,process(Integer)和process(int)是两个独立重载) - 参数顺序不同:如
build(String, int)和build(int, String)
常见坑:泛型擦除后可能造成意外冲突。比如 handle(List<string>)</string> 和 handle(List<integer>)</integer> 编译不过,因为擦除后都是 handle(List)。
重写(Override)发生在父子类之间,必须满足签名一致
重写是运行期行为,由 JVM 根据对象实际类型决定调用哪个版本。子类方法必须和父类被重写方法有完全相同的 方法名、参数列表、返回类型(Java 5 起允许协变返回类型,即子类可返回更具体的子类型),且不能比父类更严格地限制访问权限。
- 不能重写
private方法——它对子类不可见,子类里同名方法只是新定义 -
static方法不能被重写(但可以被“隐藏”,调用取决于引用类型,不是实际类型) - 加了
@Override注解却没真正重写时,javac会报错,这是最有效的防错手段
典型错误现象:java.lang.NoSuchMethodError 往往不是重写问题,而是编译时用了新版接口、运行时加载了旧版类;而 ClassCastException 或静默调用父类方法,常因忘了加 @Override 导致“以为重写了,其实没重写”。
立即学习“Java免费学习笔记(深入)”;
构造方法既不能重写,也不能被继承,但可以重载
构造方法名字固定为类名,没有返回类型,天然不满足重写条件(重写要求方法签名相同,而父类构造方法根本不会出现在子类中)。子类构造器第一行默认调用 super(),但这不是重写,只是显式/隐式调用父类构造逻辑。
- 每个类都可以定义多个构造方法,彼此是重载关系
- 如果父类只有带参构造器,子类必须显式调用
super(xxx),否则编译失败:constructor xxx in class YYY cannot be applied to given types - 抽象类的构造器也能被重载,只是不能直接 new
容易忽略的一点:匿名内部类在创建时写的构造逻辑,其实是外层类某个构造器的重载调用入口,不是重写。
泛型方法和 varargs 在重载/重写中特别容易误判
泛型方法的类型参数在编译期擦除,varargs 实际被转成数组,这两者叠加会让重载解析变得反直觉。
-
foo(List<string>)</string>和foo(List)不能共存——后者会被视为前者擦除后的签名,编译失败 -
bar(String...)和bar(String[])不能同时存在,因为 varargs 底层就是数组 - 重写时若父类方法是
<t> T get()</t>,子类重写必须保持同样泛型结构,不能改成Object get(),否则不是重写而是重载(且可能引发桥接方法问题)
最麻烦的是桥接方法(bridge method):当你重写一个泛型父类方法时,编译器会悄悄生成一个非泛型的桥接方法来保证多态正确性。这个过程对开发者透明,但如果你用反射查 getDeclaredMethods(),会看到多出的方法,别慌——那是编译器干的。







