9. 2026金三银四 面试官问不垮:Java VS Android 设计模式 16 讲

序号 问题 原因
Q1 单例模式(静态内部类、DCL volatile) 最基础,几乎 100% 必问
Q2 观察者模式 + LiveData(生命周期感知) Android 特有,面试高频
Q3 适配器模式(RecyclerView.Adapter) 日常开发用到,必考
Q4 建造者模式(AlertDialog.Builder) 链式调用典型,常问
Q5 责任链模式(View 事件分发) Android 核心机制,高频
Q6 策略模式(动画插值器、算法切换) 常与 if-else 优化结合问
Q7 工厂模式(简单工厂 vs 工厂方法 vs 抽象工厂) 基础创建型,区分度题
Q8 动态代理模式(Retrofit 原理、AOP) 进阶考点,中高频
Q9 模板方法模式(AsyncTask、Activity 生命周期) 经典框架模式
Q10 装饰器模式(ContextWrapper、IO) 与继承对比,中频
Q11 外观模式(SDK 封装、Context) 低频但设计思想好
Q12 对比:策略模式 vs 状态模式 结构相似,易混淆
Q13 对比:适配器模式 vs 装饰器模式 包装类区别,常考
Q14 对比:代理模式 vs 装饰器模式 代码相似,意图不同
Q15 对比:工厂方法模式 vs 抽象工厂模式 产品族 vs 产品等级
Q16 对比:简单工厂 vs 工厂方法 扩展性对比

Q1:单例模式

面试官问 :请你说说单例模式有哪几种写法?在 Android 中你一般用哪种?DCL 为什么要加 volatile

答案要点

  • 五种实现:饿汉、懒汉、DCL、静态内部类、枚举。
  • Android 最佳:静态内部类(懒加载、无线程锁);枚举可防反射破坏。
  • DCL 必须加 volatile:防止 instance = new Singleton() 指令重排导致其他线程拿到半初始化对象。

流程图

graph TD Start[首次调用 getInstance] --> Load[JVM 加载静态内部类 Holder] Load --> Init[执行静态初始化创建 INSTANCE] Init --> Return[返回 INSTANCE] Return --> Cache[后续直接返回,无锁]

源码(语音引擎管理器)

java 复制代码
public class VoiceEngineManager {
    private VoiceEngineManager() {}
    private static class Holder {
        private static final VoiceEngineManager INSTANCE = new VoiceEngineManager();
    }
    public static VoiceEngineManager getInstance() { return Holder.INSTANCE; }
    private SpeechRecognizer recognizer;
    public void init(Context ctx) { recognizer = SpeechRecognizer.createRecognizer(ctx); }
}

Q2:观察者模式 + LiveData

面试官问:LiveData 和普通观察者模式有什么区别?为什么能避免内存泄漏?

答案要点

  • LiveData 是生命周期感知的观察者,仅在 LifecycleOwner 处于 STARTED/RESUMED 时回调。
  • 组件销毁时自动移除观察者,无需手动注销,避免内存泄漏。
  • 普通观察者需手动管理注册/注销。

流程图

graph TD Set[setValue 数据更新] --> Loop[遍历观察者] Loop --> Check{LifecycleState >= STARTED?} Check -->|是| Callback[回调 onChanged] Check -->|否| Store[暂存,待恢复后分发]

源码(语音识别结果监听)

java 复制代码
public class VoiceRecognitionLiveData extends LiveData<String> {
    private RecognitionListener listener = new RecognitionListener() {
        @Override public void onResults(Bundle results) {
            setValue(results.getString("text"));
        }
    };
}
// Activity 中使用
voiceLiveData.observe(this, text -> tvResult.setText(text));

Q3:适配器模式

面试官问:RecyclerView 的 Adapter 是什么模式?如何支持多类型布局?

答案要点

  • 对象适配器模式:将数据源适配成 View 视图。
  • 多类型:重写 getItemViewType() 返回不同布局类型,onCreateViewHolder 根据类型创建对应 ViewHolder。

流程图

graph TD Data[数据 List] --> Type[getItemViewType 决定类型] Type --> Create[onCreateViewHolder 创建对应 ViewHolder] Create --> Bind[onBindViewHolder 填充数据] Bind --> Display[RecyclerView 展示]

源码(音频波形适配器)

java 复制代码
public class AudioWaveformAdapter extends RecyclerView.Adapter<AudioWaveformAdapter.WaveHolder> {
    private short[] audioSamples;
    @Override public WaveHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_waveform, parent, false);
        return new WaveHolder(v);
    }
    @Override public void onBindViewHolder(WaveHolder holder, int pos) {
        holder.waveView.setHeight(scaleAmplitude(audioSamples[pos]));
    }
    static class WaveHolder extends RecyclerView.ViewHolder { WaveformView waveView; }
}

Q4:建造者模式

面试官问AlertDialog.Builder 是哪种设计模式?解决了什么问题?

答案要点

  • 建造者模式:构建与表示分离,链式调用避免重叠构造器。
  • 解决参数过多、顺序易错、可读性差。
  • Android 源码:AlertDialog.BuilderOkHttpClient.Builder

流程图

graph TD Create[创建 Builder] --> Chain[链式 setXxx 返回 this] Chain --> Build[调用 build] Build --> Construct[根据参数创建对象] Construct --> Return[返回对象]

源码(语音合成参数配置)

java 复制代码
public class TTSConfig {
    private float pitch, speed;
    private TTSConfig(Builder b) { this.pitch = b.pitch; this.speed = b.speed; }
    public static class Builder {
        private float pitch = 1.0f, speed = 1.0f;
        public Builder setPitch(float pitch) { this.pitch = pitch; return this; }
        public Builder setSpeed(float speed) { this.speed = speed; return this; }
        public TTSConfig build() { return new TTSConfig(this); }
    }
}

Q5:责任链模式

面试官问:Android 事件分发如何体现责任链模式?如何拦截事件?

答案要点

  • 事件从 Activity → ViewGroup → View 逐层传递,每个节点可处理/拦截/传递。
  • 拦截:重写 onInterceptTouchEvent 返回 true,事件交由当前 ViewGroup 处理。
  • 不消费时向上回传。

流程图

graph TD Activity[Activity] --> VG[ViewGroup] VG --> Intercept{onInterceptTouchEvent?} Intercept -->|false| Child[传给子 View] Child --> View[View.onTouchEvent] View -->|消费| End[终止] View -->|不消费| Back[向上回传] Intercept -->|true| Self[自己处理]

源码(音频事件处理链)

java 复制代码
public abstract class AudioEventHandler {
    protected AudioEventHandler next;
    public void setNext(AudioEventHandler h) { this.next = h; }
    public abstract void handle(String event);
}
public class WakeupHandler extends AudioEventHandler {
    public void handle(String event) {
        if ("wakeup".equals(event)) startRecording();
        else if (next != null) next.handle(event);
    }
}

Q6:策略模式

面试官问:动画插值器是什么模式?它相比 if-else 有什么优势?

答案要点

  • 策略模式:定义一族算法,封装可互换,运行时切换。
  • 动画插值器:TimeInterpolator 接口 + 线性/加速/回弹等实现。
  • 优势:消灭 if-else,符合开闭原则。

流程图

graph TD Anim[动画引擎计算 fraction] --> Interpolator[插值器.getInterpolation] Interpolator --> Transform[返回变换后进度] Transform --> Evaluator[计算属性值] Evaluator --> Update[更新 View]

源码(语音降噪算法)

java 复制代码
public interface NoiseSuppression { short[] denoise(short[] audio); }
public class SpectralSubtraction implements NoiseSuppression { ... }
public class WienerFilter implements NoiseSuppression { ... }

public class AudioProcessor {
    private NoiseSuppression strategy;
    public void setStrategy(NoiseSuppression s) { this.strategy = s; }
    public short[] process(short[] audio) { return strategy.denoise(audio); }
}

Q7:工厂模式(简单工厂 / 工厂方法 / 抽象工厂)

面试官问:简单工厂、工厂方法、抽象工厂有什么区别?Android 中哪里用到?

答案要点

  • 简单工厂 :一个工厂根据参数创建多种产品(BitmapFactory)。
  • 工厂方法:每个产品对应一个工厂子类,符合开闭原则。
  • 抽象工厂:创建产品族,适合多套方案切换。
  • 业务开发用简单工厂,框架扩展用工厂方法。

流程图(简单工厂)

graph TD Client[传入类型] --> Factory[工厂类 if/switch] Factory -->|opus| Opus[OpusCodec] Factory -->|aac| Aac[AacCodec]

源码(编解码器工厂)

java 复制代码
public interface AudioCodec { byte[] encode(short[] pcm); }
public class OpusCodec implements AudioCodec { ... }
public class AacCodec implements AudioCodec { ... }

public class CodecFactory {
    public static AudioCodec createCodec(String type) {
        if ("opus".equals(type)) return new OpusCodec();
        else if ("aac".equals(type)) return new AacCodec();
        return null;
    }
}

Q8:动态代理模式

面试官问:Retrofit 是如何用动态代理实现接口调用的?还有哪些应用场景?

答案要点

  • 动态代理运行时生成代理类,对接口方法做无侵入增强(AOP)。
  • Retrofit:调用接口方法时被 InvocationHandler 拦截,解析注解拼装 HTTP 请求。
  • 其他:权限检查、日志耗时、缓存代理。

流程图

graph TD Call[调用接口方法] --> Proxy[InvocationHandler.invoke] Proxy --> Parse[解析注解拼装请求] Parse --> Http[发起 HTTP 请求] Http --> Return[返回结果]

源码(录音权限代理)

java 复制代码
public interface IAudioRecorder { void start(); }
public class RealRecorder implements IAudioRecorder { public void start() { /* 录音 */ } }

IAudioRecorder recorder = (IAudioRecorder) Proxy.newProxyInstance(
    IAudioRecorder.class.getClassLoader(),
    new Class[]{IAudioRecorder.class},
    (proxy, method, args) -> {
        if (!hasPermission(RECORD_AUDIO)) { requestPermission(); return null; }
        return method.invoke(new RealRecorder(), args);
    }
);

Q9:模板方法模式

面试官问AsyncTaskActivity 生命周期体现了什么模式?什么是钩子方法?

答案要点

  • 模板方法模式:父类定义算法骨架,具体步骤延迟到子类实现。
  • AsyncTaskexecute 调用 onPreExecutedoInBackgroundonPostExecute
  • 钩子方法:父类默认空实现,子类可选覆写(如 onPreExecute)。

流程图

graph TD Execute[execute] --> Pre[onPreExecute 钩子] Pre --> Thread[后台线程] Thread --> Background[doInBackground 必须实现] Background --> Post[onPostExecute 钩子]

源码(语音活动检测 VAD)

java 复制代码
public abstract class VoiceActivityDetector {
    public final void detect(short[] frame) {
        preProcess(frame);               // 钩子
        boolean isVoice = doVad(frame);  // 必须实现
        if (isVoice) onVoiceStart();     // 钩子
        postProcess(frame);              // 钩子
    }
    protected void preProcess(short[] f) {}
    protected abstract boolean doVad(short[] f);
    protected void onVoiceStart() {}
    protected void postProcess(short[] f) {}
}

Q10:装饰器模式

面试官问ContextWrapper 是装饰器模式吗?和继承有什么区别?

答案要点

  • 装饰器模式动态添加职责,不改变原类接口。
  • ContextWrapper 持有 Context 引用,委托并可在前后增加行为。
  • 优势:灵活组合多个装饰器,避免子类爆炸。

流程图

graph TD Original[原始 Context] --> Dec1[LogWrapper] Dec1 --> Dec2[SecurityWrapper] Dec2 --> Call[调用 startActivity] Call --> Log[打印日志] Log --> Security[权限检查] Security --> OriginalCall[原始调用]

源码(音频流回声消除装饰器)

java 复制代码
public interface AudioStream { byte[] read(); }
public class RawAudioStream implements AudioStream { public byte[] read() { /* 原始数据 */ } }

public class EchoCancelerDecorator implements AudioStream {
    private AudioStream decorated;
    public EchoCancelerDecorator(AudioStream s) { this.decorated = s; }
    public byte[] read() {
        byte[] raw = decorated.read();
        return cancelEcho(raw);
    }
}

Q11:外观模式

面试官问:如何用外观模式简化 SDK 使用?Android 中哪里用到了?

答案要点

  • 外观模式为复杂子系统提供统一高层接口。
  • Android:Context 封装了多个系统服务;Glide.with().load().into()
  • SDK 设计:将初始化、网络、日志等子模块封装在一个 Facade 类中。

流程图

graph TD Client[业务层调用 SDKFacade.initAll] --> Facade[门面类] Facade --> Sub1[NetModule.init] Facade --> Sub2[LogModule.init] Sub1 --> Done[完成] Sub2 --> Done

源码(语音 SDK 门面)

java 复制代码
class WakeupModule { void init() { } }
class AsrModule { void init() { } }
class TtsModule { void init() { } }

public class VoiceSDKFacade {
    private WakeupModule wakeup = new WakeupModule();
    private AsrModule asr = new AsrModule();
    private TtsModule tts = new TtsModule();
    public void initAll() { wakeup.init(); asr.init(); tts.init(); }
    public void startVoice() { wakeup.startListening(); asr.prepare(); }
}
// 调用方:new VoiceSDKFacade().initAll();

Q12:对比题 - 策略模式 vs 状态模式

面试官问:策略模式和状态模式结构相似,本质区别是什么?

答案要点

维度 策略模式 状态模式
意图 算法可互换 状态自动切换
谁控制变化 客户端主动设置策略 状态对象内部决定下一个状态
典型场景 降噪算法、编码格式 录音状态机(空闲→录音→暂停)

流程图对比

策略模式

graph TD Client[客户端] -->|setStrategy| Context Context --> Strategy[策略接口] Strategy --> ImplA[算法A] Strategy --> ImplB[算法B]

状态模式

graph TD Context[上下文] --> State[状态接口] Idle[空闲] -->|录音事件| Recording[录音中] Recording -->|暂停| Paused[暂停] Paused -->|恢复| Recording

源码对比

策略(降噪算法)

java 复制代码
processor.setStrategy(new SpectralSubtraction()); // 客户端主动选

状态(录音状态机)

java 复制代码
ctx.setState(new RecordingState()); // 状态内部自动切换

Q13:对比题 - 适配器模式 vs 装饰器模式

面试官问:适配器和装饰器都包装对象,如何区分?

答案要点

维度 适配器模式 装饰器模式
意图 接口转换 动态增加职责
是否改变接口
嵌套层数 通常一层 可多层叠加

流程图

适配器Client → Adapter → Adaptee
装饰器Client → DecoratorA → DecoratorB → Component

源码

适配器(麦克风适配)

java 复制代码
public class MicrophoneAdapter implements AudioSource {
    private AndroidMicrophone mic = new AndroidMicrophone();
    public byte[] readFrame() { return convert(mic.readRaw()); }
}

装饰器(静音检测)

java 复制代码
public class SilenceDetectorDecorator implements AudioStream {
    private AudioStream decorated;
    public byte[] read() { byte[] data = decorated.read(); checkSilence(data); return data; }
}

Q14:对比题 - 代理模式 vs 装饰器模式

面试官问:代理模式和装饰器代码结构很像,怎么区分?

答案要点

维度 代理模式 装饰器模式
意图 控制访问(权限、懒加载) 增加职责(日志、缓存)
创建时机 通常编译时确定 客户端运行时组合
典型场景 Binder 远程代理 添加分段、加日志

流程图

代理Client → Proxy → RealSubject
装饰器Client → DecoratorA → DecoratorB → Component

源码

代理(权限代理)

java 复制代码
public class PermissionProxyRecorder implements AudioRecorder {
    private RealAudioRecorder real = new RealAudioRecorder();
    public void start() { if (hasPermission()) real.start(); }
}

装饰器(分段录音装饰)

java 复制代码
public class SegmentRecorderDecorator implements AudioRecorder {
    private AudioRecorder decorated;
    public void start() { decorated.start(); startSegmentWatcher(); }
}

Q15:对比题 - 工厂方法模式 vs 抽象工厂模式

面试官问:工厂方法和抽象工厂的区别?分别在什么场景使用?

答案要点

维度 工厂方法 抽象工厂
产品数量 一种产品 一系列相关产品(产品族)
扩展方式 新增产品 → 新增工厂子类 新增产品族 → 新增具体工厂
场景 编解码器工厂 高质量/低功耗音频处理方案

流程图

工厂方法CreatorA → ProductACreatorB → ProductB
抽象工厂Factory → {Codec, NoiseSuppression, EchoCanceler}

源码

工厂方法

java 复制代码
public interface CodecFactory { AudioCodec createCodec(); }
public class OpusCodecFactory implements CodecFactory { ... }

抽象工厂

java 复制代码
public interface AudioProcessingFactory {
    AudioCodec createCodec();
    NoiseSuppression createNoiseSuppression();
}
public class HighQualityFactory implements AudioProcessingFactory { ... }
public class LowPowerFactory implements AudioProcessingFactory { ... }

Q16:对比题 - 简单工厂 vs 工厂方法

面试官问:简单工厂和工厂方法有什么实质区别?什么时候用简单工厂就够了?

答案要点

维度 简单工厂 工厂方法
开闭原则 违反(新增产品需改工厂类) 符合(新增产品加新工厂)
产品数量 少且稳定 多且经常扩展
复杂度 一个类 多个工厂类
典型场景 BitmapFactory 插件化框架

流程图

简单工厂Client → SimpleFactory (switch) → ProductA/B
工厂方法Client → FactoryA → ProductAClient → FactoryB → ProductB

源码

简单工厂(唤醒词引擎)

java 复制代码
public static WakeupEngine create(String type) {
    switch(type) {
        case "xiaomi": return new XiaoMiWakeup();
        default: return new DefaultWakeup();
    }
}

工厂方法

java 复制代码
public interface WakeupEngineFactory { WakeupEngine createEngine(); }
public class XiaoMiFactory implements WakeupEngineFactory { ... }
相关推荐
Victor3561 小时前
MongoDB(109)如何使用Robo 3T?
后端
Csvn1 小时前
前端监控体系
前端
skilllite作者1 小时前
从“记忆”到“项目 Wiki”:我在 SkillLite 里实现了一套 Markdown-only LLM Wiki 自动维护机制
开发语言·jvm·人工智能·后端·架构·rust
Victor3561 小时前
MongoDB(110)什么是MongoDB Atlas?
后端
张风捷特烈1 小时前
状态管理大乱斗#04 | Riverpod 源码评析 (上) - 核心架构
android·前端·flutter
djk88881 小时前
html table 分组合并 与导出分组后的数据
前端·html
FlyWIHTSKY1 小时前
router-viiew没有滚动条,如何修复
前端·vue.js·elementui
jinanwuhuaguo1 小时前
暗黑演化——记忆投毒、认知篡改与“数字精神分裂症”的安全悖论(第十四篇)
前端·人工智能·安全·重构·openclaw
靳向阳2 小时前
【无标题】
前端·javascript·vue.js