Java 基础先围绕一个问题展开:对象怎样组织状态和行为,方法调用又怎样使用这些对象?面试需要能解释概念,并判断一小段代码的结果。
封装、继承、多态是什么?
- 封装:通过访问控制隐藏内部状态,提供有约束的操作。例如库存字段设为 private,通过扣减方法检查库存是否充足,而不是让调用方任意修改。
- 继承:子类复用、扩展父类的行为,适用于明确的“是一个”关系。单纯复用代码时,也可以通过组合实现。
- 多态:父类或接口类型的引用指向不同实现,调用被重写的实例方法时,根据实际对象选择实现。
List<String> names = new ArrayList<>();names.add("Ada");引用声明为 List,实际对象是 ArrayList。调用方依赖接口,替换实现时改动较小。
接口与抽象类怎么选?
| 维度 | 接口 | 抽象类 |
|---|---|---|
| 表达关系 | 实现某种能力或契约 | 一类对象的共同基础 |
| 继承限制 | 类可以实现多个接口 | 类只能直接继承一个类 |
| 状态与构造 | 没有实例字段和构造器 | 可以有实例字段和构造器 |
| 方法 | 可声明抽象方法,也可有 default、static 等方法 | 可以同时有抽象方法与普通方法 |
多个业务实现只需要统一调用约定时用接口;确实有共同状态、构造逻辑和模板行为时考虑抽象类。不要背“接口所有方法都不能有实现”,现代 Java 接口可以有默认方法。
重载与重写有什么区别?
重载是在同一个方法名下提供不同参数列表,例如 find(long id) 与 find(String name)。编译器按参数和声明类型选择方法;仅改变返回类型不能形成重载。
重写是子类提供父类实例方法的新实现,运行时根据实际对象分派。重写不能缩小访问权限,也不能随意扩大检查型异常范围,返回类型可以是协变的子类型。
static 方法属于类,不参与实例方法的运行时重写分派;private 方法无法被子类重写;final 方法禁止重写。
Java 是值传递还是引用传递?
Java 一律值传递。 基本类型复制数值,引用类型复制引用值,因此两个引用可能指向同一个对象。
static void change(List<String> list) { list.add("B"); list = new ArrayList<>(); list.add("C");}
List<String> names = new ArrayList<>();names.add("A");change(names);System.out.println(names); // [A, B]add("B") 修改了双方指向的对象;重新赋值只改变方法内的参数变量,没有把调用方的 names 改成新列表。
final 与对象不可变是一回事吗?
final 变量赋值后不能再次赋值。对于引用变量,它限制的是引用指向,不保证对象内容不可变。final List 仍然可以增加元素。
不可变对象还需要控制内部状态:不暴露可变字段、不提供修改接口,必要时对输入和输出做防御性拷贝。String 就是常见的不可变类型。
深拷贝与浅拷贝
浅拷贝复制外层对象,内部引用仍可能共享。深拷贝按业务需要复制可变的嵌套对象,修改副本不会影响原对象的对应状态。Object.clone() 的默认行为是字段层面的浅拷贝,不自动复制完整对象图。
面试回答模板
Java 通过封装保护对象状态,通过继承复用和扩展行为,通过多态面向接口调用不同实现。接口更适合定义契约,抽象类适合承载共同状态和逻辑。重载由参数列表区分,重写实现运行时多态。Java 的参数传递都是值传递,对象参数传的是引用值的副本,所以可以修改同一个对象,但不能通过参数重新赋值替换调用方的引用。
自测
- 为什么修改参数对象有效,但给参数重新赋值无效?因为复制的引用仍指向原对象,重新赋值只修改局部变量。
final ArrayList能否添加元素?能,final 不限制对象内容。- 同名同参数、不同返回类型能构成重载吗?不能。