bigywater.tech
Java · 406

java.util.prefs:轻量偏好设置的生命周期

Preferences 是 JDK 自带的“轻量偏好设置”。这篇在 macOS 上用多个真实进程量出它存在哪里、什么时候真的持久、有哪些长度与类型的边界、监听器是否可靠,以及如何清理。

kill -9 后仍读到
不 flush 就 kill -9:新进程读到了刚 put 的值(macOS 后端同步写入)
80 / 8192
key(与节点名)与 value 的最大字符数,超过抛 IllegalArgumentException
0 个事件
另一个进程修改值之后,本进程监听器收到的事件数(但 get 立刻读到新值)

实现与存储位置

实测 · 本机执行输出
  userRoot 实现类 java.util.prefs.MacOSXPreferences,absolutePath = /,isUserNode = true
  节点 /j266test_zen 的 name = j266test_zen,parent = /
  put 之后 get = v,keys() = [k]
  systemRoot 写入:BackingStoreException:Synchronization failed for node '/j266test_zen_sys/'
  存储文件:com.apple.java.util.prefs.plist
  defaults read com.apple.java.util.prefs 里含有 j266test_zen = True

什么时候持久

实测 · 本机执行输出
  put 之后正常退出 → 新进程读到:读到 [exit_noflush];exit_noflush=yes;kill_noflush=无;kill_flushed=无
  put(不 flush)后立刻 kill -9 → 新进程读到:读到 [kill_noflush];exit_noflush=无;kill_noflush=yes;kill_flushed=无
  put + flush 后 kill -9 → 新进程读到:读到 [kill_noflush, kill_flushed];exit_noflush=无;kill_noflush=yes;kill_flushed=yes

长度限制与类型

实测 · 本机执行输出
  key 80 个字符:成功
  key 81 个字符:IllegalArgumentException:Key too long: k…(k 重复)
  value 8192 个字符:成功
  value 8193 个字符:IllegalArgumentException:Value too long: v…(v 重复)
  节点名 81 个字符:IllegalArgumentException:Node name n… too long
  key 为 null:NullPointerException
  value 含 NUL:IllegalArgumentException
  getInt(i) = 42,getInt(s)(值是字符串 hello)= -1,getBoolean(s) = true,getBoolean(b) = true,getDouble(d) = 3.5,getByteArray(ba) = [1, 2, 3, -56],get(不存在, null) = null,getInt(不存在, 7) = 7
  putInt("i") 存的字符串是 42,putBoolean 存的是 true,putByteArray 存的是 AQIDyA==(Base64),putDouble 存的是 3.5
  remove 之后 getInt(i, -1) = -1

外部修改与监听器

实测 · 本机执行输出
  本进程 put 之后收到的事件:[watch=1(线程 Event Dispatch Thread)]
  另一个进程:writer 已写入 watch=2 并 flush
  等待另一个进程写入 watch=2 之后(未 sync)收到的事件:[];get(watch) = 2
  调用 sync() 之后:get(watch) = 2,事件:[]

清理

实测 · 本机执行输出
  removeNode + flush 之后 nodeExists = false
  清理后 defaults read 里仍含有 j266test_zen = False

分步演示

java.util.prefs
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,macOS arm64(java.util.prefs 的后端是平台相关的:macOS 用 CFPreferences,Linux 用 ~/.java/.userPrefs 文件,Windows 用注册表);实验节点名是 /j266test_zen,实验结束后已删除
    • 没有验证:Linux 的文件后端(异步写入每 30 秒、系统级偏好需要 root)、Windows 注册表后端、systemRoot 在有权限时的行为、导出 / 导入 XML(exportNode / importPreferences)、多线程并发写入、偏好变化在 JVM 退出时的 flush 顺序
    京ICP备2026067502号-1京公网安备11010502063487号