Java单例类中系统属性与类变量有什么区别?
结论
直接用私有类变量存时间戳就行,系统属性根本不是干这个用的,完全不适合存类内部的业务状态,二者的核心差异列在下面。
核心差异对比
- 访问控制能力
私有类变量加private修饰后,只有Data类内部的代码能读写这个值,天然满足你“仅允许Data类内部访问”的要求。系统属性是JVM全局共享的,整个应用里随便哪段代码都能读、甚至随便改你存的timestamp值,没有任何访问限制,万一别的代码误改了这个值,你的process逻辑直接跑错,排查起来特别麻烦。 - 类型安全性
类变量可以直接定义成long类型存时间戳,读写都是原生类型操作,没有额外转换,也不会出类型错误。系统属性只能存字符串,你写的时候要把long转成String,读的时候还要把String转回long,要是有别的代码把这个属性改成了非数字的内容,转换的时候直接抛NumberFormatException,平白多了很多异常风险。 - 运行开销
类变量的读写是直接操作内存,速度极快,开销可以忽略。系统属性的读写要操作全局的Properties哈希表,内部还有同步锁,性能比直接读类变量差好几个量级,属于完全没必要的浪费。 - 可维护性
类变量和Data类的生命周期完全绑定,和你的单例逻辑对齐,IDE可以直接找到这个变量所有的读写位置,改逻辑、排错都很方便。系统属性本身是设计用来存JVM启动参数、全局环境配置这类公共配置的,你拿它存单个类的内部状态,代码逻辑会变得非常隐晦,后期维护的时候靠搜字符串"timestamp"根本没法准确定位到所有操作这个时间戳的位置,很容易留坑。
额外注意
你现在写的懒汉单例本身是线程不安全的,多线程并发调getInstance()的时候可能创建出多个Data实例,到时候哪怕用类变量存时间戳也可能出数据不一致的问题。建议改成线程安全的单例实现,参考代码如下:
public class Data { // 用volatile修饰禁止指令重排序,保证多线程下实例可见性 private static volatile Data instance; private List<String> list; // 私有实例变量存储上次处理时间戳,初始0代表从未处理 private long lastProcessTimestamp = 0L; private Data() { list = new ArrayList<>(); } public static Data getInstance() { // 双重检查锁实现线程安全的懒加载 if (instance == null) { synchronized (Data.class) { if (instance == null) { instance = new Data(); } } } return instance; } public void process() { long currentTime = System.currentTimeMillis(); // 读取时间戳做判断 if (currentTime - lastProcessTimestamp >= 0) { // 执行list处理逻辑 // 处理完成后更新时间戳 lastProcessTimestamp = currentTime; } } }
别用系统属性存业务内部状态,这是典型的反模式,会直接破坏类的封装性,后续坑很多。
内容的提问来源于stack exchange,提问作者scottstots
相关产品推荐
相关产品推荐

