java.util.Properties源码(全)

最近花了一些时间把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);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值