国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
一、StringBuilder
1.StringBuilder的append操作與擴容
2.StringBuilder的subString()方法toString()方法
3.StringBuilder的其它方法
二、StringBuffer
三、 總結
首頁 Java Java基礎 溫故知新(二)深入認識Java中的字符串

溫故知新(二)深入認識Java中的字符串

Sep 19, 2020 am 09:36 AM
java 字符串

溫故知新(二)深入認識Java中的字符串

相關學習推薦:java基礎教程

上篇文章我們深入分析了String的內存和它的一些特性。本篇文章我們深入的來分析一下與String相關的另外兩個類,它們分別是StringBuilder和StringBuffer。這兩個類與String有什么關系呢?首先我們看下下邊這張類圖:

溫故知新(二)深入認識Java中的字符串

從圖中可以看出StringBuilder和StringBuffer都繼承了AbstractStringBuilder,而AbstractStringBuilder與String實現了共同的接口CharSequence。

我們知道,字符串是由一系列字符組成的,String的內部就是基于char數組(jdk9之后基于byte數組)實現的,而數組通常是一塊連續(xù)的內存區(qū)域,在數組初始化的時候就需要指定數組的大小。上一篇文章中我們已經知道String是不可變的,因為它內部的數組被聲明為了final,同時,String的字符拼接、插入、刪除等操作均是通過實例化新的對象實現的。而今天要認識的StringBuilder和StringBuffer與String相比就具有了動態(tài)性。接下來就讓我們一起來認識下這兩個類。

一、StringBuilder

在StringBuilder的父類AbstractStringBuilder 中可以看到如下代碼:

abstract class AbstractStringBuilder implements Appendable, CharSequence {    /**
     * The value is used for character storage.
     */
    char[] value;    /**
     * The count is the number of characters used.
     */
    int count;
}復制代碼

StringBuilder與String一樣都是基于char數組實現的,不同的是StringBuilder沒有final修飾,這就意味著StringBuilder是可以被動態(tài)改變的。接下來看下StringBuilder無參構造方法,代碼如下:

 /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {        super(16);
    }復制代碼

在這個方法中調用了父類的構造方法,到AbstractStringBuilder 中看到其構造方法如下:

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }復制代碼

AbstractStringBuilder的構造方法內部初始化了一個容量為capacity的數組。也就是說StringBuilder默認初始化了一個容量為16的char[]數組。StringBuilder中除了無參構造外還提供了多個構造方法,源碼如下:

 /**
     * Constructs a string builder with no characters in it and an
     * initial capacity specified by the {@code capacity} argument.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {        super(capacity);
    }    /**
     * Constructs a string builder initialized to the contents of the
     * specified string. The initial capacity of the string builder is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuilder(String str) {        super(str.length() + 16);
        append(str);
    }    /**
     * Constructs a string builder that contains the same characters
     * as the specified {@code CharSequence}. The initial capacity of
     * the string builder is {@code 16} plus the length of the
     * {@code CharSequence} argument.
     *
     * @param      seq   the sequence to copy.
     */
    public StringBuilder(CharSequence seq) {        this(seq.length() + 16);
        append(seq);
    }復制代碼

這段代碼的第一個方法初始化一個指定容量大小的StringBuilder。另外兩個構造方法分別可以傳入String和CharSequence來初始化StringBuilder,這兩個構造方法的容量均會在傳入字符串長度的基礎上在加上16。

1.StringBuilder的append操作與擴容

上篇文章已經知道通過StringBuilder的append方法可以進行高效的字符串拼接,append方法是如何實現的呢?這里以append(String)為例,可以看到StringBuilder的append調用了父類的append方法,其實不止append,StringBuilder類中操作字符串的方法幾乎都是通過父類來實現的。append方法源碼如下:

    // StringBuilder
    @Override
    public StringBuilder append(String str) {        super.append(str);        return this;
    }    
  // AbstractStringBuilder
  public AbstractStringBuilder append(String str) {        if (str == null)            return appendNull();        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;        return this;
    }復制代碼

在append方法的第一行首先進行了null檢查,等于null的時候調用了appendNull方法。其源碼如下:

private AbstractStringBuilder appendNull() {        int c = count;
        ensureCapacityInternal(c + 4);        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;        return this;
    }復制代碼

appendNull方法中首先調用了ensureCapacityInternal來確保字符串數組容量充值,關于ensureCapacityInternal這個方法下邊再詳細分析。接下來可以看到把"null"的字符添加到了char[]數組value中。

上文我們提到,StringBuilder內部數組的默認容量是16,因此,在進行字符串拼接的時候需要先確保char[]數組有足夠的容量。因此,在appendNull方法以及append方法中都調用了ensureCapacityInternal方法來檢查char[]數組是否有足夠的容量,如果容量不足則會對數組進行擴容,ensureCapacityInternal源碼如下:

private void ensureCapacityInternal(int minimumCapacity) {        // overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }復制代碼

這里判讀如果拼接后的字符串長度大于字符串數組的長度則會調用expandCapacity進行擴容。

void expandCapacity(int minimumCapacity) {        int newCapacity = value.length * 2 + 2;        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;        if (newCapacity < 0) {            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }復制代碼

expandCapacity的邏輯也很簡單,首先通過原數組的長度乘2并加2后計算得到擴容后的數組長度。接下來判斷了newCapacity如果小于minimumCapacity,則將minimumCapacity值賦值給了newCapacity。這里因為調用expandCapacity方法的不止一個地方,所以加這句代碼確保安全。

而接下來的一句代碼就很有趣了,newCapacity 和minimumCapacity 還有可能小于0嗎?當minimumCapacity小于0的時候竟然還拋出了一個OutOfMemoryError異常。其實,這里小于0是因為越界了。我們知道在計算機中存儲的都是二進制,乘2相當于向左移了一位。以byte為例,一個byte有8bit,在有符號數中最左邊的一個bit位是符號位,正數的符號位為0,負數為1。那么一個byte可以表示的大小范圍為[-128~127],而如果一個數字大于127時則會出現越界,即最左邊的符號位會被左邊第二位的1頂替,就出現了負數的情況。當然,并不是byte而是int,但是原理是一樣的。

另外在這個方法的最后一句通過Arrays.copyOf進行了一個數組拷貝,其實Arrays.copyOf在上篇文章中就有見到過,在這里不妨來分析一下這個方法,看源碼:

 public static char[] copyOf(char[] original, int newLength) {        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));        return copy;
    }復制代碼

咦?copyOf方法中竟然也去實例化了一個對象??!那不會影響性能嗎?莫慌,看一下這里僅僅是實例化了一個newLength長度的空數組,對于數組的初始化其實僅僅是指針的移動而已,浪費的性能可謂微乎其微。接著這里通過System.arraycopy的native方法將原數組復制到了新的數組中。

2.StringBuilder的subString()方法toString()方法

StringBuilder中其實沒有subString方法,subString的實現是在StringBuilder的父類AbstractStringBuilder中的。它的代碼非常簡單,源碼如下:

public String substring(int start, int end) {        if (start < 0)            throw new StringIndexOutOfBoundsException(start);        if (end > count)            throw new StringIndexOutOfBoundsException(end);        if (start > end)            throw new StringIndexOutOfBoundsException(end - start);        return new String(value, start, end - start);
    }復制代碼

在進行了合法判斷之后,substring直接實例化了一個String對象并返回。這里和String的subString實現其實并沒有多大差別。 而StringBuilder的toString方法的實現其實更簡單,源碼如下:

 @Override
    public String toString() {        // Create a copy, don&#39;t share the array
        return new String(value, 0, count);
    }復制代碼

這里直接實例化了一個String對象并將StringBuilder中的value傳入,我們來看下String(value, 0, count)這個構造方法:

    public String(char value[], int offset, int count) {        if (offset < 0) {            throw new StringIndexOutOfBoundsException(offset);
        }        if (count < 0) {            throw new StringIndexOutOfBoundsException(count);
        }        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {            throw new StringIndexOutOfBoundsException(offset + count);
        }        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }復制代碼

可以看到,在String的這個構造方法中又通過Arrays.copyOfRange方法進行了數組拷貝,Arrays.copyOfRange的源碼如下:

   public static char[] copyOfRange(char[] original, int from, int to) {        int newLength = to - from;        if (newLength < 0)            throw new IllegalArgumentException(from + " > " + to);        char[] copy = new char[newLength];
        System.arraycopy(original, from, copy, 0,
                         Math.min(original.length - from, newLength));        return copy;
    }復制代碼

Arrays.copyOfRange與Arrays.copyOf類似,內部都是重新實例化了一個char[]數組,所以String構造方法中的this.value與傳入進來的value不是同一個對象。意味著StringBuilder在每次調用toString的時候生成的String對象內部的char[]數組并不是同一個!這里立一個Falg!

3.StringBuilder的其它方法

StringBuilder除了提供了append方法、subString方法以及toString方法外還提供了還提供了插入(insert)、刪除(delete、deleteCharAt)、替換(replace)、查找(indexOf)以及反轉(reverse)等一些列的字符串操作的方法。但由于實現都非常簡單,這里就不再贅述了。

二、StringBuffer

在第一節(jié)已經知道,StringBuilder的方法幾乎都是在它的父類AbstractStringBuilder中實現的。而StringBuffer同樣繼承了AbstractStringBuilder,這就意味著StringBuffer的功能其實跟StringBuilder并無太大差別。我們通過StringBuffer幾個方法來看

     /**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient char[] toStringCache;    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;        super.append(str);        return this;
    }    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     * @since      1.2
     */
    @Override
    public synchronized StringBuffer delete(int start, int end) {
        toStringCache = null;        super.delete(start, end);        return this;
    }  /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     * @since      1.2
     */
    @Override
    public synchronized StringBuffer insert(int index, char[] str, int offset,                                            int len)
    {
        toStringCache = null;        super.insert(index, str, offset, len);        return this;
    }@Override
    public synchronized String substring(int start) {        return substring(start, count);
    }    
// ...復制代碼

可以看到在StringBuffer的方法上都加上了synchronized關鍵字,也就是說StringBuffer的所有操作都是線程安全的。所以,在多線程操作字符串的情況下應該首選StringBuffer。 另外,我們注意到在StringBuffer的方法中比StringBuilder多了一個toStringCache的成員變量 ,從源碼中看到toStringCache是一個char[]數組。它的注釋是這樣描述的:

toString返回的最后一個值的緩存,當StringBuffer被修改的時候該值都會被清除。

我們再觀察一下StringBuffer中的方法,發(fā)現只要是操作過操作過StringBuffer中char[]數組的方法,toStringCache都被置空了!而沒有操作過字符數組的方法則沒有對其做置空操作。另外,注釋中還提到了 toString方法,那我們不妨來看一看StringBuffer中的 toString,源碼如下:

   @Override
    public synchronized String toString() {        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }        return new String(toStringCache, true);
    }復制代碼

這個方法中首先判斷當toStringCache 為null時會通過 Arrays.copyOfRange方法對其進行賦值,Arrays.copyOfRange方法上邊已經分析過了,他會重新實例化一個char[]數組,并將原數組賦值到新數組中。這樣做有什么影響呢?細細思考一下不難發(fā)現在不修改StringBuffer的前提下,多次調用StringBuffer的toString方法,生成的String對象都共用了同一個字符數組--toStringCache。這里是StringBuffer和StringBuilder的一點區(qū)別。至于StringBuffer中為什么這么做其實并沒有很明確的原因,可以參考StackOverRun 《Why StringBuffer has a toStringCache while StringBuilder not?》中的一個回答:

1.因為StringBuffer已經保證了線程安全,所以更容易實現緩存(StringBuilder線程不安全的情況下需要不斷同步toStringCache) 2.可能是歷史原因

三、 總結

本篇文章到此就結束了?!渡钊肜斫釰ava中的字符串》通過兩篇文章深入的分析了String、StringBuilder與StringBuffer三個字符串相關類。這塊內容其實非常簡單,只要花一點時間去讀一下源碼就很容易理解。當然,如果你沒看過此部分源碼相信這篇文章能夠幫助到你。不管怎樣,相信大家通過閱讀本文還是能有一些收獲。解了這些知識后可以幫助我們在開發(fā)中對字符串的選用做出更好的選擇。同時,這塊內容也是面試???,相信大家讀完本文去應對面試官的問題也會綽綽有余。

想了解更多編程學習,敬請關注php培訓欄目!

以上是溫故知新(二)深入認識Java中的字符串的詳細內容。更多信息請關注PHP中文網其他相關文章!

本站聲明
本文內容由網友自發(fā)貢獻,版權歸原作者所有,本站不承擔相應法律責任。如您發(fā)現有涉嫌抄襲侵權的內容,請聯系admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

人工智能驅動的應用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機

Video Face Swap

Video Face Swap

使用我們完全免費的人工智能換臉工具輕松在任何視頻中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級代碼編輯軟件(SublimeText3)

熱門話題

Laravel 教程
1601
29
PHP教程
1502
276
如何使用JDBC處理Java的交易? 如何使用JDBC處理Java的交易? Aug 02, 2025 pm 12:29 PM

要正確處理JDBC事務,必須先關閉自動提交模式,再執(zhí)行多個操作,最后根據結果提交或回滾;1.調用conn.setAutoCommit(false)以開始事務;2.執(zhí)行多個SQL操作,如INSERT和UPDATE;3.若所有操作成功則調用conn.commit(),若發(fā)生異常則調用conn.rollback()確保數據一致性;同時應使用try-with-resources管理資源,妥善處理異常并關閉連接,避免連接泄漏;此外建議使用連接池、設置保存點實現部分回滾,并保持事務盡可能短以提升性能。

如何使用Java的日歷? 如何使用Java的日歷? Aug 02, 2025 am 02:38 AM

使用java.time包中的類替代舊的Date和Calendar類;2.通過LocalDate、LocalDateTime和LocalTime獲取當前日期時間;3.使用of()方法創(chuàng)建特定日期時間;4.利用plus/minus方法不可變地增減時間;5.使用ZonedDateTime和ZoneId處理時區(qū);6.通過DateTimeFormatter格式化和解析日期字符串;7.必要時通過Instant與舊日期類型兼容;現代Java中日期處理應優(yōu)先使用java.timeAPI,它提供了清晰、不可變且線

比較Java框架:Spring Boot vs Quarkus vs Micronaut 比較Java框架:Spring Boot vs Quarkus vs Micronaut Aug 04, 2025 pm 12:48 PM

前形式攝取,quarkusandmicronautleaddueTocile timeProcessingandGraalvSupport,withquarkusoftenpernperforminglightbetterine nosserless notelless centarios.2。

了解網絡端口和防火墻 了解網絡端口和防火墻 Aug 01, 2025 am 06:40 AM

NetworkPortSandFireWallsworkTogetHertoEnableCommunication whereSeringSecurity.1.NetWorkPortSareVirtualendPointSnumbered0-655 35,with-Well-with-Newonportslike80(HTTP),443(https),22(SSH)和25(smtp)sindiessingspefificservices.2.portsoperateervertcp(可靠,c

垃圾收集如何在Java工作? 垃圾收集如何在Java工作? Aug 02, 2025 pm 01:55 PM

Java的垃圾回收(GC)是自動管理內存的機制,通過回收不可達對象釋放堆內存,減少內存泄漏風險。1.GC從根對象(如棧變量、活動線程、靜態(tài)字段等)出發(fā)判斷對象可達性,無法到達的對象被標記為垃圾。2.基于標記-清除算法,標記所有可達對象,清除未標記對象。3.采用分代收集策略:新生代(Eden、S0、S1)頻繁執(zhí)行MinorGC;老年代執(zhí)行較少但耗時較長的MajorGC;Metaspace存儲類元數據。4.JVM提供多種GC器:SerialGC適用于小型應用;ParallelGC提升吞吐量;CMS降

比較Java構建工具:Maven vs. Gradle 比較Java構建工具:Maven vs. Gradle Aug 03, 2025 pm 01:36 PM

Gradleisthebetterchoiceformostnewprojectsduetoitssuperiorflexibility,performance,andmoderntoolingsupport.1.Gradle’sGroovy/KotlinDSLismoreconciseandexpressivethanMaven’sverboseXML.2.GradleoutperformsMaveninbuildspeedwithincrementalcompilation,buildcac

以身作則,解釋說明 以身作則,解釋說明 Aug 02, 2025 am 06:26 AM

defer用于在函數返回前執(zhí)行指定操作,如清理資源;參數在defer時立即求值,函數按后進先出(LIFO)順序執(zhí)行;1.多個defer按聲明逆序執(zhí)行;2.常用于文件關閉等安全清理;3.可修改命名返回值;4.即使發(fā)生panic也會執(zhí)行,適合用于recover;5.避免在循環(huán)中濫用defer,防止資源泄漏;正確使用可提升代碼安全性和可讀性。

使用HTML'輸入類型”作為用戶數據 使用HTML'輸入類型”作為用戶數據 Aug 03, 2025 am 11:07 AM

選擇合適的HTMLinput類型能提升數據準確性、增強用戶體驗并提高可用性。1.根據數據類型選用對應input類型,如text、email、tel、number和date,可實現自動校驗和適配鍵盤;2.利用HTML5新增類型如url、color、range和search,可提供更直觀的交互方式;3.配合使用placeholder和required屬性,可提升表單填寫效率和正確率,但需注意placeholder不能替代label。

See all articles