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

一文帶你全面了解Java?Properties類

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

概述

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

介紹

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

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

構(gòu)造方法

Properties()

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

Properties(Properties defaults)

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

關(guān)鍵方法

getProperty ( String key)

根據(jù)指定的key獲取對應(yīng)的屬性value值,如果在自身的存儲(chǔ)集合中沒有找到對應(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鍵值對,要求所有的key/value鍵值對是按行存儲(chǔ),同時(shí)是用ISO-8859-1編譯的, 不支持中文。

load(Reader)

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

loadFromXML(InputStream)

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

setProperty ( String key, String value)

調(diào)用 Hashtable 的方法 put 。他通過調(diào)用基類的put方法來設(shè)置 鍵 - 值對。

store ( OutputStream out, String comments)

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

storeToXML(OutputSteam, comment, encoding)

寫出到xml文件中。

Set stringPropertyNames()

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

clear ()

清除所有裝載的 鍵值對。該方法在基類中提供。

使用案例

新建配置文件app.properties

## 用戶信息
user.name:旭陽
user.age=28
user.sex 男

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

驗(yà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屬性值,底層都是將流封裝成為LineReader對象,然后通過load0方法來加載屬性鍵值對的。

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對象
         * 
         * @param inStream
         *            屬性鍵值對對應(yīng)的字節(jié)流對象
         */
        public LineReader(InputStream inStream) {
            this.inStream = inStream;
            inByteBuf = new byte[8192];
        }

        /**
         * 根據(jù)字符流創(chuàng)建LineReader對象
         * 
         * @param reader
         *            屬性鍵值對對應(yīng)的字符流對象
         */
        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é)流對象
        InputStream inStream;
        // 字符流對象
        Reader reader;

        /**
         * 讀取一行,將行信息保存到{@link lineBuf}對象中,并返回實(shí)際的字符個(gè)數(shù)
         * 
         * @return 實(shí)際讀取的字符個(gè)數(shù)
         * @throws IOException
         */
        int readLine() throws IOException {
            // 總的字符長度
            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;
                    // 如果沒有讀取到數(shù)據(jù),那么就直接結(jié)束讀取操作
                    if (inLimit <= 0) {
                        // 如果當(dāng)前長度為0或者是改行是注釋,那么就返回-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è)字符是換行符號,那么判斷當(dāng)前字符是否也是換行符號
                if (skipLF) {
                    skipLF = false;
                    if (c == '\n') {
                        continue;
                    }
                }

                // 如果前一個(gè)字符是空格,那么判斷當(dāng)前字符是不是空格類字符
                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)前字符是不是換行符號進(jìn)行判斷操作
                if (c != '\n' && c != '\r') {
                    // 當(dāng)前字符不是換行符號
                    lineBuf[len++] = c;// 將當(dāng)前字符寫入到行信息緩沖區(qū)中,并將len自增加1.
                    // 如果len的長度大于行信息緩沖區(qū)的大小,那么對lineBuf進(jìn)行擴(kuò)容,擴(kuò)容大小為原來的兩倍,最大為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)前長度為0,那么進(jìn)行clean操作。
                        isCommentLine = false;
                        isNewLine = true;
                        skipWhiteSpace = true;
                        len = 0;
                        continue;
                    }
                    // 如果已經(jīng)沒有數(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ù);如果我們想在多行寫數(shù)據(jù),那么可以使用''來進(jìn)行轉(zhuǎn)義,在該轉(zhuǎn)義符號后面換行,是被允許的。

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的長度
            keyLen = 0;
            // value的起始位置默認(rèn)為limit
            valueStart = limit;
            // 
            hasSep = false;
            precedingBackslash = false;

            // 如果key的長度小于總的字符長度,那么就進(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)前字符是空格類字符,而且前一個(gè)字符不是轉(zhuǎn)義字符,那么表示key的描述已經(jīng)結(jié)束
                    // 指定value的起始位置為當(dāng)前位置的下一個(gè)位置
                    valueStart = keyLen + 1;
                    break;
                }
                // 如果當(dāng)前字符為'',那么跟新是否是轉(zhuǎn)義號。
                if (c == '\') {
                    precedingBackslash = !precedingBackslash;
                } else {
                    precedingBackslash = false;
                }
                keyLen++;
            }

            // 如果value的起始位置小于總的字符長度,那么就進(jìn)入該循環(huán)
            while (valueStart < limit) {
                // 獲取當(dāng)前字符
                c = lr.lineBuf[valueStart];
                // 判斷當(dāng)前字符是否是空格類字符,達(dá)到去空格的效果
                if (c != ' ' && c != '\t' && c != '\f') {
                    // 當(dāng)前字符不是空格類字符,而且當(dāng)前字符為=或者是:,并在此之前沒有出現(xiàn)過=或者:字符。
                    // 那么value的起始位置繼續(xù)往后移動(dòng)。
                    if (!hasSep && (c == '=' || c == ':')) {
                        hasSep = true;
                    } else {
                        // 當(dāng)前字符不是=或者:,或者在此之前出現(xiàn)過=或者:字符。那么結(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ì)將分割符號兩邊的空格去掉,并且分割符號可以是=,:,空格等。而且=和:的級別比空格分隔符高,即當(dāng)這兩個(gè)都存在的情況下,是按照=/:分割的??梢钥吹皆谧詈髸?huì)調(diào)用一個(gè)loadConvert方法,該方法主要是做key/value的讀取,并將十六進(jìn)制的字符進(jìn)行轉(zhuǎn)換。

總結(jié)

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

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

相關(guān)文章

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

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

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

    Spring?question問題小結(jié)

    在AppConfig配置類中,通過@Bean注解創(chuàng)建了Service和Controller的實(shí)例,Spring會(huì)自動(dòng)將這些實(shí)例納入容器的管理,并處理它們之間的依賴關(guān)系,本文給大家介紹Spring?question問題小結(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組件,通過一些校驗(yàn)器,可以對一些數(shù)據(jù)進(jìn)行統(tǒng)一的完整性和有效性等校驗(yàn),即簡單又好用
    2021-06-06
  • 詳解springmvc 中controller與jsp傳值

    詳解springmvc 中controller與jsp傳值

    本篇文章主要介紹了springmvc 中controller與jsp傳值,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    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對IOC的理解(推薦篇)

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

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

    Java多線程的用法詳解

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

    SpringBoot中加密模塊的使用

    本文主要介紹了SpringBoot中加密模塊的使用,包括對稱加密、非對稱加密和哈希加密等,同時(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

最新評論