前言
Java 面向对象面试有一套连环组合题:重载 & 重写、访问修饰符、static 关键字。
不少人的误区:
- 以为返回值不同就可以构成重载;
- 以为静态方法可以重写;
- 分不清 protected 在不同包子类的访问权限;
- 搞混静态代码块、构造代码块、构造方法执行顺序。
本文一站式搞定这一组高频考点:从底层定义、完整代码实战、边界规则、误区解析、工程选型,全部覆盖。适合 Java 基础巩固,也适合集中复盘。
一、重载 Overload
1. 核心定义
重载发生在同一个类内部 :多个方法方法名相同,参数列表不同。 参数列表不同包含三种情况:
- 参数个数不同
- 参数类型不同
- 参数顺序不同(不同类型参数调换顺序)
重点:返回值、访问修饰符不能作为重载的判断条件 。编译器在调用方法时,只看传入实参,无法通过返回值类型区分调用哪个方法。仅返回值不一样,编译报错,不构成重载。 重载属于编译期静态绑定,在编译阶段就确定调用哪个方法,也叫静态多态。
2. 代码实战示例
java
public class OverloadDemo {
// 无参方法
public void say() {
System.out.println("无参数方法");
}
// 一个参数
public void say(String name) {
System.out.println("姓名:" + name);
}
// 两个参数
public void say(String name, int age) {
System.out.println("姓名:" + name + ",年龄:" + age);
}
// 参数顺序不同,构成重载
public void say(int age, String name) {
System.out.println("年龄:" + age + ",姓名:" + name);
}
public static void main(String[] args) {
OverloadDemo demo = new OverloadDemo();
demo.say();
demo.say("张三");
demo.say("李四",20);
demo.say(22,"王五");
}
}
3. 重载的使用场景
- 构造方法重载:无参构造、带参构造,方便外部以不同方式实例化对象;
- API 工具类:同一个功能,支持多种入参形式(例如日期格式化,支持传入 Date、String);
- 框架底层大量使用:IO、集合、Spring 工具方法大量使用重载提升 API 易用性。
二、重写 Override
1. 核心定义
重写发生在存在继承关系的父子类之间 。子类对父类的实例方法 重新实现,覆盖父类原有逻辑。 @Override注解:编译期校验,检查当前方法是否满足重写规范,防止手敲写错方法名、参数类型,避免误以为重写成功。
重写约束规则
- 方法名、参数列表必须与父类完全一致;支持协变返回值(子类返回值可以是父类返回值的子类);
- 子类方法访问权限不能小于父类;父类 public,子类不能改为 protected/private;
- 子类抛出的受检异常范围不能大于父类抛出的受检异常;非受检异常无限制;
- 父类被 private /static/final 修饰的方法,不能重写;
- 重写针对实例方法,依靠虚方法表实现动态绑定,运行期确定执行方法,属于真正多态。
2. 代码实战示例
java
class Parent {
public void hello() {
System.out.println("父类 hello 方法");
}
protected Person getPerson(){
return new Person();
}
}
class Child extends Parent {
// @Override 校验重写
@Override
public void hello() {
System.out.println("子类 hello 重写方法");
}
// 协变返回:返回子类对象Student,属于合法重写
@Override
public Student getPerson(){
return new Student();
}
}
class Person{}
class Student extends Person{}
public class OverrideTest {
public static void main(String[] args) {
Parent p = new Child();
p.hello(); // 执行子类重写方法:多态
}
}
3. 重载 VS 重写完整对比表
| 对比维度 | 重载 Overload | 重写 Override |
|---|---|---|
| 发生范围 | 同一个类内部 | 存在继承关系的父类与子类之间 |
| 方法签名 | 方法名相同,参数列表不同 | 方法名、参数列表完全一致 |
| 返回值 | 返回值不参与重载判断,无强制约束 | 支持协变返回,返回值必须是父类返回值的子类或本身 |
| 异常声明 | 无任何异常限制 | 子类抛出的受检异常不能比父类更广 |
| 访问权限 | 无权限限制,可以任意修改 | 子类访问权限不能低于父类 |
| 绑定时机 | 编译期静态绑定(静态多态) | 运行期动态绑定(动态多态) |
| 注解 | 不需要注解 | 推荐添加 @Override,编译校验 |
| 适用对象 | 普通成员方法、构造方法 | 仅实例方法,static/private/final 方法无法重写 |
4. 高频误区深度解析
误区 1:返回值不同就可以构成重载
纠正:编译器调用方法只看传入实参,无法依靠返回值区分,只修改返回值编译报错。
误区 2:静态方法可以重写
纠正:static 属于类,不属于实例对象。子类同名静态方法只是静态隐藏,不是重写,不存在多态。父类引用指向子类对象,调用静态方法执行的是父类静态方法。
java
class Father{
public static void test(){
System.out.println("父类静态方法");
}
}
class Son extends Father{
public static void test(){
System.out.println("子类静态方法");
}
}
public class Test{
public static void main(String[] args) {
Father f = new Son();
f.test(); // 输出父类静态方法!没有多态
}
}
误区 3:父类 protected 方法,子类重写可以改成 private
纠正:编译报错!重写要求子类权限不能小于父类,protected > private,不允许降级。
三、Java 四种访问修饰符 public /protected/default /private
访问修饰符控制类、成员变量、成员方法 的可见范围,编码遵循最小权限原则:能小范围访问,就不要扩大权限,减少对外暴露,降低维护成本。
default:不写任何修饰符,也叫包访问权限。
| 修饰符 | 同一类 | 同一个包 | 不同包子类 | 其他包无关类 |
|---|---|---|---|---|
| public | ✅ | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ✅ | ❌ |
| default (不写) | ✅ | ✅ | ❌ | ❌ |
| private | ✅ | ❌ | ❌ | ❌ |
各修饰符详解 & 工程选型建议
- private(优先推荐) 仅当前类内部可访问。封装的核心实现手段,成员变量优先定义 private。只暴露必要 get/set。
- default(包访问权限) 同包下所有类可以访问,跨包(哪怕是子类)也无法访问。项目内部包内工具类使用;业务实体尽量避免使用,可控性差。
- protected 同包类 + 不同包的子类可以访问。用于父类预留扩展点,给子类重写;业务开发谨慎使用,对外暴露范围偏大。
- public 所有包全部可见。用于对外暴露 API、接口、对外工具方法。public 暴露越多,后续改动兼容性成本越高。
坑点
很多人以为 protected,所有子类都能访问。
必须区分:同包子类直接访问;不同包子类,只能通过子类实例访问父类 protected 成员,不能用父类对象访问。
四、static 关键字详解|静态变量、静态方法、静态代码块
static 修饰的内容属于类本身 ,不属于实例对象。在类加载阶段完成初始化,早于对象实例创建,内存只分配一份,所有对象共享。
4.1 static 修饰静态变量(类变量)
java
public static String schoolName;
- 属于类,所有实例共享同一块内存;修改一处,全部对象可见;
- 类加载时初始化,只初始化一次;
- 推荐调用方式:
类名.静态变量,不推荐对象.静态变量(语法允许,但可读性差); - 并发风险:多线程同时修改静态变量,极易出现线程安全问题。
4.2 static 修饰静态方法(类方法)
java
public static void show(){
}
- 静态方法只能直接访问静态成员(静态变量、静态方法);
- 静态方法内部不能直接使用 this、super 关键字;
- 静态方法无法访问非静态成员(非静态变量 / 非静态方法),因为调用静态方法时,可能对象还没有创建;
- 静态方法不存在重写,只有静态隐藏,不具备多态。
4.3 static 静态代码块
java
static {
System.out.println("静态代码块执行");
}
- 类加载的时候执行,整个生命周期只执行一次;
- 执行时机早于构造代码块、构造方法;
- 典型用途:加载配置文件、初始化静态资源、注册驱动;
4.4 代码演示执行顺序
java
public class StaticExecuteDemo {
static {
System.out.println("1.静态代码块");
}
{
System.out.println("2.构造代码块(每次new都会执行)");
}
public StaticExecuteDemo() {
System.out.println("3.构造方法");
}
public static void main(String[] args) {
System.out.println("main方法开始");
new StaticExecuteDemo();
new StaticExecuteDemo();
}
}
输出结果:
1.静态代码块
main方法开始
2.构造代码块(每次new都会执行)
3.构造方法
2.构造代码块(每次new都会执行)
3.构造方法
执行顺序总结: 静态代码块(类加载一次) → 构造代码块(每次 new 执行) → 构造方法(每次 new 执行)
4.5 static 高频踩坑清单
- 静态变量全局共享,多线程并发修改会产生线程安全问题;
- 静态方法不能直接调用非静态成员;非静态方法可以访问静态成员;
- 静态方法不存在多态,子类同名静态方法只是隐藏父类静态方法;
- 静态代码块只在类加载执行一次,不是创建对象就执行;
- 不要在静态代码块做耗时 IO 操作,会阻塞类加载,影响项目启动速度。
五、深度问答
Q1:重载和重写的区别?
A:重载发生在同一个类,方法名相同参数列表不同,编译期静态绑定。 重写发生在父子类之间,子类重写父类实例方法,方法签名一致,运行期动态绑定,支持多态。重写有异常、权限、返回值约束;重载没有权限、异常约束。
Q2:四种访问修饰符的范围,开发遵循什么原则?A:private 仅本类;default 同包;protected 同包 + 不同包子类;public 全部包可见。开发遵循最小权限原则,能私有就不扩大访问范围。
Q3:static 的特点,静态方法和非静态方法互相访问规则?A:static 修饰的内容属于类,类加载时初始化。静态方法只能直接访问静态成员;非静态方法可以访问静态成员。静态方法内部没有 this、super。
Q4:静态代码块什么时候执行?static 代码块、构造代码块、构造方法顺序?A:静态代码块在类加载阶段执行,仅执行一次。执行顺序:静态代码块 → 构造代码块 → 构造方法。每次 new 对象,都会执行构造代码块和构造方法。
Q5:父类 protected 方法,子类重写能不能改成 public?能不能改成 private?A:可以改成 public(权限扩大),不能改成 private(权限缩小,编译报错)。重写要求子类访问权限不能低于父类。
Q6:静态方法能不能重写?为什么?A:不能重写。static 属于类,不是实例方法。子类同名静态方法只是隐藏父类静态方法,不存在虚方法表动态绑定,没有多态。
六、总结
- 重载:优化 API 设计,给调用方多种入参选择,提升易用性;
- 重写:配合多态,实现面向抽象编程,是策略模式、框架扩展的基础;重写记得加上
@Override; - 访问修饰符:坚持最小权限原则,成员变量优先 private;谨慎使用 protected、public;
- static:静态资源慎用,静态变量共享带来线程安全风险;静态代码块适合一次性初始化轻量资源,不要做耗时操作。
结语
重载、重写、访问修饰符、static 关键字是 Java 面向对象体系里一套组合考点。很多时候都会结合 OOP 三大特性一起提问,比如:重写的权限限制、静态方法能不能多态。 学习的时候不要孤立记忆知识点,把语法规则、底层绑定、工程实践结合起来,才可以从容应对追问。