JDKとStringBuilderファミリーの3つの巨人の興味深いソースコード

AbstractStringBuilder

これは、もともとjdk1.5で登場した可変文字シーケンスクラスの抽象クラスです。

歴史

Javaの最も一般的に使用される文字データ型がStirngオブジェクトであることはよく知られていますが、Stringオブジェクトは定数クラス(内部メンテナンスの最終文字[])として設計されており、変更するたびに新しいオブジェクトが生成されます。文字値を頻繁に変更する必要があるシナリオでは、システムリソースが無駄になります。この問題を解決するために、可変文字のオブジェクトファミリが誕生しました。(StringBuffer + StringBuilder)そして、AbstractStringBuilder 彼らの父親になりました。
」という言葉が太字になっているのはなぜですか?

  • 最初は表示さによるものであるのStringBufferに生まれたjdk.10に(超自然的なイベント、彼はまだ父親に生まれていないこの時間)、彼はスレッドセーフなクラスです。その後、私たちのスマートJava開発チームは、ほとんどのアプリケーションで、ほとんどのシナリオでスレッドセーフが必要ないこと発見しました。
  • したがって、jdk1.5では、小さな父親AbstractStringBuilder(いくつかのパブリックメソッドをカプセル化)と小さな兄弟StringBuilder(非スレッドセーフクラス)が誕生しました。

クラス図

ここに画像の説明を挿入

abstract class AbstractStringBuilder implements Appendable, CharSequence {

    char[] value;

    int count;
}

AbstractStringBuilderは2つのインターフェースを実装します。彼の2人のゴッドファーザーを簡単に紹介しましょう

  • CharSequenceは読み取り可能なシーケンスインターフェイスであり、stringもそれを実装します。彼がJavaの文字型オブジェクトの基本メソッドを制御していることを理解できます(int length(); char charAt(int index); public String toString();など)。
  • 追記
    追加される文字列クラス・インターフェース、我々は理解することができたCharSequenceはキャラクターの動作を調整、文字が追記指定操作を追加し jdk8の遮断のために、それは方法を提供し、彼のオーバーロードされたメソッドを追加します

コアメンバー

  • char [] value;
    これは、文字型を格納するために使用される値の配列をよく理解しています
  • int count;
    これは、char []文字の長さを示すために使用されます。問題は、文字の長さを表すためにchar配列のvalue.lengthを使用しないのはなぜですか?質問を見下ろしましょう

一般的な方法

   /**
   * 追加字符串 str
   **/
   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;
   }

   /**
   * char数组扩容
   * @param minimumCapacity 期望的最小数组长度
   */
   private void ensureCapacityInternal(int minimumCapacity) {
        if (minimumCapacity - value.length > 0) {
            // 当前长度小于最小期望 进行扩容
            value = Arrays.copyOf(
                    value,
                    newCapacity(minimumCapacity) // 新的数组长度由 newCapacity方法产生
            ); // 产生新的数组
        }
    }
    
    /**
     * @param  minCapacity 最小期望长度
     */
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        // 先取当前数组长度 乘2在加2, “为了减少扩容次”数每次扩容长度最起码都要翻倍(不然每次都扩容的话不久和String一样了)。
        // 为什么是成“二再加二”? 不知道!有人知道么?
        // jdk注解很少告诉我为什么,但应该是经过思考的选择. 有兴趣可以深究一下
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity; // 上一步操作后还不满足 最小期望,那就使用最小期望值来作为新的数组长度
        }

        // 当新长度, 小于零或者 大于数组最大长度(MAX_ARRAY_SIZE)的时候,交给hugeCapacity方法选择新数组长度
        // MAX_ARRAY_SIZE 有意思了 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        // 为什么最大长度等于 int最大长度 - 8 呢?
        // java中外定义数组时传入数组长度参数类型为int所以无法定义超过Integer.MAX_VALUE长度的数组(编译时就会报错)
        // 那为什么要 - 8 呢 ?
        // jdk注释是这么写的:
        // Some VMs reserve some header words in an array.
        // Attempts to allocate larger arrays may result in
        // OutOfMemoryError
        // 一些虚拟机在数组中保留一些空间,尝试分配较大的数组可能会导致内存溢出错误! 所以减了个8。
        // 不过注意 当程序尝试分配的大小 在 MAX_ARRAY_SIZE 》 Integer.MAX_VALUE 之间时 hugeCapacity方法还是会返回期望值。否则返回 MAX_ARRAY_SIZE 或者抛出异常
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }
    
    /**
     * 最小期望长度 大于 Integer.MAX_VALUE 最大值时抛出异常
     * @param minCapacity 最小期望长度
     * @return 返回 MAX_ARRAY_SIZE(数组最大长度) 或者  返回minCapacity( minCapacity > MAX_ARRAY_SIZE)
     */
    private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }
    
    /**
    * 这个方法还是留着子类自己去实现了
    */
    @Override
    public abstract String toString();

それらのほとんどを体系的な組織のためにここのコメントで説明してください

上記の質問を整理する

  • char配列のvalue.lengthを使用して文字の長さを表現しないのはなぜですか?
    上記のソースコードとコメントを読むと、char []の各展開がnewCapacityメソッドによって決定されることがわかります。文字列が操作されるたびにchar配列が拡張されないようにするメカニズムがあり、拡張されるたびに、より多くの空き領域を開こうとします。次に文字を変更するときは、最初に拡張に​​十分でない空き領域を使用するため、配列の長さは実際のコンテンツの長さ以上であり、次に実際のコンテンツの長さを記録するために別のフィールドが必要です。

拡大質問:一般的に使用されているHotSpot Java仮想マシンの配列の最大長はいくつですか?

このコードを実行する

   public static void main(String[] args) {
        int i = Integer.MAX_VALUE;
        while (true) {
            try {
                System.out.println(new char[i].length);
            } catch (OutOfMemoryError e) {
                i--;
                e.printStackTrace();
                // 异常继续
            }
            System.out.println("数组最大长度 十进制:" + i);
            System.out.println("数组最大长度 二进制:" + Integer.toBinaryString(i));
            System.out.println("数组最大长度 二进制位数:" + Integer.toBinaryString(i).length());
            return;
        }
    }

どんな内容が入りますか?

java.lang.OutOfMemoryError: Requested array size exceeds VM limit
	at com.tlong.TestAbstracString.main(TestAbstracString.java:10)
数组最大长度 十进制:2147483646
数组最大长度 二进制:1111111111111111111111111111110
数组最大长度 二进制位数:31

理論的には、配列の最大長は2 ^ 31である必要があります
  • 最大31ビットのバイナリ
  • javaは、配列オブジェクトのヘッダー情報で配列の長さを記録するために使用されるスペースが32ビットであることを規定していますが、Javaのintのサイズは4バイトと32ビットで、そのうちの1つは符号を示します。したがって、int正の整数の最大値は2 ^ 31です。
実際、HotSpot1.8での配列の最大長は(2 ^ 31-1)です。
  • コメントで言及されている一部の仮想マシンは配列の一部のスペースを予約しているため、より大きな配列を割り当てようとすると、メモリオーバーフローエラーが発生する可能性があります
元の記事を17件公開 24 件を獲得 28万回以上表示

おすすめ

転載: blog.csdn.net/qq_22956867/article/details/99477637