Código fuente interesante de los tres gigantes de la familia JDK y StringBuilder

AbstractStringBuilder

Es una clase abstracta de secuencia de caracteres variable, originalmente apareció en jdk1.5.

Historia

Es bien sabido que el tipo de datos de caracteres más utilizado de Java es el objeto Stirng , pero el objeto String está diseñado como una clase constante (mantenimiento interno final char []), y cada cambio producirá un nuevo objeto. En escenarios donde los valores de los caracteres deben cambiarse con frecuencia, se desperdician recursos del sistema. Para resolver este problema, nació una familia de objetos de caracteres variables. (StringBuffer + StringBuilder) Y AbstractStringBuilder ahora es su padre.
¿Por qué está en negrita la palabra " ahora "?

  • Debido a que lo primero que apareció fue StringBuffer, que nació en jdk.10 (aún no había nacido en este momento cuando era un evento extraño ), es una clase segura para subprocesos. Más tarde, nuestro equipo inteligente de desarrollo de Java descubrió que en la mayoría de las aplicaciones, la seguridad de subprocesos no es necesaria en la mayoría de los escenarios.
  • Entonces, en jdk1.5, nacieron el pequeño padre AbstractStringBuilder (que encapsula algunos métodos públicos) y el hermano pequeño StringBuilder (clase no segura para subprocesos)

Diagrama de clase

Inserte la descripción de la imagen aquí

abstract class AbstractStringBuilder implements Appendable, CharSequence {

    char[] value;

    int count;
}

AbstractStringBuilder implementa dos interfaces. Vamos a presentar brevemente a sus dos padrinos.

  • CharSequence es una interfaz de secuencia legible, y string también la implementa. Podemos entender que regula los métodos básicos de los objetos de tipo de caracteres en java (int length (); char charAt (int index); public String toString (); etc.)
  • Anexables
    carácter interfaz de clase secuencias de ser añadido, podemos entender CharSequence a regular el funcionamiento del carácter, se añade el carácter operación especificación Anexables para corte del jdk8 se proporciona un método y anexar sus métodos sobrecargados

Miembro principal

  • valor char [];
    esta es una buena comprensión de la matriz de valores utilizada para almacenar tipos de caracteres
  • int count;
    Esto se usa para indicar la longitud de los caracteres char []. La pregunta es: ¿por qué no usar el value.length de la matriz de caracteres para representar la longitud del carácter? Miremos hacia abajo con preguntas

Métodos comunes

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

Explique la mayoría de ellos en los comentarios aquí para una organización sistemática

Organiza las preguntas anteriores

  • ¿Por qué no usar el value.length de la matriz de caracteres para representar la longitud de los caracteres?
    Después de leer el código fuente y los comentarios anteriores, puede saber que cada expansión de char [] está determinada por el método newCapacity. Hay un mecanismo para evitar que la matriz de caracteres se expanda cada vez que se manipula la cadena, y cada vez que se expande, intenta abrir más espacio libre. La próxima vez que cambie el carácter, primero usará el espacio libre que no es suficiente para la expansión, por lo que la longitud de la matriz es mayor o igual que la longitud real del contenido, entonces necesita un campo separado para registrar la longitud real del contenido.

Una pregunta extendida: ¿Cuál es la longitud máxima de la matriz en nuestra máquina virtual Java HotSpot comúnmente utilizada?

Ejecute este código

   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;
        }
    }

¿Qué tipo de contenido se ingresará?

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

Teóricamente, la longitud máxima de la matriz debería ser 2 ^ 31
  • Máximo binario de 31 bits
  • Aunque Java estipula que el espacio utilizado para registrar la longitud de la matriz en la información del encabezado del objeto de matriz es de 32 bits, pero. El tamaño de int en Java es de 4 bytes y 32 bits, uno de los cuales indica el signo. Entonces, el valor máximo de entero positivo int es 2 ^ 31
De hecho, la longitud máxima de la matriz en HotSpot1.8 es (2 ^ 31-1)
  • Como algunas de las máquinas virtuales mencionadas en los comentarios reservan algo de espacio en la matriz, intentar asignar una matriz más grande puede causar un error de desbordamiento de memoria
17 artículos originales publicados · ganó 24 · vistas 280,000 +

Supongo que te gusta

Origin blog.csdn.net/qq_22956867/article/details/99477637
Recomendado
Clasificación