JAVA语言程序设计.25

"Java_Programming"

Posted by LanZinYtt on December 20, 2025

JAVA语言程序设计 偏难点梳理

一、 面向对象三大特性深度理解

(1)继承与多态机制

(i)方法重写(Override)核心要点

方法重写发生在继承关系中,是子类为满足自身独特需求,重新实现从父类继承来的方法。其核心是“不变方法声明,变其内部实现”。


1. 必须遵守的语法规则
  1. 方法签名一致
    • 重写的方法与被重写的父类方法,其方法名、参数列表(类型、顺序、数量)必须完全相同
  2. 访问权限不能更严格
    • 子类重写方法的访问修饰符权限不能低于父类原方法。
    • 常见顺序public > protected > 默认(包私有) > private
    • 例如:父类方法为 protected,子类可改为 public 或保持 protected,但不能改为默认或 private
  3. 返回类型协变
    • 基本类型:返回类型必须完全相同。
    • 引用类型:子类方法的返回类型可以是父类方法返回类型的子类(协变返回类型)。
  4. 异常声明限制
    • 子类重写方法可以抛出更少、更具体或不抛出检查异常(Checked Exception)。
    • 不能抛出比父类方法更新、更宽或更多的检查异常。

2. 关键特性与注意事项
  1. 使用 @Override 注解(强推)
    • 用于显式声明,让编译器检查重写是否正确,避免因笔误等导致意外创建新方法。
  2. 调用父类实现 (super)
    • 在子类重写方法中,可通过 super.父类方法名() 调用父类的原始实现,这是扩展功能而非完全替换的常见做法。
  3. 不可重写的情况
    • final 方法:禁止被重写。
    • static 方法:属于类,可被“隐藏”而非“重写”,无多态性。
    • private 方法:对子类不可见,因此无法重写。
    • 构造器:不能重写。

3. 与重载(Overload)的本质区别
特性 重写 (Override) 重载 (Overload)
发生位置 继承关系的父子类之间 同一个内部(或父子类间)
方法签名 必须完全相同 必须不同(参数类型、顺序、数量至少一项不同)
返回类型 必须相同或是协变类型 可以不同
访问修饰符 不能比父类更严格 可以不同
异常声明 可以减少或细化,不能扩大 可以不同
核心目的 实现运行时多态(动态绑定) 提供同一功能的多种调用方式,是编译时多态

4. 核心目的与总结
  • 核心目的:实现运行时多态,让程序架构更灵活、可扩展。这是面向对象编程的基石之一。
  • 一句话总结:遵守规则(签名、权限、异常),善用注解(@Override)和父类调用(super),理解其与重载的根本区别,最终服务于多态。

(ii)静态块和构造代码块与构造函数执行顺序

当创建子类对象时,类加载和实例化的完整流程是固定的。关键在于理解两个阶段:类加载阶段(一次性的)和对象实例化阶段(每次new时发生)。

阶段1:类加载阶段(只执行一次)

当程序第一次主动使用某个类时(如创建实例、访问静态成员等),JVM会加载该类。如果它有父类,则会先递归加载父类

  1. 父类静态成员初始化
    • 按在代码中出现的书写顺序,依次执行父类的静态变量显式赋值和静态代码块(static {})。
  2. 子类静态成员初始化
    • 父类加载完成后,按书写顺序,依次执行子类的静态变量显式赋值和静态代码块。

重要:类加载阶段在整个程序运行期间通常只发生一次。后续再创建该类的对象,将直接从阶段2(实例化)开始。

阶段2:对象实例化阶段(每次new都执行)

当执行 new SubClass() 时,开始创建对象实例。

  1. 父类实例成员初始化
    • 为父类分配内存空间。
    • 书写顺序,依次执行父类的实例变量显式赋值和构造代码块(非静态代码块 {})。
  2. 父类构造方法执行
    • 执行父类构造方法中的剩余语句。
    • 注意:任何构造方法的第一行(无论是显式书写还是隐式存在),都是super(...)this(...)。默认情况下,子类构造方法会隐式调用父类的无参构造 super()
  3. 子类实例成员初始化
    • 父类部分初始化完成后,开始子类部分。
    • 书写顺序,依次执行子类的实例变量显式赋值和构造代码块(非静态代码块 {})。
  4. 子类构造方法执行
    • 最后执行子类构造方法中的剩余语句。

关键点与注意事项
  • 静态代码块与类绑定,与对象无关:它只在类加载时执行一次,用于初始化静态资源(如加载驱动、初始化静态配置)。
  • 构造代码块(非静态代码块)与每个对象绑定:每次创建对象时都会执行,且在构造方法主体之前执行,用于执行对象创建时的通用初始化逻辑。它比构造方法先执行,但晚于实例变量的显式赋值(按书写顺序穿插)。
  • 构造方法调用链:创建子类对象时,一定会调用到父类的构造方法(至少到Object类)。如果父类没有无参构造,子类构造方法必须通过super(...)显式指定调用父类的哪个有参构造
  • 变量、构造代码块与代码执行顺序:在同一个类中,无论是静态还是非静态,变量初始化和代码块都严格按照它们在源代码中出现的顺序执行。例如,如果构造代码块写在实例变量之前,则先执行构造代码块,反之亦然。
  • 构造代码块的典型应用场景:当多个构造方法都需要执行相同的初始化逻辑时,可将公共代码提取到构造代码块中,避免代码重复。

(2)封装的深层应用与访问控制符

封装是面向对象三大特性之一,其核心是将数据(属性)和对数据的操作(方法)捆绑在一起,并隐藏内部实现细节,仅对外暴露可控的访问接口。访问控制符是实现封装的关键语法工具。

(i) 访问控制符作用域详解

下表清晰地展示了Java中四种访问控制符的可见范围:

访问控制符 当前类 同包(package) 不同包子类 不同包非子类 核心设计意图
private 彻底隐藏,仅限类内部使用。
default 包内可见,提供包级别的模块化封装。
protected 主要服务于继承,允许子类访问,无论是否同包。
public 完全公开,定义对外承诺的稳定接口。

关键说明

  1. default (默认/包私有):不使用任何关键字修饰时即为default。它是包(package) 这一逻辑单元的“内部门禁”。
  2. protected:其“不同包子类可访问”的特性,清晰地体现了继承关系优先于包结构的面向对象设计思想。

二、 异常处理机制

finallyreturn 的复杂交互

核心规则finally块中的代码几乎总是会执行,并且它的return或修改返回值的行为会覆盖trycatch中的操作。

场景1: finally 中有 return 语句

finally中的return会“覆盖”trycatch中的返回值,并吞掉其中未捕获的异常!

场景2: finally 修改引用类型或基本类型变量的值

  • 基本类型finally中对返回变量的修改无效(因为返回时保存的是值的副本)。
  • 引用类型finally中修改对象的状态有效,但让引用指向新对象无效(原理同基本类型)。

三、 I/O流体系

流类型 方向 抽象基类 常用实现类 功能说明 使用频率
字节流 输入 InputStream FileInputStream 从文件读取字节数据 ⭐⭐⭐
      BufferedInputStream 带缓冲的字节输入流(包装类) ⭐⭐⭐⭐⭐
      ObjectInputStream 对象反序列化 ⭐⭐⭐⭐
      DataInputStream 读取Java基本数据类型 ⭐⭐⭐
      ByteArrayInputStream 从内存字节数组读取 ⭐⭐
  输出 OutputStream FileOutputStream 向文件写入字节数据 ⭐⭐⭐
      BufferedOutputStream 带缓冲的字节输出流(包装类) ⭐⭐⭐⭐⭐
      ObjectOutputStream 对象序列化 ⭐⭐⭐⭐
      DataOutputStream 写入Java基本数据类型 ⭐⭐⭐
      ByteArrayOutputStream 向内存字节数组写入 ⭐⭐
字符流 输入 Reader FileReader 从文件读取字符(默认编码) ⭐⭐⭐
      BufferedReader 带缓冲的字符输入流,支持readLine() ⭐⭐⭐⭐⭐
      InputStreamReader 字节流→字符流桥梁(可指定编码) ⭐⭐⭐⭐⭐
      CharArrayReader 从内存字符数组读取 ⭐⭐
  输出 Writer FileWriter 向文件写入字符(默认编码) ⭐⭐⭐
      BufferedWriter 带缓冲的字符输出流,支持newLine() ⭐⭐⭐⭐⭐
      OutputStreamWriter 字符流→字节流桥梁(可指定编码) ⭐⭐⭐⭐⭐
      CharArrayWriter 向内存字符数组写入 ⭐⭐

1.常见文件操作类总结

操作需求 推荐使用的类 说明
读取二进制文件 FileInputStream + BufferedInputStream 非文本文件的标准读取方式
写入二进制文件 FileOutputStream + BufferedOutputStream 非文本文件的标准写入方式
复制任意文件 字节流 + 缓冲流 通用文件复制模板
读取文本文件 BufferedReader(new FileReader(...))
BufferedReader(new InputStreamReader(...))
后者可指定编码,更推荐
写入文本文件 BufferedWriter(new FileWriter(...))
BufferedWriter(new OutputStreamWriter(...))
后者可指定编码,更推荐
按行读取文本 BufferedReader.readLine() 最便捷的文本行读取方法
读写Java对象 ObjectInputStream / ObjectOutputStream 对象必须实现Serializable接口
读写基本数据类型 DataInputStream / DataOutputStream 用于读写int、double等基本类型
内存操作 ByteArrayInputStream/OutputStream
CharArrayReader/Writer
在内存中读写数据
标准输入/输出 System.in (InputStream)
System.out (PrintStream)
控制台输入输出

2.最佳实践与要点

  1. 总是使用缓冲流:对于文件I/O,几乎总是应该用BufferedXxx包装底层流,可极大提升性能。
  2. 总是使用try-with-resources:Java 7+的try-with-resources语句可自动关闭流,避免资源泄漏。
  3. 处理文本务必指定编码FileReader/FileWriter使用平台默认编码,可能导致跨平台乱码。最佳实践是使用InputStreamReaderOutputStreamWriter并明确指定编码(如StandardCharsets.UTF_8)。
  4. 正确关闭流:关闭顺序应是“后打开的先关闭”,但使用try-with-resources时无需担心。
  5. 对象序列化
    • 实现Serializable接口的类应显式声明serialVersionUID,否则JVM会自动生成,类结构变化会导致反序列化失败。
    • transient关键字用于避免敏感字段被序列化。
    • 静态变量不会被序列化(属于类,不属于对象状态)。

NIO(New I/O)

  • BufferChannelSelector的概念
  • NIO与传统IO的区别

4. Java内存管理

  • 堆(Heap)与栈(Stack)的区别
  • 垃圾回收机制(GC)基本原理
  • 强引用、软引用、弱引用、虚引用
  • 内存泄漏的常见场景

5. 设计模式在Java中的应用

  • 单例模式的多种实现方式(特别是线程安全的实现)
  • 工厂模式、建造者模式
  • 观察者模式、装饰器模式
  • 策略模式的实际应用