java集合(四)Map集合之Properties詳解


一、Properties類介紹

java.util.Properties繼承自java.util.Hashtable,從jdk1.1版本開始,Properties的實現基本上就沒有什么大的變動。從http://docs.oracle.com/javase/7/docs/api/的jdk7的官方api文檔中我們可以看到對Properties類的介紹。Properties class是一個持久化的屬性保存對象,可以將屬性內容寫出到stream中或者從stream中讀取屬性內容,在底層的Hashtable中,每一對屬性的key和value都是按照string類型來保存的。 Properties可以將其他的Properties對象作為默認的值,Properties繼承自Hashtable,所以Hashtable的所有方法Properties對象均可以訪問。

Properties支持文本方式和xml方式的數據存儲。在文本方式中,格式為key:value,其中分隔符可以是:冒號(:)、等號(=)、空格。其中空格可以作為key的結束,同時獲取的值回將分割符號兩端的空格去掉。

Properties只支持1對1模式的屬性設置,而且不支持多層多級屬性設置。

二、Properties類屬性

protected Properties defaults:包含默認values的Properties對象,默認為null。我們在找不到對應key的情況下,就回遞歸的從這個默認列表中里面來找。

/**
     * A property list that contains default values for any keys not
     * found in this property list.
     *
     * @serial
     */
    protected Properties defaults;

Properties property

三、初始化方法

Properties提供兩種方式來創建Properties對象,第一種是不指定默認values對象的創建方法,另外一種是指定默認values對象的創建方法。但是此時是沒有加載屬性值的,加載key/value屬性必須通過專門的方法來加載。

/**
     * Creates an empty property list with no default values.
     */
    public Properties() {
        this(null);
    }

    /**
     * Creates an empty property list with the specified defaults.
     *
     * @param   defaults   the defaults.
     */
    public Properties(Properties defaults) {
        this.defaults = defaults;
    }

Properties Construction Method

四、常用方法

getProperty(String):根據指定的key獲取對應的屬性value值,如果在自身的存儲集合中沒有找到對應的key,那么就直接到默認的defaults屬性指定的Properties中獲取屬性值。

/**
     * Searches for the property with the specified key in this property list.
     * If the key is not found in this property list, the default property list,
     * and its defaults, recursively, are then checked. The method returns
     * <code>null</code> if the property is not found.
     *
     * @param   key   the property key.
     * @return  the value in this property list with the specified key value.
     * @see     #setProperty
     * @see     #defaults
     */
    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;
    }

getProperty(String)

getProperty(String, String):當getProperty(String)方法返回值為null的時候,返回給定的默認值,而不是返回null。

/**
     * Searches for the property with the specified key in this property list.
     * If the key is not found in this property list, the default property list,
     * and its defaults, recursively, are then checked. The method returns the
     * default value argument if the property is not found.
     *
     * @param   key            the hashtable key.
     * @param   defaultValue   a default value.
     *
     * @return  the value in this property list with the specified key value.
     * @see     #setProperty
     * @see     #defaults
     */
    public String getProperty(String key, String defaultValue) {
        String val = getProperty(key);
        return (val == null) ? defaultValue : val;
    }

getProperty(String,String)

load(InputStream):從byte stream中加載key/value鍵值對,要求所有的key/value鍵值對是按行存儲,同時是用ISO-8859-1編譯的。

/**
     * Reads a property list (key and element pairs) from the input
     * byte stream. The input stream is in a simple line-oriented
     * format as specified in
     * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
     * the ISO 8859-1 character encoding; that is each byte is one Latin1
     * character. Characters not in Latin1, and certain special characters,
     * are represented in keys and elements using Unicode escapes as defined in
     * section 3.3 of
     * <cite>The Java&trade; Language Specification</cite>.
     * <p>
     * The specified stream remains open after this method returns.
     *
     * @param      inStream   the input stream.
     * @exception  IOException  if an error occurred when reading from the
     *             input stream.
     * @throws     IllegalArgumentException if the input stream contains a
     *             malformed Unicode escape sequence.
     * @since 1.2
     */
    public synchronized void load(InputStream inStream) throws IOException {
        load0(new LineReader(inStream));
    }

load(InputStream)

load(Reader):從字符流中加載key/value鍵值對,要求所有的鍵值對都是按照行來存儲的。

/**
     * Reads a property list (key and element pairs) from the input
     * character stream in a simple line-oriented format.
     * <p>
     * Properties are processed in terms of lines. There are two
     * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
     * A natural line is defined as a line of
     * characters that is terminated either by a set of line terminator
     * characters (<code>\n</code> or <code>\r</code> or <code>\r\n</code>)
     * or by the end of the stream. A natural line may be either a blank line,
     * a comment line, or hold all or some of a key-element pair. A logical
     * line holds all the data of a key-element pair, which may be spread
     * out across several adjacent natural lines by escaping
     * the line terminator sequence with a backslash character
     * <code>\</code>.  Note that a comment line cannot be extended
     * in this manner; every natural line that is a comment must have
     * its own comment indicator, as described below. Lines are read from
     * input until the end of the stream is reached.
     *
     * <p>
     * A natural line that contains only white space characters is
     * considered blank and is ignored.  A comment line has an ASCII
     * <code>'#'</code> or <code>'!'</code> as its first non-white
     * space character; comment lines are also ignored and do not
     * encode key-element information.  In addition to line
     * terminators, this format considers the characters space
     * (<code>' '</code>, <code>'&#92;u0020'</code>), tab
     * (<code>'\t'</code>, <code>'&#92;u0009'</code>), and form feed
     * (<code>'\f'</code>, <code>'&#92;u000C'</code>) to be white
     * space.
     *
     * <p>
     * If a logical line is spread across several natural lines, the
     * backslash escaping the line terminator sequence, the line
     * terminator sequence, and any white space at the start of the
     * following line have no affect on the key or element values.
     * The remainder of the discussion of key and element parsing
     * (when loading) will assume all the characters constituting
     * the key and element appear on a single natural line after
     * line continuation characters have been removed.  Note that
     * it is <i>not</i> sufficient to only examine the character
     * preceding a line terminator sequence to decide if the line
     * terminator is escaped; there must be an odd number of
     * contiguous backslashes for the line terminator to be escaped.
     * Since the input is processed from left to right, a
     * non-zero even number of 2<i>n</i> contiguous backslashes
     * before a line terminator (or elsewhere) encodes <i>n</i>
     * backslashes after escape processing.
     *
     * <p>
     * The key contains all of the characters in the line starting
     * with the first non-white space character and up to, but not
     * including, the first unescaped <code>'='</code>,
     * <code>':'</code>, or white space character other than a line
     * terminator. All of these key termination characters may be
     * included in the key by escaping them with a preceding backslash
     * character; for example,<p>
     *
     * <code>\:\=</code><p>
     *
     * would be the two-character key <code>":="</code>.  Line
     * terminator characters can be included using <code>\r</code> and
     * <code>\n</code> escape sequences.  Any white space after the
     * key is skipped; if the first non-white space character after
     * the key is <code>'='</code> or <code>':'</code>, then it is
     * ignored and any white space characters after it are also
     * skipped.  All remaining characters on the line become part of
     * the associated element string; if there are no remaining
     * characters, the element is the empty string
     * <code>&quot;&quot;</code>.  Once the raw character sequences
     * constituting the key and element are identified, escape
     * processing is performed as described above.
     *
     * <p>
     * As an example, each of the following three lines specifies the key
     * <code>"Truth"</code> and the associated element value
     * <code>"Beauty"</code>:
     * <p>
     * <pre>
     * Truth = Beauty
     *  Truth:Beauty
     * Truth                    :Beauty
     * </pre>
     * As another example, the following three lines specify a single
     * property:
     * <p>
     * <pre>
     * fruits                           apple, banana, pear, \
     *                                  cantaloupe, watermelon, \
     *                                  kiwi, mango
     * </pre>
     * The key is <code>"fruits"</code> and the associated element is:
     * <p>
     * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre>
     * Note that a space appears before each <code>\</code> so that a space
     * will appear after each comma in the final result; the <code>\</code>,
     * line terminator, and leading white space on the continuation line are
     * merely discarded and are <i>not</i> replaced by one or more other
     * characters.
     * <p>
     * As a third example, the line:
     * <p>
     * <pre>cheeses
     * </pre>
     * specifies that the key is <code>"cheeses"</code> and the associated
     * element is the empty string <code>""</code>.<p>
     * <p>
     *
     * <a name="unicodeescapes"></a>
     * Characters in keys and elements can be represented in escape
     * sequences similar to those used for character and string literals
     * (see sections 3.3 and 3.10.6 of
     * <cite>The Java&trade; Language Specification</cite>).
     *
     * The differences from the character escape sequences and Unicode
     * escapes used for characters and strings are:
     *
     * <ul>
     * <li> Octal escapes are not recognized.
     *
     * <li> The character sequence <code>\b</code> does <i>not</i>
     * represent a backspace character.
     *
     * <li> The method does not treat a backslash character,
     * <code>\</code>, before a non-valid escape character as an
     * error; the backslash is silently dropped.  For example, in a
     * Java string the sequence <code>"\z"</code> would cause a
     * compile time error.  In contrast, this method silently drops
     * the backslash.  Therefore, this method treats the two character
     * sequence <code>"\b"</code> as equivalent to the single
     * character <code>'b'</code>.
     *
     * <li> Escapes are not necessary for single and double quotes;
     * however, by the rule above, single and double quote characters
     * preceded by a backslash still yield single and double quote
     * characters, respectively.
     *
     * <li> Only a single 'u' character is allowed in a Uniocde escape
     * sequence.
     *
     * </ul>
     * <p>
     * The specified stream remains open after this method returns.
     *
     * @param   reader   the input character stream.
     * @throws  IOException  if an error occurred when reading from the
     *          input stream.
     * @throws  IllegalArgumentException if a malformed Unicode escape
     *          appears in the input.
     * @since   1.6
     */
    public synchronized void load(Reader reader) throws IOException {
        load0(new LineReader(reader));
    }

load(Reader)

loadFromXML(InputStream):從xml文件中加載property,底層使用XMLUtils.load(Properties,InputStream)方法來加載。

/**
     * Loads all of the properties represented by the XML document on the
     * specified input stream into this properties table.
     *
     * <p>The XML document must have the following DOCTYPE declaration:
     * <pre>
     * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
     * </pre>
     * Furthermore, the document must satisfy the properties DTD described
     * above.
     *
     * <p>The specified stream is closed after this method returns.
     *
     * @param in the input stream from which to read the XML document.
     * @throws IOException if reading from the specified input stream
     *         results in an <tt>IOException</tt>.
     * @throws InvalidPropertiesFormatException Data on input stream does not
     *         constitute a valid XML document with the mandated document type.
     * @throws NullPointerException if <code>in</code> is null.
     * @see    #storeToXML(OutputStream, String, String)
     * @since 1.5
     */
    public synchronized void loadFromXML(InputStream in)
        throws IOException, InvalidPropertiesFormatException
    {
        if (in == null)
            throw new NullPointerException();
        XMLUtils.load(this, in);
        in.close();
    }

loadFromXML(InputStream)

store(OutputStream/Writer,comments)將所有的property(保存defaults的)都寫出到流中,同時如果給定comments的話,那么要加一個注釋。

/**
     * Writes this property list (key and element pairs) in this
     * <code>Properties</code> table to the output character stream in a
     * format suitable for using the {@link #load(java.io.Reader) load(Reader)}
     * method.
     * <p>
     * Properties from the defaults table of this <code>Properties</code>
     * table (if any) are <i>not</i> written out by this method.
     * <p>
     * If the comments argument is not null, then an ASCII <code>#</code>
     * character, the comments string, and a line separator are first written
     * to the output stream. Thus, the <code>comments</code> can serve as an
     * identifying comment. Any one of a line feed ('\n'), a carriage
     * return ('\r'), or a carriage return followed immediately by a line feed
     * in comments is replaced by a line separator generated by the <code>Writer</code>
     * and if the next character in comments is not character <code>#</code> or
     * character <code>!</code> then an ASCII <code>#</code> is written out
     * after that line separator.
     * <p>
     * Next, a comment line is always written, consisting of an ASCII
     * <code>#</code> character, the current date and time (as if produced
     * by the <code>toString</code> method of <code>Date</code> for the
     * current time), and a line separator as generated by the <code>Writer</code>.
     * <p>
     * Then every entry in this <code>Properties</code> table is
     * written out, one per line. For each entry the key string is
     * written, then an ASCII <code>=</code>, then the associated
     * element string. For the key, all space characters are
     * written with a preceding <code>\</code> character.  For the
     * element, leading space characters, but not embedded or trailing
     * space characters, are written with a preceding <code>\</code>
     * character. The key and element characters <code>#</code>,
     * <code>!</code>, <code>=</code>, and <code>:</code> are written
     * with a preceding backslash to ensure that they are properly loaded.
     * <p>
     * After the entries have been written, the output stream is flushed.
     * The output stream remains open after this method returns.
     * <p>
     *
     * @param   writer      an output character stream writer.
     * @param   comments   a description of the property list.
     * @exception  IOException if writing this property list to the specified
     *             output stream throws an <tt>IOException</tt>.
     * @exception  ClassCastException  if this <code>Properties</code> object
     *             contains any keys or values that are not <code>Strings</code>.
     * @exception  NullPointerException  if <code>writer</code> is null.
     * @since 1.6
     */
    public void store(Writer writer, String comments)
        throws IOException
    {
        store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
                                                 : new BufferedWriter(writer),
               comments,
               false);
    }

    /**
     * Writes this property list (key and element pairs) in this
     * <code>Properties</code> table to the output stream in a format suitable
     * for loading into a <code>Properties</code> table using the
     * {@link #load(InputStream) load(InputStream)} method.
     * <p>
     * Properties from the defaults table of this <code>Properties</code>
     * table (if any) are <i>not</i> written out by this method.
     * <p>
     * This method outputs the comments, properties keys and values in
     * the same format as specified in
     * {@link #store(java.io.Writer, java.lang.String) store(Writer)},
     * with the following differences:
     * <ul>
     * <li>The stream is written using the ISO 8859-1 character encoding.
     *
     * <li>Characters not in Latin-1 in the comments are written as
     * <code>&#92;u</code><i>xxxx</i> for their appropriate unicode
     * hexadecimal value <i>xxxx</i>.
     *
     * <li>Characters less than <code>&#92;u0020</code> and characters greater
     * than <code>&#92;u007E</code> in property keys or values are written
     * as <code>&#92;u</code><i>xxxx</i> for the appropriate hexadecimal
     * value <i>xxxx</i>.
     * </ul>
     * <p>
     * After the entries have been written, the output stream is flushed.
     * The output stream remains open after this method returns.
     * <p>
     * @param   out      an output stream.
     * @param   comments   a description of the property list.
     * @exception  IOException if writing this property list to the specified
     *             output stream throws an <tt>IOException</tt>.
     * @exception  ClassCastException  if this <code>Properties</code> object
     *             contains any keys or values that are not <code>Strings</code>.
     * @exception  NullPointerException  if <code>out</code> is null.
     * @since 1.2
     */
    public void store(OutputStream out, String comments)
        throws IOException
    {
        store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
               comments,
               true);
    }

store(...)

storeToXML(OutputSteam, comment, encoding):寫出到xml文件中。

/**
     * Emits an XML document representing all of the properties contained
     * in this table, using the specified encoding.
     *
     * <p>The XML document will have the following DOCTYPE declaration:
     * <pre>
     * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
     * </pre>
     *
     *<p>If the specified comment is <code>null</code> then no comment
     * will be stored in the document.
     *
     * <p>The specified stream remains open after this method returns.
     *
     * @param os        the output stream on which to emit the XML document.
     * @param comment   a description of the property list, or <code>null</code>
     *                  if no comment is desired.
     * @param  encoding the name of a supported
     *                  <a href="../lang/package-summary.html#charenc">
     *                  character encoding</a>
     *
     * @throws IOException if writing to the specified output stream
     *         results in an <tt>IOException</tt>.
     * @throws NullPointerException if <code>os</code> is <code>null</code>,
     *         or if <code>encoding</code> is <code>null</code>.
     * @throws ClassCastException  if this <code>Properties</code> object
     *         contains any keys or values that are not
     *         <code>Strings</code>.
     * @see    #loadFromXML(InputStream)
     * @since 1.5
     */
    public void storeToXML(OutputStream os, String comment, String encoding)
        throws IOException
    {
        if (os == null)
            throw new NullPointerException();
        XMLUtils.save(this, os, comment, encoding);
    }

storeToXML(...)

四、源碼分析 

主要針對加載屬性方法(load/loadFromXML)和寫出屬性到磁盤文件方法來進行分析(store/storeToXML)。

1、load(Reader)和load(InputStream)

這兩個方法是指定從文本文件中加載key/value屬性值,底層都是將流封裝成為LineReader對象,然后通過load0方法來加載屬性鍵值對的,加載完屬性后流對象是不會關閉的。這兩個方法對應的properties文件格式如下:

# this is comment
key1:value1
key2=value2
key3   :   vlaue3
key4   :   value4  
# the value is 'value4  ', because the Properties only trim the space of the split charset before and after.

#  key5  = value5
# this is error, the key not start with the space.

key6   value7

Properties Text File

LineReader源碼分析:

class LineReader {
        /**
         * 根據字節流創建LineReader對象
         * 
         * @param inStream
         *            屬性鍵值對對應的字節流對象
         */
        public LineReader(InputStream inStream) {
            this.inStream = inStream;
            inByteBuf = new byte[8192];
        }

        /**
         * 根據字符流創建LineReader對象
         * 
         * @param reader
         *            屬性鍵值對對應的字符流對象
         */
        public LineReader(Reader reader) {
            this.reader = reader;
            inCharBuf = new char[8192];
        }

        // 字節流緩沖區, 大小為8192個字節
        byte[] inByteBuf;
        // 字符流緩沖區,大小為8192個字符
        char[] inCharBuf;
        // 當前行信息的緩沖區,大小為1024個字符
        char[] lineBuf = new char[1024];
        // 讀取一行數據時候的實際讀取大小
        int inLimit = 0;
        // 讀取的時候指向當前字符位置
        int inOff = 0;
        // 字節流對象
        InputStream inStream;
        // 字符流對象
        Reader reader;

        /**
         * 讀取一行,將行信息保存到{@link lineBuf}對象中,並返回實際的字符個數
         * 
         * @return 實際讀取的字符個數
         * @throws IOException
         */
        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) {
                    // 讀取一行數據,並返回這一行的實際讀取大小
                    inLimit = (inStream == null) ? reader.read(inCharBuf) : inStream.read(inByteBuf);
                    inOff = 0;
                    // 如果沒有讀取到數據,那么就直接結束讀取操作
                    if (inLimit <= 0) {
                        // 如果當前長度為0或者是改行是注釋,那么就返回-1。否則返回len的值。
                        if (len == 0 || isCommentLine) {
                            return -1;
                        }
                        return len;
                    }
                }

                // 判斷是根據字符流還是字節流讀取當前字符
                if (inStream != null) {
                    // The line below is equivalent to calling a ISO8859-1 decoder.
                    // 字節流是根據ISO8859-1進行編碼的,所以在這里進行解碼操作。
                    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判斷中
                if (isNewLine) {
                    isNewLine = false;
                    // 如果當前字符是#或者是!,那么表示該行是一個注釋行
                    if (c == '#' || c == '!') {
                        isCommentLine = true;
                        continue;
                    }
                }

                // 根據當前字符是不是換行符號進行判斷操作
                if (c != '\n' && c != '\r') {
                    // 當前字符不是換行符號
                    lineBuf[len++] = c;// 將當前字符寫入到行信息緩沖區中,並將len自增加1.
                    // 如果len的長度大於行信息緩沖區的大小,那么對lineBuf進行擴容,擴容大小為原來的兩倍,最大為Integer.MAX_VALUE
                    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) {
                        // 如果這一行是注釋行,或者是當前長度為0,那么進行clean操作。
                        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.
                        len -= 1;
                        // skip the leading whitespace characters in following line
                        skipWhiteSpace = true;
                        appendedLineBegin = true;
                        precedingBackslash = false;
                        if (c == '\r') {
                            skipLF = true;
                        }
                    } else {
                        return len;
                    }
                }

            }
        }
    }

根據這個源碼,我們可以看出一些特征:readLine這個方法每次讀取一行數據;如果我們想在多行寫數據,那么可以使用'\'來進行轉義,在該轉義符號后面換行,是被允許的。

load0方法源碼:

private void load0(LineReader lr) throws IOException {
        char[] convtBuf = new char[1024];
        // 讀取的字符總數
        int limit;
        // 當前key所在位置
        int keyLen;
        // value的起始位置
        int valueStart;
        // 當前字符
        char c;
        // 
        boolean hasSep;
        // 是否是轉義字符
        boolean precedingBackslash;

        while ((limit = lr.readLine()) >= 0) {
            c = 0;
            // key的長度
            keyLen = 0;
            // value的起始位置默認為limit
            valueStart = limit;
            // 
            hasSep = false;
            precedingBackslash = false;

            // 如果key的長度小於總的字符長度,那么就進入循環
            while (keyLen < limit) {
                // 獲取當前字符
                c = lr.lineBuf[keyLen];
                // 如果當前字符是=或者是:,而且前一個字符不是轉義字符,那么就表示key的描述已經結束
                if ((c == '=' || c == ':') && !precedingBackslash) {
                    // 指定value的起始位置為當前keyLen的下一個位置
                    valueStart = keyLen + 1;
                    // 並且指定,去除空格
                    hasSep = true;
                    break;
                } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
                    // 如果當前字符是空格類字符,而且前一個字符不是轉義字符,那么表示key的描述已經結束
                    // 指定value的起始位置為當前位置的下一個位置
                    valueStart = keyLen + 1;
                    break;
                }
                // 如果當前字符為'\',那么跟新是否是轉義號。
                if (c == '\\') {
                    precedingBackslash = !precedingBackslash;
                } else {
                    precedingBackslash = false;
                }
                keyLen++;
            }

            // 如果value的起始位置小於總的字符長度,那么就進入該循環
            while (valueStart < limit) {
                // 獲取當前字符
                c = lr.lineBuf[valueStart];
                // 判斷當前字符是否是空格類字符,達到去空格的效果
                if (c != ' ' && c != '\t' && c != '\f') {
                    // 當前字符不是空格類字符,而且當前字符為=或者是:,並在此之前沒有出現過=或者:字符。
                    // 那么value的起始位置繼續往后移動。
                    if (!hasSep && (c == '=' || c == ':')) {
                        hasSep = true;
                    } else {
                        // 當前字符不是=或者:,或者在此之前出現過=或者:字符。那么結束循環。
                        break;
                    }
                }
                valueStart++;
            }
            // 讀取key
            String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
            // 讀取value
            String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
            // 包括key/value
            put(key, value);
        }
    }

我們可以看到,在這個過程中,會將分割符號兩邊的空格去掉,並且分割符號可以是=,:,空格等。而且=和:的級別比空格分隔符高,即當這兩個都存在的情況下,是按照=/:分割的。可以看到在最后會調用一個loadConvert方法,該方法主要是做key/value的讀取,並將十六進制的字符進行轉換。

2、loadFromXML方法

該方法主要是提供一個從XML文件中讀取key/value鍵值對的方法。底層是調用的XMLUtil的方法,加載完對象屬性后,流會被顯示的關閉。xml格式如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>comments</comment>
<entry key="key7">value7</entry>
<entry key="key6">value7</entry>
<entry key="key4">value4  </entry>
<entry key="key3">vlaue3</entry>
<entry key="key2">value2</entry>
<entry key="key1">value1</entry>
</properties>

Properties XML File

底層調用的是XMLUtil.load方法,在該方法中是使用DOM方式來訪問xml文件的,在這里不做詳細的介紹。

3、store(InputStream/Reader,String)方法

該方法主要是將屬性值寫出到文本文件中,並寫出一個comment的注釋。底層調用的是store0方法。針對store(InputStream,String)方法,我們可以看到在調用store0方法的時候,進行字節流封裝成字符流,並且指定字符集為8859-1。源碼如下:

private void store0(BufferedWriter bw, String comments, boolean escUnicode) throws IOException {
        if (comments != null) {
            // 寫出注釋, 如果是中文注釋,那么轉化成為8859-1的字符
            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);
                // 針對空格進行轉義,並根據是否需要進行8859-1編碼
                key = saveConvert(key, true, escUnicode);
                /*
                 * No need to escape embedded and trailing spaces for value,
                 * hence pass false to flag.
                 */
                // value不對空格進行轉義
                val = saveConvert(val, false, escUnicode);
                // 寫出key/value鍵值對
                bw.write(key + "=" + val);
                bw.newLine();
            }
        }
        bw.flush();
    }

4、storeToXML方法

將屬性寫出到xml文件中,底層調用的是XMLUtil.store方法。不做詳細的介紹。

五、實例

直接代碼:

package com.gerry.bd.properties.jdk;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import java.util.Set;

/**
 * 操作jdk自身操作屬性配置文件的Properties類。<br/>
 * jdk1.7文檔地址:http://docs.oracle.com/javase/7/docs/api/<br/>
 * java.util.Properties繼承自HashTable,最主要的子類是Provider
 * 
 * @author jsliuming
 * 
 */
public class PropertiesApp {
    public static void main(String[] args) {
        InputStream input = null;
        // 第一種,使用ClassLoad的方法獲取InputStram對象。
        input = PropertiesApp.class.getClassLoader().getResourceAsStream("propertiesApp.properties");
        // 第二種,直接使用Class的方法來獲取InputStream對象。必須加'/'表示在classpath路徑下,如果不加的話,那么獲取的是PropertiesApp這個類所在package下的文件。
        input = PropertiesApp.class.getResourceAsStream("/propertiesApp.properties");
        OutputStream os = null;

        try {
            os = new FileOutputStream("storePropertiesApp.xml");
        } catch (FileNotFoundException e1) {
        }

        // 第一步:創建Properties對象
        Properties prop = new Properties();
        try {
            // 第二步:加載屬性, 不會自動關閉input輸入流。
            prop.load(input);
            // 第三步:獲取屬性
            String value1 = prop.getProperty("key1");
            String value5 = prop.getProperty("key5");
            String value7 = prop.getProperty("key7", "defaultvalue");
            System.out.println("[key1:" + value1 + "],[key5:" + value5 + "],[key7:" + value7 + "]");
            Set<String> keys = prop.stringPropertyNames();
            System.out.println("全部的key/value屬性:");
            for (String key : keys) {
                System.out.println("[" + key + "][" + prop.getProperty(key) + "]");
            }
            // 第四步:設置屬性
            prop.setProperty("key7", "value7");
            // 第五步:保存成文件
            prop.storeToXML(os, "comments");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    // ignore
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
    }

}

PropertiesApp

結果console的輸出為:

[key1:value1],[key5:null],[key7:defaultvalue]
全部的key/value屬性:
[key6][value7]
[key4][value4  ]
[key3][vlaue3]
[key2][value2]
[key1][value1]

result


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM