Detailed explanation of Java basic String

Written in front

String is the first thing to be learned in Java source code. Today, we need to recognize it from the source code point of view

1. Storage structure

See the mainstream JDK version 1.8. The internal actual storage structure of String is char array. The source code is as follows:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    //Other content

2. Common methods

2.1. Construction method

Among them, StringBuffer and StringBuilder are less used as constructors of parameters, but you should also know that

	/**
     * String Is the construction method of the parameter
     * @param  original
     *        A {@code String}
     */
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    /**
     * char[] Constructing methods for parameters
     * @param  value
     *         The initial value of the string
     */
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
    
	/**
     * StringBuffer Is the construction method of the parameter
     * @param  buffer
     *         A {@code StringBuffer}
     */
    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }

    /**
     * StringBuilder Is the construction method of the parameter
     * @param   builder
     *          A {@code StringBuilder}
     * @since  1.5
     */
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

2.2.equals()

In String, equals() is to compare whether the values of two strings are equal, and = = is to compare whether the references of strings are equal. Equals() overrides the Object method of the parent class, and the parameters are also of the Object type. In the method, it will be judged by instanceof. If it is of the String type, the next step will be carried out.

As mentioned here, equals() and = = in the Object parent class have the same effect on the reference type.

	/**
	* @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    public boolean equals(Object anObject) {
        if (this == anObject) {// The same object reference directly returns true
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])//Convert to character array, compare each character, there is a difference is fasle
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

There is a similar method called equalsIgnoreCase(String), which ignores case comparison and passes parameters of String type

2.3.compareTo()

compareTo() and equels() have similar processing methods, both of which are character comparison. The difference is that equels() returns true when comparing two strings, and false when comparing two strings. compareTo() returns 0 when comparing two strings, and other int type values when comparing two strings. And compareTo() can only receive String types.

There is also a method similar to compareTo(), compareToIgnoreCase(), which is used to compare two strings after ignoring case.

	/**
     * @param   anotherString   the {@code String} to be compared.
     * @return  the value {@code 0} if the argument string is equal to
     *          this string; a value less than {@code 0} if this string
     *          is lexicographically less than the string argument; and a
     *          value greater than {@code 0} if this string is
     *          lexicographically greater than the string argument.
     */
    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

2.4. Other important methods

  • indexOf(): the first occurrence of a query string
  • lastIndexOf(): the last subscript position of the query string
  • contains(): whether to include another string in the query string
  • toLowerCase(): converts all strings to lowercase
  • toUpperCase(): convert all strings to uppercase
  • length(): length of query string
  • trim(): remove the leading and trailing spaces from the string
  • replace(): replace some characters in the string
  • split(): splits a string and returns an array of strings
  • join(): convert string array to string

3. Common problems

3.1.String, StringBuilder and StringBuffer

String is immutable. Using string in string splicing will consume a lot of performance. Therefore, with StringBuilder and StringBuffer, they have two methods, append and insert, which can realize string splicing. The only difference is that StringBuffer uses synchronized to ensure thread safety

//The snippet of StringBuffer can be seen in the source code of StringBuffer class

	@Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }

    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    //Other......


    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public synchronized StringBuffer insert(int offset, Object obj) {
        toStringCache = null;
        super.insert(offset, String.valueOf(obj));
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public synchronized StringBuffer insert(int offset, String str) {
        toStringCache = null;
        super.insert(offset, str);
        return this;
    }
   //Other......
//The snippet of StringBuilder can be seen in the source code of StringBuilder class

 	@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
	//Other......


	/**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, Object obj) {
            super.insert(offset, obj);
            return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, String str) {
        super.insert(offset, str);
        return this;
    }
    //Other......

StringBuffer ensures thread safety, so the performance is not very high. StringBuilder is available in JDK 1.5

3.2. Why is string decorated with final

The first benefit of using final decorating is security; the second benefit is efficiency, such as

String s1 = "java";
String s2 = "java";

Only when the string is immutable can we realize the string constant pool, which can cache the string for us and improve the operation efficiency of the program, as shown in the following figure:

3.3. Storage in JVM

There are two common ways to create a string: String a1 = "java" and String a2 = new Strring("java"), but they are stored in different ways in memory. When creating a variable a1 in JDK 1.8, you will first find the string "java" from the constant pool, If there is a direct return, if not, create the string in the constant pool first and then return, and the variable a2 will be created directly in the heap memory, and the a2 call method intern() will save the string to the constant pool, for example

String a1 = "java";
String a2 = new Strring("java");
String a3 = "a2.intern();

System.out.println(a1 == a2); // false
System.out.println(a1 == a3); // true

The JVM storage location is shown in the figure PS: after JDK 1.7, the Metaspace replaced by immortality is moved from the method area to the Java heap.

End......

If there is anything wrong, please identify it. Thank you in advance

Tags: Java JDK less jvm

Posted on Sat, 14 Mar 2020 08:59:08 -0400 by ppatwari