欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你全面了解Java?Properties類(lèi)

 更新時(shí)間:2022年09月15日 08:32:22   作者:JAVA旭陽(yáng)  
Properties是JDK1.0中引入的java類(lèi),目前也在項(xiàng)目中大量使用,主要用來(lái)讀取外部的配置,那除了這個(gè),你對(duì)它其他的一些api也了解嗎??你了解它是怎么實(shí)現(xiàn)的嗎??如果不清楚的話,就通過(guò)本篇文章帶你一探究竟

概述

Properties是JDK1.0中引入的java類(lèi),目前也在項(xiàng)目中大量使用,主要用來(lái)讀取外部的配置,那除了這個(gè),你對(duì)它其他的一些api也了解嗎? 你了解它是怎么實(shí)現(xiàn)的嗎? 如果不清楚的話,就通過(guò)本篇文章帶你一探究竟。

介紹

java.util.Properties繼承自java.util.Hashtable,是一個(gè)持久化的屬性保存對(duì)象,可以將屬性內(nèi)容寫(xiě)出到stream中或者從stream中讀取屬性內(nèi)容。 它的重要特性如下:

  • 在底層的Hashtable中,每一對(duì)屬性的key和value都是按照string類(lèi)型來(lái)保存的。
  • Properties支持文本方式和xml方式的數(shù)據(jù)存儲(chǔ)。在文本方式中,格式為key:value,其中分隔符可以是:冒號(hào)(:)、等號(hào)(=)、空格。其中空格可以作為key的結(jié)束,同時(shí)獲取的值回將分割符號(hào)兩端的空格去掉。
  • Properties可以將其他的Properties對(duì)象作為默認(rèn)的值。
  • Hashtable的所有方法Properties對(duì)象均可以訪問(wèn),但是不建議這么做,因?yàn)镠ashtable可以存放其他數(shù)據(jù)類(lèi)型,這樣會(huì)導(dǎo)致Properties一些方法調(diào)用報(bào)錯(cuò)。
  • 在properties文件中,可以用井號(hào)"#"來(lái)作注釋。
  • 線程安全
  • key、value不可以是null

構(gòu)造方法

Properties()

創(chuàng)建一個(gè)無(wú)默認(rèn)值的空屬性列表。

Properties(Properties defaults)

創(chuàng)建一個(gè)帶有指定默認(rèn)值的空屬性列表。

關(guān)鍵方法

getProperty ( String key)

根據(jù)指定的key獲取對(duì)應(yīng)的屬性value值,如果在自身的存儲(chǔ)集合中沒(méi)有找到對(duì)應(yīng)的key,那么就直接到默認(rèn)的defaults屬性指定的Properties中獲取屬性值。

getProperty(String, String)

當(dāng)getProperty(String)方法返回值為null的時(shí)候,返回給定的默認(rèn)值,而不是返回null。

load ( InputStream inStream)

從byte stream中加載key/value鍵值對(duì),要求所有的key/value鍵值對(duì)是按行存儲(chǔ),同時(shí)是用ISO-8859-1編譯的, 不支持中文。

load(Reader)

從字符流中加載key/value鍵值對(duì),要求所有的鍵值對(duì)都是按照行來(lái)存儲(chǔ)的。

loadFromXML(InputStream)

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

setProperty ( String key, String value)

調(diào)用 Hashtable 的方法 put 。他通過(guò)調(diào)用基類(lèi)的put方法來(lái)設(shè)置 鍵 - 值對(duì)。

store ( OutputStream out, String comments)

將所有的property(保存defaults的)都寫(xiě)出到流中,同時(shí)如果給定comments的話,那么要加一個(gè)注釋。

storeToXML(OutputSteam, comment, encoding)

寫(xiě)出到xml文件中。

Set stringPropertyNames()

獲取所有Properties中所有的key集合

clear ()

清除所有裝載的 鍵值對(duì)。該方法在基類(lèi)中提供。

使用案例

新建配置文件app.properties

## 用戶信息
user.name:旭陽(yáng)
user.age=28
user.sex 男

通過(guò)idea設(shè)置它的格式為UTF-8。

驗(yàn)證讀取以及中文亂碼的問(wèn)題

 @Test
    public void test1() throws IOException {
        Properties properties = new Properties();
        // 使用load inputstream
        properties.load(this.getClass().getClassLoader().getResourceAsStream("app.properties"));
        // 出現(xiàn)亂碼
        System.out.println(properties);
        // 轉(zhuǎn)碼
        System.out.println(new String(properties.getProperty("user.name").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));

        Properties properties2 = new Properties();
        // 使用load read
        BufferedReader bf = new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("app.properties"), "UTF-8"));
        properties2.load(bf);
        System.out.println(properties2);
    }

運(yùn)行結(jié)果:

保存為xml格式

 @Test
    public void test2() throws IOException {
        Properties properties2 = new Properties();
        // 使用load read
        BufferedReader bf = new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("app.properties"), "UTF-8"));
        properties2.load(bf);
        System.out.println(properties2);

        // 保存到xml
        FileOutputStream fileOutputStream = new FileOutputStream("app.xml");
        properties2.storeToXML(fileOutputStream, "alvin info", "UTF-8");
    }

運(yùn)行結(jié)果:

源碼解析

源碼這部分主要分析下load(Reader)和load(InputStream)這兩個(gè)最常用的方法,這兩個(gè)方法是指定從文本文件中加載key/value屬性值,底層都是將流封裝成為L(zhǎng)ineReader對(duì)象,然后通過(guò)load0方法來(lái)加載屬性鍵值對(duì)的。

public synchronized void load(InputStream inStream) throws IOException {
        load0(new LineReader(inStream));
    }

將inputStream封裝程一個(gè)LineReader,每次可以讀取一行數(shù)據(jù)。

LineReader源碼分析:

class LineReader {
        /**
         * 根據(jù)字節(jié)流創(chuàng)建LineReader對(duì)象
         * 
         * @param inStream
         *            屬性鍵值對(duì)對(duì)應(yīng)的字節(jié)流對(duì)象
         */
        public LineReader(InputStream inStream) {
            this.inStream = inStream;
            inByteBuf = new byte[8192];
        }

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

        // 字節(jié)流緩沖區(qū), 大小為8192個(gè)字節(jié)
        byte[] inByteBuf;
        // 字符流緩沖區(qū),大小為8192個(gè)字符
        char[] inCharBuf;
        // 當(dāng)前行信息的緩沖區(qū),大小為1024個(gè)字符
        char[] lineBuf = new char[1024];
        // 讀取一行數(shù)據(jù)時(shí)候的實(shí)際讀取大小
        int inLimit = 0;
        // 讀取的時(shí)候指向當(dāng)前字符位置
        int inOff = 0;
        // 字節(jié)流對(duì)象
        InputStream inStream;
        // 字符流對(duì)象
        Reader reader;

        /**
         * 讀取一行,將行信息保存到{@link lineBuf}對(duì)象中,并返回實(shí)際的字符個(gè)數(shù)
         * 
         * @return 實(shí)際讀取的字符個(gè)數(shù)
         * @throws IOException
         */
        int readLine() throws IOException {
            // 總的字符長(zhǎng)度
            int len = 0;
            // 當(dāng)前字符
            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) {
                    // 讀取一行數(shù)據(jù),并返回這一行的實(shí)際讀取大小
                    inLimit = (inStream == null) ? reader.read(inCharBuf) : inStream.read(inByteBuf);
                    inOff = 0;
                    // 如果沒(méi)有讀取到數(shù)據(jù),那么就直接結(jié)束讀取操作
                    if (inLimit <= 0) {
                        // 如果當(dāng)前長(zhǎng)度為0或者是改行是注釋?zhuān)敲淳头祷?1。否則返回len的值。
                        if (len == 0 || isCommentLine) {
                            return -1;
                        }
                        return len;
                    }
                }

                // 判斷是根據(jù)字符流還是字節(jié)流讀取當(dāng)前字符
                if (inStream != null) {
                    // The line below is equivalent to calling a ISO8859-1 decoder.
                    // 字節(jié)流是根據(jù)ISO8859-1進(jìn)行編碼的,所以在這里進(jìn)行解碼操作。
                    c = (char) (0xff & inByteBuf[inOff++]);
                } else {
                    c = inCharBuf[inOff++];
                }

                // 如果前一個(gè)字符是換行符號(hào),那么判斷當(dāng)前字符是否也是換行符號(hào)
                if (skipLF) {
                    skipLF = false;
                    if (c == '\n') {
                        continue;
                    }
                }

                // 如果前一個(gè)字符是空格,那么判斷當(dāng)前字符是不是空格類(lèi)字符
                if (skipWhiteSpace) {
                    if (c == ' ' || c == '\t' || c == '\f') {
                        continue;
                    }
                    if (!appendedLineBegin && (c == '\r' || c == '\n')) {
                        continue;
                    }
                    skipWhiteSpace = false;
                    appendedLineBegin = false;
                }

                // 如果當(dāng)前新的一行,那么進(jìn)入該if判斷中
                if (isNewLine) {
                    isNewLine = false;
                    // 如果當(dāng)前字符是#或者是!,那么表示該行是一個(gè)注釋行
                    if (c == '#' || c == '!') {
                        isCommentLine = true;
                        continue;
                    }
                }

                // 根據(jù)當(dāng)前字符是不是換行符號(hào)進(jìn)行判斷操作
                if (c != '\n' && c != '\r') {
                    // 當(dāng)前字符不是換行符號(hào)
                    lineBuf[len++] = c;// 將當(dāng)前字符寫(xiě)入到行信息緩沖區(qū)中,并將len自增加1.
                    // 如果len的長(zhǎng)度大于行信息緩沖區(qū)的大小,那么對(duì)lineBuf進(jìn)行擴(kuò)容,擴(kuò)容大小為原來(lái)的兩倍,最大為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;
                    }
                    // 是否是轉(zhuǎn)義字符
                    // flip the preceding backslash flag
                    if (c == '\') {
                        precedingBackslash = !precedingBackslash;
                    } else {
                        precedingBackslash = false;
                    }
                } else {
                    // reached EOL
                    if (isCommentLine || len == 0) {
                        // 如果這一行是注釋行,或者是當(dāng)前長(zhǎng)度為0,那么進(jìn)行clean操作。
                        isCommentLine = false;
                        isNewLine = true;
                        skipWhiteSpace = true;
                        len = 0;
                        continue;
                    }
                    // 如果已經(jīng)沒(méi)有數(shù)據(jù)了,就重新讀取
                    if (inOff >= inLimit) {
                        inLimit = (inStream == null) ? reader.read(inCharBuf) : inStream.read(inByteBuf);
                        inOff = 0;
                        if (inLimit <= 0) {
                            return len;
                        }
                    }
                    // 查看是否是轉(zhuǎn)義字符
                    if (precedingBackslash) {
                        // 如果是,那么表示是另起一行,進(jìn)行屬性的定義,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這個(gè)方法每次讀取一行數(shù)據(jù);如果我們想在多行寫(xiě)數(shù)據(jù),那么可以使用''來(lái)進(jìn)行轉(zhuǎn)義,在該轉(zhuǎn)義符號(hào)后面換行,是被允許的。

load0方法源碼如下:

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

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

            // 如果key的長(zhǎng)度小于總的字符長(zhǎng)度,那么就進(jìn)入循環(huán)
            while (keyLen < limit) {
                // 獲取當(dāng)前字符
                c = lr.lineBuf[keyLen];
                // 如果當(dāng)前字符是=或者是:,而且前一個(gè)字符不是轉(zhuǎn)義字符,那么就表示key的描述已經(jīng)結(jié)束
                if ((c == '=' || c == ':') && !precedingBackslash) {
                    // 指定value的起始位置為當(dāng)前keyLen的下一個(gè)位置
                    valueStart = keyLen + 1;
                    // 并且指定,去除空格
                    hasSep = true;
                    break;
                } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
                    // 如果當(dāng)前字符是空格類(lèi)字符,而且前一個(gè)字符不是轉(zhuǎn)義字符,那么表示key的描述已經(jīng)結(jié)束
                    // 指定value的起始位置為當(dāng)前位置的下一個(gè)位置
                    valueStart = keyLen + 1;
                    break;
                }
                // 如果當(dāng)前字符為'',那么跟新是否是轉(zhuǎn)義號(hào)。
                if (c == '\') {
                    precedingBackslash = !precedingBackslash;
                } else {
                    precedingBackslash = false;
                }
                keyLen++;
            }

            // 如果value的起始位置小于總的字符長(zhǎng)度,那么就進(jìn)入該循環(huán)
            while (valueStart < limit) {
                // 獲取當(dāng)前字符
                c = lr.lineBuf[valueStart];
                // 判斷當(dāng)前字符是否是空格類(lèi)字符,達(dá)到去空格的效果
                if (c != ' ' && c != '\t' && c != '\f') {
                    // 當(dāng)前字符不是空格類(lèi)字符,而且當(dāng)前字符為=或者是:,并在此之前沒(méi)有出現(xiàn)過(guò)=或者:字符。
                    // 那么value的起始位置繼續(xù)往后移動(dòng)。
                    if (!hasSep && (c == '=' || c == ':')) {
                        hasSep = true;
                    } else {
                        // 當(dāng)前字符不是=或者:,或者在此之前出現(xiàn)過(guò)=或者:字符。那么結(jié)束循環(huán)。
                        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);
        }
    }

會(huì)將分割符號(hào)兩邊的空格去掉,并且分割符號(hào)可以是=,:,空格等。而且=和:的級(jí)別比空格分隔符高,即當(dāng)這兩個(gè)都存在的情況下,是按照=/:分割的??梢钥吹皆谧詈髸?huì)調(diào)用一個(gè)loadConvert方法,該方法主要是做key/value的讀取,并將十六進(jìn)制的字符進(jìn)行轉(zhuǎn)換。

總結(jié)

本文闡述了Properties的基本作用以及源碼實(shí)現(xiàn),是不是對(duì)Properties有了更近一步的認(rèn)識(shí)呢。

以上就是一文帶你全面了解Java Properties類(lèi)的詳細(xì)內(nèi)容,更多關(guān)于Java Properties類(lèi)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot使用Flyway進(jìn)行數(shù)據(jù)庫(kù)遷移的實(shí)現(xiàn)示例

    SpringBoot使用Flyway進(jìn)行數(shù)據(jù)庫(kù)遷移的實(shí)現(xiàn)示例

    Flyway是一個(gè)數(shù)據(jù)庫(kù)遷移工具,它提供遷移歷史和回滾的功能,本文主要介紹了如何使用Flyway來(lái)管理Spring Boot應(yīng)用程序中的SQL數(shù)據(jù)庫(kù)架構(gòu),感興趣的可以了解一下
    2023-08-08
  • Spring?question問(wèn)題小結(jié)

    Spring?question問(wèn)題小結(jié)

    在AppConfig配置類(lèi)中,通過(guò)@Bean注解創(chuàng)建了Service和Controller的實(shí)例,Spring會(huì)自動(dòng)將這些實(shí)例納入容器的管理,并處理它們之間的依賴關(guān)系,本文給大家介紹Spring?question問(wèn)題小結(jié),感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • MyBatis中SqlSession實(shí)現(xiàn)增刪改查案例

    MyBatis中SqlSession實(shí)現(xiàn)增刪改查案例

    這篇文章主要介紹了MyBatis中SqlSession實(shí)現(xiàn)增刪改查案例,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • 淺談自定義校驗(yàn)注解ConstraintValidator

    淺談自定義校驗(yàn)注解ConstraintValidator

    鑒于通用性和普遍性,Spring框架提供了validator組件,通過(guò)一些校驗(yàn)器,可以對(duì)一些數(shù)據(jù)進(jìn)行統(tǒng)一的完整性和有效性等校驗(yàn),即簡(jiǎn)單又好用
    2021-06-06
  • 詳解springmvc 中controller與jsp傳值

    詳解springmvc 中controller與jsp傳值

    本篇文章主要介紹了springmvc 中controller與jsp傳值,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • Java代碼實(shí)現(xiàn)酒店管理系統(tǒng)

    Java代碼實(shí)現(xiàn)酒店管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java代碼實(shí)現(xiàn)酒店管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 詳談Spring對(duì)IOC的理解(推薦篇)

    詳談Spring對(duì)IOC的理解(推薦篇)

    下面小編就為大家?guī)?lái)一篇詳談Spring對(duì)IOC的理解(推薦篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Java多線程的用法詳解

    Java多線程的用法詳解

    本篇文章介紹了,在Java中多線程的用法詳解。需要的朋友參考下
    2013-04-04
  • SpringBoot中加密模塊的使用

    SpringBoot中加密模塊的使用

    本文主要介紹了SpringBoot中加密模塊的使用,包括對(duì)稱(chēng)加密、非對(duì)稱(chēng)加密和哈希加密等,同時(shí)還會(huì)提供相應(yīng)的代碼示例,感興趣的朋友可以參考一下
    2023-05-05
  • Spring SpringMVC在啟動(dòng)完成后執(zhí)行方法源碼解析

    Spring SpringMVC在啟動(dòng)完成后執(zhí)行方法源碼解析

    這篇文章主要介紹了SpringMVC在啟動(dòng)完成后執(zhí)行方法源碼解析,還是非常不錯(cuò)的,在這里分享給大家,需要的朋友可以參考下。
    2017-09-09

最新評(píng)論