[explain Java interview questions with pictures and text] - String, StringBuilder, StringBuffer, Exception, Error

Posted by jemgames on Tue, 18 Jan 2022 09:46:56 +0100

Differences among String, StringBuilder and StringBuffer

Know what it is and why. We want to answer the basic characteristics of String.

String

  • String is Java A class under Lang package
  • Modified by the final keyword and cannot be inherited
  • The internal data structure of JDK8 is char[] value modified by final, and JDK9 is Byte[] value array;
  • Its modification and addition operations will lead to the creation of a new string object in the string constant pool in the heap;
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
   	//JDK8 uses string array to store values;
    private final char value[];
    private int hash; // Default to 0

String depth description

 public static void main(String[] args) {
        String s="123456";
        System.out.println("vlaue="+s);
        s="abcde";
        
    }

  1. Execute s = "123456";

    s refers to "123456" in the string constant pool;
  2. Execute s = "abcde"

    Due to the immutability of String, the JVM will create a memory to store the new String in the String constant pool, and then store the new String in it. s points to the memory address of the new String;
  • Is String really immutable?
//Create the string "Hello World" and assign it to the reference s
String s = "Hello World"; 
System.out.println("s = " + s);

//Gets the value field in the String class
Field valueFieldOfString = String.class.getDeclaredField("value");
//Change access to the value attribute
valueFieldOfString.setAccessible(true);

//Gets the value of the value attribute on the s object
char[] value = (char[]) valueFieldOfString.get(s);
//Change the fifth character in the array referenced by value
value[5] = '_';
System.out.println("s = " + s);

Get the String array value attribute of String through reflection technology, and dynamically modify the value of String array, so as to modify the value of String.

StringBuilder and StringBuffer

  • Are modified by the final keyword and cannot be inherited;
  • The implementation principle is based on the modifiable char array, with a default size of 16
  • Both inherit from AbstractStringBuilder
  • StringBuffer thread safe, StringBuilder non thread safe
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    static final long serialVersionUID = 4383685877147921099L;

    public StringBuilder() {
        super(16);
    }
}
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    static final long serialVersionUID = 3388685877147921107L;

    public StringBuffer() {
        super(16);
    }
}
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;
	//...
}

When StringBuilder and StringBuffer are instantiated, the data is stored in the character array value of AbstractStringBuilder;

  • StringBuffer source code: all its internal methods are decorated with synchronized to ensure thread safety, but StringBuilder does not.
	// StringBuffer source code
    @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;
    }

Modification of both

StringBuffer stb = new StringBuffer (8).append("123456");
stb.append("78910");


First, create a character array with a length of 8, add "123456" to this array, and there are two remaining positions;
When appending ("78910"), the JVM first determines whether the capacity is sufficient. If not, it creates a memory with a larger capacity in the heap again, and then copies the original string array to the new memory. Of course, if the capacity is enough, you can put data into this array without opening up new memory space again.

Application scenario

  • String
    In business scenarios where the String content does not change frequently, the String class is preferred;
    For example: constant declaration, a small number of string splicing operations, etc
  • StringBuffer
    In a multithreaded environment, string operations (splicing, replacement, deletion, etc.) are performed frequently.
    For example, XML parsing, HTTP parameter parsing and encapsulation.
  • StringBuilder
    Frequently perform string operations (such as splicing, replacement, deletion, etc.) and run in a single threaded environment
    For example, SQL statement assembly, JSON encapsulation, etc.

Exception,Error

What are exceptions and errors

  • Exception usually refers to the possible exceptions of our program, which can be divided into checkable exceptions and runtime exceptions.
  • You can check for exceptions that will be prompted when we write programs, such as IOException.
  • Runtime exceptions will not be prompted when we write the program, but only appear during the running process, such as
    RuntimeException,NullPointerException.
  • Error refers to system level errors, such as JVM crash, stack overflow, etc; For example, OutofMemoryError exception and StackOverflowError error error.

-Both Exception and Error inherit the Throwable class.

Exception handling principle

  • Try to catch specific exceptions instead of general exceptions
    Don't use Exception directly when catching exceptions in general. It's best to be specific to specific exceptions;
  • Don't swallow exceptions raw
    After the exception is caught, no substantive processing is done.
  • Capture only the necessary code snippets
  • Do not use exception handling blocks to control code flow

Why is the code in finally bound to execute?

When the JVM runs the bytecode file, the code in finally will be executed before return; This ensures that finally code blocks will be executed;

Procedure 1

 public static void main(String[] args) throws IllegalAccessException {
        System.out.println(test10());
    }

    private static int test10() {
        int i = 0;
        try{
            i++;
            System.out.println("try block, i = "+i);
            return i;
        }catch(Exception e){ //NumberFormatException
        //Here are two questions: 1 Catch exceptions in general and use Exception directly
        //2. Abnormal endocytosis without substantive treatment;
            i++;
            System.out.println("catch block i = "+i);
            return i;
        }finally{
            i++;
            System.out.println("finally block i = "+i);
        }

    }
Operation results:
try block, i = 1
finally block i = 2
1

  • Why did the return value last print 1?
  • Take a look at the decompiled code first
 public static void main(String[] args) throws IllegalAccessException {
        System.out.println(test10());
    }

    private static int test10() {
        int i = 0;

        int var2;
        try {
            ++i;
            System.out.println("try block, i = " + i);
            int var1 = i;
            return var1;
        } catch (Exception var6) {
            ++i;
            System.out.println("catch block i = " + i);
            var2 = i;
        } finally {
            ++i;
            System.out.println("finally block i = " + i);
        }

        return var2;
    }

A new variable var1 is used in try to store the value of variable i. The value of var1 is 1,
The finally code block is an operation on i, not var1, and the operation on i is after the var1 assignment of the try code block and before return, so the returned i value is actually the value of var1, which is actually 1, not 2;

Procedure 2

 public static void main(String[] args) throws IllegalAccessException {
        System.out.println(test11());
    }

    private static List test11() {
        List<String> list = new ArrayList<String>();
        try{
            list.add("step try");
            System.out.println("try block");
            return list;
        }catch(Exception e){
            list.add("step catch");
            System.out.println("catch block");
            return list;
        }finally{
            list.add("step finally");
            System.out.println("finally block ");
        }

    }
 Operation results:
try block
finally block 
[step try, step finally]
  • Why is there step finally in the last printed content?
    The reference used by objects in Java, i.e. list, stores the memory address. Adding data to the list actually operates on the data in the memory address. The reference address of list will not change.
  • Take another look at the decompiled code
 public static void main(String[] args) throws IllegalAccessException {
        System.out.println(test11());
    }

    private static List test11() {
        ArrayList list = new ArrayList();

        ArrayList var2;
        try {
            list.add("step try");
            System.out.println("try block");
            ArrayList var1 = list;
            return var1;
        } catch (Exception var6) {
            list.add("step catch");
            System.out.println("catch block");
            var2 = list;
        } finally {
            list.add("step finally");
            System.out.println("finally block ");
        }

        return var2;
    }

The var1 variable stores the reference address. Therefore, the printed content will have step finanly content.

Topics: Java jvm string