这一篇补充面向对象概念之外的常见基础题:谁能访问成员,成员属于类还是实例,创建对象时先执行什么,以及 Object 提供哪些能力。
四种访问范围
下面讨论类的成员,不能把这张表直接套到顶层类上。
| 修饰符 | 同一类 | 同包其他类 | 不同包的子类 | 不同包的普通类 |
|---|---|---|---|---|
| private | 可以 | 不可以 | 不可以 | 不可以 |
| 不写修饰符,即包访问 | 可以 | 可以 | 不可以 | 不可以 |
| protected | 可以 | 可以 | 可以,但受继承访问规则限制 | 不可以 |
| public | 可以 | 可以 | 可以 | 可以 |
跨包子类访问 protected 实例成员时,要遵守接收者类型等限制,不能通过任意父类对象自由访问。基础面试先记住“同包可用,跨包子类通过继承访问”,不用展开所有边界题。
普通顶层类不能声明为 private 或 protected;public 顶层类通常需要与源文件同名。嵌套类的访问修饰规则则不同。
static 与实例成员
static 字段和方法属于类,实例字段和方法属于对象。准确地说,静态状态与具体类身份关联,不同类加载器定义的同名类可能各自有静态状态。
静态方法没有 this,不能直接访问实例字段或调用实例方法;有明确对象引用时仍可以访问它的实例成员。实例方法可以使用静态成员,但业务上通常通过类名表达静态访问。
静态方法不会像实例方法一样按实际对象类型进行重写分派。子类声明同名同参数静态方法属于隐藏,调用选择取决于编译时的类型信息。static 也不意味着线程安全,多个线程仍可能竞争同一个静态可变字段。
final 分别限制什么?
| 用法 | 限制 |
|---|---|
| final 变量 | 赋值后不能重新赋值;引用指向不变不等于对象内容不变 |
| final 方法 | 不能被子类重写 |
| final 类 | 不能被继承 |
static final 也不一定是编译期常量。例如通过方法计算的值,或指向可变对象的引用,不会因为这两个修饰符就自动具备常量折叠与不可变语义。
final、finally、finalize 是不同概念:final 是语言修饰符,finally 是异常处理中的清理代码块,finalize 是已弃用的历史对象终结方法。不要用 finalize 代替文件或连接的显式关闭。
初始化顺序怎么判断?
常见继承场景的主线是:父类静态初始化 → 子类静态初始化 → 父类实例初始化与构造器 → 子类实例初始化与构造器。静态初始化通常每个类身份只执行一次,实例初始化每次创建对象都会执行。
class Parent { static { System.out.println("parent static"); } { System.out.println("parent instance"); } Parent() { System.out.println("parent constructor"); }}
class Child extends Parent { static { System.out.println("child static"); } { System.out.println("child instance"); } Child() { System.out.println("child constructor"); }}
class InitDemo { public static void main(String[] args) { new Child(); }}首次运行输出:
parent staticchild staticparent instanceparent constructorchild instancechild constructor同一类中的字段初始化表达式和初始化块通常按源代码顺序执行。对象字段在这些显式初始化之前已有默认零值;实例初始化逻辑在父类构造返回后、当前类构造器的剩余主体之前执行。构造器链和静态初始化是两套机制,不要混为一谈。
如果再 new Child,父子类的静态块不会因创建新对象重新执行。访问编译期常量等初始化触发条件,接着读 类加载与初始化。
Object 常用方法
| 方法 | 基础用途与追问 |
|---|---|
| getClass | 获取对象实际运行时类型,和引用声明类型不一定相同 |
| equals | 默认比较身份,业务类型可重写逻辑相等 |
| hashCode | 哈希结构使用的哈希值,相等对象必须有相同值;不是保证唯一的地址 |
| toString | 提供文本表示,常用于日志;输出业务信息时注意敏感字段 |
| clone | 默认字段级浅拷贝,支持条件和可见性要按类型处理,并非所有对象可直接调用 |
| wait / notify / notifyAll | 对象监视器上的等待与通知,调用时必须持有对应监视器 |
面试回答模板
访问修饰符控制成员可见范围,protected 包含同包访问和有条件的跨包继承访问。static 属于类,实例成员属于对象;静态方法没有 this,也不参与实例重写分派。final 限制赋值、重写或继承,不自动保证对象不可变。初始化通常先父类静态、子类静态,再父类实例与构造、子类实例与构造。Object 提供类型、相等、哈希、文本、拷贝和监视器协作等基础能力。
自测
- static 方法能直接使用 this 吗?不能。
- static final List 能否添加元素?取决于列表本身,修饰符不禁止内容变化。
- 第二次创建 Child,父类静态块会再次执行吗?不会因这次对象创建重新执行。
- hashCode 是否保证不同对象都不同?不保证,允许哈希冲突。