最近花了一些时间把java.util.Properties源码看了一遍,其中的内部类LineReader写的非常的好,内部类的方法readLine()在读取每一行有效数据的同时,将数据写入到缓冲区当中;这为我以后写代码提供了另外的一种思路。
// 源码
// 继承 Hashtable,可以使用Hashtable中的部分方法
public class Properties extends Hashtable<Object,Object> {
private static final long serialVersionUID = 4112578634029874840L;
// 定义properties用来接收传入的值
protected Properties defaults;
// 构造函数,
public Properties() {
this(null);
}
// 构造函数, 传入properties对象
public Properties(Properties defaults) {
this.defaults = defaults;
}
// 设置property键值对
public synchronized Object setProperty(String key, String value) {]
// 底层中直接调用hashTable中的的put
return put(key, value);
}
// 加载Reader流对象
public synchronized void load(Reader reader) throws IOException {
// 实际上底层调用的是LineReader对象,每次读取一行
load0(new LineReader(reader));
}
// 加载InputStream流对象
// InputStream in = TestProperties.class.getClassLoader()
// .getResourceAsStream("testproperties.properties");
public synchronized void load(InputStream inStream) throws IOException {
load0(new LineReader(inStream));
}
// 私有方法,加载流对象
private void load0 (LineReader lr) throws IOException {
char[] convtBuf = new char[1024];
// 有效字符的长度
int limit;
// 键的长度
int keyLen;
// 值的开始下标
int valueStart;
// 每个字符的缓存
char c;
boolean hasSep;
// '\\'的标签
boolean precedingBackslash;
// 遍历读取数据,遍历到最后一行
while ((limit = lr.readLine()) >= 0) {
c = 0;
keyLen = 0;
valueStart = limit;
hasSep = false;
precedingBackslash = false;
// 对有效的字符进行遍历, 找到key的长度,与value的下标
while (keyLen < limit) {
c = lr.lineBuf[keyLen];
// 找到每行字符串中第一个'='或者':'
if ((c == '=' || c == ':') && !precedingBackslash) {
valueStart = keyLen + 1;
hasSep = true;
break;
// 可以用' ' '\t' '\f'作为key value的区分
} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
// 遇到'\\'去掉其中一个\
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
keyLen++;
}
while (valueStart < limit) {
c = lr.lineBuf[valueStart];
if (c != ' ' && c != '\t' && c != '\f') {
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
valueStart++;
}
// 分别获取到key 与 value
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
// 存储为键值对
put(key, value);
}
}
// 内部类,
class LineReader {
// 构造函数,传入流对象InputStream
public LineReader(InputStream inStream) {
this.inStream = inStream;
// 每次读取的最大字节?8192=1024*8 8Kb
inByteBuf = new byte[8192];
}
// 构造函数,传入流对象Reader
public LineReader(Reader reader) {
this.reader = reader;
// 每次读取的最大字符数?8192=1024*8 8Kb
inCharBuf = new char[8192];
}
// 字节流缓冲区
byte[] inByteBuf;
// 字符流缓冲区
char[] inCharBuf;
// 当前行信息的缓冲区
char[] lineBuf = new char[1024];
// 有效字符的长度
int inLimit = 0;
// 字符的下标
int inOff = 0;
// 用来接收传入的流对象
InputStream inStream;
Reader reader;
// 读取一行数据,返回有效字符串的长度, 并将有效的字符串放入lineBuf中
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
if (inOff >= inLimit) {
// 读取一行数据,并返回这一行的实际读取大小
inLimit = (inStream==null)?reader.read(inCharBuf)
:inStream.read(inByteBuf);
inOff = 0;
// 如果没有读取到数据
if (inLimit <= 0) {
// 长度为0,或者是注释
if (len == 0 || isCommentLine) {
return -1;
}
// 如果有读取到'\\',长度-1
if (precedingBackslash) {
len--;
}
return len;
}
}
// 判断是根据字符流还是字节流读取当前字符
if (inStream != null) {
c = (char) (0xff & inByteBuf[inOff++]);
} else {
c = inCharBuf[inOff++];
}
// skipLf为true,置为false,如果当前字符为换行符,则跳过
// 跳过第一次遇到的换行符
if (skipLF) {
skipLF = false;
if (c == '\n') {
continue;
}
}
// 跳过一行字符串中前面所有的空白符
if (skipWhiteSpace) {
// ' '空格 '\t'制表符 '\f'换页
if (c == ' ' || c == '\t' || c == '\f') {
continue;
}
// '\r'回到行首 '\n'换行
if (!appendedLineBegin && (c == '\r' || c == '\n')) {
continue;
}
// 去除字符串首段的空白字符后,以下置为false表示有效字符开始,
skipWhiteSpace = false;
appendedLineBegin = false;
}
// 新的一行,进入if,后将标志置为false
if (isNewLine) {
isNewLine = false;
// 判定第一个字符是否是#,!,是则
if (c == '#' || c == '!') {
// 将注释标签置为true
isCommentLine = true;
continue;
}
}
// 判定字符是否是'\n'与'\r', 不是在if中处理,是则在else中处理
if (c != '\n' && c != '\r') {
// 将字符放入lineBuf中
lineBuf[len++] = c;
// 当len等于缓冲区的长度时就进行扩容,扩容为原来的2倍
if (len == lineBuf.length) {
int newLength = lineBuf.length * 2;
// 扩容的上限为Integer.MAX_VALUE
if (newLength < 0) {
newLength = Integer.MAX_VALUE;
}
char[] buf = new char[newLength];
// 将lineBuf中的数据复制到buf中
System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
lineBuf = buf;
}
// 如果c是'\\'则反转标志,不是直接置为false
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
} else {
// 如果是注释,或者其长度为0,重置各种状态,跳过
if (isCommentLine || len == 0) {
isCommentLine = false;
isNewLine = true;
skipWhiteSpace = true;
len = 0;
continue;
}
// 读取开始或者读取结束时进入if
if (inOff >= inLimit) {
// 重新读取
inLimit = (inStream==null)
?reader.read(inCharBuf)
:inStream.read(inByteBuf);
// 重新读取,下标置0
inOff = 0;
// 没有读取到数据返回,之前已经读取到的长度
if (inLimit <= 0) {
if (precedingBackslash) {
len--;
}
return len;
}
}
// 读取到'\\',
if (precedingBackslash) {
len -= 1;
//skip the leading whitespace characters in following line
skipWhiteSpace = true;
appendedLineBegin = true;
precedingBackslash = false;
// 新的一行,将换行标签置为true
if (c == '\r') {
skipLF = true;
}
} else {
return len;
}
}
}
}
}
/**
* 获取key value
* @param in 读取到的有效字符串
* @param off in中字符开始下标
* @param len 截取长度
* @param convtBuf 缓冲区
* @return
*/
private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
// 扩容, 扩容成len的2倍
if (convtBuf.length < len) {
int newLen = len * 2;
if (newLen < 0) {
newLen = Integer.MAX_VALUE;
}
convtBuf = new char[newLen];
}
char aChar;
char[] out = convtBuf;
int outLen = 0;
int end = off + len;
// 对in进行遍历
while (off < end) {
aChar = in[off++];
// 遇到字符串的'\\'
if (aChar == '\\') {
// 获取到'\\'后面的字符
aChar = in[off++];
// "\\u"处理16进制的数据
if(aChar == 'u') {
int value=0;
// \\uXXXX 遍历u后面的4位
for (int i=0; i<4; i++) {
aChar = in[off++];
switch (aChar) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
value = (value << 4) + aChar - '0';
break;
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException(
"Malformed \\uxxxx encoding.");
}
}
// 将16进制写的数据强成char
out[outLen++] = (char)value;
} else {
// 处理其它的转义字符
if (aChar == 't') aChar = '\t';
else if (aChar == 'r') aChar = '\r';
else if (aChar == 'n') aChar = '\n';
else if (aChar == 'f') aChar = '\f';
out[outLen++] = aChar;
}
} else {
out[outLen++] = aChar;
}
}
// 将字符串反回
return new String (out, 0, outLen);
}
// 私有方法,将保存的数据进行转换
private String saveConvert(String theString,
boolean escapeSpace,
boolean escapeUnicode) {
int len = theString.length();
int bufLen = len * 2;
if (bufLen < 0) {
bufLen = Integer.MAX_VALUE;
}
StringBuffer outBuffer = new StringBuffer(bufLen);
// 对传入字符串进行遍历
for(int x=0; x<len; x++) {
char aChar = theString.charAt(x);
// 处理编码大于61的字符(包括所有的字符)
if ((aChar > 61) && (aChar < 127)) {
if (aChar == '\\') {
outBuffer.append('\\'); outBuffer.append('\\');
continue;
}
outBuffer.append(aChar);
continue;
}
// 处理转义字符, 以及数字字符
switch(aChar) {
case ' ':
if (x == 0 || escapeSpace)
outBuffer.append('\\');
outBuffer.append(' ');
break;
case '\t':outBuffer.append('\\'); outBuffer.append('t');
break;
case '\n':outBuffer.append('\\'); outBuffer.append('n');
break;
case '\r':outBuffer.append('\\'); outBuffer.append('r');
break;
case '\f':outBuffer.append('\\'); outBuffer.append('f');
break;
case '=': // Fall through
case ':': // Fall through
case '#': // Fall through
case '!':
outBuffer.append('\\'); outBuffer.append(aChar);
break;
default:
if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) {
outBuffer.append('\\');
outBuffer.append('u');
// 在toHex中
outBuffer.append(toHex((aChar >> 12) & 0xF));
outBuffer.append(toHex((aChar >> 8) & 0xF));
outBuffer.append(toHex((aChar >> 4) & 0xF));
outBuffer.append(toHex( aChar & 0xF));
} else {
outBuffer.append(aChar);
}
}
}
return outBuffer.toString();
}
// nibble = (aChar >> 12) & 0xF
// (nibble & 0xF) --> (aChar >> 12) & 0xF & 0F --> aChars
// 返回hexDigit中的十六进制字符
private static char toHex(int nibble) {
return hexDigit[(nibble & 0xF)];
}
private static final char[] hexDigit = {
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
// 将注释写入到文件中去, comments传入的字符串
private static void writeComments(BufferedWriter bw, String comments)
throws IOException {
// 首先写入#,表示注释
bw.write("#");
// 注释字符串的长度
int len = comments.length();
// 下标
int current = 0;
int last = 0;
// 十六进制长度 \\uxxxx
char[] uu = new char[6];
uu[0] = '\\';
uu[1] = 'u';
//遍历注释中的每一个字符,并将字符写入指定的文件中
while (current < len) {
char c = comments.charAt(current);
// '\u00ff' = 31 空格的编码' '是32, 大于31的字符才是有意义的字符
if (c > '\u00ff' || c == '\n' || c == '\r') {
// last==current说明存在有字符是占多个char的,如:汉字
if (last != current)
bw.write(comments.substring(last, current));
// 写入字符
if (c > '\u00ff') {
uu[2] = toHex((c >> 12) & 0xf);
uu[3] = toHex((c >> 8) & 0xf);
uu[4] = toHex((c >> 4) & 0xf);
uu[5] = toHex( c & 0xf);
bw.write(new String(uu));
} else {
// 写入转义的'\r', '\n'
bw.newLine();
if (c == '\r' &&
current != len - 1 &&
comments.charAt(current + 1) == '\n') {
current++;
}
if (current == len - 1 ||
(comments.charAt(current + 1) != '#' &&
comments.charAt(current + 1) != '!'))
bw.write("#");
}
last = current + 1;
}
current++;
}
if (last != current)
bw.write(comments.substring(last, current));
bw.newLine();
}
@Deprecated
public void save(OutputStream out, String comments) {
try {
store(out, comments);
} catch (IOException e) {}
}
// 将数据写入到文件中去, comments对存入的信息进行描述,传入 的字符串会以 #comments 存在开头
public void store(Writer writer, String comments)
throws IOException {
// 如果是writer,包装成BufferedWriter,
store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
: new BufferedWriter(writer),
comments,
false);
}
// 将数据写入到指定的文件中去, comments对存入的信息进行描述,传入 的字符串会以 #comments 存在开头
public void store(OutputStream out, String comments)
throws IOException {
// outputStream包装成OutputStreamWriter并指定编码,再包装成BufferedWriter
store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
comments,
true);
}
// 私有方法,将流对象传来的数据存入到文件中
private void store0(BufferedWriter bw, String comments, boolean escUnicode)
throws IOException
{
// 传入的comments不为空,将其作为注释写入到文件的开头
if (comments != null) {
writeComments(bw, comments);
}
// 将写入文件的时间一起写入
bw.write("#" + new Date().toString());
bw.newLine();
// 方法加锁
synchronized (this) {
// 对读取到的文件进行遍历,将数据写入到文件中去
for (Enumeration<?> e = keys(); e.hasMoreElements();) {
String key = (String)e.nextElement();
String val = (String)get(key);
// 获取到key, value并将key, value写入文件
key = saveConvert(key, true, escUnicode);
val = saveConvert(val, false, escUnicode);
bw.write(key + "=" + val);
bw.newLine();
}
}
// 清空bw中的数据
bw.flush();
}
// 从xml文件中加载数据
public synchronized void loadFromXML(InputStream in)
throws IOException, InvalidPropertiesFormatException {
XmlSupport.load(this, Objects.requireNonNull(in));
in.close();
}
// 将数据存储在xml文件中
public void storeToXML(OutputStream os, String comment)
throws IOException {
// 调用storeToXML,指定编码为UTF-8
storeToXML(os, comment, "UTF-8");
}
// 将数据存储在xml文件中,指定编码的格式
public void storeToXML(OutputStream os, String comment, String encoding)
throws IOException {
XmlSupport.save(this, Objects.requireNonNull(os), comment,
Objects.requireNonNull(encoding));
}
// 通过key,获取到值
public String getProperty(String key) {
// 调用的是父类hashTable中的get方法
Object oval = super.get(key);
String sval = (oval instanceof String) ? (String)oval : null;
return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}
// 通过key获取到值,并设置默认的值,没有通过key获取到值,就返回默认值
public String getProperty(String key, String defaultValue) {
String val = getProperty(key);
return (val == null) ? defaultValue : val;
}
// 获取所有的key的集合
public Enumeration<?> propertyNames() {
Hashtable<String,Object> h = new Hashtable<>();
// 将default中的键值对存入h中
enumerate(h);
// 获取所有的key的集合
return h.keys();
}
// 获取所有的key的集合
public Set<String> stringPropertyNames() {
Hashtable<String, String> h = new Hashtable<>();
// 比enumerate(h);多了一层对key, value中类型的判断
enumerateStringProperties(h);
// 获取所有的key的集合
return h.keySet();
}
// 将所有的数据写入到文件中
public void list(PrintStream out) {
// 文件的开头
out.println("-- listing properties --");
Hashtable<String,Object> h = new Hashtable<>();
// 将所有的key, value存入到h中
enumerate(h);
// 对集合进行遍历,将key,value写入到文件中
for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
String key = e.nextElement();
String val = (String)h.get(key);
// value最长写入40个字符
if (val.length() > 40) {
val = val.substring(0, 37) + "...";
}
out.println(key + "=" + val);
}
}
// 与list(PrintStream out)几乎完全一样
public void list(PrintWriter out) {
out.println("-- listing properties --");
Hashtable<String,Object> h = new Hashtable<>();
enumerate(h);
for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
String key = e.nextElement();
String val = (String)h.get(key);
if (val.length() > 40) {
val = val.substring(0, 37) + "...";
}
out.println(key + "=" + val);
}
}
// 将default中的数据存入到h中
private synchronized void enumerate(Hashtable<String,Object> h) {
if (defaults != null) {
defaults.enumerate(h);
}
// 对key进行遍历
for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
String key = (String)e.nextElement();
// 将key ,value放入h中
h.put(key, get(key));
}
}
// 同enumerate() 多了一层对key,value是否 是String的校验
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);
}
}
}
// 内部类,加载xml文件,与将数据保存在xml中的时候用到
// 不是太懂这里的代码
private static class XmlSupport {
//
private static XmlPropertiesProvider loadProviderFromProperty(ClassLoader cl) {
String cn = System.getProperty("sun.util.spi.XmlPropertiesProvider");
if (cn == null)
return null;
try {
Class<?> c = Class.forName(cn, true, cl);
return (XmlPropertiesProvider)c.newInstance();
} catch (ClassNotFoundException |
IllegalAccessException |
InstantiationException x) {
throw new ServiceConfigurationError(null, x);
}
}
// 遍历xml文件中的每一行数据,将数据取出来
private static XmlPropertiesProvider loadProviderAsService(ClassLoader cl) {
Iterator<XmlPropertiesProvider> iterator =
ServiceLoader.load(XmlPropertiesProvider.class, cl).iterator();
return iterator.hasNext() ? iterator.next() : null;
}
private static XmlPropertiesProvider loadProvider() {
return AccessController.doPrivileged(
new PrivilegedAction<XmlPropertiesProvider>() {
public XmlPropertiesProvider run() {
ClassLoader cl = ClassLoader.getSystemClassLoader();
XmlPropertiesProvider provider = loadProviderFromProperty(cl);
if (provider != null)
return provider;
provider = loadProviderAsService(cl);
if (provider != null)
return provider;
return new jdk.internal.util.xml.BasicXmlPropertiesProvider();
}});
}
private static final XmlPropertiesProvider PROVIDER = loadProvider();
// 加载xml文件
static void load(Properties props, InputStream in)
throws IOException, InvalidPropertiesFormatException {
PROVIDER.load(props, in);
}
// 将数据保存到xml文凭中
static void save(Properties props, OutputStream os, String comment,
String encoding)
throws IOException {
PROVIDER.store(props, os, comment, encoding);
}
}
}