1. 引言

类结构和初始化是每个Java程序员必须掌握的基础知识。本文整理了该主题下常见的面试问题及解答,帮助开发者巩固核心概念。

2. Q1: 描述final关键字应用于类、方法、字段或局部变量时的含义

final关键字在不同场景下有不同作用:

final类:不可被继承
final方法:不可被子类重写
final字段:必须在构造器或初始化块中赋值,且后续不可修改
final局部变量:只能赋值一次,之后不可修改

⚠️ 简单粗暴记忆:final就是"不可变"的代名词

3. Q2: 什么是默认方法?

Java 8前接口只能包含抽象方法(无方法体)。从Java 8开始,接口方法可以有默认实现:

public interface Iterable<T> {
    Iterator<T> iterator();
    
    default void forEach(Consumer<? super T> action) { /* */ }
    
    default Spliterator<T> spliterator() { /* */ }
}

核心价值:向现有接口添加新方法时,用default修饰可保持向后兼容
典型案例Iterable接口在Java 8新增forEachspliterator方法,未破坏已有实现

4. Q3: 什么是静态类成员?

静态字段和方法不绑定到特定实例,而是绑定到类本身:

编译期解析:调用静态方法/访问静态字段在编译时确定
无实例依赖:无需创建对象即可使用
⚠️ 踩坑点:静态方法中不能直接访问非静态成员

5. Q4: 没有抽象成员的类能否声明为抽象类?目的是什么?

可以:即使没有抽象方法,类仍可声明为abstract
典型用途

  • 作为继承体系的基类
  • 提供通用工具方法
  • 防止直接实例化(如java.util.Collections

6. Q5: 什么是构造器链?

构造器链通过多个构造器相互调用简化对象初始化:

public class Discount {
    private int percent;
    private int days;
    
    public Discount() {
        this(10); // 调用单参数构造器
    }
    
    public Discount(int percent) {
        this(percent, 2); // 调用双参数构造器
    }
    
    public Discount(int percent, int days) {
        this.percent = percent;
        this.days = days;
    }
}

优势:避免代码重复,提供灵活的初始化方式
⚠️ 注意:this()调用必须是构造器的第一条语句

7. Q6: 什么是方法重写和重载?区别是什么?

特性 重写 (Overriding) 重载 (Overloading)
发生位置 子类与父类之间 同一个类中
方法签名 必须完全相同 方法名相同,参数列表不同
返回类型 必须相同或子类型 无限制
绑定时机 运行时动态绑定 编译时静态绑定

重载示例(来自java.io.Writer):

public abstract class Writer {
    public void write(int c) throws IOException { /* */ }
    public void write(char cbuf[]) throws IOException { /* */ }
}

8. Q7: 能否重写静态方法?

不能!静态方法属于类级别,编译时绑定,而重写依赖运行时动态绑定
⚠️ 踩坑点:子类可定义同名静态方法,但这属于"隐藏"而非重写

9. Q8: 什么是不可变类?如何创建?

不可变类实例创建后状态不可修改。创建规则

✅ 所有字段声明为private final
✅ 不提供setter方法
✅ 确保所有字段不可变(或返回防御性拷贝)
✅ 类声明为final或所有方法为final

经典案例StringInteger等包装类

10. Q9: 如何比较枚举值:equals()还是==

✅ **推荐使用==**:

  • 枚举值本质是单例,==更高效
  • 避免空指针风险(equals()需检查null)
  • 代码更简洁
// 推荐写法
if (status == Status.ACTIVE) { ... }

// 可行但啰嗦
if (status.equals(Status.ACTIVE)) { ... }

11. Q10: 什么是初始化块?静态初始化块?

初始化块:类中的{}代码块,在创建实例时执行(编译器会将其复制到每个构造器开头)
静态初始化块:带static修饰的{}代码块,类加载时执行一次

public class Example {
    // 实例初始化块
    {
        System.out.println("实例初始化");
    }
    
    // 静态初始化块
    static {
        System.out.println("静态初始化");
    }
}

执行顺序:静态初始化块 → 实例初始化块 → 构造器

12. Q11: 什么是标记接口?Java中有哪些典型例子?

标记接口是无任何方法的接口,用于标识特定属性:

典型例子

  • Serializable:标识类可序列化
  • Cloneable:允许对象克隆(否则抛CloneNotSupportedException
  • Remote:RMI中标识可远程调用的接口

13. Q12: 什么是单例?Java中如何实现?

单例模式确保类只有一个全局可访问实例:

饿汉式实现(线程安全):

public class SingletonExample {
    private static final SingletonExample INSTANCE = new SingletonExample();
    
    private SingletonExample() {}
    
    public static SingletonExample getInstance() {
        return INSTANCE;
    }
}

⚠️ 懒加载需求?使用双重检查锁定(Double-Checked Locking):

public class SingletonExample {
    private static volatile SingletonExample instance;
    
    private SingletonExample() {}
    
    public static SingletonExample getInstance() {
        if (instance == null) {
            synchronized (SingletonExample.class) {
                if (instance == null) {
                    instance = new SingletonExample();
                }
            }
        }
        return instance;
    }
}

14. Q13: 什么是可变参数(Var-Arg)?有哪些限制?方法体内如何使用?

可变参数允许方法接受零到多个同类型参数:

规则

  • 每个方法只能有一个可变参数
  • 必须是参数列表的最后一个
  • 语法:Type... name

方法体内使用:作为数组处理

示例(来自Collections.addAll):

public static <T> boolean addAll(
    Collection<? super T> c, T... elements) {
    boolean result = false;
    for (T element : elements) { // 当作数组遍历
        result |= c.add(element);
    }
    return result;
}

15. Q14: 能否访问父类被重写的方法?能否访问祖父类被重写的方法?

访问父类方法:使用super关键字
访问祖父类方法:Java不支持直接访问

示例(来自LinkedHashMap):

public void clear() {
    super.clear(); // 调用HashMap的clear()
    head = tail = null; // 清理链表引用
}

⚠️ 踩坑点:多层继承时,无法跳过直接父类访问祖父类方法


原始标题:Java Structure and Initialization Interview Questions

« 上一篇: Spring Session介绍
» 下一篇: Java中的文件大小