Properties类的操作
1.Properties类概述
A : 属于集合类,是一个可以和IO流相结合使用的集合类。
B : 可保存在流中或从流中加载,属性列表中每个键及其对应值都是一个字符串。
C : 是Hashtable的子类,是一个Map集合,具有Map的属性。
class Properties extends Hashtable
public class PropertiesDemo {
public static void main(String[] args) {
Properties prop = new Properties();
//添加元素
prop.put("01", "hello");
prop.put("02", "world");
prop.put("03", "java");
//遍历集合
Set<Object> set = prop.keySet();
for (Object key : set) {
Object value = prop.get(key);
System.out.println(key + "---" + value);
}
}
}
2.Properties类的特殊方法
A: public Object setProperty(String key,String value):添加元素
B: public String getProperty(String key): 获取元素
C: public Set stringPropertyNames(): 获取所有的键的集合
class Properties extends Hashtable {
//A:添加元素,调用的是底层Map的set()方法
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
//B:获取元素,底层是调用Map的get()方法
public String getProperty(String key) {
Object oval = super.get(key);
String sval = (oval instanceof String) ? (String)oval : null;
return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}
//C:获取所有的键的集合,底层也是调用Map的keySet()方法。
public Set<String> stringPropertyNames() {
Hashtable<String, String> h = new Hashtable<>();
enumerateStringProperties(h);
return h.keySet();
}
private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
if (defaults != null) {
defaults.enumerateStringProperties(h);
}
for (Enumeration e = keys() ; e.hasMoreElements() ;) {
Object k = e.nextElement();
Object v = get(k);
if (k instanceof String && v instanceof String) {
h.put((String) k, (String) v);
}
}
}
}
演示案例:
public class PropertiesDemo2 {
public static void main(String[] args) {
Properties prop = new Properties();
prop.setProperty("语文", "Chinese");
prop.setProperty("数学", "Math");
prop.setProperty("英语", "English");
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
String value = prop.getProperty(key);
System.out.println(key + "---" + value);
}
}
}
3.读属性、写属性以及需要注意的问题。
这里的集合必须是Properties集合:
public void load(Reader reader):把文件中的数据读取到集合中。
public void store(Writer writer, String comments):把集合中的数据存储到文件。
public class PropertiesDemo3 {
public static void main(String[] args) throws IOException {
myLoad();
myStore();
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("语文", "Chinese");
prop.setProperty("数学", "Math");
prop.setProperty("英语", "English");
//public void store(Writer writer,String comments):把集合中的数据存储到文件
Writer w = new FileWriter("course.txt");
prop.store(w, "科目中英文对照");
w.close();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
/*public void load(Reader reader):把文件中的数据读取到集合中。
注意:这个文件的数据必须是键值对形式
*/
Reader r = new FileReader("course.txt");
prop.load(r);
r.close();
System.out.println("prop:" +prop);
}
}
在JDK文档中我们看到同名方法如下:
load(InputStream inStream)
store(OutputStream out, String comments)
若是中文文本会出现乱码,为什么?
原因:在Properties类中store(OutputStream out, String comments)方法,已经把输入流的格式写死了【格式为ISO-8859-1】。
load(InputStream inStream) 将InputStream装换成内部类LineReader,字节流使用ISO8859-1来解码。
见源码:
public void store(OutputStream out, String comments) throws IOException {
store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), comments, true);
}
load(OutputStream out, String comments),源码还是将OutputStream转换成LineReader,【其中LineReader是Properties的内部类】
public synchronized void load(InputStream inStream) throws IOException {
load0(new LineReader(inStream));
}
/*
Properties从流中加载属性集合,是通过将流中或者字节分成一行行来处理的。
LineReader的定义如下:
*/
class LineReader {
public LineReader(InputStream inStream) {
this.inStream = inStream;
inByteBuf = new byte[8192];
}
public LineReader(Reader reader) {
this.reader = reader;
inCharBuf = new char[8192];
}
byte[] inByteBuf;
char[] inCharBuf;
char[] lineBuf = new char[1024];
int inLimit = 0;
int inOff = 0;
InputStream inStream;
Reader reader;
//读取一行
int readLine() throws IOException {
int len = 0;
char c = 0;
boolean skipWhiteSpace = true; //空白
boolean isCommentLine = false; //注释
boolean isNewLine = true; //新行
boolean appendedLineBegin = false; //加至行开始
boolean precedingBackslash = false; //反斜杠 "\"
boolean skipLF = false;
while (true) {
if (inOff >= inLimit) {
/*从输入流中读取一定数量的字节并将其存在缓冲区数组inCharBuf/inByteBuf中,
这里区分字节流和字符流
*/
inLimit = (inStream==null)?reader.read(inCharBuf)
:inStream.read(inByteBuf);
inOff = 0;
//读取到的为空
if (inLimit <= 0) {
if (len == 0 || isCommentLine) {
return -1;
}
return len;
}
}
if (inStream != null) {
//字节流,使用ISO8859-1来解码
//The line below is equivalent to calling a
//ISO8859-1 decoder.
c = (char) (0xff & inByteBuf[inOff++]);
} else {
c = inCharBuf[inOff++];
}
if (skipLF) {
skipLF = false;
if (c == '\n') {
continue;
}
}
if (skipWhiteSpace) {
if (c == ' ' || c == '\t' || c == '\f') {
continue;
}
if (!appendedLineBegin && (c == '\r' || c == '\n')) {
continue;
}
skipWhiteSpace = false;
appendedLineBegin = false;
}
if (isNewLine) {
isNewLine = false;
if (c == '#' || c == '!') {
//注释行,忽略。
isCommentLine = true;
continue;
}
}
//读取真正的属性内容
if (c != '\n' && c != '\r') {
//这里类似于ArrayList内部的容量扩充,使用字符数组来保存读取的内容。
lineBuf[len++] = c;
if (len == lineBuf.length) {
int newLength = lineBuf.length * 2;
if (newLength < 0) {
newLength = Integer.MAX_VALUE;
}
char[] buf = new char[newLength];
System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
lineBuf = buf;
}
//flip the preceding backslash flag
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
}
else {
// reached EOL文件结束
if (isCommentLine || len == 0) {
isCommentLine = false;
isNewLine = true;
skipWhiteSpace = true;
len = 0;
continue;
}
if (inOff >= inLimit) {
inLimit = (inStream==null)
?reader.read(inCharBuf)
:inStream.read(inByteBuf);
inOff = 0;
if (inLimit <= 0) {
return len;
}
}
//反斜杠
if (precedingBackslash) {
len -= 1;
//skip the leading whitespace characters in following line
skipWhiteSpace = true;
appendedLineBegin = true;
precedingBackslash = false;
if (c == '\r') {
skipLF = true;
}
} else {
return len;
}
}
}
}
}
4.stringPropertyNames()方法的使用案例。
/*
*文本文件(user.txt),我知道数据是键值对形式的,但是不知道具体内容。
*写一程序判断是否有"Chinese"这样的键存在,如果有就改变其实为"90"。
* 分析:
* A:把文件中的数据加载到集合中
* B:遍历集合,获取得到每一个键
* C:判断键是否有为"Chinese"的,如果有就修改其值为"90"
* D:把集合中的数据重新存储到文件中
*/
public class PropertiesTest {
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
Reader r = new FileReader("user.txt");
prop.load(r);
r.close();
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
if ("Chinese".equals(key)) {
prop.setProperty(key, "90");
break;
}
}
Writer w = new FileWriter("user.txt");
prop.store(w, null);
w.close();
}
}