重写Properties类,实现对properties文件的有序读写,数据追加,解决中文乱码
前言
*.properties文件,是 Java 支持的一种配置文件类型,并且 Java 提供了 properties 类来读取 properties 文件中的信息。文件中以键值对 "键=值"的形式,存储工程中会多次重复使用的配置信息,通过“Properties”类来读取这些信息,以实现“一次编写,多处调用;需要修改配置文件时,只修改一处即可”的效果。
本文介绍【读写 properties 文件】,【追加数据】,【实现有序读写】,【解决中文乱码】问题。
正文
一、Properties 类简介
Properties有一个特殊的作用,专门用来加载xxx.properties配置文件。Properties继承于Hashtable,表示了一个持久的属性集,可保存在流中或从流中加载。属性列表中,每个键及其对应值都是一个字符串。
- 常用方法:
方法名 | 含义 |
---|---|
public String getProperty(String key) | 用指定的键在此属性列表中搜索属性 |
public Object setProperty(String key,String value) | 向Properties中增加属性,有则覆盖,没有则新增 |
public void load(InputStream in) | 从输入流中读取属性列表(键和元素对) |
public void load(Reader reader) | 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对) |
public void store(OutputStream out, String comments) | 将此 Properties 表中的属性列表(键和元素对)写入输出流 |
public void store(Writer writer, String comments) | 将此 Properties 表中的属性列表(键和元素对)写入输出字符 |
二、读写 properties 文件
由于Properties继承于Hashtable,所以,当新增属性写入到 .properties 文件的时候,结果显示的顺序可能并不是我们当初set属性的顺序。这个问题要注意,我们再后面会解决。
- Properties继承于Hashtable
- 借助IO流,利用Properties类操作.properties文件
// .properties文件的相对路径private static final String FILE_ADDRESS = "src/main/resources/application.properties";@GetMapping("/test")public String test() {log.info("test 接口调用:【开始】 --------------------");this.writeIntoText(new Properties());Properties prop = this.readFromText();log.info("jdbc.username = " + properties.getProperty("jdbc.username"));log.info("test 接口调用:【结束】 --------------------");return "success";}/*** 模拟向 properties 文件写入数据*/private void writeIntoText(Properties properties){OutputStream output = null;try {output = new FileOutputStream(FILE_ADDRESS);properties.setProperty("jdbc.driver", "com.mysql.jdbc.Driver");properties.setProperty("jdbc.url", "jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8");properties.setProperty("jdbc.username", "root");properties.setProperty("jdbc.password", "123456");properties.store(output, "tjm modify");} catch (IOException io) {io.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 读取 properties 文件,返回 Properties 对象*/private Properties readFromText(){Properties properties = new Properties();InputStream input = null;try {input = new FileInputStream(FILE_ADDRESS);properties.load(input);} catch (IOException io) {io.printStackTrace();} finally {if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}return properties;}
- 运行上述代码会产生几个问题:
- 顺序是无序的,或者说是hash排序的,并不是set的顺序;
- 每次write,properties文件的数据都会被覆盖掉,只展示最新数据;
- 如果value里面包含中文,那一定会是乱码;
下面,我们就以此解决这几个问题。
三、解决追加、有序、乱码问题
2.1 实现有序读写
自定义一个PropertiesUtil类,该类继承自Properties。PropertiesUtil提供一个返回由key按照存入顺序组成的List的方法,getKeyList(),这样就可以解决有序写的问题。至于读嘛,也是一样的道理。
- 如何保证getKeyList()方法返回的就是有序的key组成的集合?
通过查询Properties方法的源码,发现:Properties.load()方法从.properties文件加载内容时,是按照文件顺序进行读取,而Properties.setProperty()方法底层调用的是父类HashTable.put()方法进行储存。
HashTable本身就是无序的,所以,解决办法就是让PropertiesUtil持有一个私有的可以有序存储key的集合,然后重写父类的put()方法,在方法体中照常通过super.put()进行属性的存储,同时将key添加到存储key的集合中。
再查源码,发现: Properties将当前对象的内容存放到指定的输出流的方法又2个,save()和store(),但是它们的底层逻辑都是一样的,都是通过调用Properties.keys()方法获取一个Enumeration,然后对该Enumeration进行遍历,依次将对应的key和value写入到输出流中。要保证写入是有序的,就要保证遍历keys()方法返回的Enumeration取出的元素key是有序的。
所以,得到最终的解决方法是:重写keys()方法,保证遍历Enumeration得到的key是有序的。
- PropertiesUtil类的完整代码如下:
import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;/*** @Description: Java Punk* @Created: by JavaPunk on 2023/5/10 16:33* @Version: 1.0.0*/
public class PropertiesUtil extends Properties {private static final long serialVersionUID = 1L;private List<Object> keyList = new ArrayList<Object>();/*** 默认构造方法*/public PropertiesUtil() {}/*** 从指定路径加载信息到Properties* @param path*/public PropertiesUtil(String path) {try {InputStream is = new FileInputStream(path);this.load(is);} catch (FileNotFoundException e) {e.printStackTrace();throw new RuntimeException("指定文件不存在!");} catch (IOException e) {e.printStackTrace();}}/*** 重写put方法,按照property的存入顺序保存key到keyList,遇到重复的后者将覆盖前者。*/@Overridepublic synchronized Object put(Object key, Object value) {this.removeKeyIfExists(key);keyList.add(key);return super.put(key, value);}/*** 重写remove方法,删除属性时清除keyList中对应的key。*/@Overridepublic synchronized Object remove(Object key) {this.removeKeyIfExists(key);return super.remove(key);}/*** keyList中存在指定的key时则将其删除*/private void removeKeyIfExists(Object key) {keyList.remove(key);}/*** 获取Properties中key的有序集合* @return*/public List<Object> getKeyList() {return keyList;}/*** 保存Properties到指定文件,默认使用UTF-8编码* @param path 指定文件路径*/public void store(String path) {this.store(path, "UTF-8");}/*** 保存Properties到指定文件,并指定对应存放编码* @param path 指定路径* @param charset 文件编码*/public void store(String path, String charset) {if (path != null && !"".equals(path)) {try {OutputStream os = new FileOutputStream(path);BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os, charset));this.store(bw, null);bw.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}} else {throw new RuntimeException("存储路径不能为空!");}}/*** 重写keys方法,返回根据keyList适配的Enumeration,且保持HashTable keys()方法的原有语义,* 每次都调用返回一个新的Enumeration对象,且和之前的不产生冲突*/@Overridepublic synchronized Enumeration<Object> keys() {return new EnumerationAdapter<Object>(keyList);}/*** List到Enumeration的适配器*/private class EnumerationAdapter<T> implements Enumeration<T> {private int index = 0;private final List<T> list;private final boolean isEmpty;public EnumerationAdapter(List<T> list) {this.list = list;this.isEmpty = list.isEmpty();}public boolean hasMoreElements() {// isEmpty的引入是为了更贴近HashTable原有的语义,在HashTable中添加元素前调用其keys()方法获得一个Enumeration的引用,// 之后往HashTable中添加数据后,调用之前获取到的Enumeration的hasMoreElements()将返回false,但如果此时重新获取一个// Enumeration的引用,则新Enumeration的hasMoreElements()将返回true,而且之后对HashTable数据的增、删、改都是可以在// nextElement中获取到的。return !isEmpty && index < list.size();}public T nextElement() {if (this.hasMoreElements()) {return list.get(index++);}return null;}}
}
2.2 实现数据追加
在上面【读写 properties 文件】中简单介绍了写入的方法,因为每次都会 new Properties(),所以每次都会把源数据全部覆盖掉。清楚了原因,自然也就找到了解决办法:写入之前先将源文件加载出来,再进行有序的追加。
- 源码:先加载输入流,随后set新属性,最后store输出流
/*** 模拟向 properties 文件追加写入数据*/private void addToText(){// 将 PropertiesUtil 换成重写前的 Properties 类,最后写入的顺序是hash排序的// Properties properties = new Properties();PropertiesUtil properties = new PropertiesUtil();InputStream input = null;OutputStream output = null;try {// 先用输入流加载.properties文件input = new BufferedInputStream(new FileInputStream(FILE_ADDRESS));properties.load(new InputStreamReader(input));// 输出流(FileOutputStream)对象,必须在Properties类加载(load)完以后创建(new)output = new FileOutputStream(FILE_ADDRESS);properties.setProperty("jdbc2.username", "PropertiesUtil Orderly test");properties.store(output, "tjm modify");} catch (IOException io) {io.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}}
- PropertiesUtil运行结果:
- Properties运行结果:
2.3 解决中文乱码
上面测试使用的都是中文,如果参数中包含【中文】,就会出现乱码问题,解决办法:输入输出时,设置【charsetName = "utf-8"】。
- 代码展示:
/*** 模拟向 properties 文件追加写入数据*/private void addToText(){// 将 PropertiesUtil 换成重写前的 Properties 类,最后写入的顺序是hash排序的
// Properties properties = new Properties();PropertiesUtil properties = new PropertiesUtil();InputStream input = null;OutputStream output = null;try {// 先用输入流加载.properties文件input = new BufferedInputStream(new FileInputStream(FILE_ADDRESS));properties.load(new InputStreamReader(input, "utf-8"));// 输出流(FileOutputStream)对象,必须在Properties类加载(load)完以后创建(new)output = new FileOutputStream(FILE_ADDRESS);properties.setProperty("jdbc3.username", "PropertiesUtil 有序测试");properties.store(new OutputStreamWriter(output, "utf-8"), "tjm modify");} catch (IOException io) {io.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 读取 properties 文件,返回 Properties 对象*/private Properties readFromText(){PropertiesUtil properties = new PropertiesUtil();InputStream input = null;try {input = new FileInputStream(FILE_ADDRESS);properties.load(new InputStreamReader(input, "utf-8"));log.info("举例说明:jdbc3.username = " + properties.getProperty("jdbc3.username"));} catch (IOException io) {io.printStackTrace();} finally {if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}return properties;}
- 结果展示:
四、PropertiesConfiguration介绍
PropertiesConfiguration 是 Apache 帮我们实现按照文件的顺序读取properties文件的类,Properties类能做的它都能做。不仅如此,他还有许多方便实用的附加功能。
- 示例代码:功能展示
/*** 模拟向 properties 文件写入数据*/private void setParam(){try {PropertiesConfiguration propsConfig = new PropertiesConfiguration(FILE_ADDRESS);propsConfig.setEncoding("utf-8");// 修改属性之后自动保存,省去了propsConfig.save()过程propsConfig.setAutoSave(true);// setProperty:遇到同名key会替换value,没有则新增propsConfig.setProperty("set.name", "123456");// addProperty:只会新增,即使遇到遇到同名key也不会替换propsConfig.addProperty("add.name", "456789");}catch (Exception ex) {ex.printStackTrace();}}/*** 模拟向 properties 文件读取数据*/private void getParam(){try {PropertiesConfiguration propsConfig = new PropertiesConfiguration(FILE_ADDRESS);propsConfig.setEncoding("utf-8");String username = propsConfig.getString("jdbc.username");log.info("举例说明:jdbc.username = " + username);}catch (Exception ex) {ex.printStackTrace();}}
相关文章:

重写Properties类,实现对properties文件的有序读写,数据追加,解决中文乱码
前言 *.properties文件,是 Java 支持的一种配置文件类型,并且 Java 提供了 properties 类来读取 properties 文件中的信息。文件中以键值对 "键值"的形式,存储工程中会多次重复使用的配置信息,通过“Properties”类来读…...

态势感知与信质、信量
未来的新智能是人机环境系统智能,而人机融合的态势感知是其关键,简单地说,态势感知(situation awareness)就是智能体在“一定时间和空间环境中的元素的感知,对它们的含义的理解,并对他们稍后状态…...
20230508----重返学习-call()与bind()重写-JS中数据类型检测汇总-装箱与拆箱-类的多种继承方案
day-065-sixty-five-20230508-call()与bind()重写-JS中数据类型检测汇总-装箱与拆箱-类的多种继承方案 call()与bind()重写 call()重写 call()的作用例子 let obj {name: 珠峰培训 } const fn function fn(x, y, ev) {console.log(this, x, y, ev)return x y } let res f…...

Node.js对ES6 及更高版本的支持
目录 1、简介 2、默认情况下什么特性随着 Node.js 一起发布? 3、有哪些特性在开发中? 4、移除这个标记(--harmony)吗 5、Node.js 对应 V8 引擎 1、简介 Node.js 是针对 V8 引擎构建的。通过与此引擎的最新版本保持同步&…...
【华为OD机试2023】工作安排 100% C++ Java Python
【华为OD机试2023】工作安排 100% C++ Java Python 前言 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能最优),不能保证通过率。 Tips1:机试为ACM 模式 你的代码需要处理输入输出,input/cin接收…...
面试题Spring - 关于Spring的25个经典问题和答案
文章目录 1 什么是Spring框架?Spring框架有哪些主要模块?2 使用Spring框架能带来哪些好处?3 什么是控制反转(IOC)?什么是依赖注入?4 请解释下Spring框架中的IoC?5 BeanFactory和ApplicationContext有什么区…...

C++学习day--10 条件判断、分支
1、if语句 if 语句的三种形态 形态1:如果。。。那么。。。 #include <iostream> using namespace std; int main( void ) { int salary; cout << " 你月薪多少 ?" ; cin >> salary; if (salary < 20000) { cout <&…...

和月薪5W的聊过后,才发现自己一直在打杂···
前几天和一个朋友聊面试,他说上个月同时拿到了腾讯和阿里的offer,最后选择了阿里。 我了解了下他的面试过程,就一点,不管是阿里还是腾讯的面试,这个级别的程序员,都会考察项目管理能力,并且权重…...

SSM框架学习-AOP通知类型
在AOP中,通知(Advice)是对切点进行操作的方法,用于实现切面定义的具体逻辑。Spring框架支持五种类型的通知: 1. 前置通知(Before advice) 在连接点执行前,执行通知 Before("**…...

微信小程序原生开发功能合集十四:登录健权及注册功能实现
本章实现微信自动登录及注册修改功能,包括匿名账号生成、权限自动检测、注册界面及注册流程的实现。 另外还提供小程序开发基础知识讲解课程,包括小程序开发基础知识、组件封装、常用接口组件使用及常用功能实现等内容,具体如下: 1. CSDN课程: https://edu.csdn…...

【Java零基础入门篇】第 ⑤ 期 - 抽象类和接口(二)
博主:命运之光 专栏:Java零基础入门 学习目标 1.了解什么是抽象类,什么是接口; 2.掌握抽象类和接口的定义方法; 3.理解接口和抽象类的使用场景; 4.掌握多态的含义和用法; 5.掌握内部类的定义方法…...
Halcon 集合运算(差集difference、交集intersection、并集union2、打散connection与 合集 union1)
文章目录 1 差集difference2. 交集intersection3. 并集union24 打散connection与 合集 union1 (二者互为反义词)4.1 打散connection与4.2 合集 union1 (注意与交集的区别)5 示例原图1 差集difference difference (Operator) Name difference — Calculate the difference …...
Allegro约束规则设计
首先是物理规则。 然后是间距规则。 如果有些特殊要求,还需要设计电气规则。 原则上,把规则设计好,然后把规则赋值给网络。 物理规则。PCS。 对于名字为DEFAULT的PCS,这是最基础的整板默认规则。 没有特殊要求的网络,…...

PyQt5桌面应用开发(11):摸鱼也要讲基本法之桌面精灵
本文目录 PyQt5桌面应用系列鼠标不要钱,手腕还不要钱吗?PyQt5源程序python文件资源定义界面定义文件 技术要素资源文件StyleSheetsQMainWindow设置窗体几何 结论 PyQt5桌面应用系列 PyQt5桌面应用开发(1):需求分析 PyQ…...

Talk预告 | 大连理工大学IIAU Lab在读博士生严彬:走向通用实例感知
本期为TechBeat人工智能社区第495期线上Talk! 北京时间5月10日(周三)20:00,大连理工大学IIAU Lab在读博士生—严彬的Talk将准时在TechBeat人工智能社区开播! 他与大家分享的主题是: “走向通用实例感知”,届时将介绍和探讨通用实…...
2023-05-04 LeetCode每日一题(摘水果)
2023-05-04每日一题 一、题目编号 2106. 摘水果二、题目链接 点击跳转到题目位置 三、题目描述 在一个无限的 x 坐标轴上,有许多水果分布在其中某些位置。给你一个二维整数数组 fruits ,其中 fruits[i] [positioni, amounti] 表示共有 amounti 个水…...

[工具]Pytorch-lightning的使用
Pytorch-lightning的使用 Pytorch-lightning介绍Pytorch-lightning与Pytorch的区别Pytorch-lightning框架的优势Pytorch-lightning框架 重要资源 Pytorch-lightning介绍 这里介绍Pytorch_lighting框架. Pytorch-lightning与Pytorch的区别 Pytorch-lightning可以简单的看作是…...
互联网摸鱼日报(2023-05-09)
互联网摸鱼日报(2023-05-09) InfoQ 热门话题 面向数字化提质提效的低代码架构设计 | 低代码技术内幕 提升字节规模化效能的平台化思路 | 极客有约 从微服务转为单体架构、成本降低 90%,亚马逊内部案例引发轰动!CTO&…...

MySQL常见的存储引擎
InnoDB:InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL 5.5之后,InnoDB是默认的MySQL存储引擎。 特点:1、DML操作遵循ACID模型,支持事务; 2、行级锁,提高并发访问性能; 3、支持外键FOREIGN KEY约…...

迅为i.MX6ULL开发板生成 KEY 文件,并安装
使用“ssh-keygen” 生成个四个 key 文件“ssh_host_rsa_key” “ssh_host_dsa_key” “ssh_host_ecdsa_key” 和“ssh_host_ed25519_key” 。 1 在虚拟机 Ubuntu 控制台, “ /home/ssh/openssh-4.6p1” 目录下, 使用命 令“ssh-keygen -t rsa -f ssh…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇
根据 QYResearch 发布的市场报告显示,全球市场规模预计在 2031 年达到 9848 万美元,2025 - 2031 年期间年复合增长率(CAGR)为 3.7%。在竞争格局上,市场集中度较高,2024 年全球前十强厂商占据约 74.0% 的市场…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...