Based on JDK1.8, the underlying source code of Properties collection is introduced in detail. Finally, the use case of reading files of Properties is given.

1 Overview of Properties

public class Properties extends Hashtable<Object,Object>

Properties, a collection class from JDK1.0, inherits Hashtable, which is thread-safe.

This class is not generic because its element types are fixed: both key and value can only be strings, although you can use the PUT method to store key-values of other types, but this is not recommended.

The key-value pairs stored in the Properties collection are called the Properties of the collection, so the collection is also called the property list collection.

The most important of all, the collection can carry on the combination and IO flow, data can be stored in a stream or loaded from a stream, can also and external. The properties or configuration file. The XML file to interact, read configuration information from a file, allows the user to configure some information removed from the code, realize decoupling of the code and configuration information, Avoid hard coding.

2 Properties source code analysis

2.1 Main class attributes

Properties inherits Hashtable and naturally inherits its Properties, such as table, Threshold, count, and so on. So, before you look at Properties, you can look at Hashtable: Java collection – Hashtable source code in-depth parsing and application introduction.

It also has a property of its own called defaults. The effect of this property is to look in defaults(if there is one) when you don’t find the corresponding property in the property list itself. The defaults are used to store the default value of the property, so the value of this property is optional (depending on your needs).

/** * An internal property list that contains default values for any keys not found in this property list. * If no property is found in the property list itself, look in defaults(if present), which stores the property's default value */
protected Properties defaults;
Copy the code

2.2 the constructor

2.2.1 the Properties ()

public Properties()

Creates an empty property sheet collection with no default values.

public Properties(a) {
    // Call another constructor internally, passing in defaults null
    this(null);
}
Copy the code

2.2.2 the Properties (Properties defaults)

public Properties(Properties defaults)

Creates an empty property list with a specified default value. The default value can also be specified as null.

public Properties(Properties defaults) {
    this.defaults = defaults;
}
Copy the code

2.3 Traversal method

Properties inherits all of the Hashtable traversal methods, including keySet(), entrySet(), values(), keys(), elements(), and has its own two traversal methods: The putobject and stringPropertyNames.

Let’s focus on its own traversal methods, which are explained in detail in Hashtable!

2.3.1 putobject method

public Enumeration< ? > propertyNames()

Returns an enumeration of all keys in the property sheet, including the default property sheet key (if present). JDK1.0 method.

If there is a non-string key in the Properties file, a ClassCastException will be reported.

/ * * *@returnReturns an enumeration of all keys in the property table, including those in the default property table. * /
publicEnumeration<? > propertyNames() {// Create a hashtable named h
    Hashtable<String, Object> h = new Hashtable<>();
    // This method is used to store the traversed key-value into h
    enumerate(h);
    // Returns an enumeration of keys via the keys method of the hashtable
    return h.keys();
}

/** * stores iterated key-values in h. If there is a key that is not of String type, ClassCastException ** will be reported@param h
 */
private synchronized void enumerate(Hashtable<String, Object> h) {
    // This step shows that if the internal defaults are not null, the defaults will recursively call this method... Until defaults is null, then start the following code
    if(defaults ! =null) {
        defaults.enumerate(h);
    }
    // Start iterating through the keys enumeration of each level of defaults from inside to outside, and add it to the hashtable!
    for(Enumeration<? > e = keys(); e.hasMoreElements(); ) {// If you do not store a key of type String, you will get an error
        String key = (String) e.nextElement();
        // Insert key into hashtable,value if value is null.h.put(key, get(key)); }}Copy the code

2.3.2 stringPropertyNames method

public Set< String > stringPropertyNames()

Returns the set of keys in this property sheet, where the key and its corresponding value are strings, as well as different keys in the default property list if no key with the same name is found in the main property list.

JDK1.6 new method. Compared with propertyNames, it is easier to manipulate the result and is relatively friendly. For keys and values that are not strings, an error is reported directly in propertyNames, which is ignored and returned normally.

public Set<String> stringPropertyNames(a) {
    // Create a hashtable named h
    Hashtable<String, String> h = new Hashtable<>();
    // This method is used to store the traversed key-value into h
    enumerateStringProperties(h);
    // Return a set of keys via the keySet method of hashtable
    return h.keySet();
}

/** * is used to store String key-values traversed in h. ** is ignored if there are non-string key-values@param h
 */
private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
    // This step shows that if the internal defaults are not null, the defaults will recursively call this method... Until defaults is null, then start the following code
    if(defaults ! =null) {
        defaults.enumerateStringProperties(h);
    }
    // Start iterating through the keys enumeration of each level of defaults from inside to outside, and add it to the hashtable!
    for(Enumeration<? > e = keys(); e.hasMoreElements(); ) {// Get the key value
        Object k = e.nextElement();
        Object v = get(k);
        // If k and v are not both strings, they are ignored instead of reporting an error
        if (k instanceof String && v instanceof String) {
            // If k and v are both strings, h will be addedh.put((String) k, (String) v); }}}Copy the code

2.3.3 summary

When we iterate over Properties, these two methods will optionally iterate over Properties in defaults, whereas the Hashtable method does not, so choose carefully!

If you want to iterate through properties in defaults and return them, you can only use propertyNames and stringPropertyNames. The difference is that the propertyNames and stringPropertyNames methods return different types of values. The first is an Enumeration Enumeration and the second is a set. Remove, set. remove, removeAll, retainAll, and clear are supported. That is, this set supports element removal, which can remove a mapping from a mapping, but it does not support add or addAll operations.

And the propertyNames method throws an exception if it encounters a non-String key; StringPropertyNames ignores, rather than throws, exceptions for key-values that are not all strings.

2.4 Methods of reading from external files

The most important function of the Properties collection is to obtain configuration information from external configuration files. Properties can be obtained from.properties files and. XML file to read the information, then natural Properties provides the corresponding method!

The data in the.properties file must be key-value pairs, separated by symbols such as “=”, “:”, “\t”, and “\f”. Note that \t stands for TAB and \f stands for page feed. The “space” here can be \u00A0 (uninterrupted space), \ U0020 (half corner/English space), \u3000 (full corner/Chinese space). At the same time “#”, “!” The first line counts as a comment and is not parsed.

Read the properties file:

public void load(InputStream inStream)

Reads the property list (key and element pairs) from the byte input stream.

public void load(Reader reader)

Read the property list (key and element pairs) from the input character stream in a simple line-oriented format.

Read the XML file:

public void loadFromXML(InputStream in)

Loads into this property sheet all attributes represented by an XML document in the specified byte input stream.

Against 2.4.1 load method

Load (InputStream inStream) method source code, these methods are similar principle, are reading data from the IO stream.

public synchronized void load(InputStream inStream) throws IOException {
    // Call the loca0 method and pass in the LineReader object, which, as the name implies, reads the data line by line
    load0(new LineReader(inStream));
}

/** * Read data from LineReader into propeties **@param lr
 * @throws IOException
 */
private void load0(LineReader lr) throws IOException {
    char[] convtBuf = new char[1024];
    int limit;
    int keyLen;
    int valueStart;
    char c;
    boolean hasSep;
    boolean precedingBackslash;
    /* Read one line at a time, loop through * if there is data, then linit is greater than 0 */
    while ((limit = lr.readLine()) >= 0) {
        c = 0;
        keyLen = 0;
        valueStart = limit;
        hasSep = false;

        //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
        precedingBackslash = false;
        while (keyLen < limit) {
            c = lr.lineBuf[keyLen];
            // Check the delimiter
            // = : can be used as a separator
            if ((c == '=' || c == ':') && !precedingBackslash) {
                valueStart = keyLen + 1;
                hasSep = true;
                break;
                // the space \t \f can also be used as a separator
            } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
                valueStart = keyLen + 1;
                break;
            }
            // \\ will be parsed to \
            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++;
        }
        // Get the parsed key and value
        String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
        String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
        // Call the put method to store the collectionput(key, value); }}Copy the code

2.5 Method of output to external files

Output to the properties file:

public void store(OutputStream out,String comments)

Write output byte stream. Out: Specifies an output stream; Comments: description of the property list, which can be null if there are no required comments.

public void store(Writer writer,String comments)

Write output character stream.

Output to an XML file:

public void storeToXML(OutputStream os,String comment)

Issue an XML document that represents all the attributes contained in this table.

public void storeToXML(OutputStream os,String comment,String encoding)

Emits an XML document representing all the attributes contained in this table using the specified encoding.

2.6 Other Methods

public synchronized Object setProperty(String key, String value)

Add a property that requires the key and value of the property to be strings. The internal call to the Hashtable method put returns the result of the Hashtable call to PUT. This method stores key and value pairs in table properties, not defaults properties.

The Properties collection should hold Properties using this method instead of PUT.

public synchronized Object setProperty(String key, String value) {
    return put(key, value);
}
Copy the code

public String getProperty(String key)

Searches for properties in this property list with the specified key. If the key is not found in this property list, then the default property list and its default values are recursively checked. If no property is found, this method returns NULL.

The Properties collection should use this method instead of GET to get the value of the property.

public String getProperty(String key) {
    // Use the hashtable method to get value (Object)
    Object oval = super.get(key);
    // If oval is a String, it is mandatory to assign String to sval, otherwise sval=null
    String sval = (oval instanceof String) ? (String) oval : null;
    // If sval=null and defaults are not empty, then the key/value pair is not found in the table
    // This is the default method called getProperty
    return ((sval == null) && (defaults ! =null))? defaults.getProperty(key) : sval; }Copy the code

3 File Reading case demonstration

We will briefly demonstrate how to read the configuration information of the.properties file on startup. There are two main steps:

  1. Get the IO stream of the file;
  2. Read data from an IO stream;
public class ReadFileByClassLoader {
    /** * defines an empty set of proerties as class variables that are loaded as the class is loaded, and all blocks and methods of the class can access */
    private static Properties properties = new Properties();

    /** * define the method to read the classpath configuration file using the loader in a static block, so that the configuration file information can be read when the class is created */
    static {
        try {
            // There are three ways to get a bytecode file object!
            //Class<? extends ReadFileByClassLoader> class1 = ReadFileByClassLoader.class;
            //Class
       class1 = Class.forName("collection.map.ReadFileByClassLoader"); / / 2
            //Class
       aClass = new ReadFileByClassLoader().getClass(); / / 3

            /* Method 1 ==> get the bytecode file object, then get the classloader, call the classloader method to get the input stream */
            // The arguments to the getResourceAsStream() method require the package path to start at the resources level below the package +properties file name +. The suffix
            // The student.properties file here exists under the Resources package, so write the name directly
            //InputStream resourceAsStream = ReadFileByClassLoader.class.getClassLoader().getResourceAsStream("student.properties");

            /* Method 2 ==> You can also get the input stream */ directly using the getResourceAsStream method of the bytecode file object
            // Note the addition of a /
            //InputStream resourceAsStream = ReadFileByClassLoader.class.getResourceAsStream("/student.properties");

            / * method 3 = = > class loader getSystemResourceAsStream method is used to obtain the input stream * /
            //InputStream resourceAsStream = ClassLoader.getSystemResourceAsStream("student.properties");

            /* Method 4 ==> directly use the input stream to read file bytes */
            // The argument to new File() is formatted to require the package path to start from SRC package +properties File name +. The suffix
            // The student.properties file is stored under the Resources package
            InputStream resourceAsStream = new BufferedInputStream(new FileInputStream(new File("src/main/resources/student.properties")));
            // Call load of properties to load the input stream
            properties.load(resourceAsStream);
        } catch(IOException e) { e.printStackTrace(); }}public static void main(String[] args) throws IOException {
        // Start to get configuration file informationSystem.out.println(properties); }}Copy the code

There are four main methods of obtaining input streams. Note that they need to be passed in different paths. Here, my project is an IDEA to create a normal Maven project. The loading path may be different.

The student.properties configuration file here reads as follows:

! ! As the annotation
# # as a comment
# equal
Name = zhang SAN
# : separated
age:22
# space separation
score 22.1
a           i
# \ t space
b  xx
# \\ parse into \
c=x\\
Copy the code

The output is as follows (note that utF-8 encoding is used, otherwise garbled characters are possible) :

{b=xx, a=i, age=22, name= Zhang SAN, score=22.1, c=x\}
Copy the code

If you don’t understand or need to communicate, you can leave a message. In addition, I hope to like, collect, pay attention to, I will continue to update a variety of Java learning blog!